Skip to content
Draft
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
159 changes: 86 additions & 73 deletions rust/ql/lib/codeql/rust/internal/PathResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -110,18 +110,15 @@ pragma[nomagic]
private ItemNode getAChildSuccessor(ItemNode item, string name, SuccessorKind kind) {
item = result.getImmediateParent() and
name = result.getName() and
// Associated types in `impl` and `trait` blocks are handled elsewhere
not (item instanceof ImplOrTraitItemNode and result instanceof AssocItem) and
// type parameters are only available inside the declaring item
if result instanceof TypeParam
then kind.isInternal()
else
// associated items must always be qualified, also within the declaring
// item (using `Self`)
if item instanceof ImplOrTraitItemNode and result instanceof AssocItem
then kind.isExternal()
else
if result.isPublic()
then kind.isBoth()
else kind.isInternal()
if result.isPublic()
then kind.isBoth()
else kind.isInternal()
}

private module UseOption = Option<Use>;
Expand Down Expand Up @@ -327,30 +324,24 @@ abstract class ItemNode extends Locatable {
)
)
or
// a trait has access to the associated items of its supertraits
this =
any(TraitItemNodeImpl trait |
result = trait.resolveABoundCand().getASuccessor(name, kind, useOpt) and
kind.isExternalOrBoth() and
result instanceof AssocItemNode and
not trait.hasAssocItem(name)
)
exists(TraitItemNodeImpl trait | this = trait |
result = trait.getAssocItem(name)
or
// a trait has access to the associated items of its supertraits
not trait.hasAssocItem(name) and
result = trait.resolveABoundCand().getASuccessor(name).(AssocItemNode)
) and
kind.isExternal() and
useOpt.isNone()
or
// items made available by an implementation where `this` is the implementing type
typeImplEdge(this, _, name, kind, result, useOpt)
or
// trait items with default implementations made available in an implementation
exists(ImplItemNodeImpl impl, TraitItemNode trait |
this = impl and
trait = impl.resolveTraitTyCand() and
result = trait.getASuccessor(name, kind, useOpt) and
// do not inherit default implementations from super traits; those are inherited by
// their `impl` blocks
result = trait.getAssocItem(name) and
result.(AssocItemNode).hasImplementation() and
kind.isExternalOrBoth() and
not impl.hasAssocItem(name)
)
typeImplEdge(this, _, name, result) and
kind.isExternal() and
useOpt.isNone()
or
implEdge(this, name, result) and
kind.isExternal() and
useOpt.isNone()
or
// type parameters have access to the associated items of its bounds
result =
Expand Down Expand Up @@ -413,14 +404,8 @@ abstract class ItemNode extends Locatable {
this instanceof SourceFile and
builtin(name, result)
or
exists(ImplOrTraitItemNode i |
name = "Self" and
this = i.getAnItemInSelfScope()
|
result = i.(Trait)
or
result = i.(ImplItemNodeImpl).resolveSelfTyCand()
)
name = "Self" and
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
or
name = "crate" and
this = result.(CrateItemNode).getASourceFile()
Expand Down Expand Up @@ -755,7 +740,7 @@ abstract class ImplOrTraitItemNode extends ItemNode {
}

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

/** Gets the associated item named `name` belonging to this trait or `impl` block. */
pragma[nomagic]
Expand Down Expand Up @@ -807,12 +792,12 @@ final class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {

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

override AssocItemNode getAnAssocItem() { result = this.getADescendant() }

override string getName() { result = "(impl)" }

override Namespace getNamespace() {
result.isType() // can be referenced with `Self`
// `impl` blocks are refered to using `Self` paths which can appear both as
// types and as values (when the implementing type is a tuple-like struct).
result.isType() or result.isValue()
}

override TypeParam getTypeParam(int i) { result = super.getGenericParamList().getTypeParam(i) }
Expand Down Expand Up @@ -985,6 +970,18 @@ private class ImplItemNodeImpl extends ImplItemNode {
}

TraitItemNodeImpl resolveTraitTyCand() { result = resolvePathCand(this.getTraitPath()) }

/**
* Gets the associated item named `name` in this impl block or the default
* inherited from the trait being implemented.
*/
AssocItemNode getAssocItemOrDefault(string name) {
result = this.getAssocItem(name)
or
not this.hasAssocItem(name) and
result = this.resolveTraitTyCand().getAssocItem(name) and
result.hasImplementation()
}
}

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

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

override AssocItemNode getAnAssocItem() { result = this.getADescendant() }

override string getName() { result = Trait.super.getName().getText() }

override Namespace getNamespace() { result.isType() }
Expand Down Expand Up @@ -1790,7 +1785,15 @@ private module DollarCrateResolution {

pragma[nomagic]
private ItemNode resolvePathCand0(PathExt path, Namespace ns) {
result = unqualifiedPathLookup(path, ns, _)
exists(ItemNode res |
res = unqualifiedPathLookup(path, ns, _) and
if
not any(PathExt parent).getQualifier() = path and
isUnqualifiedSelfPath(path) and
res instanceof ImplItemNode
then result = res.(ImplItemNodeImpl).resolveSelfTyCand()
else result = res
)
or
DollarCrateResolution::resolveDollarCrate(path, result) and
ns = result.getNamespace()
Expand Down Expand Up @@ -1852,35 +1855,12 @@ private predicate checkQualifiedVisibility(
not i instanceof TypeParam
}

pragma[nomagic]
private predicate isImplSelfQualifiedPath(
ImplItemNode impl, PathExt qualifier, PathExt path, string name
) {
qualifier = impl.getASelfPath() and
qualifier = path.getQualifier() and
name = path.getText()
}

private ItemNode resolveImplSelfQualified(PathExt qualifier, PathExt path, Namespace ns) {
exists(ImplItemNode impl, string name |
isImplSelfQualifiedPath(impl, qualifier, path, name) and
result = impl.getAssocItem(name) and
ns = result.getNamespace()
)
}

/**
* Gets the item that `path` resolves to in `ns` when `qualifier` is the
* qualifier of `path` and `qualifier` resolves to `q`, if any.
*/
pragma[nomagic]
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
// Special case for `Self::Assoc`; this always refers to the associated
// item in the enclosing `impl` block, if available.
q = resolvePathCandQualifier(qualifier, path, _) and
result = resolveImplSelfQualified(qualifier, path, ns)
or
not exists(resolveImplSelfQualified(qualifier, path, ns)) and
exists(string name, SuccessorKind kind, UseOption useOpt |
q = resolvePathCandQualifier(qualifier, path, name) and
result = getASuccessor(q, name, ns, kind, useOpt) and
Expand Down Expand Up @@ -1940,6 +1920,37 @@ private predicate macroExportEdge(CrateItemNode crate, string name, MacroItemNod
name = macro.getName()
}

/**
* Holds if a `Self` path inside `impl` might refer to a function named `name`
* from another impl block.
*/
pragma[nomagic]
private predicate relevantSelfFunctionName(ImplItemNodeImpl impl, string name) {
any(Path path | path.getQualifier() = impl.getASelfPath()).getText() = name and
not impl.hasAssocItem(name)
}

/**
* Holds if `impl` has a `node` available externally at `name`.
*
* Since `Self` in an impl block resolves to the impl block, this corresponds to
* the items that should be available on `Self` within the `impl` block.
*/
private predicate implEdge(ImplItemNodeImpl impl, string name, AssocItemNode node) {
node = impl.getAssocItemOrDefault(name)
or
// Associated types from the implemented trait are available on `Self`.
not impl.hasAssocItem(name) and
node = impl.resolveTraitTyCand().getASuccessor(name).(TypeAliasItemNode)
or
// Items available on the implementing type are available on `Self`. We only
// add these edges when they are relevant. If a type has `n` impl blocks with
// `m` functions each, we would otherwise end up always constructing somethong
// proportional to `O(n * m)`.
relevantSelfFunctionName(impl, name) and
node = impl.resolveSelfTyCand().getASuccessor(name)
}

/**
* Holds if item `i` contains a `mod` or `extern crate` definition that
* makes the macro `macro` named `name` available using a `#[macro_use]`
Expand Down Expand Up @@ -2011,7 +2022,7 @@ private ItemNode resolvePathCand(PathExt path) {
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
exists(TypeItemNode type, ImplItemNodeImpl impl |
node = resolvePathCandQualified(_, type, path, _) and
typeImplEdge(type, impl, _, _, node, _) and
typeImplEdge(type, impl, _, node) and
result = impl.resolveTraitTyCand()
)
}
Expand Down Expand Up @@ -2182,12 +2193,14 @@ private predicate externCrateEdge(
* makes `assoc` available as `name` at `kind`.
*/
private predicate typeImplEdge(
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, SuccessorKind kind,
AssocItemNode assoc, UseOption useOpt
TypeItemNode typeItem, ImplItemNodeImpl impl, string name, AssocItemNode assoc
) {
assoc = impl.getAssocItemOrDefault(name) and
typeItem = impl.resolveSelfTyCand() and
assoc = impl.getASuccessor(name, kind, useOpt) and
kind.isExternalOrBoth()
// Functions in `impl` blocks are made available on the implementing type
// (e.g., `S::fun` is valid) but associated types are not (e.g., `S::Output`
// is invalid).
(assoc instanceof FunctionItemNode or assoc instanceof ConstItemNode)
}

pragma[nomagic]
Expand Down
4 changes: 2 additions & 2 deletions rust/ql/test/library-tests/definitions/Definitions.expected
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
| main.rs:3:5:3:7 | lib | lib.rs:1:1:1:1 | SourceFile | file |
| main.rs:9:14:9:14 | S | main.rs:7:9:7:21 | struct S | path |
| main.rs:10:36:10:39 | Self | main.rs:7:9:7:21 | struct S | path |
| main.rs:10:36:10:39 | Self | main.rs:9:9:13:9 | impl S { ... } | path |
| main.rs:11:17:11:17 | S | main.rs:7:9:7:21 | struct S | path |
| main.rs:16:22:16:22 | T | main.rs:16:19:16:19 | T | path |
| main.rs:18:13:18:14 | S2 | main.rs:16:5:16:24 | struct S2 | path |
| main.rs:18:16:18:16 | T | main.rs:18:10:18:10 | T | path |
| main.rs:19:23:19:23 | T | main.rs:18:10:18:10 | T | path |
| main.rs:19:29:19:32 | Self | main.rs:16:5:16:24 | struct S2 | path |
| main.rs:19:29:19:32 | Self | main.rs:18:5:22:5 | impl S2::<...> { ... } | path |
| main.rs:20:13:20:14 | S2 | main.rs:16:5:16:24 | struct S2 | path |
| main.rs:20:16:20:16 | x | main.rs:19:20:19:20 | x | local variable |
| main.rs:29:5:29:11 | println | {EXTERNAL LOCATION} | MacroRules | path |
Expand Down
94 changes: 93 additions & 1 deletion rust/ql/test/library-tests/path-resolution/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ mod m23 {
fn f(&self) {
println!("m23::<S as Trait1<S>>::f"); // $ item=println
} // I5
}
} // implTrait1forS

#[rustfmt::skip]
pub fn f() {
Expand Down Expand Up @@ -870,6 +870,71 @@ mod associated_types {
}
}

mod associated_types_subtrait {
trait Super {
type Out; // SuperAssoc
} // Super

trait Sub: Super // $ item=Super
{
fn f() -> Self::Out // $ item=SuperAssoc
; // Sub_f
} // Sub

struct S<ST>(
ST, // $ item=ST
);

#[rustfmt::skip]
impl Super for S<i32> { // $ item=Super item=S item=i32
type Out = char // $ item=char
; // S<i32>::Out
}

#[rustfmt::skip]
impl Super for S<bool> { // $ item=Super item=S item=bool
type Out = i64 // $ item=i64
; // S<bool>::Out
}

#[rustfmt::skip]
impl Sub for S<i32> { // $ item=Sub item=S item=i32
fn f() -> Self::Out { // $ item=SuperAssoc
'a'
}
}

#[rustfmt::skip]
impl Sub for S<bool> { // $ item=Sub item=S item=bool
fn f() -> Self::Out { // $ item=SuperAssoc
1
}
}

trait SuperAlt {
type Out; // SuperAltAssoc
} // SuperAlt

trait SubAlt: SuperAlt // $ item=SuperAlt
{
fn f(self) -> Self::Out // $ item=SuperAltAssoc
; // SubAlt_f
} // SubAlt

#[rustfmt::skip]
impl<A> SuperAlt for S<A> { // $ item=SuperAlt item=S item=A
type Out = A // $ item=A
; // S<A>::Out
}

#[rustfmt::skip]
impl<A> SubAlt for S<A> { // $ item=SubAlt item=S item=A
fn f(self) -> Self::Out { // $ item=SuperAltAssoc
self.0
}
}
}

use std::{self as ztd}; // $ item=std

fn use_ztd(x: ztd::string::String) {} // $ item=String
Expand Down Expand Up @@ -938,6 +1003,33 @@ mod patterns {
}
}

/// Tests for refering to constructors via `Self`
mod self_constructors {
struct TupleStruct(i32); // $ item=i32

#[rustfmt::skip]
impl TupleStruct { // $ item=TupleStruct
#[rustfmt::skip]
fn new(x: i32) -> Self { // $ item=i32 item=TupleStruct
let _ = Self(0); // $ item=TupleStruct
let constructor = Self; // $ item=TupleStruct
constructor(x)
} // new
} // ImplTupleStruct

struct StructStruct {
a: i32, // $ item=i32
}

#[rustfmt::skip]
impl StructStruct { // $ item=StructStruct
#[rustfmt::skip]
fn new(a: i32) -> Self { // $ item=i32 item=StructStruct
Self { a } // $ item=StructStruct
} // new
} // ImplStructStruct
}

fn main() {
my::nested::nested1::nested2::f(); // $ item=I4
my::f(); // $ item=I38
Expand Down
Loading
Loading