Extract generic method prototypes

These feature substituted types according to their declaring generic specialisation, with wildcards that reach top-level being converted to their upper or lower bound depending on usage context.

This commit also includes an incidental fix such that constructors declare their return-type as unit, consistent with the Java extractor.
This commit is contained in:
Chris Smowton
2021-12-03 18:15:03 +00:00
committed by Ian Lynagh
parent 26a0925f99
commit 2f8b8fadc3
8 changed files with 386 additions and 57 deletions

View File

@@ -0,0 +1,33 @@
class Generic2<T> {
public Generic2(T init) { stored = init; }
private T stored;
T identity2(T param) { return identity(param); }
T identity(T param) { return param; }
T getter() { return stored; }
void setter(T param) { stored = param; }
}
public class Test {
public static void user() {
Generic2<String> invariant = new Generic2<String>("hello world");
invariant.identity("hello world");
invariant.identity2("hello world");
Generic2<? extends String> projectedOut = invariant;
projectedOut.getter();
Generic2<? super String> projectedIn = invariant;
projectedIn.setter("hi planet");
projectedIn.getter();
}
}

View File

@@ -0,0 +1,92 @@
calls
| Test.java:7:33:7:47 | identity(...) | Test.java:7:5:7:13 | identity2 | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | Test.java:1:7:1:14 | Generic2 |
| Test.java:19:5:19:37 | identity(...) | Test.java:16:22:16:25 | user | Test.java:14:14:14:17 | Test | Generic2.class:0:0:0:0 | identity | Generic2.class:0:0:0:0 | Generic2<String> |
| Test.java:20:5:20:38 | identity2(...) | Test.java:16:22:16:25 | user | Test.java:14:14:14:17 | Test | Generic2.class:0:0:0:0 | identity2 | Generic2.class:0:0:0:0 | Generic2<String> |
| Test.java:23:5:23:25 | getter(...) | Test.java:16:22:16:25 | user | Test.java:14:14:14:17 | Test | Generic2.class:0:0:0:0 | getter | Generic2.class:0:0:0:0 | Generic2<? extends String> |
| Test.java:26:5:26:35 | setter(...) | Test.java:16:22:16:25 | user | Test.java:14:14:14:17 | Test | Generic2.class:0:0:0:0 | setter | Generic2.class:0:0:0:0 | Generic2<? super String> |
| Test.java:27:5:27:24 | getter(...) | Test.java:16:22:16:25 | user | Test.java:14:14:14:17 | Test | Generic2.class:0:0:0:0 | getter | Generic2.class:0:0:0:0 | Generic2<? super String> |
| test.kt:1:1:10:1 | <obinit>(...) | test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | <obinit> | test.kt:1:1:10:1 | Generic |
| test.kt:5:32:5:46 | identity(...) | test.kt:5:3:5:46 | identity2 | test.kt:1:1:10:1 | Generic | test.kt:6:3:6:35 | identity | test.kt:1:1:10:1 | Generic |
| test.kt:7:21:7:26 | <get-stored>(...) | test.kt:7:3:7:26 | getter | test.kt:1:1:10:1 | Generic | test.kt:3:3:3:19 | <get-stored> | test.kt:1:1:10:1 | Generic |
| test.kt:8:26:8:31 | <set-stored>(...) | test.kt:8:3:8:41 | setter | test.kt:1:1:10:1 | Generic | test.kt:3:3:3:19 | <set-stored> | test.kt:1:1:10:1 | Generic |
| test.kt:15:13:15:35 | identity(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | test.kt:6:3:6:35 | identity | test.kt:1:1:10:1 | Generic<String> |
| test.kt:16:13:16:36 | identity2(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | test.kt:5:3:5:46 | identity2 | test.kt:1:1:10:1 | Generic<String> |
| test.kt:19:16:19:23 | getter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | test.kt:7:3:7:26 | getter | test.kt:1:1:10:1 | Generic<? extends String> |
| test.kt:22:15:22:33 | setter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | test.kt:8:3:8:41 | setter | test.kt:1:1:10:1 | Generic<? super String> |
| test.kt:23:15:23:22 | getter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | test.kt:7:3:7:26 | getter | test.kt:1:1:10:1 | Generic<? super String> |
constructors
| Generic2.class:0:0:0:0 | Generic2<? extends String> | Generic2.class:0:0:0:0 | Generic2<? extends String> | Generic2<? extends String>(java.lang.String) | ? extends String | void |
| Generic2.class:0:0:0:0 | Generic2<? super String> | Generic2.class:0:0:0:0 | Generic2<? super String> | Generic2<? super String>(java.lang.Object) | ? super String | void |
| Generic2.class:0:0:0:0 | Generic2<String> | Generic2.class:0:0:0:0 | Generic2<String> | Generic2<String>(java.lang.String) | String | void |
| Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | Generic2(java.lang.Object) | T | void |
| Test.java:14:14:14:17 | Test | Test.java:14:14:14:17 | Test | Test() | No parameters | void |
| test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | Generic | Generic(java.lang.Object) | T | void |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:1:1:10:1 | Generic<? extends String> | Generic<? extends String>(java.lang.Void) | Void | void |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:1:1:10:1 | Generic<? super String> | Generic<? super String>(java.lang.String) | String | void |
| test.kt:1:1:10:1 | Generic<String> | test.kt:1:1:10:1 | Generic<String> | Generic<String>(java.lang.String) | String | void |
constructorCalls
| Test.java:18:34:18:68 | new Generic2<String>(...) | Generic2.class:0:0:0:0 | Generic2<String> |
| test.kt:14:19:14:48 | new Generic(...) | test.kt:1:1:10:1 | Generic |
refTypes
| Test.java:1:7:1:14 | Generic2 |
| Test.java:1:16:1:16 | T |
| Test.java:14:14:14:17 | Test |
| test.kt:0:0:0:0 | TestKt |
| test.kt:1:1:10:1 | Generic |
| test.kt:1:15:1:15 | T |
#select
| Generic2.class:0:0:0:0 | Generic2<? extends String> | Generic2.class:0:0:0:0 | getter | getter() | No parameters | ? extends String |
| Generic2.class:0:0:0:0 | Generic2<? extends String> | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | ? extends String | ? extends String |
| Generic2.class:0:0:0:0 | Generic2<? extends String> | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | ? extends String | ? extends String |
| Generic2.class:0:0:0:0 | Generic2<? extends String> | Generic2.class:0:0:0:0 | setter | setter(java.lang.String) | ? extends String | void |
| Generic2.class:0:0:0:0 | Generic2<? super String> | Generic2.class:0:0:0:0 | getter | getter() | No parameters | ? super String |
| Generic2.class:0:0:0:0 | Generic2<? super String> | Generic2.class:0:0:0:0 | identity | identity(java.lang.Object) | ? super String | ? super String |
| Generic2.class:0:0:0:0 | Generic2<? super String> | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.Object) | ? super String | ? super String |
| Generic2.class:0:0:0:0 | Generic2<? super String> | Generic2.class:0:0:0:0 | setter | setter(java.lang.Object) | ? super String | void |
| Generic2.class:0:0:0:0 | Generic2<String> | Generic2.class:0:0:0:0 | getter | getter() | No parameters | String |
| Generic2.class:0:0:0:0 | Generic2<String> | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | String | String |
| Generic2.class:0:0:0:0 | Generic2<String> | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | String | String |
| Generic2.class:0:0:0:0 | Generic2<String> | Generic2.class:0:0:0:0 | setter | setter(java.lang.String) | String | void |
| Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | identity2(java.lang.Object) | T | T |
| Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | identity(java.lang.Object) | T | T |
| Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | getter() | No parameters | T |
| Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | setter(java.lang.Object) | T | void |
| Test.java:14:14:14:17 | Test | Test.java:16:22:16:25 | user | user() | No parameters | void |
| test.kt:0:0:0:0 | TestKt | test.kt:12:1:25:1 | user | user() | No parameters | void |
| test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | <obinit> | <obinit>() | No parameters | Unit |
| test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | equals | equals(java.lang.Object) | Object | boolean |
| test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | hashCode | hashCode() | No parameters | int |
| test.kt:1:1:10:1 | Generic | test.kt:1:1:10:1 | toString | toString() | No parameters | String |
| test.kt:1:1:10:1 | Generic | test.kt:3:3:3:19 | <get-stored> | <get-stored>() | No parameters | T |
| test.kt:1:1:10:1 | Generic | test.kt:3:3:3:19 | <set-stored> | <set-stored>(java.lang.Object) | T | void |
| test.kt:1:1:10:1 | Generic | test.kt:5:3:5:46 | identity2 | identity2(java.lang.Object) | T | T |
| test.kt:1:1:10:1 | Generic | test.kt:6:3:6:35 | identity | identity(java.lang.Object) | T | T |
| test.kt:1:1:10:1 | Generic | test.kt:7:3:7:26 | getter | getter() | No parameters | T |
| test.kt:1:1:10:1 | Generic | test.kt:8:3:8:41 | setter | setter(java.lang.Object) | T | void |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:1:1:10:1 | equals | equals(java.lang.Object) | Object | boolean |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:1:1:10:1 | hashCode | hashCode() | No parameters | int |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:1:1:10:1 | toString | toString() | No parameters | String |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:3:3:3:19 | <get-stored> | <get-stored>() | No parameters | String |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:3:3:3:19 | <set-stored> | <set-stored>(java.lang.Void) | Void | void |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:5:3:5:46 | identity2 | identity2(java.lang.Void) | Void | String |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:6:3:6:35 | identity | identity(java.lang.Void) | Void | String |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:7:3:7:26 | getter | getter() | No parameters | String |
| test.kt:1:1:10:1 | Generic<? extends String> | test.kt:8:3:8:41 | setter | setter(java.lang.Void) | Void | void |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:1:1:10:1 | equals | equals(java.lang.Object) | Object | boolean |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:1:1:10:1 | hashCode | hashCode() | No parameters | int |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:1:1:10:1 | toString | toString() | No parameters | String |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:3:3:3:19 | <get-stored> | <get-stored>() | No parameters | Object |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:3:3:3:19 | <set-stored> | <set-stored>(java.lang.String) | String | void |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:5:3:5:46 | identity2 | identity2(java.lang.String) | String | Object |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:6:3:6:35 | identity | identity(java.lang.String) | String | Object |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:7:3:7:26 | getter | getter() | No parameters | Object |
| test.kt:1:1:10:1 | Generic<? super String> | test.kt:8:3:8:41 | setter | setter(java.lang.String) | String | void |
| test.kt:1:1:10:1 | Generic<String> | test.kt:1:1:10:1 | equals | equals(java.lang.Object) | Object | boolean |
| test.kt:1:1:10:1 | Generic<String> | test.kt:1:1:10:1 | hashCode | hashCode() | No parameters | int |
| test.kt:1:1:10:1 | Generic<String> | test.kt:1:1:10:1 | toString | toString() | No parameters | String |
| test.kt:1:1:10:1 | Generic<String> | test.kt:3:3:3:19 | <get-stored> | <get-stored>() | No parameters | String |
| test.kt:1:1:10:1 | Generic<String> | test.kt:3:3:3:19 | <set-stored> | <set-stored>(java.lang.String) | String | void |
| test.kt:1:1:10:1 | Generic<String> | test.kt:5:3:5:46 | identity2 | identity2(java.lang.String) | String | String |
| test.kt:1:1:10:1 | Generic<String> | test.kt:6:3:6:35 | identity | identity(java.lang.String) | String | String |
| test.kt:1:1:10:1 | Generic<String> | test.kt:7:3:7:26 | getter | getter() | No parameters | String |
| test.kt:1:1:10:1 | Generic<String> | test.kt:8:3:8:41 | setter | setter(java.lang.String) | String | void |

View File

@@ -0,0 +1,27 @@
class Generic<T>(init: T) {
var stored = init
fun identity2(param: T): T = identity(param)
fun identity(param: T): T = param
fun getter(): T = stored
fun setter(param: T) { stored = param }
}
fun user() {
val invariant = Generic<String>("hello world")
invariant.identity("hello world")
invariant.identity2("hello world")
val projectedOut: Generic<out String> = invariant
projectedOut.getter()
val projectedIn: Generic<in String> = invariant
projectedIn.setter("hi planet")
projectedIn.getter()
}

View File

@@ -0,0 +1,38 @@
import java
string paramTypeIfPresent(Callable m) {
if exists(m.getAParamType())
then result = m.getAParamType().toString()
else result = "No parameters"
}
query predicate calls(MethodAccess ma, Callable caller, RefType callerType, Callable called, RefType calledType) {
ma.getEnclosingCallable() = caller and
ma.getCallee() = called and
caller.fromSource() and
callerType = caller.getDeclaringType() and
calledType = called.getDeclaringType()
}
query predicate constructors(RefType t, Constructor c, string signature, string paramType, string returnType) {
t.getSourceDeclaration().fromSource() and
c.getDeclaringType() = t and
signature = c.getSignature() and
paramType = paramTypeIfPresent(c) and
returnType = c.getReturnType().toString()
}
query predicate constructorCalls(ConstructorCall cc, Constructor callee) {
callee = cc.getConstructor() and
callee.getSourceDeclaration().fromSource()
}
query predicate refTypes(RefType rt) {
rt.fromSource()
}
from RefType t, Method m
where t.getSourceDeclaration().fromSource() and m.getDeclaringType() = t
select t, m, m.getSignature(), paramTypeIfPresent(m), m.getReturnType().toString()

View File

@@ -34,11 +34,25 @@ function
| generics.kt:11:6:11:19 | toString | toString() |
| generics.kt:13:1:18:1 | <obinit> | <obinit>() |
| generics.kt:13:1:18:1 | C1 | C1(java.lang.Object) |
| generics.kt:13:1:18:1 | C1<Integer,Integer> | C1<Integer,Integer>(int) |
| generics.kt:13:1:18:1 | C1<String,Integer> | C1<String,Integer>(java.lang.String) |
| generics.kt:13:1:18:1 | equals | equals(java.lang.Object) |
| generics.kt:13:1:18:1 | equals | equals(java.lang.Object) |
| generics.kt:13:1:18:1 | equals | equals(java.lang.Object) |
| generics.kt:13:1:18:1 | hashCode | hashCode() |
| generics.kt:13:1:18:1 | hashCode | hashCode() |
| generics.kt:13:1:18:1 | hashCode | hashCode() |
| generics.kt:13:1:18:1 | toString | toString() |
| generics.kt:13:1:18:1 | toString | toString() |
| generics.kt:13:1:18:1 | toString | toString() |
| generics.kt:13:16:13:23 | <get-t> | <get-t>() |
| generics.kt:13:16:13:23 | <get-t> | <get-t>() |
| generics.kt:13:16:13:23 | <get-t> | <get-t>() |
| generics.kt:14:5:14:19 | f1 | f1(int) |
| generics.kt:14:5:14:19 | f1 | f1(java.lang.Object) |
| generics.kt:14:5:14:19 | f1 | f1(java.lang.String) |
| generics.kt:15:5:17:5 | f2 | f2(java.lang.Object) |
| generics.kt:15:5:17:5 | f2 | f2(java.lang.Object) |
| generics.kt:15:5:17:5 | f2 | f2(java.lang.Object) |
| generics.kt:20:1:22:1 | <obinit> | <obinit>() |
| generics.kt:20:1:22:1 | C2 | C2() |
@@ -59,8 +73,6 @@ genericFunction
| generics.kt:15:5:17:5 | f2 | generics.kt:15:10:15:10 | U | 0 |
| generics.kt:21:5:21:23 | f4 | generics.kt:21:10:21:10 | P | 0 |
genericCall
| generics.kt:27:17:27:22 | f2(...) | generics.kt:15:10:15:10 | U | String |
| generics.kt:30:17:30:21 | f2(...) | generics.kt:15:10:15:10 | U | Integer |
| generics.kt:32:8:32:12 | f4(...) | generics.kt:21:10:21:10 | P | Integer |
genericCtor
| generics.kt:16:16:16:26 | new C1(...) | 0 | U |