Skip to content

Commit 04a522b

Browse files
Fix a FP case in S7479
This was seen on apache lucene.
1 parent 5083899 commit 04a522b

File tree

2 files changed

+31
-14
lines changed

2 files changed

+31
-14
lines changed

java-checks-test-sources/default/src/main/java/checks/ClassBuilderWithMethodCheckSample.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package checks;
22

33
import java.lang.classfile.ClassBuilder;
4+
import java.lang.classfile.attribute.ExceptionsAttribute;
45
import java.lang.constant.ClassDesc;
56

67
import static java.lang.classfile.ClassFile.ACC_PUBLIC;
@@ -53,4 +54,14 @@ ClassBuilder addMethodCompliantWithMethodBody(ClassBuilder builder) {
5354
.invokevirtual(ClassDesc.of("java.io.PrintStream"), "println", MTD_void)
5455
.return_());
5556
}
57+
58+
ClassBuilder addMethodCompliantWithCall(ClassBuilder builder) {
59+
return builder
60+
// Compliant because `withCode` is not called directly on the `methodBuilder`
61+
.withMethod("foo", MTD_void, ACC_PUBLIC | ACC_STATIC, methodBuilder -> methodBuilder.with(ExceptionsAttribute.ofSymbols(Exception.class.describeConstable().get()))
62+
.withCode(codeBuilder -> codeBuilder.getstatic(ClassDesc.of("java.lang.System"), "out", ClassDesc.of("java.io.PrintStream"))
63+
.ldc("Hello World")
64+
.invokevirtual(ClassDesc.of("java.io.PrintStream"), "println", MTD_void)
65+
.return_()));
66+
}
5667
}

java-checks/src/main/java/org/sonar/java/checks/ClassBuilderWithMethodCheck.java

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,19 @@
2222
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
2323
import org.sonar.plugins.java.api.semantic.MethodMatchers;
2424
import org.sonar.plugins.java.api.tree.ExpressionTree;
25+
import org.sonar.plugins.java.api.tree.LambdaExpressionTree;
2526
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
2627
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
2728
import org.sonar.plugins.java.api.tree.Tree;
29+
import org.sonar.plugins.java.api.tree.VariableTree;
2830

29-
import static org.sonar.java.matcher.TreeMatcher.any;
3031
import static org.sonar.java.matcher.TreeMatcher.calls;
3132
import static org.sonar.java.matcher.TreeMatcher.hasSize;
33+
import static org.sonar.java.matcher.TreeMatcher.invokedOn;
3234
import static org.sonar.java.matcher.TreeMatcher.isExpression;
35+
import static org.sonar.java.matcher.TreeMatcher.isIdentifier;
3336
import static org.sonar.java.matcher.TreeMatcher.isInvocationOf;
3437
import static org.sonar.java.matcher.TreeMatcher.statementAt;
35-
import static org.sonar.java.matcher.TreeMatcher.withBody;
3638

3739
@Rule(key = "S7479")
3840
public class ClassBuilderWithMethodCheck extends IssuableSubscriptionVisitor {
@@ -49,16 +51,16 @@ public class ClassBuilderWithMethodCheck extends IssuableSubscriptionVisitor {
4951
.withAnyParameters()
5052
.build();
5153

52-
/** Matches lambda expression composed of just a call to `MethodBuilder.withBody`. */
53-
private final TreeMatcher<ExpressionTree> matcher = TreeMatcher
54-
.isLambdaExpression(
55-
withBody(
56-
// Case with curly braces
57-
hasSize(1)
58-
.and(
59-
statementAt(0, isInvocationOf(withCode, any())))
60-
// Case with no curly braces
61-
.or(isExpression(calls(withCode, any())))));
54+
/** Matches lambda bodies composed of just a call to `MethodBuilder.withBody` on the given variable. */
55+
private TreeMatcher<LambdaExpressionTree> makeMatcher(VariableTree variableCalledOn) {
56+
return TreeMatcher.withBody(
57+
// Case with curly braces
58+
hasSize(1)
59+
.and(
60+
statementAt(0, isInvocationOf(withCode, invokedOn(isIdentifier(variableCalledOn.symbol())))))
61+
// Case with no curly braces
62+
.or(isExpression(calls(withCode, invokedOn(isIdentifier(variableCalledOn.symbol()))))));
63+
}
6264

6365
@Override
6466
public List<Tree.Kind> nodesToVisit() {
@@ -71,8 +73,12 @@ public void visitNode(Tree tree) {
7173
MethodInvocationTree invocation = (MethodInvocationTree) tree;
7274
if (withMethod.matches(invocation)) {
7375
ExpressionTree lastArgument = invocation.arguments().get(invocation.arguments().size() - 1);
74-
if (matcher.check(lastArgument)) {
75-
reportIssue(findLocation(invocation), "Replace call with `ClassBuilder.withMethodBody`.");
76+
77+
if (lastArgument instanceof LambdaExpressionTree lambda) {
78+
VariableTree parameter = lambda.parameters().get(0);
79+
if (makeMatcher(parameter).check(lambda)) {
80+
reportIssue(findLocation(invocation), "Replace call with `ClassBuilder.withMethodBody`.");
81+
}
7682
}
7783
}
7884
}

0 commit comments

Comments
 (0)