diff --git a/CHANGELOG.md b/CHANGELOG.md index a5c8c92c..6fe368da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- [#122](https://github.com/green-code-initiative/creedengo-java/issues/122) GCI82 - remove false positive triggered on pattern variables of `instanceof`, record patterns and `switch` patterns (e.g. `if (o instanceof final String s)`) - [#69](https://github.com/green-code-initiative/creedengo-java/issues/69) correction of NullPointer in GCI79 rule + technical refactoring of GCI79 - update integration tests system to use the new component "creedengo-integration-test" - compatibility updates for SonarQube up to 26.2.0 diff --git a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/GCI82/MakeNonReassignedVariablesConstants.java b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/GCI82/MakeNonReassignedVariablesConstants.java index dc192cdd..693af408 100644 --- a/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/GCI82/MakeNonReassignedVariablesConstants.java +++ b/src/it/test-projects/creedengo-java-plugin-test-project/src/main/java/org/greencodeinitiative/creedengo/java/checks/GCI82/MakeNonReassignedVariablesConstants.java @@ -41,6 +41,14 @@ public void parameterReassigned(String reassigned) { logger.info(reassigned); } + public void parameterNotReassignedInstance() { + final Object o = new Object(); + // instanceof with final keyword - Compliant (pattern variable is skipped) + if (o instanceof final String k) { + logger.info(k); + } + } + public void parameterNotReassigned(final String notReassigned) { logger.info(notReassigned); } @@ -107,13 +115,13 @@ void reassignedInMethod() { String varDefinedInMethodReassignedInMethod = "0"; // Compliant String varDefinedInMethodInFinalMethod = "0"; // Noncompliant {{The variable is never reassigned and can be 'final'}} String varDefinedInMethodNotReassignedInMethod = "0"; // Compliant (the String was passed as a non-final parameter to the method) - this.parameterReassigned(varDefinedInMethodReassignedInMethod); this.parameterReassigned(this.varDefinedInClassReassignedInMethod); this.parameterNotReassigned(varDefinedInMethodInFinalMethod); this.parameterNotReassigned(this.varDefinedInClassInFinalMethod); this.parameterNotReassignedNotFinal(varDefinedInMethodNotReassignedInMethod); this.parameterNotReassignedNotFinal(this.varDefinedInClassNotReassignedInMethod); + this.parameterNotReassignedInstance();// Compliant } void reassignedInConstructor(){ diff --git a/src/main/java/org/greencodeinitiative/creedengo/java/checks/MakeNonReassignedVariablesConstants.java b/src/main/java/org/greencodeinitiative/creedengo/java/checks/MakeNonReassignedVariablesConstants.java index 495bc4e2..bbcecdb0 100644 --- a/src/main/java/org/greencodeinitiative/creedengo/java/checks/MakeNonReassignedVariablesConstants.java +++ b/src/main/java/org/greencodeinitiative/creedengo/java/checks/MakeNonReassignedVariablesConstants.java @@ -25,6 +25,16 @@ public List nodesToVisit() { @Override public void visitNode(@Nonnull Tree tree) { VariableTree variableTree = (VariableTree) tree; + + // Issue #122 : skip pattern variables (instanceof pattern, record pattern, switch pattern). + // Pattern variables are scope-limited to their branch and cannot meaningfully be made + // "more final". SonarJava does not consistently expose the `final` modifier on pattern + // variables, which would produce a false positive when the user has explicitly written + // `final` — e.g. `if (o instanceof final String s)`. + if (isPatternVariable(variableTree)) { + return; + } + if (LOGGER.isDebugEnabled()) { LOGGER.debug("Variable > {}", getVariableNameForLogger(variableTree)); LOGGER.debug(" => isNotFinalAndNotStatic(variableTree) = {}", isNotFinalAndNotStatic(variableTree)); @@ -39,6 +49,21 @@ public void visitNode(@Nonnull Tree tree) { } } + /** + * Returns {@code true} when the given variable is a pattern variable, i.e. it is the variable + * bound by a type pattern in: + * + * In all those cases, the {@code VariableTree} is the direct child of a {@code TypePatternTree}. + */ + private static boolean isPatternVariable(VariableTree variableTree) { + Tree parent = variableTree.parent(); + return parent != null && parent.is(Kind.TYPE_PATTERN); + } + private static boolean isNotReassigned(VariableTree variableTree) { return variableTree.symbol() .usages()