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

Fix for the heuristics that recognizes immutable classes #1603

Closed

Conversation

baloghadamsoftware
Copy link
Contributor

Class names containting the substring "immutable" are considered now immutable.
Fixing issue #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

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.

Please resolve a conflict in the CHANGELOG.md file.

And I personally feel that it's not good to use class/package name to verify its immutability.
The target classes could be implemented by coder who has less knowledge about immutability, and they may use .*immutable.* as a magic word to remove "noisy" warning made by SpotBugs... :\

@baloghadamsoftware
Copy link
Contributor Author

And I personally feel that it's not good to use class/package name to verify its immutability.
The target classes could be implemented by coder who has less knowledge about immutability, and they may use .*immutable.* as a magic word to remove "noisy" warning made by SpotBugs... :\

I agree with you, coders may misuse this heuristic. I do not know how often that happens, actually I never thought that someone would intentionally silence a true positive just because of laziness. Now I changed it to only look for the word "immutable" in the class name but not in the package name. Is it OK? If not, then I can simply put the signatures of the Guava immutable classes into the list of know immutables. (However, this does not protect against false positives coming from other APIs.)

@KengoTODA
Copy link
Member

Now I changed it to only look for the word "immutable" in the class name but not in the package name. Is it OK?

Not sure. I personally think it's not enough and better to use the signatures of Guava immutable classes, but let's wait opinions from other community members.

@baloghadamsoftware
Copy link
Contributor Author

Now I changed it to only look for the word "immutable" in the class name but not in the package name. Is it OK?

Not sure. I personally think it's not enough and better to use the signatures of Guava immutable classes, but let's wait opinions from other community members.

OK, I will put the Guava signatures into the list for now. However, it came to my mind that it is much easier for a developer to use a @immutable annotation than change the name of the class or the package. And we must support this annotation to avoid false positives reported by the users. Thus I do not think that many would rename their classes or packages to forcefully silence false positives.

@ThrawnCA
Copy link
Contributor

ThrawnCA commented Jul 8, 2021

I think there's too much scope for false positives. Just within the past few months, I overrode an ImmutableMultiDict class to enable a single limited operation for a specific reason, and named the result MostlyImmutableMultiDict. Using this heuristic, my subclass would be treated as immutable, when it shouldn't be.

@rkraneis
Copy link

rkraneis commented Jul 9, 2021

Should immutable classes from java.time package be added as well? Since 4.3.0 we get EI_EXPOSE_REP for those, too ...
(java.time package description reads “[...] All the classes are immutable and thread-safe. [...]”)

@baloghadamsoftware
Copy link
Contributor Author

Should immutable classes from java.time package be added as well? Since 4.3.0 we get EI_EXPOSE_REP for those, too ...
(java.time package description reads “[...] All the classes are immutable and thread-safe. [...]”)

Sure. I will update this PR to include them.

@callum-kilby
Copy link

Just checking in, what is the status of this request? There's other reporters with the same issue, with some additional suggestions for checking for immutability

Ádám Balogh added 3 commits July 15, 2021 13:20
Class names containting the substring "immutable" are considered now immutable.
Fixing issue spotbugs#1601.
…ckage

name. Also added a check for an annotation ending with "Immutable".
@baloghadamsoftware
Copy link
Contributor Author

baloghadamsoftware commented Jul 15, 2021

Should immutable classes from java.time package be added as well? Since 4.3.0 we get EI_EXPOSE_REP for those, too ...
(java.time package description reads “[...] All the classes are immutable and thread-safe. [...]”)

Sure. I will update this PR to include them.

I think there's too much scope for false positives. Just within the past few months, I overrode an ImmutableMultiDict class to enable a single limited operation for a specific reason, and named the result MostlyImmutableMultiDict. Using this heuristic, my subclass would be treated as immutable, when it shouldn't be.

What about Detecting *.Immutable*? I think there is hardly anyone who calls a class ImmutableLikeXXX.

Balogh, Ádám and others added 2 commits July 15, 2021 13:52
…Test.java

Co-authored-by: Kengo TODA <skypencil+github@gmail.com>
@baloghadamsoftware
Copy link
Contributor Author

@KengoTODA & @ThrawnCA For now I only added the Guava immutable classes, the java.date package and the @immutable annotation.

@baloghadamsoftware
Copy link
Contributor Author

Just checking in, what is the status of this request? There's other reporters with the same issue, with some additional suggestions for checking for immutability

