Skip to content

Commit 4a0ab4a

Browse files
authored
Merge pull request github#14402 from Marcono1234/marcono1234/MemberRefExpr-getReceiverExpr
Java: Add predicate `MemberRefExpr::getReceiverExpr`
2 parents 8c6a1be + f3e5045 commit 4a0ab4a

File tree

5 files changed

+116
-32
lines changed

5 files changed

+116
-32
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: feature
3+
---
4+
* Added predicate `MemberRefExpr::getReceiverExpr`

java/ql/lib/semmle/code/java/Expr.qll

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,22 +1333,48 @@ class MemberRefExpr extends FunctionalExpr, @memberref {
13331333
*/
13341334
override Method asMethod() { result = this.getAnonymousClass().getAMethod() }
13351335

1336+
private Expr getResultExpr() {
1337+
exists(Stmt stmt |
1338+
stmt = this.asMethod().getBody().(SingletonBlock).getStmt() and
1339+
(
1340+
result = stmt.(ReturnStmt).getResult()
1341+
or
1342+
// Note: Currently never an ExprStmt, but might change once https://github.com/github/codeql/issues/3605 is fixed
1343+
result = stmt.(ExprStmt).getExpr()
1344+
)
1345+
)
1346+
}
1347+
1348+
/**
1349+
* Gets the expression whose member this member reference refers to, that is, the left
1350+
* side of the `::`. For example, for the member reference `this::toString` the receiver
1351+
* expression is the `this` expression.
1352+
*
1353+
* This predicate might not have a result in all cases where the receiver expression is
1354+
* a type access, for example `MyClass::...`.
1355+
*/
1356+
Expr getReceiverExpr() {
1357+
exists(Expr resultExpr | resultExpr = this.getResultExpr() |
1358+
result = resultExpr.(Call).getQualifier() and
1359+
// Ignore if the qualifier is a parameter of the method of the synthetic anonymous class
1360+
// (this is the case for method refs of instance methods which don't capture the instance, e.g. `Object::toString`)
1361+
// Could try to use TypeAccess as result here from child of MemberRefExpr, but that complexity might not be worth it
1362+
not this.asMethod().getAParameter().getAnAccess() = result
1363+
or
1364+
result = resultExpr.(ClassInstanceExpr).getTypeName()
1365+
// Don't cover array creation because ArrayCreationExpr currently does not have a predicate
1366+
// to easily get ArrayTypeAccess which should probably be the result here
1367+
)
1368+
}
1369+
13361370
/**
13371371
* Gets the receiver type whose member this expression refers to. The result might not be
13381372
* the type which actually declares the member. For example, for the member reference `ArrayList::toString`,
13391373
* this predicate has the result `java.util.ArrayList`, the type explicitly referred to, while
13401374
* `getReferencedCallable` will have `java.util.AbstractCollection.toString` as result, which `ArrayList` inherits.
13411375
*/
13421376
RefType getReceiverType() {
1343-
exists(Stmt stmt, Expr resultExpr |
1344-
stmt = this.asMethod().getBody().(SingletonBlock).getStmt() and
1345-
(
1346-
resultExpr = stmt.(ReturnStmt).getResult()
1347-
or
1348-
// Note: Currently never an ExprStmt, but might change once https://github.com/github/codeql/issues/3605 is fixed
1349-
resultExpr = stmt.(ExprStmt).getExpr()
1350-
)
1351-
|
1377+
exists(Expr resultExpr | resultExpr = this.getResultExpr() |
13521378
result = resultExpr.(MethodAccess).getReceiverType() or
13531379
result = resultExpr.(ClassInstanceExpr).getConstructedType() or
13541380
result = resultExpr.(ArrayCreationExpr).getType()
Lines changed: 54 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
1-
| Test.java:24:26:24:51 | ...::... | Test$Generic<Number>$Inner<>.Inner<> | Test$Generic$Inner.class:0:0:0:0 | Inner<> |
2-
| Test.java:38:29:38:42 | ...::... | java.lang.Object.toString | Test.java:1:7:1:10 | Test |
3-
| Test.java:39:29:39:42 | ...::... | java.lang.Object.hashCode | Test.java:1:7:1:10 | Test |
4-
| Test.java:40:29:40:39 | ...::... | java.lang.Object.clone | Test.java:1:7:1:10 | Test |
5-
| Test.java:41:40:41:64 | ...::... | java.lang.Object.toString | Test$Generic.class:0:0:0:0 | Generic<String> |
6-
| Test.java:43:23:43:36 | ...::... | java.lang.Object.toString | Test.java:1:7:1:10 | Test |
7-
| Test.java:44:23:44:36 | ...::... | java.lang.Object.hashCode | Test.java:1:7:1:10 | Test |
8-
| Test.java:45:23:45:33 | ...::... | java.lang.Object.clone | Test.java:1:7:1:10 | Test |
9-
| Test.java:48:22:48:35 | ...::... | java.lang.Object.toString | Test.java:1:7:1:10 | Test |
10-
| Test.java:51:13:51:21 | ...::... | Test.Test | Test.java:1:7:1:10 | Test |
11-
| Test.java:52:13:52:32 | ...::... | Test$Generic<String>.Generic<String> | Test$Generic.class:0:0:0:0 | Generic<String> |
12-
| Test.java:56:13:56:22 | ...::... | | file://:0:0:0:0 | int[] |
13-
| Test.java:57:13:57:26 | ...::... | | file://:0:0:0:0 | Generic<>[] |
14-
| Test.java:61:31:61:47 | ...::... | Test.doSomething | Test.java:1:7:1:10 | Test |
1+
getReferencedCallable
2+
| Test.java:26:31:26:52 | ...::... | java.lang.Object.toString |
3+
| Test.java:27:31:27:53 | ...::... | java.lang.Object.toString |
4+
| Test.java:32:27:32:52 | ...::... | Test$Generic<Number>$Inner<>.Inner<> |
5+
| Test.java:33:27:33:41 | ...::... | java.lang.Object.toString |
6+
| Test.java:49:29:49:42 | ...::... | java.lang.Object.toString |
7+
| Test.java:50:29:50:42 | ...::... | java.lang.Object.hashCode |
8+
| Test.java:51:29:51:39 | ...::... | java.lang.Object.clone |
9+
| Test.java:52:40:52:64 | ...::... | java.lang.Object.toString |
10+
| Test.java:54:23:54:36 | ...::... | java.lang.Object.toString |
11+
| Test.java:55:23:55:36 | ...::... | java.lang.Object.hashCode |
12+
| Test.java:56:23:56:33 | ...::... | java.lang.Object.clone |
13+
| Test.java:57:23:57:59 | ...::... | java.lang.Object.toString |
14+
| Test.java:57:35:57:48 | ...::... | java.lang.Object.toString |
15+
| Test.java:60:23:60:36 | ...::... | java.lang.Object.toString |
16+
| Test.java:62:23:62:40 | ...::... | Test.staticMethod |
17+
| Test.java:65:13:65:21 | ...::... | Test.Test |
18+
| Test.java:66:13:66:32 | ...::... | Test$Generic<String>.Generic<String> |
19+
| Test.java:75:31:75:47 | ...::... | Test.doSomething |
20+
getReceiverExpr
21+
| Test.java:26:31:26:52 | ...::... | Test.java:26:31:26:42 | Generic<>.this |
22+
| Test.java:27:31:27:53 | ...::... | Test.java:27:31:27:43 | Generic<>.super |
23+
| Test.java:32:27:32:52 | ...::... | Test.java:32:27:32:47 | Generic<Number>.Inner<> |
24+
| Test.java:33:27:33:41 | ...::... | Test.java:33:27:33:31 | super |
25+
| Test.java:54:23:54:36 | ...::... | Test.java:54:23:54:26 | this |
26+
| Test.java:55:23:55:36 | ...::... | Test.java:55:23:55:26 | this |
27+
| Test.java:56:23:56:33 | ...::... | Test.java:56:23:56:26 | this |
28+
| Test.java:57:23:57:59 | ...::... | Test.java:57:24:57:48 | (...)... |
29+
| Test.java:57:35:57:48 | ...::... | Test.java:57:35:57:38 | this |
30+
| Test.java:60:23:60:36 | ...::... | Test.java:60:23:60:26 | this |
31+
| Test.java:62:23:62:40 | ...::... | Test.java:62:23:62:26 | Test |
32+
| Test.java:65:13:65:21 | ...::... | Test.java:65:13:65:16 | Test |
33+
| Test.java:66:13:66:32 | ...::... | Test.java:66:13:66:27 | Generic<String> |
34+
getReceiverType
35+
| Test.java:26:31:26:52 | ...::... | Test.java:19:18:19:24 | Generic |
36+
| Test.java:27:31:27:53 | ...::... | Test.java:16:18:16:26 | BaseClass |
37+
| Test.java:32:27:32:52 | ...::... | Test$Generic$Inner.class:0:0:0:0 | Inner<> |
38+
| Test.java:33:27:33:41 | ...::... | Test.java:16:18:16:26 | BaseClass |
39+
| Test.java:49:29:49:42 | ...::... | Test.java:1:7:1:10 | Test |
40+
| Test.java:50:29:50:42 | ...::... | Test.java:1:7:1:10 | Test |
41+
| Test.java:51:29:51:39 | ...::... | Test.java:1:7:1:10 | Test |
42+
| Test.java:52:40:52:64 | ...::... | Test$Generic.class:0:0:0:0 | Generic<String> |
43+
| Test.java:54:23:54:36 | ...::... | Test.java:1:7:1:10 | Test |
44+
| Test.java:55:23:55:36 | ...::... | Test.java:1:7:1:10 | Test |
45+
| Test.java:56:23:56:33 | ...::... | Test.java:1:7:1:10 | Test |
46+
| Test.java:57:23:57:59 | ...::... | Test.java:10:15:10:22 | Supplier |
47+
| Test.java:57:35:57:48 | ...::... | Test.java:1:7:1:10 | Test |
48+
| Test.java:60:23:60:36 | ...::... | Test.java:1:7:1:10 | Test |
49+
| Test.java:62:23:62:40 | ...::... | Test.java:1:7:1:10 | Test |
50+
| Test.java:65:13:65:21 | ...::... | Test.java:1:7:1:10 | Test |
51+
| Test.java:66:13:66:32 | ...::... | Test$Generic.class:0:0:0:0 | Generic<String> |
52+
| Test.java:70:13:70:22 | ...::... | file://:0:0:0:0 | int[] |
53+
| Test.java:71:13:71:26 | ...::... | file://:0:0:0:0 | Generic<>[] |
54+
| Test.java:75:31:75:47 | ...::... | Test.java:1:7:1:10 | Test |
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import java
22

3-
string getReferencedCallable(MemberRefExpr e) {
4-
if exists(e.getReferencedCallable())
5-
then result = e.getReferencedCallable().getQualifiedName()
6-
else result = ""
3+
query string getReferencedCallable(MemberRefExpr e) {
4+
// Use qualified name because some callables don't have a source location (e.g. `Object.toString`)
5+
result = e.getReferencedCallable().getQualifiedName()
76
}
87

9-
from MemberRefExpr e
10-
select e, getReferencedCallable(e), e.getReceiverType()
8+
query Expr getReceiverExpr(MemberRefExpr e) { result = e.getReceiverExpr() }
9+
10+
query RefType getReceiverType(MemberRefExpr e) { result = e.getReceiverType() }

java/ql/test/library-tests/MemberRefExpr/Test.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,31 @@ interface Supplier {
1313

1414
public Test() { }
1515

16-
static class Generic<T> {
16+
static class BaseClass {
17+
}
18+
19+
static class Generic<T> extends BaseClass {
1720
public Generic() { }
1821

1922
class Inner {
2023
public Inner() { }
24+
25+
void test() {
26+
Supplier s0 = Generic.this::toString;
27+
Supplier s1 = Generic.super::toString;
28+
}
2129
}
2230

2331
void test() {
24-
Supplier s = Generic<Number>.Inner::new;
32+
Supplier s0 = Generic<Number>.Inner::new;
33+
Supplier s1 = super::toString;
2534
}
2635
}
2736

2837
void doSomething() { }
2938

39+
static void staticMethod() { }
40+
3041
static class Sub extends Test {
3142
}
3243

@@ -43,9 +54,12 @@ void test() {
4354
Supplier s0 = this::toString;
4455
Supplier s1 = this::hashCode;
4556
Supplier s2 = this::clone;
57+
Supplier s3 = ((Supplier) this::toString)::toString;
4658

4759
// Discards result of method call
48-
Runnable r = this::toString;
60+
Runnable r0 = this::toString;
61+
62+
Runnable r1 = Test::staticMethod;
4963

5064
Supplier[] classInstances = {
5165
Test::new,

0 commit comments

Comments
 (0)