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

Introduce NonStaticImport check #450

Merged
merged 20 commits into from
Oct 26, 2023
Merged

Introduce NonStaticImport check #450

merged 20 commits into from
Oct 26, 2023

Conversation

cernat-catalin
Copy link
Contributor

@cernat-catalin cernat-catalin commented Jan 6, 2023

Introduce a rule that flags bad static imports (complements the existing StaticImport rule).

BadStaticImport might not be the best name but it was in line with com.google.errorprone.bugpatterns.BadImport.

Approach
Initially I tried matching static imports and then finding the relevant identifiers but that seemed too complicated 🧶 . After many rewrites matching method invocations and identifiers seemed to work (and was simpler than expected) though I'm not sure if this covers all cases or if there are false positives. I'm also not sure what (if any) is the performance penalty for matching on identifiers.

Static imports and bad static imports
Since these two rules need to coexist they need to be mutually exclusive (otherwise bad things could happen like the order in which are applied would matter). As far as I can tell, there are multiple ways to do this depending on how much flexibility we want to allow when defining exceptions (i.e. exempted members).

I've tried to allow the rules to be as flexible as possible so the following should be possible to specify:

  1. All identifiers from type X except A,B,C... should be statically imported
  2. All identifiers from type X except A,B,C... should not be statically imported

The mutual exclusion is guaranteed by the tests. Consider the following notation:

  • T.* Types that should be statically imported (defined in StaticImport)
  • T.ID Type identifier pairs that should be statically imported (defined in StaticImport)
  • T'.* Types that should not be statically imported (defined in BadStaticImport)
  • T'.ID' Type identifier that should not be statically imported (defined in BadStaticImport)
  • *.ID' Identifiers that should not be statically imported (defined in BadStaticImport)

