Skip to content

Commit fc3e490

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

File tree

5 files changed

+114
-133
lines changed

5 files changed

+114
-133
lines changed

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

Lines changed: 79 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 only 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,26 @@ 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 |
328+
this = trait and
329+
kind.isExternal() and
330+
useOpt.isNone()
331+
|
332+
result = trait.getAssocItem(name)
333+
or
334+
// a trait has access to the associated items of its supertraits
335+
not trait.hasAssocItem(name) and
336+
result = trait.resolveABoundCand().getASuccessor(name).(AssocItemNode)
337+
)
338338
or
339339
// 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-
)
340+
typeImplEdge(this, _, name, result) and
341+
kind.isExternal() and
342+
useOpt.isNone()
343+
or
344+
implEdge(this, name, result) and
345+
kind.isExternal() and
346+
useOpt.isNone()
354347
or
355348
// type parameters have access to the associated items of its bounds
356349
result =
@@ -413,14 +406,8 @@ abstract class ItemNode extends Locatable {
413406
this instanceof SourceFile and
414407
builtin(name, result)
415408
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-
)
409+
name = "Self" and
410+
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
424411
or
425412
name = "crate" and
426413
this = result.(CrateItemNode).getASourceFile()
@@ -755,7 +742,7 @@ abstract class ImplOrTraitItemNode extends ItemNode {
755742
}
756743

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

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

808795
TraitItemNode resolveTraitTy() { result = resolvePath(this.getTraitPath()) }
809796

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

814799
override Namespace getNamespace() {
@@ -985,6 +970,18 @@ private class ImplItemNodeImpl extends ImplItemNode {
985970
}
986971

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

990987
private class StructItemNode extends TypeItemTypeItemNode, ParameterizableItemNode instanceof Struct
@@ -1020,8 +1017,6 @@ final class TraitItemNode extends ImplOrTraitItemNode, TypeItemNode instanceof T
10201017

10211018
ItemNode resolveABound() { result = this.resolveBound(_) }
10221019

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

10271022
override Namespace getNamespace() { result.isType() }
@@ -1852,35 +1847,12 @@ private predicate checkQualifiedVisibility(
18521847
not i instanceof TypeParam
18531848
}
18541849

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-
18721850
/**
18731851
* Gets the item that `path` resolves to in `ns` when `qualifier` is the
18741852
* qualifier of `path` and `qualifier` resolves to `q`, if any.
18751853
*/
18761854
pragma[nomagic]
18771855
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
18841856
exists(string name, SuccessorKind kind, UseOption useOpt |
18851857
q = resolvePathCandQualifier(qualifier, path, name) and
18861858
result = getASuccessor(q, name, ns, kind, useOpt) and
@@ -1940,6 +1912,37 @@ private predicate macroExportEdge(CrateItemNode crate, string name, MacroItemNod
19401912
name = macro.getName()
19411913
}
19421914

1915+
/**
1916+
* Holds if a `Self` path inside `impl` might refer to a function named `name`
1917+
* from another impl block.
1918+
*/
1919+
pragma[nomagic]
1920+
private predicate relevantSelfFunctionName(ImplItemNodeImpl impl, string name) {
1921+
any(Path path | path.getQualifier() = impl.getASelfPath()).getText() = name and
1922+
not impl.hasAssocItem(name)
1923+
}
1924+
1925+
/**
1926+
* Holds if `impl` has a `node` available externally at `name`.
1927+
*
1928+
* Since `Self` in an impl block resolves to the impl block, this corresponds to
1929+
* the items that should be available on `Self` within the `impl` block.
1930+
*/
1931+
private predicate implEdge(ImplItemNodeImpl impl, string name, AssocItemNode node) {
1932+
node = impl.getAssocItemOrDefault(name)
1933+
or
1934+
// Associated types from the implemented trait are available on `Self`.
1935+
not impl.hasAssocItem(name) and
1936+
node = impl.resolveTraitTyCand().getASuccessor(name).(TypeAliasItemNode)
1937+
or
1938+
// Items available on the implementing type are available on `Self`. We only
1939+
// add these edges when they are relevant. If a type has `n` impl blocks with
1940+
// `m` functions each, we would otherwise end up always constructing somethong
1941+
// proportional to `O(n * m)`.
1942+
relevantSelfFunctionName(impl, name) and
1943+
node = impl.resolveSelfTyCand().getASuccessor(name)
1944+
}
1945+
19431946
/**
19441947
* Holds if item `i` contains a `mod` or `extern crate` definition that
19451948
* makes the macro `macro` named `name` available using a `#[macro_use]`
@@ -1984,7 +1987,9 @@ private ItemNode resolvePathCand(PathExt path) {
19841987
then result instanceof TraitItemNode
19851988
else
19861989
if path = any(PathTypeRepr p).getPath()
1987-
then result instanceof TypeItemNode
1990+
then
1991+
result instanceof TypeItemNode or
1992+
result instanceof ImplItemNodeImpl
19881993
else
19891994
if path instanceof IdentPat
19901995
then
@@ -2011,7 +2016,7 @@ private ItemNode resolvePathCand(PathExt path) {
20112016
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
20122017
exists(TypeItemNode type, ImplItemNodeImpl impl |
20132018
node = resolvePathCandQualified(_, type, path, _) and
2014-
typeImplEdge(type, impl, _, _, node, _) and
2019+
typeImplEdge(type, impl, _, node) and
20152020
result = impl.resolveTraitTyCand()
20162021
)
20172022
}
@@ -2182,12 +2187,14 @@ private predicate externCrateEdge(
21822187
* makes `assoc` available as `name` at `kind`.
21832188
*/
21842189
private predicate typeImplEdge(
2185-
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind,
2186-
AssocItemNode assoc, UseOption useOpt
2190+
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, AssocItemNode assoc
21872191
) {
2192+
assoc = impl.getAssocItemOrDefault(name) and
21882193
typeItem = impl.resolveSelfTyCand() and
2189-
assoc = impl.getASuccessor(name, kind, useOpt) and
2190-
kind.isExternalOrBoth()
2194+
// Functions in `impl` blocks are made available on the implementing type
2195+
// (e.g., `S::fun` is valid) but associated types are not (e.g., `S::Output`
2196+
// is invalid).
2197+
(assoc instanceof FunctionItemNode or assoc instanceof ConstItemNode)
21912198
}
21922199

21932200
pragma[nomagic]

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)