Skip to content

Commit 6508afe

Browse files
authored
Merge pull request github#6900 from Marcono1234/marcono1234/MemberRefExpr-receiver-type
Java: Add `MemberRefExpr.getReceiverType()`
2 parents 3f3c79f + 86d5393 commit 6508afe

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1227,6 +1227,28 @@ class MemberRefExpr extends FunctionalExpr, @memberref {
12271227
*/
12281228
override Method asMethod() { result = this.getAnonymousClass().getAMethod() }
12291229

1230+
/**
1231+
* Gets the receiver type whose member this expression refers to. The result might not be
1232+
* the type which actually declares the member. For example, for the member reference `ArrayList::toString`,
1233+
* this predicate has the result `java.util.ArrayList`, the type explicitly referred to, while
1234+
* `getReferencedCallable` will have `java.util.AbstractCollection.toString` as result, which `ArrayList` inherits.
1235+
*/
1236+
RefType getReceiverType() {
1237+
exists(Stmt stmt, Expr resultExpr |
1238+
stmt = asMethod().getBody().(SingletonBlock).getStmt() and
1239+
(
1240+
resultExpr = stmt.(ReturnStmt).getResult()
1241+
or
1242+
// Note: Currently never an ExprStmt, but might change once https://github.com/github/codeql/issues/3605 is fixed
1243+
resultExpr = stmt.(ExprStmt).getExpr()
1244+
)
1245+
|
1246+
result = resultExpr.(MethodAccess).getReceiverType() or
1247+
result = resultExpr.(ClassInstanceExpr).getConstructedType() or
1248+
result = resultExpr.(ArrayCreationExpr).getType()
1249+
)
1250+
}
1251+
12301252
/**
12311253
* Gets the method or constructor referenced by this member reference expression.
12321254
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
| Test.java:24:26:24:51 | ...::... | Inner<>.Inner<> | Test$Generic$Inner.class:0:0:0:0 | Inner<> |
2+
| Test.java:38:29:38:42 | ...::... | Object.toString | Test.java:1:7:1:10 | Test |
3+
| Test.java:39:29:39:42 | ...::... | Object.hashCode | Test.java:1:7:1:10 | Test |
4+
| Test.java:40:29:40:39 | ...::... | Object.clone | Test.java:1:7:1:10 | Test |
5+
| Test.java:41:40:41:64 | ...::... | Object.toString | Test$Generic.class:0:0:0:0 | Generic<String> |
6+
| Test.java:43:23:43:36 | ...::... | Object.toString | Test.java:1:7:1:10 | Test |
7+
| Test.java:44:23:44:36 | ...::... | Object.hashCode | Test.java:1:7:1:10 | Test |
8+
| Test.java:45:23:45:33 | ...::... | Object.clone | Test.java:1:7:1:10 | Test |
9+
| Test.java:48:22:48:35 | ...::... | 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 | ...::... | 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 |
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import java
2+
3+
string getReferencedCallable(MemberRefExpr e) {
4+
if exists(e.getReferencedCallable())
5+
then result = e.getReferencedCallable().getQualifiedName()
6+
else result = ""
7+
}
8+
9+
from MemberRefExpr e
10+
select e, getReferencedCallable(e), e.getReceiverType()
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
class Test {
2+
interface Function<T> {
3+
Object apply(T o) throws Exception;
4+
}
5+
6+
interface IntFunction {
7+
Object apply(int i);
8+
}
9+
10+
interface Supplier {
11+
Object get() throws Exception;
12+
}
13+
14+
public Test() { }
15+
16+
static class Generic<T> {
17+
public Generic() { }
18+
19+
class Inner {
20+
public Inner() { }
21+
}
22+
23+
void test() {
24+
Supplier s = Generic<Number>.Inner::new;
25+
}
26+
}
27+
28+
void doSomething() { }
29+
30+
static class Sub extends Test {
31+
}
32+
33+
interface SubtypeConsumer {
34+
void consume(Sub s);
35+
}
36+
37+
void test() {
38+
Function<Test> f0 = Test::toString;
39+
Function<Test> f1 = Test::hashCode;
40+
Function<Test> f2 = Test::clone;
41+
Function<Generic<String>> f3 = Generic<String>::toString;
42+
43+
Supplier s0 = this::toString;
44+
Supplier s1 = this::hashCode;
45+
Supplier s2 = this::clone;
46+
47+
// Discards result of method call
48+
Runnable r = this::toString;
49+
50+
Supplier[] classInstances = {
51+
Test::new,
52+
Generic<String>::new,
53+
};
54+
55+
IntFunction[] arrays = {
56+
int[]::new,
57+
Generic[]::new,
58+
};
59+
60+
// SubtypeConsumer has `Sub` as parameter type, but receiver here is `Test` (supertype)
61+
SubtypeConsumer sub = Test::doSomething;
62+
}
63+
}

0 commit comments

Comments
 (0)