Skip to content

Commit 79cc731

Browse files
authored
Merge pull request #20096 from paldepind/rust/path-resolution-associated-type-fix
Rust: Path resolution associated type fix
2 parents 05572b4 + 2885046 commit 79cc731

File tree

6 files changed

+2861
-2705
lines changed

6 files changed

+2861
-2705
lines changed

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

Lines changed: 66 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,18 @@ abstract class ItemNode extends Locatable {
112112
result = this.(SourceFileItemNode).getSuper()
113113
}
114114

115+
pragma[nomagic]
116+
private ItemNode getAChildSuccessor(string name) {
117+
this = result.getImmediateParent() and
118+
name = result.getName()
119+
}
120+
115121
cached
116122
ItemNode getASuccessorRec(string name) {
117123
Stages::PathResolutionStage::ref() and
118124
sourceFileEdge(this, name, result)
119125
or
120-
this = result.getImmediateParent() and
121-
name = result.getName()
126+
result = this.getAChildSuccessor(name)
122127
or
123128
fileImportEdge(this, name, result)
124129
or
@@ -224,6 +229,38 @@ abstract class ItemNode extends Locatable {
224229
result.(CrateItemNode).isPotentialDollarCrateTarget()
225230
}
226231

232+
/**
233+
* Holds if the successor `item` with the name `name` is not available locally
234+
* for unqualified paths.
235+
*
236+
* This has the effect that a path of the form `name` inside `this` will not
237+
* resolve to `item`.
238+
*/
239+
pragma[nomagic]
240+
predicate excludedLocally(string name, ItemNode item) {
241+
// Associated items in an impl or trait block are not directly available
242+
// inside the block, they require a qualified path with a `Self` prefix.
243+
item = this.getAChildSuccessor(name) and
244+
this instanceof ImplOrTraitItemNode and
245+
item instanceof AssocItemNode
246+
}
247+
248+
/**
249+
* Holds if the successor `item` with the name `name` is not available
250+
* externally for qualified paths that resolve to this item.
251+
*
252+
* This has the effect that a path of the form `Qualifier::name`, where
253+
* `Qualifier` resolves to this item, will not resolve to `item`.
254+
*/
255+
pragma[nomagic]
256+
predicate excludedExternally(string name, ItemNode item) {
257+
// Type parameters for an `impl` or trait block are not available outside of
258+
// the block.
259+
item = this.getAChildSuccessor(name) and
260+
this instanceof ImplOrTraitItemNode and
261+
item instanceof TypeParamItemNode
262+
}
263+
227264
pragma[nomagic]
228265
private predicate hasSourceFunction(string name) {
229266
this.getASuccessorFull(name).(Function).fromSource()
@@ -1145,7 +1182,9 @@ pragma[nomagic]
11451182
private predicate declares(ItemNode item, Namespace ns, string name) {
11461183
exists(ItemNode child | child.getImmediateParent() = item |
11471184
child.getName() = name and
1148-
child.getNamespace() = ns
1185+
child.getNamespace() = ns and
1186+
// If `item` is excluded locally then it does not declare `name`.
1187+
not item.excludedLocally(name, child)
11491188
or
11501189
useTreeDeclares(child.(Use).getUseTree(), name) and
11511190
exists(ns) // `use foo::bar` can refer to both a value and a type
@@ -1193,38 +1232,27 @@ private ItemNode getOuterScope(ItemNode i) {
11931232
result = i.getImmediateParent()
11941233
}
11951234

1196-
pragma[nomagic]
1197-
private ItemNode getAdjustedEnclosing(ItemNode encl0, Namespace ns) {
1198-
// functions in `impl` blocks need to use explicit `Self::` to access other
1199-
// functions in the `impl` block
1200-
if encl0 instanceof ImplOrTraitItemNode and ns.isValue()
1201-
then result = encl0.getImmediateParent()
1202-
else result = encl0
1203-
}
1204-
12051235
/**
12061236
* Holds if the unqualified path `p` references an item named `name`, and `name`
12071237
* may be looked up in the `ns` namespace inside enclosing item `encl`.
12081238
*/
12091239
pragma[nomagic]
12101240
private predicate unqualifiedPathLookup(ItemNode encl, string name, Namespace ns, RelevantPath p) {
1211-
exists(ItemNode encl0 | encl = getAdjustedEnclosing(encl0, ns) |
1212-
// lookup in the immediately enclosing item
1213-
p.isUnqualified(name) and
1214-
encl0.getADescendant() = p and
1215-
exists(ns) and
1216-
not name = ["crate", "$crate", "super", "self"]
1217-
or
1218-
// lookup in an outer scope, but only if the item is not declared in inner scope
1219-
exists(ItemNode mid |
1220-
unqualifiedPathLookup(mid, name, ns, p) and
1221-
not declares(mid, ns, name) and
1222-
not (
1223-
name = "Self" and
1224-
mid = any(ImplOrTraitItemNode i).getAnItemInSelfScope()
1225-
) and
1226-
encl0 = getOuterScope(mid)
1227-
)
1241+
// lookup in the immediately enclosing item
1242+
p.isUnqualified(name) and
1243+
encl.getADescendant() = p and
1244+
exists(ns) and
1245+
not name = ["crate", "$crate", "super", "self"]
1246+
or
1247+
// lookup in an outer scope, but only if the item is not declared in inner scope
1248+
exists(ItemNode mid |
1249+
unqualifiedPathLookup(mid, name, ns, p) and
1250+
not declares(mid, ns, name) and
1251+
not (
1252+
name = "Self" and
1253+
mid = any(ImplOrTraitItemNode i).getAnItemInSelfScope()
1254+
) and
1255+
encl = getOuterScope(mid)
12281256
)
12291257
}
12301258

@@ -1245,10 +1273,10 @@ private predicate sourceFileHasCratePathTc(ItemNode i1, ItemNode i2) =
12451273

12461274
/**
12471275
* Holds if the unqualified path `p` references a keyword item named `name`, and
1248-
* `name` may be looked up in the `ns` namespace inside enclosing item `encl`.
1276+
* `name` may be looked up inside enclosing item `encl`.
12491277
*/
12501278
pragma[nomagic]
1251-
private predicate keywordLookup(ItemNode encl, string name, Namespace ns, RelevantPath p) {
1279+
private predicate keywordLookup(ItemNode encl, string name, RelevantPath p) {
12521280
// For `($)crate`, jump directly to the root module
12531281
exists(ItemNode i | p.isCratePath(name, i) |
12541282
encl instanceof SourceFile and
@@ -1259,18 +1287,17 @@ private predicate keywordLookup(ItemNode encl, string name, Namespace ns, Releva
12591287
or
12601288
name = ["super", "self"] and
12611289
p.isUnqualified(name) and
1262-
exists(ItemNode encl0 |
1263-
encl0.getADescendant() = p and
1264-
encl = getAdjustedEnclosing(encl0, ns)
1265-
)
1290+
encl.getADescendant() = p
12661291
}
12671292

12681293
pragma[nomagic]
12691294
private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns) {
1270-
exists(ItemNode encl, string name | result = getASuccessorFull(encl, name, ns) |
1295+
exists(ItemNode encl, string name |
1296+
result = getASuccessorFull(encl, name, ns) and not encl.excludedLocally(name, result)
1297+
|
12711298
unqualifiedPathLookup(encl, name, ns, p)
12721299
or
1273-
keywordLookup(encl, name, ns, p)
1300+
keywordLookup(encl, name, p) and exists(ns)
12741301
)
12751302
}
12761303

@@ -1291,7 +1318,8 @@ private ItemNode resolvePath0(RelevantPath path, Namespace ns) {
12911318
or
12921319
exists(ItemNode q, string name |
12931320
q = resolvePathQualifier(path, name) and
1294-
result = getASuccessorFull(q, name, ns)
1321+
result = getASuccessorFull(q, name, ns) and
1322+
not q.excludedExternally(name, result)
12951323
)
12961324
or
12971325
result = resolveUseTreeListItem(_, _, path) and

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

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,60 @@ impl AStruct // $ item=I123
636636
pub fn z(&self) {} // I125
637637
}
638638

639+
mod associated_types {
640+
use std::marker::PhantomData; // $ item=PhantomData
641+
use std::result::Result; // $ item=Result
642+
643+
trait Reduce {
644+
type Input; // ReduceInput
645+
type Error; // ReduceError
646+
type Output; // ReduceOutput
647+
fn feed(
648+
&mut self,
649+
item: Self::Input, // $ item=ReduceInput
650+
) -> Result<Self::Output, Self::Error>; // $ item=Result item=ReduceOutput item=ReduceError
651+
} // IReduce
652+
653+
struct MyImpl<Input, Error> {
654+
_input: PhantomData<Input>, // $ item=PhantomData item=Input
655+
_error: PhantomData<Error>, // $ item=PhantomData item=Error
656+
} // MyImpl
657+
658+
#[rustfmt::skip]
659+
impl<
660+
Input, // IInput
661+
Error, // IError
662+
> Reduce // $ item=IReduce
663+
for MyImpl<
664+
Input, // $ item=IInput
665+
Error, // $ item=IError
666+
> // $ item=MyImpl
667+
{
668+
type Input = Result<
669+
Input, // $ item=IInput
670+
Self::Error, // $ item=IErrorAssociated
671+
> // $ item=Result
672+
; // IInputAssociated
673+
type Error = Option<
674+
Error // $ item=IError
675+
> // $ item=Option
676+
; // IErrorAssociated
677+
type Output =
678+
Input // $ item=IInput
679+
; // IOutputAssociated
680+
681+
fn feed(
682+
&mut self,
683+
item: Self::Input // $ item=IInputAssociated
684+
) -> Result<
685+
Self::Output, // $ item=IOutputAssociated
686+
Self::Error // $ item=IErrorAssociated
687+
> { // $ item=Result
688+
item
689+
}
690+
}
691+
}
692+
639693
use std::{self as ztd}; // $ item=std
640694

641695
fn use_ztd(x: ztd::string::String) {} // $ item=String

0 commit comments

Comments
 (0)