-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathChaining.qll
More file actions
63 lines (60 loc) · 2.1 KB
/
Chaining.qll
File metadata and controls
63 lines (60 loc) · 2.1 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
import java
/**
* Find methods that always return `this`, or interface methods that are
* overridden by such methods.
*
* For native or compiled methods (which have no body), we approximate this by
* requiring that the method returns a subtype of its declaring type, is not
* declared in an immutable type, and that every method overriding it is also
* designed for chaining.
*/
predicate designedForChaining(Method m) {
not nonChaining(m)
}
private predicate nonChaining(Method m) {
// The method has a body, and at least one of the return values is not suitable for chaining.
exists(ReturnStmt ret | ret.getEnclosingCallable() = m | nonChainingReturn(m, ret))
or
(
// The method has no body, and is not chaining because ...
not exists(m.getBody()) and
(
// ... it has the wrong return type, ...
not hasSubtype*(m.getReturnType(), m.getDeclaringType()) or
// ... it is defined on an immutable type, or ...
m.getDeclaringType() instanceof ImmutableType or
// ... it has an override that is non-chaining.
exists(Method override | override.overrides(m) | nonChaining(override))
)
)
}
private predicate nonChainingReturn(Method m, ReturnStmt ret) {
// The wrong `this` is returned.
(
ret.getResult() instanceof ThisAccess and
ret.getResult().getType() != m.getDeclaringType()
) or
// A method call to the wrong method is returned.
(
ret.getResult() instanceof MethodAccess and
exists(MethodAccess delegateCall, Method delegate |
delegateCall = ret.getResult() and
delegate = delegateCall.getMethod()
|
delegate.getDeclaringType() != m.getDeclaringType() or
delegate.isStatic() or
not hasSubtype*(m.getReturnType(), delegate.getReturnType()) or
// A method on the wrong object is called.
not (
delegateCall.getQualifier().getProperExpr() instanceof ThisAccess or
not exists(delegateCall.getQualifier())
) or
nonChaining(delegate)
)
) or
// Something else is returned.
not (
ret.getResult() instanceof ThisAccess or
ret.getResult() instanceof MethodAccess
)
}