Where are they? I searched in the issues and discussions, but I could not find any. It would be good to read them and include all the classes they claims immutable into the list of known immutable classes.

@callum-kilby
Copy link

Just checking in, what is the status of this request? There's other reporters with the same issue, with some additional suggestions for checking for immutability

Where are they? I searched in the issues and discussions, but I could not find any. It would be good to read them and include all the classes they claims immutable into the list of known immutable classes.

I'm looking at the other comments in my issue:
#1601

It's mostly java.time classes, but also Java.lang.Class. There's also some discussion on checking for a jdk.internal.ValueBased annotation, or providing the previous behaviour for these checks.

@callum-kilby
Copy link

Is it possible to include an additional whitelist that is set as a plugin configuration option? It would let users specify immutable classes that they have defined in their project without asking them to be added to the spotbugs whitelist

@baloghadamsoftware
Copy link
Contributor Author

With the last modification it seems that we finally got rid of all false positives. Now all the findings are true positives. However, there are quite many of them so I would propose changing the priority of all the findings of the FindReturnRef detector to low.

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.

This PR does not fix existing false positive, it introduces many new false negatives and it won't benefit most of Java developers.


private static final List<String> SETTER_LIKE_NAMES = Arrays.asList(
"set", "put", "add", "insert", "delete", "remove", "erase", "clear", "push", "pop",
"enqueue", "dequeue", "write", "append", "replace");
"enqueue", "dequeue", "append", "replace");
Copy link
Member

Choose a reason for hiding this comment

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

This change is backward compatible and will affect many users.
So personally I don't want to merge this change to SpotBugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean the omission of "write" from the list? Actually, I found that methods called write...() are usually no setters but they write to a stream.

return false;
}

// Non-public setters cannot be used from outside the package so disregard them.
Copy link
Member

Choose a reason for hiding this comment

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

I think this change is not so natural. The package affect nothing to the immutability of class architecture.

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 can be disputed. If you whish, I can change this part to only omit methods which are private or protected.

@baloghadamsoftware
Copy link
Contributor Author

Thank you, for your review @KengoTODA!

This PR does not fix existing false positive, it introduces many new false negatives and it won't benefit most of Java developers.

Which part, do you mean? Actually, this PR is quite complex and contains different fixes. Originally, I implemented a heuristics to replace the placeholder (which was just a list of 4 mutable classes and all the rest were considered as immutable) in PR #1533. However, my heuristics was incomplete and contained some bugs. It caused tons of findings, especially in the FindReturnRef detector, and some of them were false positives. Users reported them here: #1601.

In this patch I tried to fix these issues and also made a long and tiring manual research to find all the possible false positives and fix the heuristics to not to report them. The fixes in this PR:

  1. Static methods are surely no setters, because they cannot write instance fields. I fixed this in this PR.

  2. Setter-like methods which throw UnsupportedOperationException are no setters. I fixed this in this PR.

  3. Private methods are no setters. If there are no public setters, then the only usage of private methods is in the constructor or block initializers. This means that the class is still immutable. What could be disputed here is the case of package-public methods. In this PR I decided to omit them, but if you think it is wrong then I can include them.

  4. When checking the findigs I noticed that methods beginning with the word write are usually no setters, but stream output methods. For this reason I removed this tag from the list in this PR.

  5. There are methods with name set...(), add...() etc which create a new object instead of modifying the current one. In the original heuristics I tried to detect them by comparing their result type with the class they are defined in, but for some reason it did not work as I expected. (I forgot to test them explicitly.) Furthermore, I found cases where they return a different class type but do not modify the this object. For this reason I changed this part of the algorithm in this PR to disregard methods which return any object type. This can be disputed, of course, if you do not agree.

  6. For performance reasons (and also because the heuristics was too weak initially) I also had a fixed list of immutable classes. In this PR I extended this list with e.g. Guava immutable classes.

  7. There are packages in the Java API that contain immutable classes only. Instead of putting these classes into the previous list one-by-one, I created a new list for these packages in this PR.

The result is that maybe we have some additional false negatives but all the false positives disappeared.

@candrews
Copy link
Contributor

Here's a (simple) PR that fixes some incorrect class names in MutableClasses and adds a missing one (java.net.Inet6Address): #1654

@candrews
Copy link
Contributor

This PR fixes #1634

@baloghadamsoftware
Copy link
Contributor Author

This pull request is now split up to #1670, #1671, #1672, #1673, #1674, #1676, #1677 and #1678.

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.

6 participants