diff --git a/java-checks-test-sources/default/src/main/files/non-compiling/checks/spring/AsyncMethodsReturnTypeCheckSample.java b/java-checks-test-sources/default/src/main/files/non-compiling/checks/spring/AsyncMethodsReturnTypeCheckSample.java new file mode 100644 index 00000000000..76e5cef64e8 --- /dev/null +++ b/java-checks-test-sources/default/src/main/files/non-compiling/checks/spring/AsyncMethodsReturnTypeCheckSample.java @@ -0,0 +1,35 @@ +package checks.spring; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Future; +import org.springframework.scheduling.annotation.Async; + +public class AsyncMethodsReturnTypeCheckSample { + + @Async + public CompletableFuture unknownTypeArg() { // Compliant + return CompletableFuture.completedFuture(new Data()); + } + + @Async + public void voidType() { // Compliant + return; + } + + @Async + public CompletableFuture futureSubtype() { // Compliant + return CompletableFuture.completedFuture(42); + } + + @Async + public Data unknownType() { // Compliant, no issue is raised when the return type is unknown (it might be a subtype of Future) + return new Data(); + } + + @Async + public Integer builtinType() { // Noncompliant {{Async methods should return 'void' or a 'Future' type.}} +// ^^^^^^^ + return 42; + } + +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java index 1dc23272e1c..0fca7cca747 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheck.java @@ -20,8 +20,10 @@ import org.sonar.check.Rule; import org.sonar.java.checks.helpers.SpringUtils; import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.semantic.Type; import org.sonar.plugins.java.api.tree.MethodTree; import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.TypeTree; @Rule(key = "S6810") public class AsyncMethodsReturnTypeCheck extends IssuableSubscriptionVisitor { @@ -35,12 +37,14 @@ public List nodesToVisit() { public void visitNode(Tree tree) { var mt = (MethodTree) tree; if (mt.symbol().metadata().isAnnotatedWith(SpringUtils.ASYNC_ANNOTATION)) { - var returnType = mt.returnType(); + TypeTree returnType = mt.returnType(); // returnType can only be null if the method is a constructor. Since the @Async annotation is not allowed on constructors, and since // we hence only visit methods, not constructors, we assume that returnType is not null. - if (!returnType.symbolType().isVoid() && !returnType.symbolType().isSubtypeOf("java.util.concurrent.Future")) { - reportIssue(returnType, "Async methods should return 'void' or a 'Future' type."); + Type symbolType = returnType.symbolType(); + if (symbolType.isUnknown() || symbolType.isVoid() || symbolType.isSubtypeOf("java.util.concurrent.Future")) { + return; } + reportIssue(returnType, "Async methods should return 'void' or a 'Future' type."); } } } diff --git a/java-checks/src/test/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheckTest.java index 4c412ddcdf4..e3390a735c1 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheckTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/spring/AsyncMethodsReturnTypeCheckTest.java @@ -20,6 +20,7 @@ import org.sonar.java.checks.verifier.CheckVerifier; import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath; +import static org.sonar.java.checks.verifier.TestUtils.nonCompilingTestSourcesPath; class AsyncMethodsReturnTypeCheckTest { @Test @@ -29,4 +30,12 @@ void test() { .withCheck(new AsyncMethodsReturnTypeCheck()) .verifyIssues(); } + + @Test + void test_non_compiling() { + CheckVerifier.newVerifier() + .onFile(nonCompilingTestSourcesPath("checks/spring/AsyncMethodsReturnTypeCheckSample.java")) + .withCheck(new AsyncMethodsReturnTypeCheck()) + .verifyIssues(); + } }