Skip to content

Commit 95da5fe

Browse files
committed
SONARJAVA-5657 S6541: use NODV metric instead of NOAV
1 parent 34bfb95 commit 95da5fe

7 files changed

Lines changed: 38 additions & 28 deletions

File tree

java-checks/src/main/java/org/sonar/java/checks/design/BrainMethodCheck.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ public class BrainMethodCheck extends IssuableSubscriptionVisitor implements End
5959
@RuleProperty(key = "nestingThreshold", description = "The maximum nesting level allowed.", defaultValue = "" + DEFAULT_NESTING_THRESHOLD)
6060
public int nestingThreshold = DEFAULT_NESTING_THRESHOLD;
6161

62-
@RuleProperty(key = "noavThreshold", description = "The maximum number of accessed variables allowed.", defaultValue = "" + DEFAULT_VARIABLES_THRESHOLD)
63-
public int noavThreshold = DEFAULT_VARIABLES_THRESHOLD;
62+
@RuleProperty(key = "nodvThreshold", description = "The maximum number of defined local variables allowed.", defaultValue = "" + DEFAULT_VARIABLES_THRESHOLD)
63+
public int nodvThreshold = DEFAULT_VARIABLES_THRESHOLD;
6464

6565
@Override
6666
public List<Tree.Kind> nodesToVisit() {
@@ -84,19 +84,19 @@ public void visitNode(Tree tree) {
8484
int cyclomaticComplexity = metricsComputer.getComplexityNodes(methodTree).size();
8585
int maxNestingLevel = metricsComputer.getMethodNestingLevel(methodTree);
8686
int linesOfCode = metricsComputer.getLinesOfCode(methodTree.block());
87-
int numberOfAccessedVariables = metricsComputer.getNumberOfAccessedVariables(methodTree);
87+
int numberOfDefinedVariables = metricsComputer.getNumberOfDefinedVariables(methodTree);
8888

8989
if (linesOfCode >= locThreshold &&
9090
cyclomaticComplexity >= cyclomaticThreshold &&
9191
maxNestingLevel >= nestingThreshold &&
92-
numberOfAccessedVariables >= noavThreshold) {
92+
numberOfDefinedVariables >= nodvThreshold) {
9393

94-
int brainScore = numberOfAccessedVariables + cyclomaticComplexity + maxNestingLevel * linesOfCode;
94+
int brainScore = numberOfDefinedVariables + cyclomaticComplexity + maxNestingLevel * linesOfCode;
9595
String issueMessage = String.format(ISSUE_MESSAGE,
9696
linesOfCode, locThreshold - 1,
9797
cyclomaticComplexity, cyclomaticThreshold - 1,
9898
maxNestingLevel, nestingThreshold - 1,
99-
numberOfAccessedVariables, noavThreshold - 1);
99+
numberOfDefinedVariables, nodvThreshold - 1);
100100

101101
AnalyzerMessage analyzerMessage = new AnalyzerMessage(this, context.getInputFile(),
102102
AnalyzerMessage.textSpanFor(methodTree.simpleName()), issueMessage, 0);

java-checks/src/test/java/org/sonar/java/checks/design/BrainMethodCheckTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ void testHighComplexityFileWithHigherThresholds() {
4040
var check = new BrainMethodCheck();
4141

4242
check.locThreshold = 120;
43-
check.noavThreshold = 36;
43+
check.nodvThreshold = 36;
4444
check.nestingThreshold = 8;
4545
check.cyclomaticThreshold = 45;
4646

@@ -63,7 +63,7 @@ void testLowComplexityFileWithLowerThresholds() {
6363
var check = new BrainMethodCheck();
6464

6565
check.locThreshold = 14;
66-
check.noavThreshold = 4;
66+
check.nodvThreshold = 4;
6767
check.cyclomaticThreshold = 5;
6868

6969
CheckVerifier.newVerifier()
@@ -77,7 +77,7 @@ void testSubsetOfIssuesWithLowerThresholds() {
7777
var check = new BrainMethodCheck();
7878

7979
check.locThreshold = 4;
80-
check.noavThreshold = 2;
80+
check.nodvThreshold = 2;
8181
check.cyclomaticThreshold = 1;
8282
check.nestingThreshold = 1;
8383

java-frontend/src/main/java/org/sonar/java/ast/visitors/NumberOfAccessedVariablesVisitor.java renamed to java-frontend/src/main/java/org/sonar/java/ast/visitors/NumberOfDefinedVariablesVisitor.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,18 @@
2020
import org.sonar.plugins.java.api.tree.MethodTree;
2121
import org.sonar.plugins.java.api.tree.VariableTree;
2222

23-
public class NumberOfAccessedVariablesVisitor extends BaseTreeVisitor {
24-
private int numberOfAccessedVariables = 0;
23+
public class NumberOfDefinedVariablesVisitor extends BaseTreeVisitor {
24+
private int numberOfDefinedVariables = 0;
2525

2626
@Override
2727
public void visitVariable(VariableTree tree) {
28-
numberOfAccessedVariables++;
28+
numberOfDefinedVariables++;
2929
}
3030

3131
public int getNumberOfAccessedVariables(MethodTree tree) {
32-
numberOfAccessedVariables = 0;
32+
numberOfDefinedVariables = 0;
3333
scan(tree);
34-
return numberOfAccessedVariables;
34+
return numberOfDefinedVariables;
3535
}
3636

3737
}

java-frontend/src/main/java/org/sonar/java/metrics/MetricsComputer.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import org.sonar.java.ast.visitors.ComplexityVisitor;
2727
import org.sonar.java.ast.visitors.LinesOfCodeVisitor;
2828
import org.sonar.java.ast.visitors.MethodNestingLevelVisitor;
29-
import org.sonar.java.ast.visitors.NumberOfAccessedVariablesVisitor;
29+
import org.sonar.java.ast.visitors.NumberOfDefinedVariablesVisitor;
3030
import org.sonar.java.ast.visitors.StatementVisitor;
3131
import org.sonar.plugins.java.api.tree.CompilationUnitTree;
3232
import org.sonar.plugins.java.api.tree.MethodTree;
@@ -37,7 +37,7 @@ public class MetricsComputer {
3737
private final Map<Integer, List<Tree>> methodComplexityNodes = new HashMap<>();
3838
private final Map<Integer, CognitiveComplexityVisitor.Result> methodComplexity = new HashMap<>();
3939
private final Map<Integer, Integer> compilationUnityComplexity = new HashMap<>();
40-
private final Map<Integer, Integer> methodNumberOfAccessedVariables = new HashMap<>();
40+
private final Map<Integer, Integer> methodNumberOfDefinedVariables = new HashMap<>();
4141
private final Map<Integer, Integer> treeLinesOfCode = new HashMap<>();
4242
private final Map<Integer, Integer> treeNumberOfStatements = new HashMap<>();
4343
private final Map<Integer, Integer> treeNumberOfCommentedLines = new HashMap<>();
@@ -54,10 +54,10 @@ public CognitiveComplexityVisitor.Result getMethodComplexity(MethodTree tree) {
5454
return methodComplexity.computeIfAbsent(tree.hashCode(), k -> CognitiveComplexityVisitor.methodComplexity(tree));
5555
}
5656

57-
NumberOfAccessedVariablesVisitor methodBodyVisitor = new NumberOfAccessedVariablesVisitor();
57+
NumberOfDefinedVariablesVisitor methodBodyVisitor = new NumberOfDefinedVariablesVisitor();
5858

59-
public int getNumberOfAccessedVariables(MethodTree tree) {
60-
return methodNumberOfAccessedVariables.computeIfAbsent(tree.hashCode(), k -> methodBodyVisitor.getNumberOfAccessedVariables(tree));
59+
public int getNumberOfDefinedVariables(MethodTree tree) {
60+
return methodNumberOfDefinedVariables.computeIfAbsent(tree.hashCode(), k -> methodBodyVisitor.getNumberOfAccessedVariables(tree));
6161
}
6262

6363
LinesOfCodeVisitor linesOfCodeVisitor = new LinesOfCodeVisitor();
@@ -114,8 +114,8 @@ Map<Integer, Integer> getCompilationUnityComplexity() {
114114
}
115115

116116
@VisibleForTesting
117-
Map<Integer, Integer> getMethodNumberOfAccessedVariables() {
118-
return methodNumberOfAccessedVariables;
117+
Map<Integer, Integer> getMethodNumberOfDefinedVariables() {
118+
return methodNumberOfDefinedVariables;
119119
}
120120

121121
@VisibleForTesting

java-frontend/src/test/java/org/sonar/java/ast/visitors/NumberOfAccessedVariablesVisitorTest.java renamed to java-frontend/src/test/java/org/sonar/java/ast/visitors/NumberOfDefinedVariablesVisitorTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@
2424

2525
import static org.assertj.core.api.Assertions.assertThat;
2626

27-
class NumberOfAccessedVariablesVisitorTest {
27+
class NumberOfDefinedVariablesVisitorTest {
2828

2929
@Test
3030
void zeroVariables() {
3131
CompilationUnitTree cut = JParserTestUtils.parse("class A {" +
3232
" private Object foo(){ }" +
3333
"}");
3434
MethodTree methodTree = (MethodTree) ((ClassTree) cut.types().get(0)).members().get(0);
35-
int numberOfVariables = new NumberOfAccessedVariablesVisitor().getNumberOfAccessedVariables(methodTree);
35+
int numberOfVariables = new NumberOfDefinedVariablesVisitor().getNumberOfAccessedVariables(methodTree);
3636
assertThat(numberOfVariables).isZero();
3737
}
3838

@@ -45,7 +45,7 @@ void threeVariables() {
4545
+ "}" +
4646
"}");
4747
MethodTree methodTree = (MethodTree) ((ClassTree) cut.types().get(0)).members().get(0);
48-
int numberOfVariables = new NumberOfAccessedVariablesVisitor().getNumberOfAccessedVariables(methodTree);
48+
int numberOfVariables = new NumberOfDefinedVariablesVisitor().getNumberOfAccessedVariables(methodTree);
4949
assertThat(numberOfVariables).isEqualTo(3);
5050
}
5151

@@ -60,7 +60,7 @@ void multipleAccessesOnSameVariableDoNotCount() {
6060
+ "}" +
6161
"}");
6262
MethodTree methodTree = (MethodTree) ((ClassTree) cut.types().get(0)).members().get(0);
63-
int numberOfVariables = new NumberOfAccessedVariablesVisitor().getNumberOfAccessedVariables(methodTree);
63+
int numberOfVariables = new NumberOfDefinedVariablesVisitor().getNumberOfAccessedVariables(methodTree);
6464
assertThat(numberOfVariables).isEqualTo(2);
6565
}
6666

java-frontend/src/test/java/org/sonar/java/metrics/MetricsComputerTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ void testMetricsPresence() throws SecurityException, IllegalArgumentException {
5656
mc.getMethodComplexity(methodTree);
5757
assertThat(mc.getMethodComplexity()).containsKey(methodTree.hashCode());
5858

59-
assertThat(mc.getMethodNumberOfAccessedVariables()).isEmpty();
60-
mc.getNumberOfAccessedVariables(methodTree);
61-
assertThat(mc.getMethodNumberOfAccessedVariables()).containsKey(methodTree.hashCode());
59+
assertThat(mc.getMethodNumberOfDefinedVariables()).isEmpty();
60+
mc.getNumberOfDefinedVariables(methodTree);
61+
assertThat(mc.getMethodNumberOfDefinedVariables()).containsKey(methodTree.hashCode());
6262

6363
assertThat(mc.getTreeLinesOfCode()).isEmpty();
6464
mc.getLinesOfCode(methodTree);

sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S6541.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@ <h2>Why is this an issue?</h2>
66
too many conditions, using lots of variables, and ultimately making it difficult to understand, maintain and reuse.
77
<br>
88
It is characterized by high LOC number, high cyclomatic and cognitive complexity, and a large number of variables being used.</p>
9+
<table>
10+
<tbody>
11+
<tr>
12+
<td>Note</td>
13+
<td>This rule uses a modified definition of Brain Method. Instead of the <strong>Number of Accessed Variables (NOAV)</strong> metric described
14+
in the original research, this implementation uses the <strong>Number of Defined Variables (NODV)</strong> metric, which counts the number of
15+
distinct local variables defined within the method’s scope.</td>
16+
</tr>
17+
</tbody>
18+
</table>
919
<h3>What is the potential impact?</h3>
1020
<p>Brain Methods are often hard to cover with tests, because of their deep nesting, and they are error-prone, because of the many local variables they
1121
usually introduce. Such methods will be very hard to read and understand for anyone outside who created them, and therefore hard to maintain and fix

0 commit comments

Comments
 (0)