Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions graph/java/engine/resolution/external-types.dl
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,16 @@ simple_name_declared(n) :- lib_type(n, _, _, _, _, _, _, _, _, _, _, _, _, _).
name_resolves_in_file(tn, fp) :- type_resolves_in_file(_, tn, fp, _).
name_resolves_in_file(tn, fp) :- ref_file(_, tn, fp), type_resolves_java_lang(_, tn, _).

// `var` is not a type name (JLS 3.9, 14.4). The parser records `var h = r.lookup(k)` as a
// LOCAL_VARIABLE reference written `var` that nothing resolves, so without this guard every call
// on an inferred local got an `external:var.<m>` boundary beside its real target (3,842 of 85,025
// edges on a 1,360-file project). Its type is the initializer's: local-flow.dl carries a client
// one, and the `var` clause of expr_type below an external one. #1401.
candidate_ref(ref, tn, ctn, fp) :- java_type_reference(kind, ctx, _, _, _, _, _, "0", tn, ctn, _, _, _, _, _, _, _, ref),
ext_ref_kind(kind),
ext_ref_context(ctx),
tn != "",
tn != "var",
ref_file(ref, tn, fp),
!name_resolves_in_file(tn, fp),
!simple_name_declared(tn).
Expand Down Expand Up @@ -149,6 +155,16 @@ expr_type("external", e, x) :- java_expression(_, _, _, _, _, _, _, _, _, _, nam
java_local_variable(name, _, _, _, _, _, _, _, _, _, _, _, _, m, _, _, _, _, local),
!local_creation_pinned(local),
local_var_type_external(local, x).
// A `var` local's static type IS its initializer's (JLS 14.4.1), fixed at the declaration, so a
// reassignment does not widen it. When that initializer is typed external (`var f = source()`
// with `Format source()` unstaged), the local is that external type exactly as `Format f` would
// be. Without it the receiver has no external type at all and the call is a declared unknown.
// java_local_variable (19): 11 isVarInferred 13 method 18 hash
expr_type("external", e, x) :- java_expression(_, _, _, _, _, _, _, _, _, _, name, _, _, _, "LOCAL_VARIABLE", _, _, _, _, _, _, _, _, _, e),
expr_ultimate_method("client", e, m),
java_local_variable(name, _, _, _, _, _, _, _, _, _, _, "true", _, m, _, _, _, _, local),
local_init_expr(local, init),
expr_type("external", init, x).
expr_type("external", e, x) :- java_expression("CAST_EXPRESSION", _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, e),
cast_type_external(e, x).
// A chained receiver whose callee is a client method declared to return the external type.
Expand Down
3 changes: 2 additions & 1 deletion graph/java/engine/resolution/reference-types.dl
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ field_type_resolves(prov, field, ref, t) :- java_type_reference(_, "FIELD_TYPE",
// position-0 references, so each caught type is still captured.
local_var_type_resolves(prov, local, ref, t) :- java_type_reference(_, "LOCAL_VARIABLE", _, _, _, _, _, "0", _, _, _, _, _, _, _, local, "LOCAL_VARIABLE", ref),
type_ref_resolves(prov, ref, t).
// A `var` local has no written type reference, so the rule above yields nothing. Infer
// A `var` local's only reference is written `var` and resolves to nothing, so the rule above
// yields nothing (and external-types.dl keeps it from naming an external type). Infer
// the type from a direct object-creation initializer (var x = new Holder<String>()): the
// created type — carrying its written type arguments — becomes the local's resolved type,
// which feeds callee resolution AND type-argument binding uniformly. Gated on the isVar
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package probe;

public interface Handler {
void handle();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package probe;

public class Registry {
public Handler lookup(String key) {
return null;
}
}
110 changes: 110 additions & 0 deletions graph/test/java/cases/61-var-local-type/src/probe/VarLocals.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package probe;

import java.text.Format;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;

/**
* `var` is a reserved type name (JLS 14.4), so a local declared with it never has a type called
* `var`: its type is the initializer's. Each construct below has a control written with the
* declared type, which must resolve the same way.
*/
public class VarLocals {

/** var from a client call's return: one known edge, no library label. */
void fromCall(Registry r) {
var h = r.lookup("x");
h.handle();
}

/** control: the same call with the type written down. */
void fromCallDeclared(Registry r) {
Handler h = r.lookup("x");
h.handle();
}

/** var from a static factory. */
void fromFactory() {
var w = Widget.create();
w.paint();
}

/** control: var from `new` was already pinned to the created type. */
void fromNew() {
var w = new Widget();
w.paint();
}

/** var from `new`, then reassigned: no longer pinned, still a Widget. */
void fromNewReassigned() {
var w = new Widget();
w = Widget.create();
w.paint();
}

/** var from a literal: String's own method, and nothing named var. */
int fromLiteral() {
var s = "abc";
return s.length();
}

/** control: the literal itself, with no local; a var must answer the same way. */
int literalInline() {
return "abc".length();
}

/** control: the declared String. */
int fromLiteralDeclared() {
String s = "abc";
return s.length();
}

/** var loop variable over a platform list (not staged): unresolved, not a library call. */
void fromEnhancedFor(List<Widget> ws) {
for (var x : ws) {
x.paint();
}
}

/** control: the declared loop variable. */
void fromEnhancedForDeclared(List<Widget> ws) {
for (Widget x : ws) {
x.paint();
}
}

/** var resource in try-with-resources. */
void fromResource() {
try (var w = Widget.create()) {
w.paint();
}
}

/** var from an unstaged platform call: the type is unknown, so the call stays unresolved. */
int fromUnknown() {
var xs = Collections.emptyList();
return xs.size();
}

/** control: a declared external local still types its receiver as that external type. */
String declaredExternal(Object o) {
Format f = source();
return f.format(o);
}

/** var from a client call declared to return an external type: that type, not `var`. */
String varExternal(Object o) {
var f = source();
return f.format(o);
}

Format source() {
return null;
}

/** var lambda parameters (Java 11). */
BiConsumer<Widget, Widget> lambdaParams() {
return (var a, var b) -> a.paint();
}
}
14 changes: 14 additions & 0 deletions graph/test/java/cases/61-var-local-type/src/probe/Widget.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package probe;

public class Widget implements AutoCloseable {
public static Widget create() {
return new Widget();
}

public void paint() {
}

@Override
public void close() {
}
}
4 changes: 0 additions & 4 deletions graph/test/java/expected/36-generic-two-hop.edges
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,12 @@ boundary_lib method probe.TwoHop#nestedArgOnLibRef() -> dep.Lookup#entries()
boundary_lib method probe.TwoHop#nestedArgOnLibRef() -> dep.Pair#value()
boundary_lib method probe.TwoHop#oneHop() -> dep.Box#get(int)
boundary_lib method probe.TwoHop#oneHopVar() -> dep.Box#get(int)
boundary_lib method probe.TwoHop#oneHopVar() -> external:var.get
boundary_lib method probe.TwoHop#twoHopBothVars() -> dep.Box#iterator()
boundary_lib method probe.TwoHop#twoHopBothVars() -> dep.Iter#next()
boundary_lib method probe.TwoHop#twoHopBothVars() -> external:var.iterator
boundary_lib method probe.TwoHop#twoHopBothVars() -> external:var.next
boundary_lib method probe.TwoHop#twoHopChain() -> dep.Box#iterator()
boundary_lib method probe.TwoHop#twoHopChain() -> dep.Iter#next()
boundary_lib method probe.TwoHop#twoHopVar() -> dep.Box#iterator()
boundary_lib method probe.TwoHop#twoHopVar() -> dep.Iter#next()
boundary_lib method probe.TwoHop#twoHopVar() -> external:var.next
known_edge method probe.TwoHop#nestedArgOnClientRef() -> probe.TwoHop#pairs()
known_edge method probe.TwoHop#nestedArgOnClientRef() -> probe.TwoHop.Widget#id()
known_edge method probe.TwoHop#nestedArgOnLibRef() -> probe.TwoHop#lookup()
Expand Down
25 changes: 25 additions & 0 deletions graph/test/java/expected/61-var-local-type.edges
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ambiguous_unknown method probe.VarLocals#fromEnhancedFor(List) -> -
ambiguous_unknown method probe.VarLocals#fromLiteral() -> -
ambiguous_unknown method probe.VarLocals#fromUnknown() -> -
ambiguous_unknown method probe.VarLocals#lambdaParams() -> -
ambiguous_unknown method probe.VarLocals#literalInline() -> -
boundary_lib method probe.VarLocals#declaredExternal(Object) -> external:java.text.Format.format
boundary_lib method probe.VarLocals#fromLiteralDeclared() -> external:String.length
boundary_lib method probe.VarLocals#varExternal(Object) -> external:java.text.Format.format
known_edge method probe.VarLocals#declaredExternal(Object) -> probe.VarLocals#source()
known_edge method probe.VarLocals#fromCall(Registry) -> probe.Handler#handle()
known_edge method probe.VarLocals#fromCall(Registry) -> probe.Registry#lookup(String)
known_edge method probe.VarLocals#fromCallDeclared(Registry) -> probe.Handler#handle()
known_edge method probe.VarLocals#fromCallDeclared(Registry) -> probe.Registry#lookup(String)
known_edge method probe.VarLocals#fromEnhancedForDeclared(List) -> probe.Widget#paint()
known_edge method probe.VarLocals#fromFactory() -> probe.Widget#create()
known_edge method probe.VarLocals#fromFactory() -> probe.Widget#paint()
known_edge method probe.VarLocals#fromNew() -> probe.Widget#paint()
known_edge method probe.VarLocals#fromNewReassigned() -> probe.Widget#create()
known_edge method probe.VarLocals#fromNewReassigned() -> probe.Widget#paint()
known_edge method probe.VarLocals#fromResource() -> probe.Widget#create()
known_edge method probe.VarLocals#fromResource() -> probe.Widget#paint()
known_edge method probe.VarLocals#varExternal(Object) -> probe.VarLocals#source()
known_edge new probe.VarLocals#fromNew() -> probe.Widget#<init>()
known_edge new probe.VarLocals#fromNewReassigned() -> probe.Widget#<init>()
known_edge new probe.Widget#create() -> probe.Widget#<init>()
25 changes: 25 additions & 0 deletions graph/test/java/expected/61-var-local-type.type-use
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ambiguous_unknown ANNOTATION_TYPE 0 probe.Widget [ANNOTATION] -> -
ambiguous_unknown IMPLEMENTS_INTERFACE 0 probe.Widget [TYPE] -> -
ambiguous_unknown LOCAL_VARIABLE 0 probe.VarLocals#declaredExternal(Object) [LOCAL_VARIABLE] -> -
ambiguous_unknown LOCAL_VARIABLE 0 probe.VarLocals#fromLiteralDeclared() [LOCAL_VARIABLE] -> -
ambiguous_unknown METHOD_PARAM 0 probe.Registry#lookup(String) [METHOD_PARAM] -> -
ambiguous_unknown METHOD_PARAM 0 probe.VarLocals#declaredExternal(Object) [METHOD_PARAM] -> -
ambiguous_unknown METHOD_PARAM 0 probe.VarLocals#fromEnhancedFor(List) [METHOD_PARAM] -> -
ambiguous_unknown METHOD_PARAM 0 probe.VarLocals#fromEnhancedForDeclared(List) [METHOD_PARAM] -> -
ambiguous_unknown METHOD_PARAM 0 probe.VarLocals#varExternal(Object) [METHOD_PARAM] -> -
ambiguous_unknown METHOD_RETURN 0 probe.VarLocals#declaredExternal(Object) [METHOD] -> -
ambiguous_unknown METHOD_RETURN 0 probe.VarLocals#lambdaParams() [METHOD] -> -
ambiguous_unknown METHOD_RETURN 0 probe.VarLocals#source() [METHOD] -> -
ambiguous_unknown METHOD_RETURN 0 probe.VarLocals#varExternal(Object) [METHOD] -> -
known_edge LOCAL_VARIABLE 0 probe.VarLocals#fromCallDeclared(Registry) [LOCAL_VARIABLE] -> probe.Handler
known_edge LOCAL_VARIABLE 0 probe.VarLocals#fromEnhancedForDeclared(List) [LOCAL_VARIABLE] -> probe.Widget
known_edge METHOD_PARAM 0 probe.VarLocals#fromCall(Registry) [METHOD_PARAM] -> probe.Registry
known_edge METHOD_PARAM 0 probe.VarLocals#fromCallDeclared(Registry) [METHOD_PARAM] -> probe.Registry
known_edge METHOD_PARAM 1 probe.VarLocals#fromEnhancedFor(List) [METHOD_PARAM] -> probe.Widget
known_edge METHOD_PARAM 1 probe.VarLocals#fromEnhancedForDeclared(List) [METHOD_PARAM] -> probe.Widget
known_edge METHOD_RETURN 0 probe.Registry#lookup(String) [METHOD] -> probe.Handler
known_edge METHOD_RETURN 0 probe.Widget#create() [METHOD] -> probe.Widget
known_edge METHOD_RETURN 1 probe.VarLocals#lambdaParams() [METHOD] -> probe.Widget
known_edge OBJECT_CREATION_TYPE 0 probe.VarLocals#fromNew() [EXPRESSION] -> probe.Widget
known_edge OBJECT_CREATION_TYPE 0 probe.VarLocals#fromNewReassigned() [EXPRESSION] -> probe.Widget
known_edge OBJECT_CREATION_TYPE 0 probe.Widget#create() [EXPRESSION] -> probe.Widget
Loading