Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude methods throwing UnsupportedOperationException from setter-like methods when considering a class as immutable #1672

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

baloghadamsoftware
Copy link
Contributor

Methods throwing UnsupportedOperationException must not be considered as setters, even if their name is setter-like.

Partial fix for issue [#1601](https://github.com/spotbugs/spotbugs/issues/1601\)


Make sure these boxes are checked before submitting your PR -- thank you!

  • Added an entry into CHANGELOG.md if you have changed SpotBugs code

…like methods when considering a class as immutable

Methods throwing `UnsupportedOperationException` must not be considered as setters, even if their name is setter-like.

Partial fix for issue [spotbugs#1601](spotbugs#1601)
ThrawnCA
ThrawnCA previously approved these changes Sep 2, 2021
@@ -54,6 +54,10 @@ public int getN() {
public Immutable setN(int n) {
return new Immutable(n);
}

public void setNUnsupported(int n) throws UnsupportedOperationException {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure - would it work without declaration of UnsupportedOperationException being thrown?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, because we cannot check here the body of the function. IMO UnsupportedOperationException being thrown is better to be declared if the whole function call is not supported in this class.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can method declare to throw UnsupportedOperationException and modify state anyway? And even not throw anything at all?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the end, if code deliberately tries to mask bad behaviour, it will probably succeed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is just a heuristics to find immutable classes. If you declare a function as a thrower but modify the state then this is a setter and the class is not immutable. The heuristics cannot be 100% accurate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it lead to false-negatives?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course. As I mentioned, this is a heuristics. Unfortunately, unlike C++'s const member functions, Java has no means that forbids a method to modify the attributes of the class instance. Instead, we try to find methods which look like setters. If we do not find any then we consider the class for immutable. However, if the methods are named in a foreign language, or the setters are ill named (like eg. foo()), then we consider the class as immutable which leads to false negatives.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thank you for your explanation.

Copy link
Contributor

@ThrawnCA ThrawnCA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs conflict resolved before this can be properly reviewed.

ThrawnCA
ThrawnCA previously approved these changes Sep 17, 2021
Copy link
Member

@KengoTODA KengoTODA left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThrawnCA I think this change isn't so valuable and better to keep our implementation simple. No matter how methods are implemented, just check fields and the class are final or not then it should be enough. I know that the current SpotBugs implementation is not like what I mean, but it's better to stop making it far from the ideal state.

And even if this change works, UnsupportedOperationException is an unchecked exception so ExceptionTable could have no record for the exception. So I think this PR is not reasonable nor practical.

It's just my personal opinion. 🙇‍♂️
How do you think? Is it still better to merge this PR? Thanks in advance!

@baloghadamsoftware
Copy link
Contributor Author

You are right, UnsupportedOperationException is an unchecked exception. This means that we should not look at the exception specification but the body of the function: if it consists of a single throw of this exception then it is definitely no setter. This is the way the Guava classes work, because they extend java.util.AbstractCollection which already has setters such as add() and remove(). The question is how can we check the body statically? We must somehow compare the body to the byte code of single line throw new UnsuppoertedOperationException.

Although Guava classes are considered as immutable explicitly, we cannot be sure that there are no similar classes in a user's code. Thus I think that this PR is useful to reduce the number of false positives but not in its current format but after rewriting it to check the body of the function instead of its exception specification.

@jochenberger
Copy link

IIUC, this can be unblocked, i.e. the requested changes are no longer required.

@baloghadamsoftware
Copy link
Contributor Author

@KengoTODA?

@github-actions github-actions bot added the Stale label Mar 5, 2024
@github-actions github-actions bot closed this Apr 5, 2024
@hazendaz hazendaz reopened this Apr 12, 2024
@github-actions github-actions bot removed the Stale label Apr 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants