Repository navigation
Expand file tree
/
Copy pathUnusedReturnValueRule.java
More file actions
98 lines (79 loc) · 3.94 KB
/
Copy pathUnusedReturnValueRule.java
File metadata and controls
98 lines (79 loc) · 3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.errorprone;
import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement;
import net.sourceforge.pmd.lang.java.ast.ASTMethodCall;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil;
import net.sourceforge.pmd.lang.java.symbols.AnnotableSymbol;
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.types.InvocationMatcher;
import net.sourceforge.pmd.lang.java.types.JMethodSig;
import net.sourceforge.pmd.lang.java.types.Substitution;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
import net.sourceforge.pmd.reporting.RuleContext;
/**
* @since 7.27.0
*/
public class UnusedReturnValueRule extends AbstractJavaRulechainRule {
private static final String CHECK_RETURN_VALUE_ANNOTATION = "CheckReturnValue";
private static final String CAN_IGNORE_RETURN_VALUE_ANNOTATION = "CanIgnoreReturnValue";
private static final InvocationMatcher.CompoundInvocationMatcher METHODS_RETURNING_NUMBER_OF_BYTES_READ = InvocationMatcher.parseAll(
"java.io.InputStream#skip(long)",
"java.io.InputStream#read(byte[])",
"java.io.InputStream#read(byte[],int,int)"
);
private static final InvocationMatcher MOCKITO_VERIFY = InvocationMatcher.parse("org.mockito.Mockito#verify(_*)");
public UnusedReturnValueRule() {
super(ASTMethodCall.class);
}
@Override
public RuleContext visit(ASTMethodCall call, Object data) {
RuleContext ctx = (RuleContext) data;
if (shouldCheckResult(call) && !isResultUsed(call)) {
ctx.addViolation(call, formatCall(call));
}
return ctx;
}
private String formatCall(ASTMethodCall call) {
JMethodSig methodSig = call.getMethodType();
return ((JClassSymbol) methodSig.getDeclaringType().getSymbol()).getCanonicalName() + "." + methodSig.getName() + "()";
}
private boolean shouldCheckResult(ASTMethodCall call) {
return !MOCKITO_VERIFY.matchesCall(call.getQualifier())
&& (isCheckReturnValueAnnotated(call)
|| JavaRuleUtil.isKnownPure(call)
|| METHODS_RETURNING_NUMBER_OF_BYTES_READ.anyMatch(call));
}
// visible for testing
/* private */ static boolean isCheckReturnValueAnnotated(ASTMethodCall call) {
JExecutableSymbol methodSymbol = call.getMethodType().getSymbol();
if (methodSymbol.getReturnType(Substitution.EMPTY).isVoid()) {
return false;
}
if (isAnnotatedWith(methodSymbol, CHECK_RETURN_VALUE_ANNOTATION)) {
return true;
}
JTypeDeclSymbol classSymbol = call.getMethodType().getDeclaringType().getSymbol();
if (isAnnotatedWith(classSymbol, CHECK_RETURN_VALUE_ANNOTATION)
&& !isAnnotatedWith(methodSymbol, CAN_IGNORE_RETURN_VALUE_ANNOTATION)
) {
return true;
}
TypeSystem typeSystem = classSymbol.getTypeSystem();
AnnotableSymbol packageSymbol = typeSystem.getPackageSymbol(classSymbol.getPackageName());
return packageSymbol != null
&& isAnnotatedWith(packageSymbol, CHECK_RETURN_VALUE_ANNOTATION)
&& !isAnnotatedWith(classSymbol, CAN_IGNORE_RETURN_VALUE_ANNOTATION)
&& !isAnnotatedWith(methodSymbol, CAN_IGNORE_RETURN_VALUE_ANNOTATION);
}
private static boolean isAnnotatedWith(AnnotableSymbol symbol, String name) {
return symbol.getDeclaredAnnotations().stream().anyMatch(annotation -> name.equals(annotation.getSimpleName()));
}
private boolean isResultUsed(ASTMethodCall call) {
return !(call.getParent() instanceof ASTExpressionStatement);
}
}