Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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<Data> unknownTypeArg() { // Compliant
return CompletableFuture.completedFuture(new Data());
}

@Async
public void voidType() { // Compliant
return;
}

@Async
public CompletableFuture<Integer> 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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -35,12 +37,14 @@ public List<Tree.Kind> 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.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
}
}
Loading