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
47 changes: 42 additions & 5 deletions rust/ql/lib/codeql/rust/internal/PathResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ abstract class ItemNode extends Locatable {
// items made available through `use` are available to nodes that contain the `use`
exists(UseItemNode use |
use = this.getASuccessor(_, _) and
result = use.(ItemNode).getASuccessor(name, kind)
result = use.getASuccessor(name, kind)
)
or
exists(ExternCrateItemNode ec | result = ec.(ItemNode).getASuccessor(name, kind) |
Expand Down Expand Up @@ -1311,6 +1311,7 @@ private predicate declares(ItemNode item, Namespace ns, string name) {
class RelevantPath extends Path {
RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() }

/** Holds if this is an unqualified path with the textual value `name`. */
pragma[nomagic]
predicate isUnqualified(string name) {
not exists(this.getQualifier()) and
Expand Down Expand Up @@ -1421,6 +1422,12 @@ private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKi
pragma[nomagic]
private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") }

/** Holds if the trait `trait` is visible at the element `element`. */
bindingset[element, trait]
predicate traitIsVisible(Element element, TraitItemNode trait) {
exists(ItemNode encl | encl.getADescendant*() = element and trait = encl.getASuccessor(_, _))
}

pragma[nomagic]
private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) {
exists(ItemNode res |
Expand All @@ -1447,7 +1454,15 @@ private ItemNode resolvePathCandQualifier(RelevantPath qualifier, RelevantPath p
}

pragma[nomagic]
private ItemNode resolvePathCandQualified(
private TraitItemNode assocItemImplementsTrait(AssocItemNode assoc) {
exists(ImplItemNodeImpl impl |
impl.getAnAssocItem() = assoc and
result = impl.resolveTraitTyCand()
)
}

pragma[nomagic]
private ItemNode resolvePathCandQualified0(
RelevantPath qualifier, ItemNode q, RelevantPath path, Namespace ns
) {
exists(string name, SuccessorKind kind |
Expand All @@ -1457,6 +1472,20 @@ private ItemNode resolvePathCandQualified(
)
}

pragma[nomagic]
private ItemNode resolvePathCandQualified(
RelevantPath qualifier, ItemNode q, RelevantPath path, Namespace ns
) {
result = resolvePathCandQualified0(qualifier, q, path, ns) and
(
// When the result is an associated item of a trait implementation the
// implemented trait must be visible.
traitIsVisible(path, assocItemImplementsTrait(pragma[only_bind_out](result)))
or
not exists(ImplItemNode impl | impl.getAnAssocItem() = result and impl.(Impl).hasTrait())
)
}

/** Holds if path `p` must be looked up in namespace `n`. */
private predicate pathUsesNamespace(Path p, Namespace n) {
n.isValue() and
Expand Down Expand Up @@ -1606,8 +1635,16 @@ private predicate useImportEdge(Use use, string name, ItemNode item, SuccessorKi
not tree.hasRename() and
name = item.getName()
or
name = tree.getRename().getName().getText() and
name != "_"
exists(Rename rename | rename = tree.getRename() |
name = rename.getName().getText()
or
// When the rename doesn't have a name it's an underscore import. This
// makes the imported item visible but unnameable. We represent this
// by using the name `_` which can never occur in a path. See also:
// https://doc.rust-lang.org/reference/items/use-declarations.html#r-items.use.as-underscore
not rename.hasName() and
name = "_"
)
)
)
)
Expand Down Expand Up @@ -1693,7 +1730,7 @@ private module Debug {
useImportEdge(use, name, item, kind)
}

ItemNode debuggetASuccessor(ItemNode i, string name, SuccessorKind kind) {
ItemNode debugGetASuccessor(ItemNode i, string name, SuccessorKind kind) {
i = getRelevantLocatable() and
result = i.getASuccessor(name, kind)
}
Expand Down
9 changes: 7 additions & 2 deletions rust/ql/lib/codeql/rust/internal/TypeInference.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1891,7 +1891,7 @@ private predicate methodCandidate(Type type, string name, int arity, Impl impl)
*/
pragma[nomagic]
private predicate methodCandidateTrait(Type type, Trait trait, string name, int arity, Impl impl) {
trait = resolvePath(impl.(ImplItemNode).getTraitPath()) and
trait = impl.(ImplItemNode).resolveTraitTy() and
methodCandidate(type, name, arity, impl)
}

Expand All @@ -1912,7 +1912,12 @@ private module IsInstantiationOfInput implements IsInstantiationOfInputSig<Metho
methodCandidateTrait(rootType, mc.getTrait(), name, arity, impl)
or
not exists(mc.getTrait()) and
methodCandidate(rootType, name, arity, impl)
methodCandidate(rootType, name, arity, impl) and
(
traitIsVisible(mc, impl.(ImplItemNode).resolveTraitTy())
or
not exists(impl.(ImplItemNode).resolveTraitTy())
)
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@ multipleCallTargets
| test.rs:179:30:179:68 | ...::_print(...) |
| test.rs:188:26:188:105 | ...::_print(...) |
| test.rs:229:22:229:72 | ... .read_to_string(...) |
| test.rs:513:22:513:50 | file.read_to_end(...) |
| test.rs:519:22:519:53 | file.read_to_string(...) |
| test.rs:697:18:697:38 | ...::_print(...) |
| test.rs:702:18:702:45 | ...::_print(...) |
| test.rs:706:25:706:49 | address.to_socket_addrs() |
| test.rs:720:38:720:42 | ...::_print(...) |
| test.rs:724:38:724:54 | ...::_print(...) |
| test.rs:729:38:729:51 | ...::_print(...) |
Expand Down Expand Up @@ -76,12 +73,6 @@ multipleCallTargets
| test.rs:977:14:977:29 | ...::_print(...) |
| test.rs:979:27:979:36 | ...::_print(...) |
| test.rs:980:28:980:41 | ...::_print(...) |
| test_futures_io.rs:35:26:35:63 | pinned.poll_read(...) |
| test_futures_io.rs:62:22:62:50 | pinned.poll_fill_buf(...) |
| test_futures_io.rs:69:23:69:67 | ... .poll_fill_buf(...) |
| test_futures_io.rs:93:26:93:63 | pinned.poll_read(...) |
| test_futures_io.rs:116:22:116:50 | pinned.poll_fill_buf(...) |
| test_futures_io.rs:145:26:145:49 | ...::with_capacity(...) |
| web_frameworks.rs:13:14:13:22 | a.as_str() |
| web_frameworks.rs:13:14:13:23 | a.as_str() |
| web_frameworks.rs:14:14:14:24 | a.as_bytes() |
Expand Down
45 changes: 45 additions & 0 deletions rust/ql/test/library-tests/path-resolution/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,50 @@ mod m16 {
} // I83
}

mod trait_visibility {
mod m {
pub trait Foo {
fn a_method(&self); // Foo::a_method
} // Foo

pub trait Bar {
fn a_method(&self); // Bar::a_method
} // Bar

pub struct X;
#[rustfmt::skip]
impl Foo for X { // $ item=Foo item=X
fn a_method(&self) {
println!("foo!");
} // X_Foo::a_method
}
#[rustfmt::skip]
impl Bar for X { // $ item=Bar item=X
fn a_method(&self) {
println!("bar!");
} // X_Bar::a_method
}
}

use m::X; // $ item=X

pub fn f() {
let x = X; // $ item=X
{
use m::Foo; // $ item=Foo
X::a_method(&x); // $ item=X_Foo::a_method
}
{
use m::Bar; // $ item=Bar
X::a_method(&x); // $ item=X_Bar::a_method
}
{
use m::Bar as _; // $ item=Bar
X::a_method(&x); // $ item=X_Bar::a_method
}
} // trait_visibility::f
}

mod m17 {
trait MyTrait {
fn f(&self); // I1
Expand Down Expand Up @@ -730,6 +774,7 @@ fn main() {
m11::f(); // $ item=I63
m15::f(); // $ item=I75
m16::f(); // $ item=I83
trait_visibility::f(); // $ item=trait_visibility::f
m17::f(); // $ item=I99
nested6::f(); // $ item=I116
nested8::f(); // $ item=I119
Expand Down
Loading