-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathInconsistentCallOnResult.ql
More file actions
111 lines (101 loc) · 3.56 KB
/
InconsistentCallOnResult.ql
File metadata and controls
111 lines (101 loc) · 3.56 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
99
100
101
102
103
104
105
106
107
108
109
110
111
/**
* @name Inconsistent operation on return value
* @description If the same operation is usually performed
* on the result of a method call, any cases where it
* is not performed may indicate resource leaks or other problems.
* @kind problem
* @problem.severity warning
* @precision low
* @id java/inconsistent-call-on-result
* @tags reliability
* correctness
* external/cwe/cwe-252
* statistical
* non-attributable
*/
import java
import Chaining
predicate exclude(Method m) {
exists(string name | name = m.getName().toLowerCase() |
name.matches("get%") or
name.matches("is%") or
name.matches("has%") or
name.matches("add%")
)
}
/**
* The method access `otherCall`
* - is described by operation,
* - operates on `v`,
* - is different from `callToCheck`, and
* - is not a call to an excluded method.
*/
predicate checkExpr(MethodCall callToCheck, MethodCall otherCall, string operation, Variable v) {
not exclude(otherCall.getMethod()) and
v.getAnAssignedValue() = callToCheck and
otherCall != callToCheck and
otherCall.getMethod().getName() = operation and
(
otherCall.getAnArgument() = getChainedAccess(v) or
otherCall.getQualifier() = getChainedAccess(v)
)
}
/**
* Holds if `operation` is implicitly called on `v`, and `v` is assigned the result of `callToCheck`.
*/
predicate implicitCheckExpr(MethodCall callToCheck, string operation, Variable v) {
exists(TryStmt try, LocalVariableDeclExpr decl |
try.getAResourceDecl().getAVariable() = decl and
decl.getVariable() = v and
decl.getInit() = callToCheck and
operation = "close"
)
}
/**
* Get all accesses to a variable, either directly or by a chain of method calls.
*/
Expr getChainedAccess(Variable v) {
result = v.getAnAccess()
or
exists(MethodCall chainedAccess | chainedAccess.getQualifier() = getChainedAccess(v) |
designedForChaining(chainedAccess.getMethod()) and result = chainedAccess
)
}
/**
* The result of `ma` and a call to a method named `operation` are both assigned to the same variable.
*/
predicate checkedFunctionCall(MethodCall ma, string operation) {
relevantFunctionCall(ma, _) and
exists(Variable v | not v instanceof Field |
v.getAnAssignedValue() = ma and
(checkExpr(ma, _, operation, v) or implicitCheckExpr(ma, operation, v))
)
}
/**
* The method access `ma` is a call to `m` where the result is assigned.
*/
predicate relevantFunctionCall(MethodCall ma, Method m) {
ma.getMethod() = m and
exists(Variable v | v.getAnAssignedValue() = ma) and
not okToIgnore(ma)
}
predicate okToIgnore(MethodCall ma) { not ma.getCompilationUnit().fromSource() }
predicate functionStats(Method m, string operation, int used, int total, int percentage) {
m.getReturnType() instanceof RefType and
// Calls to `m` where we also perform `operation`.
used = strictcount(MethodCall ma | checkedFunctionCall(ma, operation) and m = ma.getMethod()) and
// Calls to `m`.
total = strictcount(MethodCall ma | relevantFunctionCall(ma, m)) and
percentage = used * 100 / total
}
from MethodCall unchecked, Method m, string operation, int percent
where
relevantFunctionCall(unchecked, m) and
not checkedFunctionCall(unchecked, operation) and
functionStats(m, operation, _, _, percent) and
percent >= 90 and
not m.getName() = operation and
not unchecked.getEnclosingStmt().(ExprStmt).isFieldDecl()
select unchecked,
"After " + percent.toString() + "% of calls to " + m.getName() + " there is a call to " +
operation + " on the return value. The call may be missing in this case."