Skip to content

Commit 1412c9c

Browse files
committed
Rust: Paths to associated types resolve to the associated type if implementation is unclear
1 parent d7cf14d commit 1412c9c

File tree

6 files changed

+114
-135
lines changed

6 files changed

+114
-135
lines changed

rust/ql/lib/codeql/rust/internal/PathResolution.qll

Lines changed: 77 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -110,18 +110,15 @@ pragma[nomagic]
110110
private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) {
111111
item = result.getImmediateParent() and
112112
name = result.getName() and
113+
// Associated types in `impl` and `trait` blocks are handled elsewhere
114+
not (item instanceof ImplOrTraitItemNode and result instanceof AssocItem) and
113115
// type parameters are only available inside the declaring item
114116
if result instanceof TypeParam
115117
then kind.isInternal()
116118
else
117-
// associated items must always be qualified, also within the declaring
118-
// item (using `Self`)
119-
if item instanceof ImplOrTraitItemNode and result instanceof AssocItem
120-
then kind.isExternal()
121-
else
122-
if result.isPublic()
123-
then kind.isBoth()
124-
else kind.isInternal()
119+
if result.isPublic()
120+
then kind.isBoth()
121+
else kind.isInternal()
125122
}
126123

127124
private module UseOption = Option<Use>;
@@ -327,30 +324,24 @@ abstract class ItemNode extends Locatable {
327324
)
328325
)
329326
or
330-
// a trait has access to the associated items of its supertraits
331-
this =
332-
any(TraitItemNodeImpl trait |
333-
result = trait.resolveABoundCand().getASuccessor(name, kind, useOpt) and
334-
kind.isExternalOrBoth() and
335-
result instanceof AssocItemNode and
336-
not trait.hasAssocItem(name)
337-
)
327+
exists(TraitItemNodeImpl trait | this = trait |
328+
result = trait.getAssocItem(name)
329+
or
330+
// a trait has access to the associated items of its supertraits
331+
not trait.hasAssocItem(name) and
332+
result = trait.resolveABoundCand().getASuccessor(name).(AssocItemNode)
333+
) and
334+
kind.isExternal() and
335+
useOpt.isNone()
338336
or
339337
// items made available by an implementation where `this` is the implementing type
340-
typeImplEdge(this, _, name, kind, result, useOpt)
341-
or
342-
// trait items with default implementations made available in an implementation
343-
exists(ImplItemNodeImpl impl, TraitItemNode trait |
344-
this = impl and
345-
trait = impl.resolveTraitTyCand() and
346-
result = trait.getASuccessor(name, kind, useOpt) and
347-
// do not inherit default implementations from super traits; those are inherited by
348-
// their `impl` blocks
349-
result = trait.getAssocItem(name) and
350-
result.(AssocItemNode).hasImplementation() and
351-
kind.isExternalOrBoth() and
352-
not impl.hasAssocItem(name)
353-
)
338+
typeImplEdge(this, _, name, result) and
339+
kind.isExternal() and
340+
useOpt.isNone()
341+
or
342+
implEdge(this, name, result) and
343+
kind.isExternal() and
344+
useOpt.isNone()
354345
or
355346
// type parameters have access to the associated items of its bounds
356347
result =
@@ -413,14 +404,8 @@ abstract class ItemNode extends Locatable {
413404
this instanceof SourceFile and
414405
builtin(name, result)
415406
or
416-
exists(ImplOrTraitItemNode i |
417-
name = "Self" and
418-
this = i.getAnItemInSelfScope()
419-
|
420-
result = i.(Trait)
421-
or
422-
result = i.(ImplItemNodeImpl).resolveSelfTyCand()
423-
)
407+
name = "Self" and
408+
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
424409
or
425410
name = "crate" and
426411
this = result.(CrateItemNode).getASourceFile()
@@ -755,7 +740,7 @@ abstract class ImplOrTraitItemNode extends ItemNode {
755740
}
756741

757742
/** Gets an associated item belonging to this trait or `impl` block. */
758-
abstract AssocItemNode getAnAssocItem();
743+
AssocItemNode getAnAssocItem() { result = this.getADescendant() }
759744

760745
/** Gets the associated item named `name` belonging to this trait or `impl` block. */
761746
pragma[nomagic]
@@ -807,8 +792,6 @@ final class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
807792

808793
TraitItemNode resolveTraitTy() { result = resolvePath(this.getTraitPath()) }
809794

810-
override AssocItemNode getAnAssocItem() { result = this.getADescendant() }
811-
812795
override string getName() { result = "(impl)" }
813796

814797
override Namespace getNamespace() {
@@ -985,6 +968,18 @@ private class ImplItemNodeImpl extends ImplItemNode {
985968
}
986969

987970
TraitItemNodeImpl resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }
971+
972+
/**
973+
* Gets the associated item named `name` in this impl block or the default
974+
* inherited from the trait being implemented.
975+
*/
976+
AssocItemNode getAssocItemOrDefault(string name) {
977+
result = this.getAssocItem(name)
978+
or
979+
not this.hasAssocItem(name) and
980+
result = this.resolveTraitTyCand().getAssocItem(name) and
981+
result.hasImplementation()
982+
}
988983
}
989984

