Skip to content

Commit 51d2f9a

Browse files
committed
Rust: Account for trait visibility when resolving paths and methods
1 parent 01991c4 commit 51d2f9a

File tree

8 files changed

+53
-31
lines changed

8 files changed

+53
-31
lines changed

rust/ql/lib/codeql/rust/elements/internal/AssocItemImpl.qll

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,6 @@ module Impl {
2626
*/
2727
class AssocItem extends Generated::AssocItem {
2828
/** Holds if this item implements trait item `other`. */
29-
pragma[nomagic]
30-
predicate implements(AssocItem other) {
31-
exists(TraitItemNode t, ImplItemNode i, string name |
32-
other = t.getAssocItem(pragma[only_bind_into](name)) and
33-
t = i.resolveTraitTy() and
34-
this = i.getAssocItem(pragma[only_bind_into](name))
35-
)
36-
}
29+
predicate implements(AssocItem other) { this.(AssocItemNode).implements(_, other) }
3730
}
3831
}

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

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ abstract class ItemNode extends Locatable {
216216
// items made available through `use` are available to nodes that contain the `use`
217217
exists(UseItemNode use |
218218
use = this.getASuccessor(_, _) and
219-
result = use.(ItemNode).getASuccessor(name, kind)
219+
result = use.getASuccessor(name, kind)
220220
)
221221
or
222222
exists(ExternCrateItemNode ec | result = ec.(ItemNode).getASuccessor(name, kind) |
@@ -476,10 +476,20 @@ class ExternCrateItemNode extends ItemNode instanceof ExternCrate {
476476
}
477477

478478
/** An item that can occur in a trait or an `impl` block. */
479-
abstract private class AssocItemNode extends ItemNode instanceof AssocItem {
479+
abstract class AssocItemNode extends ItemNode instanceof AssocItem {
480480
/** Holds if this associated item has an implementation. */
481481
abstract predicate hasImplementation();
482482

483+
/** Holds if this item implements item `other` of `trait`. */
484+
pragma[nomagic]
485+
predicate implements(TraitItemNode trait, AssocItem other) {
486+
exists(ImplItemNode impl, string name |
487+
other = trait.getAssocItem(pragma[only_bind_into](name)) and
488+
trait = impl.resolveTraitTy() and
489+
this = impl.getAssocItem(pragma[only_bind_into](name))
490+
)
491+
}
492+
483493
override predicate hasCanonicalPath(Crate c) { this.hasCanonicalPathPrefix(c) }
484494

485495
bindingset[c]
@@ -1215,6 +1225,7 @@ private predicate declares(ItemNode item, Namespace ns, string name) {
12151225
class RelevantPath extends Path {
12161226
RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() }
12171227

1228+
/** Holds if this is an unqualified path with the textual value `name`. */
12181229
pragma[nomagic]
12191230
predicate isUnqualified(string name) {
12201231
not exists(this.getQualifier()) and
@@ -1325,6 +1336,12 @@ private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKi
13251336
pragma[nomagic]
13261337
private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") }
13271338

1339+
/** Holds if the trait `trait` is visible at the element `element`. */
1340+
bindingset[element, trait]
1341+
predicate traitIsVisible(Element element, TraitItemNode trait) {
1342+
exists(ItemNode encl | encl.getADescendant*() = element and trait = encl.getASuccessor(_, _))
1343+
}
1344+
13281345
pragma[nomagic]
13291346
private ItemNode resolvePath0(RelevantPath path, Namespace ns, SuccessorKind kind) {
13301347
exists(ItemNode res |
@@ -1341,6 +1358,14 @@ private ItemNode resolvePath0(RelevantPath path, Namespace ns, SuccessorKind kin
13411358
q = resolvePathQualifier(path, name) and
13421359
result = getASuccessor(q, name, ns, kind) and
13431360
kind.isExternalOrBoth()
1361+
|
1362+
// When the result is an associated item of a trait implementation the
1363+
// implemented trait must be visible.
1364+
exists(TraitItemNode trait |
1365+
result.(AssocItemNode).implements(trait, _) and traitIsVisible(path, trait)
1366+
)
1367+
or
1368+
not exists(ImplItemNode impl | impl.getAnAssocItem() = result and impl.(Impl).hasTrait())
13441369
)
13451370
or
13461371
result = resolveUseTreeListItem(_, _, path, kind) and
@@ -1477,8 +1502,16 @@ private predicate useImportEdge(Use use, string name, ItemNode item, SuccessorKi
14771502
not tree.hasRename() and
14781503
name = item.getName()
14791504
or
1480-
name = tree.getRename().getName().getText() and
1481-
name != "_"
1505+
exists(Rename rename | rename = tree.getRename() |
1506+
name = rename.getName().getText()
1507+
or
1508+
// When the rename doesn't have a name it's an underscore import. This
1509+
// makes the imported item visible but unnameable. We represent this
1510+
// by using the name `_` which can never occur in a path. See also:
1511+
// https://doc.rust-lang.org/reference/items/use-declarations.html#r-items.use.as-underscore
1512+
not rename.hasName() and
1513+
name = "_"
1514+
)
14821515
)
14831516
)
14841517
)
@@ -1550,7 +1583,7 @@ private module Debug {
15501583
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
15511584
result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
15521585
filepath.matches("%/main.rs") and
1553-
startline = 52
1586+
startline = [463 .. 511]
15541587
)
15551588
}
15561589

@@ -1572,7 +1605,7 @@ private module Debug {
15721605
useImportEdge(use, name, item, kind)
15731606
}
15741607

1575-
ItemNode debuggetASuccessor(ItemNode i, string name, SuccessorKind kind) {
1608+
ItemNode debugGetASuccessor(ItemNode i, string name, SuccessorKind kind) {
15761609
i = getRelevantLocatable() and
15771610
result = i.getASuccessor(name, kind)
15781611
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,7 +1891,7 @@ private predicate methodCandidate(Type type, string name, int arity, Impl impl)
18911891
*/
18921892
pragma[nomagic]
18931893
private predicate methodCandidateTrait(Type type, Trait trait, string name, int arity, Impl impl) {
1894-
trait = resolvePath(impl.(ImplItemNode).getTraitPath()) and
1894+
trait = impl.(ImplItemNode).resolveTraitTy() and
18951895
methodCandidate(type, name, arity, impl)
18961896
}
18971897

@@ -1912,7 +1912,12 @@ private module IsInstantiationOfInput implements IsInstantiationOfInputSig<Metho
19121912
methodCandidateTrait(rootType, mc.getTrait(), name, arity, impl)
19131913
or
19141914
not exists(mc.getTrait()) and
1915-
methodCandidate(rootType, name, arity, impl)
1915+
methodCandidate(rootType, name, arity, impl) and
1916+
(
1917+
traitIsVisible(mc, impl.(ImplItemNode).resolveTraitTy())
1918+
or
1919+
not exists(impl.(ImplItemNode).resolveTraitTy())
1920+
)
19161921
)
19171922
}
19181923

rust/ql/test/library-tests/path-resolution/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
multipleCallTargets
22
| main.rs:118:9:118:11 | f(...) |
3-
| main.rs:494:13:494:27 | ...::a_method(...) |
4-
| main.rs:498:13:498:27 | ...::a_method(...) |
5-
| main.rs:502:13:502:27 | ...::a_method(...) |
63
| proc_macro.rs:9:5:9:10 | ...::new(...) |
74
multiplePathResolutions
85
| main.rs:685:3:685:12 | proc_macro |

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -491,15 +491,15 @@ mod trait_visibility {
491491
let x = X; // $ item=X
492492
{
493493
use m::Foo; // $ item=Foo
494-
X::a_method(&x); // $ item=X_Foo::a_method SPURIOUS: item=X_Bar::a_method
494+
X::a_method(&x); // $ item=X_Foo::a_method
495495
}
496496
{
497497
use m::Bar; // $ item=Bar
498-
X::a_method(&x); // $ item=X_Bar::a_method SPURIOUS: item=X_Foo::a_method
498+
X::a_method(&x); // $ item=X_Bar::a_method
499499
}
500500
{
501501
use m::Bar as _; // $ item=Bar
502-
X::a_method(&x); // $ item=X_Bar::a_method SPURIOUS: item=X_Foo::a_method
502+
X::a_method(&x); // $ item=X_Bar::a_method
503503
}
504504
} // trait_visibility::f
505505
}

rust/ql/test/library-tests/path-resolution/path-resolution.expected

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,13 @@ resolvePath
236236
| main.rs:493:17:493:22 | ...::Foo | main.rs:465:9:467:9 | trait Foo |
237237
| main.rs:494:13:494:13 | X | main.rs:473:9:473:21 | struct X |
238238
| main.rs:494:13:494:23 | ...::a_method | main.rs:475:26:478:13 | fn a_method |
239-
| main.rs:494:13:494:23 | ...::a_method | main.rs:481:26:484:13 | fn a_method |
240239
| main.rs:497:17:497:17 | m | main.rs:464:5:486:5 | mod m |
241240
| main.rs:497:17:497:22 | ...::Bar | main.rs:469:9:471:9 | trait Bar |
242241
| main.rs:498:13:498:13 | X | main.rs:473:9:473:21 | struct X |
243-
| main.rs:498:13:498:23 | ...::a_method | main.rs:475:26:478:13 | fn a_method |
244242
| main.rs:498:13:498:23 | ...::a_method | main.rs:481:26:484:13 | fn a_method |
245243
| main.rs:501:17:501:17 | m | main.rs:464:5:486:5 | mod m |
246244
| main.rs:501:17:501:22 | ...::Bar | main.rs:469:9:471:9 | trait Bar |
247245
| main.rs:502:13:502:13 | X | main.rs:473:9:473:21 | struct X |
248-
| main.rs:502:13:502:23 | ...::a_method | main.rs:475:26:478:13 | fn a_method |
249246
| main.rs:502:13:502:23 | ...::a_method | main.rs:481:26:484:13 | fn a_method |
250247
| main.rs:515:10:515:16 | MyTrait | main.rs:508:5:510:5 | trait MyTrait |
251248
| main.rs:516:9:516:9 | S | main.rs:512:5:512:13 | struct S |

rust/ql/test/library-tests/type-inference/CONSISTENCY/PathResolutionConsistency.expected

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
multipleCallTargets
22
| dereference.rs:61:15:61:24 | e1.deref() |
3-
| main.rs:153:13:153:24 | x.a_method() |
4-
| main.rs:157:13:157:24 | x.a_method() |
5-
| main.rs:161:13:161:24 | x.a_method() |
63
| main.rs:2357:13:2357:31 | ...::from(...) |
74
| main.rs:2358:13:2358:31 | ...::from(...) |
85
| main.rs:2359:13:2359:31 | ...::from(...) |

rust/ql/test/library-tests/type-inference/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,15 +150,15 @@ mod trait_visibility {
150150
let x = X;
151151
{
152152
use m::Foo;
153-
x.a_method(); // $ target=Foo::a_method SPURIOUS: target=Bar::a_method
153+
x.a_method(); // $ target=Foo::a_method
154154
}
155155
{
156156
use m::Bar;
157-
x.a_method(); // $ target=Bar::a_method SPURIOUS: target=Foo::a_method
157+
x.a_method(); // $ target=Bar::a_method
158158
}
159159
{
160160
use m::Bar as _;
161-
x.a_method(); // $ target=Bar::a_method SPURIOUS: target=Foo::a_method
161+
x.a_method(); // $ target=Bar::a_method
162162
}
163163
{
164164
use m::Bar;

0 commit comments

Comments
 (0)