diff --git a/pom.xml b/pom.xml
index fa0c7e9..d788256 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,7 @@
1.7
- 1.5.1
+ 1.5.5-SNAPSHOT
diff --git a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java
index f4ef343..d75b2cf 100644
--- a/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java
+++ b/src/main/java/fr/greencodeinitiative/java/JavaCheckRegistrar.java
@@ -33,6 +33,7 @@
import fr.greencodeinitiative.java.checks.FreeResourcesOfAutoCloseableInterface;
import fr.greencodeinitiative.java.checks.IncrementCheck;
import fr.greencodeinitiative.java.checks.InitializeBufferWithAppropriateSize;
+import fr.greencodeinitiative.java.checks.MakeVariableConstant;
import fr.greencodeinitiative.java.checks.NoFunctionCallWhenDeclaringForLoop;
import fr.greencodeinitiative.java.checks.OptimizeReadFileExceptions;
import org.sonar.plugins.java.api.CheckRegistrar;
@@ -62,7 +63,8 @@ public class JavaCheckRegistrar implements CheckRegistrar {
InitializeBufferWithAppropriateSize.class,
AvoidSetConstantInBatchUpdate.class,
FreeResourcesOfAutoCloseableInterface.class,
- AvoidMultipleIfElseStatement.class
+ AvoidMultipleIfElseStatement.class,
+ MakeVariableConstant.class
);
/**
diff --git a/src/main/java/fr/greencodeinitiative/java/checks/MakeVariableConstant.java b/src/main/java/fr/greencodeinitiative/java/checks/MakeVariableConstant.java
new file mode 100644
index 0000000..d2da3cc
--- /dev/null
+++ b/src/main/java/fr/greencodeinitiative/java/checks/MakeVariableConstant.java
@@ -0,0 +1,136 @@
+/*
+ * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs
+ * Copyright © 2023 Green Code Initiative (https://www.ecocode.io)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package fr.greencodeinitiative.java.checks;
+
+import org.sonar.check.Rule;
+import org.sonar.java.model.ExpressionUtils;
+import org.sonar.java.model.JavaTree;
+import org.sonar.java.model.ModifiersUtils;
+import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
+import org.sonar.plugins.java.api.tree.*;
+import org.sonar.plugins.java.api.tree.Tree.Kind;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+import java.util.function.Predicate;
+
+@Rule(key = "EC82")
+public class MakeVariableConstant extends IssuableSubscriptionVisitor {
+
+ protected static final String MESSAGE_RULE = "Make variable constant";
+
+ @Override
+ public List nodesToVisit() {
+ return List.of(Kind.VARIABLE);
+ }
+
+ @Override
+ public void visitNode(@Nonnull Tree tree) {
+ VariableTree variableTree = (VariableTree) tree;
+ System.out.println("Variable > " + variableTree.simpleName().name());
+ System.out.println(" => isNotFinalAndNotStatic(variableTree) = " + isNotFinalAndNotStatic(variableTree));
+ System.out.println(" => hasConstantInitializer(variableTree) = " + hasConstantInitializer(variableTree));
+ System.out.println(" => usages = " + variableTree.symbol().usages().size());
+ System.out.println(" => isNotReassigned = " + isNotReassigned(variableTree));
+
+ if (isNotFinalAndNotStatic(variableTree) && hasConstantInitializer(variableTree) && isNotReassigned(variableTree)) {
+ reportIssue(tree, MESSAGE_RULE);
+ } else {
+ super.visitNode(tree);
+ }
+ }
+
+ private static boolean isNotReassigned(VariableTree variableTree) {
+ return variableTree.symbol()
+ .usages()
+ .stream()
+ .noneMatch(MakeVariableConstant::parentIsAssignment);
+ }
+
+ private static boolean parentIsAssignment(Tree tree) {
+ return parentIsKind(tree, Kind.ASSIGNMENT);
+ }
+
+ private static boolean parentIsKind(Tree tree, Kind kind) {
+ Tree parent = tree.parent();
+ return parent != null && parent.is(kind);
+ }
+
+ private static boolean isNotFinalAndNotStatic(VariableTree variableTree) {
+ return ModifiersUtils.hasNoneOf(variableTree.modifiers(), Modifier.FINAL, Modifier.STATIC);
+ }
+
+ // Reprise de https://github.com/SonarSource/sonar-java/blob/master/java-checks/src/main/java/org/sonar/java/checks/ConstantsShouldBeStaticFinalCheck.java
+ private static boolean hasConstantInitializer(VariableTree variableTree) {
+ ExpressionTree init = variableTree.initializer();
+ if (init == null) {
+ return false;
+ }
+
+ var deparenthesized = ExpressionUtils.skipParentheses(init);
+
+ if (deparenthesized instanceof MethodReferenceTree && isInstanceIdentifier(((MethodReferenceTree) deparenthesized).expression())) {
+ return false;
+ }
+ return !containsChildMatchingPredicate((JavaTree) deparenthesized, MakeVariableConstant::isNonStaticOrFinal);
+ }
+
+ private static boolean isNonStaticOrFinal(Tree tree) {
+ switch (tree.kind()) {
+ case METHOD_INVOCATION:
+ case NEW_CLASS:
+ case NEW_ARRAY:
+ case ARRAY_ACCESS_EXPRESSION:
+ return true;
+ case IDENTIFIER:
+ String name = ((IdentifierTree) tree).name();
+ if ("super".equals(name) || "this".equals(name)) {
+ return true;
+ } else {
+ var symbol = ((IdentifierTree) tree).symbol();
+ return symbol.isVariableSymbol() && !(symbol.isStatic() && symbol.isFinal());
+ }
+ default:
+ return false;
+ }
+ }
+
+ private static boolean isInstanceIdentifier(Tree expression) {
+ if (!expression.is(Tree.Kind.IDENTIFIER)) {
+ return false;
+ }
+ IdentifierTree identifierTree = (IdentifierTree) expression;
+ return identifierTree.symbol().isStatic();
+ }
+
+ private static boolean containsChildMatchingPredicate(JavaTree tree, Predicate predicate) {
+ if (predicate.test(tree)) {
+ return true;
+ }
+ if (!tree.isLeaf()) {
+ for (Tree javaTree : tree.getChildren()) {
+ if (javaTree != null && containsChildMatchingPredicate((JavaTree) javaTree, predicate)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+}
diff --git a/src/test/files/MakeVariableConstant.java b/src/test/files/MakeVariableConstant.java
new file mode 100644
index 0000000..aa5510d
--- /dev/null
+++ b/src/test/files/MakeVariableConstant.java
@@ -0,0 +1,54 @@
+/*
+ * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs
+ * Copyright © 2023 Green Code Initiative (https://www.ecocode.io)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+public class MakeVariableConstant {
+ private static final int constant = 0; // ok
+ private int varDefinedInClassNotReassigned = 0; // Noncompliant {{Make variable constant}}
+ private int varDefinedInClassReassigned = 0; // ok
+
+ void changeVarDefinedInClassReassigned() {
+ varDefinedInClassReassigned = 1;
+ System.out.println("varDefinedInClassReassigned = " + varDefinedInClassReassigned);
+ System.out.println("varDefinedInClassNotReassigned = " + varDefinedInClassNotReassigned);
+ System.out.println("constant = " + constant);
+
+ }
+
+ void simpleMethod() {
+ String varDefinedInMethodNotReassigned = "hello"; // Noncompliant {{Make variable constant}}
+ String varDefinedInMethodReassigned = "hello"; // ok
+ varDefinedInMethodReassigned = "bye";
+
+ System.out.println("varDefinedInMethodNotReassigned = " + varDefinedInMethodNotReassigned);
+ System.out.println("varDefinedInMethodReassigned = " + varDefinedInMethodReassigned);
+ }
+
+ /*
+ void methodWithFor() {
+ double varDefinedInMethodForNotReassigned = 1.0d; // Noncompliant {{Make variable constant}}
+ double varDefinedInMethodForReassigned = 100.0d; // ok
+
+ for (int i = 0; i < 10; i++) {
+ varDefinedInMethodForReassigned += i;
+ }
+
+ System.out.println("varDefinedInMethodForNotReassigned = " + varDefinedInMethodForNotReassigned);
+ System.out.println("varDefinedInMethodForReassigned = " + varDefinedInMethodForReassigned);
+ }
+ */
+
+}
\ No newline at end of file
diff --git a/src/test/java/fr/greencodeinitiative/java/checks/MakeVariableConstantTest.java b/src/test/java/fr/greencodeinitiative/java/checks/MakeVariableConstantTest.java
new file mode 100644
index 0000000..ba9ff11
--- /dev/null
+++ b/src/test/java/fr/greencodeinitiative/java/checks/MakeVariableConstantTest.java
@@ -0,0 +1,41 @@
+/*
+ * ecoCode - Java language - Provides rules to reduce the environmental footprint of your Java programs
+ * Copyright © 2023 Green Code Initiative (https://www.ecocode.io)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package fr.greencodeinitiative.java.checks;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.java.checks.verifier.CheckVerifier;
+
+class MakeVariableConstantTest {
+
+ @Test
+ void test() {
+ CheckVerifier.newVerifier()
+ .onFile("src/test/files/MakeVariableConstant.java")
+ .withCheck(new MakeVariableConstant())
+ .verifyIssues();
+ }
+
+ @Test
+ void testConstantsShouldBeStaticFinalCheck() {
+ CheckVerifier.newVerifier()
+ .onFile("src/test/files/ConstantsShouldBeStaticFinalCheck.java")
+ .withCheck(new MakeVariableConstant())
+ .verifyIssues();
+ }
+
+}
\ No newline at end of file