990985
private class StructItemNode extends TypeItemTypeItemNode, ParameterizableItemNode instanceof Struct
@@ -1020,8 +1015,6 @@ final class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof T
10201015

10211016
ItemNode resolveABound() { result = this.resolveBound(_) }
10221017

1023-
override AssocItemNode getAnAssocItem() { result = this.getADescendant() }
1024-
10251018
override string getName() { result = Trait.super.getName().getText() }
10261019

10271020
override Namespace getNamespace() { result.isType() }
@@ -1852,35 +1845,12 @@ private predicate checkQualifiedVisibility(
18521845
not i instanceof TypeParam
18531846
}
18541847

1855-
pragma[nomagic]
1856-
private predicate isImplSelfQualifiedPath(
1857-
ImplItemNode impl, PathExt qualifier, PathExt path, string name
1858-
) {
1859-
qualifier = impl.getASelfPath() and
1860-
qualifier = path.getQualifier() and
1861-
name = path.getText()
1862-
}
1863-
1864-
private ItemNode resolveImplSelfQualified(PathExt qualifier, PathExt path, Namespace ns) {
1865-
exists(ImplItemNode impl, string name |
1866-
isImplSelfQualifiedPath(impl, qualifier, path, name) and
1867-
result = impl.getAssocItem(name) and
1868-
ns = result.getNamespace()
1869-
)
1870-
}
1871-
18721848
/**
18731849
* Gets the item that `path` resolves to in `ns` when `qualifier` is the
18741850
* qualifier of `path` and `qualifier` resolves to `q`, if any.
18751851
*/
18761852
pragma[nomagic]
18771853
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
1878-
// Special case for `Self::Assoc`; this always refers to the associated
1879-
// item in the enclosing `impl` block, if available.
1880-
q = resolvePathCandQualifier(qualifier, path, _) and
1881-
result = resolveImplSelfQualified(qualifier, path, ns)
1882-
or
1883-
not exists(resolveImplSelfQualified(qualifier, path, ns)) and
18841854
exists(string name, SuccessorKind kind, UseOption useOpt |
18851855
q = resolvePathCandQualifier(qualifier, path, name) and
18861856
result = getASuccessor(q, name, ns, kind, useOpt) and
@@ -1940,6 +1910,37 @@ private predicate macroExportEdge(CrateItemNode crate, string name, MacroItemNod
19401910
name = macro.getName()
19411911
}
19421912

1913+
/**
1914+
* Holds if a `Self` path inside `impl` might refer to a function named `name`
1915+
* from another impl block.
1916+
*/
1917+
pragma[nomagic]
1918+
private predicate relevantSelfFunctionName(ImplItemNodeImpl impl, string name) {
1919+
any(Path path | path.getQualifier() = impl.getASelfPath()).getText() = name and
1920+
not impl.hasAssocItem(name)
1921+
}
1922+
1923+
/**
1924+
* Holds if `impl` has a `node` available externally at `name`.
1925+
*
1926+
* Since `Self` in an impl block resolves to the impl block, this corresponds to
1927+
* the items that should be available on `Self` within the `impl` block.
1928+
*/
1929+
private predicate implEdge(ImplItemNodeImpl impl, string name, AssocItemNode node) {
1930+
node = impl.getAssocItemOrDefault(name)
1931+
or
1932+
// Associated types from the implemented trait are available on `Self`.
1933+
not impl.hasAssocItem(name) and
1934+
node = impl.resolveTraitTyCand().getASuccessor(name).(TypeAliasItemNode)
1935+
or
1936+
// Items available on the implementing type are available on `Self`. We only
1937+
// add these edges when they are relevant. If a type has `n` impl blocks with
1938+
// `m` functions each, we would otherwise end up always constructing somethong
1939+
// proportional to `O(n * m)`.
1940+
relevantSelfFunctionName(impl, name) and
1941+
node = impl.resolveSelfTyCand().getASuccessor(name)
1942+
}
1943+
19431944
/**
19441945
* Holds if item `i` contains a `mod` or `extern crate` definition that
19451946
* makes the macro `macro` named `name` available using a `#[macro_use]`
@@ -1984,7 +1985,9 @@ private ItemNode resolvePathCand(PathExt path) {
19841985
then result instanceof TraitItemNode
19851986
else
19861987
if path = any(PathTypeRepr p).getPath()
1987-
then result instanceof TypeItemNode
1988+
then
1989+
result instanceof TypeItemNode or
1990+
result instanceof ImplItemNodeImpl
19881991
else
19891992
if path instanceof IdentPat
19901993
then
@@ -2011,7 +2014,7 @@ private ItemNode resolvePathCand(PathExt path) {
20112014
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
20122015
exists(TypeItemNode type, ImplItemNodeImpl impl |
20132016
node = resolvePathCandQualified(_, type, path, _) and
2014-
typeImplEdge(type, impl, _, _, node, _) and
2017+
typeImplEdge(type, impl, _, node) and
20152018
result = impl.resolveTraitTyCand()
20162019
)
20172020
}
@@ -2182,12 +2185,14 @@ private predicate externCrateEdge(
21822185
* makes `assoc` available as `name` at `kind`.
21832186
*/
21842187
private predicate typeImplEdge(
2185-
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind,
2186-
AssocItemNode assoc, UseOption useOpt
2188+
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, AssocItemNode assoc
21872189
) {
2190+
assoc = impl.getAssocItemOrDefault(name) and
21882191
typeItem = impl.resolveSelfTyCand() and
2189-
assoc = impl.getASuccessor(name, kind, useOpt) and
2190-
kind.isExternalOrBoth()
2192+
// Functions in `impl` blocks are made available on the implementing type
2193+
// (e.g., `S::fun` is valid) but associated types are not (e.g., `S::Output`
2194+
// is invalid).
2195+
(assoc instanceof FunctionItemNode or assoc instanceof ConstItemNode)
21912196
}
21922197

21932198
pragma[nomagic]

rust/ql/test/library-tests/definitions/Definitions.expected

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
| main.rs:3:5:3:7 | lib | lib.rs:1:1:1:1 | SourceFile | file |
22
| main.rs:9:14:9:14 | S | main.rs:7:9:7:21 | struct S | path |
3-
| main.rs:10:36:10:39 | Self | main.rs:7:9:7:21 | struct S | path |
3+
| main.rs:10:36:10:39 | Self | main.rs:9:9:13:9 | impl S { ... } | path |
44
| main.rs:11:17:11:17 | S | main.rs:7:9:7:21 | struct S | path |
55
| main.rs:16:22:16:22 | T | main.rs:16:19:16:19 | T | path |
66
| main.rs:18:13:18:14 | S2 | main.rs:16:5:16:24 | struct S2 | path |
77
| main.rs:18:16:18:16 | T | main.rs:18:10:18:10 | T | path |
88
| main.rs:19:23:19:23 | T | main.rs:18:10:18:10 | T | path |
9-
| main.rs:19:29:19:32 | Self | main.rs:16:5:16:24 | struct S2 | path |
9+
| main.rs:19:29:19:32 | Self | main.rs:18:5:22:5 | impl S2::<...> { ... } | path |
1010
| main.rs:20:13:20:14 | S2 | main.rs:16:5:16:24 | struct S2 | path |
1111
| main.rs:20:16:20:16 | x | main.rs:19:20:19:20 | x | local variable |
1212
| main.rs:29:5:29:11 | println | {EXTERNAL LOCATION} | MacroRules | path |

rust/ql/test/library-tests/path-resolution/main.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -716,13 +716,13 @@ mod m23 {
716716

717717
#[rustfmt::skip]
718718
impl Trait1<
719-
Self // $ SPURIOUS: item=I4 MISSING: item=implTrait1forS
719+
Self // $ item=implTrait1forS
720720
> // $ item=I2
721721
for S { // $ item=I4
722722
fn f(&self) {
723723
println!("m23::<S as Trait1<S>>::f"); // $ item=println
724724
} // I5
725-
}
725+
} // implTrait1forS
726726

727727
#[rustfmt::skip]
728728
pub fn f() {
@@ -899,14 +899,14 @@ mod associated_types_subtrait {
899899

900900
#[rustfmt::skip]
901901
impl Sub for S<i32> { // $ item=Sub item=S item=i32
902-
fn f() -> Self::Out { // $ MISSING: item=SuperAssoc SPURIOUS: item=S<i32>::Out item=S<bool>::Out item=S<A>::Out
902+
fn f() -> Self::Out { // $ item=SuperAssoc
903903
'a'
904904
}
905905
}
906906

907907
#[rustfmt::skip]
908908
impl Sub for S<bool> { // $ item=Sub item=S item=bool
909-
fn f() -> Self::Out { // $ MISSING: item=SuperAssoc SPURIOUS: item=S<i32>::Out item=S<bool>::Out item=S<A>::Out
909+
fn f() -> Self::Out { // $ item=SuperAssoc
910910
1
911911
}
912912
}
@@ -929,7 +929,7 @@ mod associated_types_subtrait {
929929

930930
#[rustfmt::skip]
931931
impl<A> SubAlt for S<A> { // $ item=SubAlt item=S item=A
932-
fn f(self) -> Self::Out { // $ MISSING: item=SuperAltAssoc SPURIOUS: item=S<i32>::Out item=S<bool>::Out item=S<A>::Out
932+
fn f(self) -> Self::Out { // $ item=SuperAltAssoc
933933
self.0
934934
}
935935
}

0 commit comments

Comments
 (0)