In StaticImport we allow all type identifier pairs from this set: (T.* \ T'.ID' \ *.ID') U T.ID
In BadStaticImport we allow all type identifier pairs from this set: (T'.* \ T.ID) U T'.ID' U *.ID'
In the tests we also enforce the following which should guarantee the mutual exclusion of the above defined sets (^ is intersection):

  • T.* ^ T'.* = empty
  • T.ID ^ T'.ID' = empty
  • T.ID ^ *.ID' = empty

There are still some comments to resolve plus candidates and tests to be added (will get to it in some subsequent commits).

@github-actions
Copy link

github-actions bot commented Jan 6, 2023

  • Surviving mutants in this change: 5
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.BadStaticImport 5 28
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@github-actions
Copy link

github-actions bot commented Jan 6, 2023

  • Surviving mutants in this change: 7
  • Killed mutants in this change: 34
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.BadStaticImport 7 29
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@github-actions
Copy link

github-actions bot commented Jan 6, 2023

  • Surviving mutants in this change: 13
  • Killed mutants in this change: 37
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.BadStaticImport 13 32
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@cernat-catalin cernat-catalin changed the title Introduce BugPattern for disallowing static imports Introduce BugPattern for disallowing specified static imports Jan 6, 2023
@cernat-catalin cernat-catalin force-pushed the cernat-catalin/static-import branch from 679f597 to 56ad071 Compare January 9, 2023 12:03
@github-actions
Copy link

github-actions bot commented Jan 9, 2023

  • Surviving mutants in this change: 13
  • Killed mutants in this change: 37
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.BadStaticImport 13 32
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie
Copy link
Member

rickie commented Jan 9, 2023

Hey @cernat-catalin, thanks for opening the PR 🚀 ! We have a GitHub Action that runs Pitest on the changes of the PR. This helps identifying edge cases that are not tested. Can you look at the comments the bot placed on the changes in the Files changed tab? If you need help interpreting the results, let me know!

@github-actions
Copy link

github-actions bot commented Jan 9, 2023

  • Surviving mutants in this change: 3
  • Killed mutants in this change: 53
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.BadStaticImport 3 48
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@cernat-catalin
Copy link
Contributor Author

@rickie Pitest is lit! It's the first time I encounter it but it was really helpful.

There still are three mutations on some conditionals. I could remove them (and suppress a warning generated by nullaway) but I have a feeling that they are useful however I can't find any examples 😞 .

@Stephan202
Copy link
Member

I might wait for @rickie to do a first review round (I'm quite swamped atm), but let me say: great PR description! ❤️

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Added two commits.

Till now I have mostly placed comments and improvements on the style. I needed to do a round of that first before I can dive in a little more. I took some time to get a full picture of the check.

So will have to do another round some other time. Already left some comments. Let me know if you have any questions :).

private Description getDescription(IdentifierTree tree, VisitorState state) {
Symbol symbol = ASTHelpers.getSymbol(tree);
SuggestedFix.Builder builder =
SuggestedFix.builder().removeStaticImport(getImportToRemove(symbol));
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I'm curious if we can always do this 🤔. Removing an import can be dangerous. This is usually a task for the RemoveUnusedImport check in a subsequent run.

However, I'm thinking that we can actually get away with this one. What if there are two identifiers statically imported that we update, is there a possibility that one of the two cannot be imported 🤔. If there is a possibility that can happen, we cannot remove the static import.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good question. I'm thinking the following:

Say we have an identifier X that with two occurrences (or more) in the same file. IIUC, the situation we want to avoid is the isMatch method returning true in one place and false in another.

However, for that to happen, one of the following must return different results for the occurrences:

  • symbol.owner != TYP Can't have different owners
  • isDefinedInThisFile(...) They can't be defined in different files
  • isIdentifierStaticallyImported(...) Since it is the same identifier we will have the same result
  • isExempted and isCandidate Will return the same results

I guess it boils down to the fact that we never use the surrounding context of the identifier to make a decision. WDYT?

".", symbol.getEnclosingElement().getQualifiedName(), symbol.getSimpleName());
}

private static boolean isMatch(IdentifierTree tree, VisitorState state) {
Copy link
Member

Choose a reason for hiding this comment

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

Hmm isMatch should have a more descriptive name. Will check this out later. Or feel free to propose something.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hard one as it is a composition of multiple conditions. Maybe something like isValidCandidate. Still a bit vague and clashes somewhat with isCandidate.

@github-actions
Copy link

  • Surviving mutants in this change: 1
  • Killed mutants in this change: 51
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 46
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Another thought.

private static String getImportToRemove(Symbol symbol) {
return String.join(
".",
requireNonNull(symbol.getEnclosingElement()).getQualifiedName(),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
requireNonNull(symbol.getEnclosingElement()).getQualifiedName(),
symbol.owner.getQualifiedName(),

Now that I think of it, we can probably just use owner directly. That also makes me wonder if we should call it "enclosingSymbol" further down, that's probably not the correct term 🤔.

@github-actions
Copy link

  • Surviving mutants in this change: 1
  • Killed mutants in this change: 49
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 44
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

2 similar comments
@github-actions
Copy link

  • Surviving mutants in this change: 1
  • Killed mutants in this change: 49
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 44
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@github-actions
Copy link

  • Surviving mutants in this change: 1
  • Killed mutants in this change: 49
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 44
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@cernat-catalin
Copy link
Contributor Author

Thanks @rickie for the review! Will try to go over it later today (and see if I can spot any other things as well).

@cernat-catalin
Copy link
Contributor Author

Rebased, added some more identifiers and 🔫 all of the surviving mutants (with a somewhat weird example).

@github-actions
Copy link

Looks good. All 50 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5
🎉tech.picnic.errorprone.bugpatterns.NonStaticImport 0 45

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@cernat-catalin cernat-catalin requested a review from rickie March 23, 2023 17:49
@cernat-catalin
Copy link
Contributor Author

@rickie @Stephan202 Is this something that we still want?

@rickie
Copy link
Member

rickie commented Mar 24, 2023

@rickie @Stephan202 Is this something that we still want?

Hi @cernat-catalin, yes definitely. Indeed, there is not a lot of activity from our side on this PR lately. There are a few (relatively) big PRs open in EPS that require quite some time + deep focus to review. The fact we didn't review recently does not mean the PR is not good.

We are aware of the PR andit will be reviewed, given our schedules it's hard to give an estimation. All I can say is that, this still a really nice PR and we definitely didn't forget about it.

@cernat-catalin
Copy link
Contributor Author

Thanks for the answer @rickie ! No rush from my side and I understand there is a lot of work to be done and not that many people that can do it. Glad to see that it is still on your radar!

@rickie rickie force-pushed the cernat-catalin/static-import branch from 4a9350b to bf21b9f Compare October 26, 2023 08:47
@rickie
Copy link
Member

rickie commented Oct 26, 2023

Rebased and added a commit with the revert, will merge once 🟢 !

@rickie rickie force-pushed the cernat-catalin/static-import branch from bf21b9f to 6fea4d8 Compare October 26, 2023 08:48
@github-actions
Copy link

  • Surviving mutants in this change: 3
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport$1 2 5
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 23
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
@github-actions
Copy link

  • Surviving mutants in this change: 3
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport$1 2 5
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 23
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@sonarqubecloud
Copy link

Please retry analysis of this Pull-Request directly on SonarCloud.

@github-actions
Copy link

  • Surviving mutants in this change: 3
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport$1 2 5
🧟tech.picnic.errorprone.bugpatterns.NonStaticImport 1 23
🎉tech.picnic.errorprone.bugpatterns.StaticImport 0 5

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie merged commit a435b65 into master Oct 26, 2023
17 checks passed
@rickie rickie deleted the cernat-catalin/static-import branch October 26, 2023 09:00
@Stephan202 Stephan202 changed the title Introduce BugPattern for disallowing specified static imports Introduce NonStaticImport check Feb 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

3 participants