Skip to content

Commit 156b7eb

Browse files
author
Angelo Buono
authored
SONARJAVA-4681: Fix bug due to wrong semantics usage on S6832 (#4524)
1 parent f25d9b7 commit 156b7eb

4 files changed

Lines changed: 48 additions & 68 deletions

File tree

java-checks-test-sources/src/main/java/checks/spring/NonSingletonAutowiredInSingletonCheckSample.java

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,18 @@
11
package checks.spring;
22

3+
import checks.spring.NonSingletonAutowiredInSingletonCheckSampleNonSingletonBeansDefinition.PrototypeBean1;
4+
import checks.spring.NonSingletonAutowiredInSingletonCheckSampleNonSingletonBeansDefinition.PrototypeBean2;
5+
import checks.spring.NonSingletonAutowiredInSingletonCheckSampleNonSingletonBeansDefinition.PrototypeBean3;
6+
import checks.spring.NonSingletonAutowiredInSingletonCheckSampleNonSingletonBeansDefinition.RequestBean1;
37
import javax.inject.Inject;
48
import org.springframework.beans.factory.annotation.Autowired;
59
import org.springframework.context.annotation.Scope;
6-
import org.springframework.context.annotation.ScopedProxyMode;
7-
import org.springframework.stereotype.Component;
8-
import org.springframework.web.context.WebApplicationContext;
910

1011
import static org.springframework.context.annotation.ScopedProxyMode.TARGET_CLASS;
1112

1213
public class NonSingletonAutowiredInSingletonCheckSample {
1314

1415
private static final String SINGLETON_SCOPE = "singleton";
15-
private static final String PROTOTYPE_SCOPE = "prototype";
16-
private static final String CUSTOM_SCOPE = "custom";
17-
18-
@Component
19-
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = TARGET_CLASS)
20-
public class RequestBean1 { }
21-
22-
@Scope("prototype")
23-
public class PrototypeBean1 { }
24-
25-
@Scope(value = "prototype")
26-
public class PrototypeBean2 { }
27-
28-
29-
@Scope(value = PROTOTYPE_SCOPE, scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
30-
public class PrototypeBean3 { }
31-
3216

3317
public class SingletonBean {
3418
@Autowired
@@ -180,7 +164,7 @@ public void setPrototypeBean2(PrototypeBean2 prototypeBean2) { // Noncompliant
180164
}
181165
}
182166

183-
@Scope(value = CUSTOM_SCOPE, scopeName = "custom", proxyMode = TARGET_CLASS)
167+
@Scope(value = "custom", scopeName = "custom", proxyMode = TARGET_CLASS)
184168
public class CustomBean {
185169
@Autowired
186170
private SingletonBean singletonBean; // Compliant, since scope is non-Singleton
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package checks.spring;
2+
3+
import org.springframework.context.annotation.Scope;
4+
import org.springframework.context.annotation.ScopedProxyMode;
5+
import org.springframework.stereotype.Component;
6+
import org.springframework.web.context.WebApplicationContext;
7+
8+
import static org.springframework.context.annotation.ScopedProxyMode.TARGET_CLASS;
9+
10+
public class NonSingletonAutowiredInSingletonCheckSampleNonSingletonBeansDefinition {
11+
private static final String PROTOTYPE_SCOPE = "prototype";
12+
13+
@Component
14+
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = TARGET_CLASS)
15+
public class RequestBean1 {
16+
}
17+
18+
@Scope("prototype")
19+
public class PrototypeBean1 {
20+
}
21+
22+
@Scope(value = "prototype")
23+
public class PrototypeBean2 {
24+
}
25+
26+
@Scope(value = PROTOTYPE_SCOPE, scopeName = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
27+
public class PrototypeBean3 {
28+
}
29+
}

java-checks/src/main/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheck.java

Lines changed: 13 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,9 @@
2727
import org.sonar.java.checks.helpers.MethodTreeUtils;
2828
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
2929
import org.sonar.plugins.java.api.semantic.Symbol;
30+
import org.sonar.plugins.java.api.semantic.SymbolMetadata;
3031
import org.sonar.plugins.java.api.tree.AnnotationTree;
31-
import org.sonar.plugins.java.api.tree.Arguments;
32-
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
3332
import org.sonar.plugins.java.api.tree.ClassTree;
34-
import org.sonar.plugins.java.api.tree.IdentifierTree;
35-
import org.sonar.plugins.java.api.tree.LiteralTree;
36-
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
3733
import org.sonar.plugins.java.api.tree.MethodTree;
3834
import org.sonar.plugins.java.api.tree.Tree;
3935
import org.sonar.plugins.java.api.tree.VariableTree;
@@ -45,7 +41,6 @@ public class NonSingletonAutowiredInSingletonCheck extends IssuableSubscriptionV
4541
private static final String JAVAX_INJECT_ANNOTATION = "javax.inject.Inject";
4642
private static final String JAKARTA_INJECT_ANNOTATION = "jakarta.inject.Inject";
4743
private static final Set<String> AUTO_WIRING_ANNOTATIONS = Set.of(AUTOWIRED_ANNOTATION, JAVAX_INJECT_ANNOTATION, JAKARTA_INJECT_ANNOTATION);
48-
private static final Set<String> SINGLETON_LITERALS = Set.of("singleton", "\"singleton\"");
4944

5045
@Override
5146
public List<Tree.Kind> nodesToVisit() {
@@ -157,57 +152,32 @@ private void reportIfNonSingletonInSingleton(ClassTree enclosingClassTree, Varia
157152
}
158153

159154
private static boolean hasTypeNotSingletonBean(VariableTree variableTree) {
160-
var typeSymbolDeclaration = variableTree.type().symbolType().symbol().declaration();
161-
return typeSymbolDeclaration != null && hasNotSingletonScopeAnnotation(typeSymbolDeclaration.modifiers().annotations());
155+
return hasNotSingletonScopeAnnotation(variableTree.symbol().type().symbol().metadata().annotations());
162156
}
163157

164158
private static boolean isAutoWiringAnnotation(AnnotationTree annotationTree) {
165159
return AUTO_WIRING_ANNOTATIONS.contains(annotationTree.symbolType().fullyQualifiedName());
166160
}
167161

168162
private static boolean isSingletonBean(ClassTree classTree) {
169-
return !hasNotSingletonScopeAnnotation(classTree.modifiers().annotations());
163+
return !hasNotSingletonScopeAnnotation(classTree.symbol().metadata().annotations());
170164
}
171165

172-
private static boolean hasNotSingletonScopeAnnotation(List<AnnotationTree> annotations) {
166+
private static boolean hasNotSingletonScopeAnnotation(List<SymbolMetadata.AnnotationInstance> annotations) {
173167
// Only classes annotated with @Scope, having a value different from "singleton", are considered as non-Singleton
174168
return annotations.stream().anyMatch(NonSingletonAutowiredInSingletonCheck::isNotSingletonScopeAnnotation);
175169
}
176170

177-
private static boolean isNotSingletonScopeAnnotation(AnnotationTree annotationTree) {
178-
return annotationTree.symbolType().is(SCOPED_ANNOTATION)
179-
&& (isNotSingletonLiteralValue(annotationTree.arguments()) || isNotSingletonAssignmentValue(annotationTree.arguments()));
171+
private static boolean isNotSingletonScopeAnnotation(SymbolMetadata.AnnotationInstance annotationInstance) {
172+
return annotationInstance.symbol().type().is(SCOPED_ANNOTATION)
173+
&& annotationInstance.values()
174+
.stream()
175+
.anyMatch(NonSingletonAutowiredInSingletonCheck::isNotSingletonAnnotationValue);
180176
}
181177

182-
private static boolean isNotSingletonLiteralValue(Arguments arguments) {
183-
return arguments.size() == 1
184-
&& arguments.get(0).is(Tree.Kind.STRING_LITERAL)
185-
&& isNotSingletonLiteral(((LiteralTree) arguments.get(0)).value());
178+
private static boolean isNotSingletonAnnotationValue(SymbolMetadata.AnnotationValue annotationValue) {
179+
return ("value".equals(annotationValue.name()) || "scopeName".equals(annotationValue.name()))
180+
// both "value" and "scopeName" in @Scope annotation have String type
181+
&& !"singleton".equalsIgnoreCase((String) annotationValue.value());
186182
}
187-
188-
private static boolean isNotSingletonAssignmentValue(Arguments arguments) {
189-
return arguments
190-
.stream()
191-
.filter(argument -> argument.is(Tree.Kind.ASSIGNMENT))
192-
.map(AssignmentExpressionTree.class::cast)
193-
.anyMatch(NonSingletonAutowiredInSingletonCheck::isNotAssignmentToSingletonValue);
194-
}
195-
196-
private static boolean isNotAssignmentToSingletonValue(AssignmentExpressionTree assignmentExpressionTree) {
197-
var expression = assignmentExpressionTree.expression();
198-
199-
if (expression.is(Tree.Kind.STRING_LITERAL)) {
200-
return isNotSingletonLiteral(((LiteralTree) expression).value());
201-
}
202-
203-
var variable = (IdentifierTree) assignmentExpressionTree.variable();
204-
205-
return ("value".equals(variable.name()) || "scopeName".equals(variable.name()))
206-
&& (expression.is(Tree.Kind.MEMBER_SELECT) && isNotSingletonLiteral(((MemberSelectExpressionTree) expression).identifier().name()));
207-
}
208-
209-
private static boolean isNotSingletonLiteral(String value) {
210-
return SINGLETON_LITERALS.stream().noneMatch(singletonLiteral -> singletonLiteral.equalsIgnoreCase(value));
211-
}
212-
213183
}

java-checks/src/test/java/org/sonar/java/checks/spring/NonSingletonAutowiredInSingletonCheckTest.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323
import org.sonar.java.checks.verifier.CheckVerifier;
2424
import org.sonar.java.checks.verifier.TestUtils;
2525

26-
import static org.junit.jupiter.api.Assertions.*;
27-
import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath;
28-
2926
class NonSingletonAutowiredInSingletonCheckTest {
3027

3128
@Test
@@ -39,7 +36,7 @@ void test() {
3936
@Test
4037
void test_non_semantics() {
4138
CheckVerifier.newVerifier()
42-
.onFile(mainCodeSourcesPath("checks/spring/NonSingletonAutowiredInSingletonCheckSample.java"))
39+
.onFile(TestUtils.mainCodeSourcesPath("checks/spring/NonSingletonAutowiredInSingletonCheckSample.java"))
4340
.withCheck(new NonSingletonAutowiredInSingletonCheck())
4441
.withoutSemantic()
4542
.verifyNoIssues();

0 commit comments

Comments
 (0)