Skip to content

Commit eaaf510

Browse files
authored
Merge pull request #18728 from hvitved/rust/path-resolution-namespaces
Rust: Model namespaces in path resolution
2 parents e1c810a + c7441ab commit eaaf510

File tree

4 files changed

+303
-63
lines changed

4 files changed

+303
-63
lines changed

rust/ql/lib/codeql/rust/AstConsistency.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ private import codeql.rust.elements.internal.PathResolution
7878
/** Holds if `p` may resolve to multiple items including `i`. */
7979
query predicate multiplePathResolutions(Path p, ItemNode i) {
8080
i = resolvePath(p) and
81+
// `use foo::bar` may use both a type `bar` and a value `bar`
82+
not p =
83+
any(UseTree use |
84+
not use.isGlob() and
85+
not use.hasUseTreeList()
86+
).getPath() and
8187
strictcount(resolvePath(p)) > 1
8288
}
8389

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

Lines changed: 161 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,31 @@
55
private import rust
66
private import codeql.rust.elements.internal.generated.ParentChild
77

8+
private newtype TNamespace =
9+
TTypeNamespace() or
10+
TValueNamespace()
11+
12+
/**
13+
* A namespace.
14+
*
15+
* Either the _value_ namespace or the _type_ namespace, see
16+
* https://doc.rust-lang.org/reference/names/namespaces.html.
17+
*/
18+
final class Namespace extends TNamespace {
19+
/** Holds if this is the value namespace. */
20+
predicate isValue() { this = TValueNamespace() }
21+
22+
/** Holds if this is the type namespace. */
23+
predicate isType() { this = TTypeNamespace() }
24+
25+
/** Gets a textual representation of this namespace. */
26+
string toString() {
27+
this.isValue() and result = "value"
28+
or
29+
this.isType() and result = "type"
30+
}
31+
}
32+
833
/**
934
* An item that may be referred to by a path, and which is a node in
1035
* the _item graph_.
@@ -46,11 +71,15 @@ private import codeql.rust.elements.internal.generated.ParentChild
4671
* - https://doc.rust-lang.org/reference/names/scopes.html
4772
* - https://doc.rust-lang.org/reference/paths.html
4873
* - https://doc.rust-lang.org/reference/visibility-and-privacy.html
74+
* - https://doc.rust-lang.org/reference/names/namespaces.html
4975
*/
5076
abstract class ItemNode extends AstNode {
5177
/** Gets the (original) name of this item. */
5278
abstract string getName();
5379

80+
/** Gets the namespace that this item belongs to, if any. */
81+
abstract Namespace getNamespace();
82+
5483
/** Gets the visibility of this item, if any. */
5584
abstract Visibility getVisibility();
5685

@@ -143,30 +172,44 @@ abstract private class ModuleLikeNode extends ItemNode {
143172
private class SourceFileItemNode extends ModuleLikeNode, SourceFile {
144173
override string getName() { result = "(source file)" }
145174

175+
override Namespace getNamespace() {
176+
result.isType() // can be referenced with `super`
177+
}
178+
146179
override Visibility getVisibility() { none() }
147180
}
148181

149182
private class ConstItemNode extends ItemNode instanceof Const {
150183
override string getName() { result = Const.super.getName().getText() }
151184

185+
override Namespace getNamespace() { result.isValue() }
186+
152187
override Visibility getVisibility() { result = Const.super.getVisibility() }
153188
}
154189

155190
private class EnumItemNode extends ItemNode instanceof Enum {
156191
override string getName() { result = Enum.super.getName().getText() }
157192

193+
override Namespace getNamespace() { result.isType() }
194+
158195
override Visibility getVisibility() { result = Enum.super.getVisibility() }
159196
}
160197

161198
private class VariantItemNode extends ItemNode instanceof Variant {
162199
override string getName() { result = Variant.super.getName().getText() }
163200

201+
override Namespace getNamespace() {
202+
if super.getFieldList() instanceof RecordFieldList then result.isType() else result.isValue()
203+
}
204+
164205
override Visibility getVisibility() { result = Variant.super.getVisibility() }
165206
}
166207

167208
private class FunctionItemNode extends ItemNode instanceof Function {
168209
override string getName() { result = Function.super.getName().getText() }
169210

211+
override Namespace getNamespace() { result.isValue() }
212+
170213
override Visibility getVisibility() { result = Function.super.getVisibility() }
171214
}
172215

@@ -184,57 +227,92 @@ abstract private class ImplOrTraitItemNode extends ItemNode {
184227
}
185228
}
186229

187-
private class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
230+
class ImplItemNode extends ImplOrTraitItemNode instanceof Impl {
231+
ItemNode resolveSelfTy() { result = resolvePath(super.getSelfTy().(PathTypeRepr).getPath()) }
232+
188233
override string getName() { result = "(impl)" }
189234

235+
override Namespace getNamespace() {
236+
result.isType() // can be referenced with `Self`
237+
}
238+
190239
override Visibility getVisibility() { result = Impl.super.getVisibility() }
191240
}
192241

193242
private class MacroCallItemNode extends ItemNode instanceof MacroCall {
194243
override string getName() { result = "(macro call)" }
195244

245+
override Namespace getNamespace() { none() }
246+
196247
override Visibility getVisibility() { none() }
197248
}
198249

199250
private class ModuleItemNode extends ModuleLikeNode instanceof Module {
200251
override string getName() { result = Module.super.getName().getText() }
201252

253+
override Namespace getNamespace() { result.isType() }
254+
202255
override Visibility getVisibility() { result = Module.super.getVisibility() }
203256
}
204257

205258
private class StructItemNode extends ItemNode instanceof Struct {
206259
override string getName() { result = Struct.super.getName().getText() }
207260

261+
override Namespace getNamespace() {
262+
result.isType() // the struct itself
263+
or
264+
not super.getFieldList() instanceof RecordFieldList and
265+
result.isValue() // the constructor
266+
}
267+
208268
override Visibility getVisibility() { result = Struct.super.getVisibility() }
209269
}
210270

211-
private class TraitItemNode extends ImplOrTraitItemNode instanceof Trait {
271+
class TraitItemNode extends ImplOrTraitItemNode instanceof Trait {
212272
override string getName() { result = Trait.super.getName().getText() }
213273

274+
override Namespace getNamespace() { result.isType() }
275+
214276
override Visibility getVisibility() { result = Trait.super.getVisibility() }
215277
}
216278

279+
class TypeAliasItemNode extends ItemNode instanceof TypeAlias {
280+
override string getName() { result = TypeAlias.super.getName().getText() }
281+
282+
override Namespace getNamespace() { result.isType() }
283+
284+
override Visibility getVisibility() { result = TypeAlias.super.getVisibility() }
285+
}
286+
217287
private class UnionItemNode extends ItemNode instanceof Union {
218288
override string getName() { result = Union.super.getName().getText() }
219289

290+
override Namespace getNamespace() { result.isType() }
291+
220292
override Visibility getVisibility() { result = Union.super.getVisibility() }
221293
}
222294

223295
private class UseItemNode extends ItemNode instanceof Use {
224296
override string getName() { result = "(use)" }
225297

298+
override Namespace getNamespace() { none() }
299+
226300
override Visibility getVisibility() { none() }
227301
}
228302

229303
private class BlockExprItemNode extends ItemNode instanceof BlockExpr {
230304
override string getName() { result = "(block expr)" }
231305

306+
override Namespace getNamespace() { none() }
307+
232308
override Visibility getVisibility() { none() }
233309
}
234310

235311
private class TypeParamItemNode extends ItemNode instanceof TypeParam {
236312
override string getName() { result = TypeParam.super.getName().getText() }
237313

314+
override Namespace getNamespace() { result.isType() }
315+
238316
override Visibility getVisibility() { none() }
239317
}
240318

@@ -320,19 +398,22 @@ private predicate useTreeDeclares(UseTree tree, string name) {
320398
}
321399

322400
/**
323-
* Holds if `item` explicitly declares a sub item named `name`. This includes
324-
* items declared by `use` statements, except for glob imports.
401+
* Holds if `item` explicitly declares a sub item named `name` in the
402+
* namespace `ns`. This includes items declared by `use` statements,
403+
* except for glob imports.
325404
*/
326405
pragma[nomagic]
327-
private predicate declares(ItemNode item, string name) {
406+
private predicate declares(ItemNode item, Namespace ns, string name) {
328407
exists(ItemNode child | child.getImmediateParent() = item |
329-
child.getName() = name
408+
child.getName() = name and
409+
child.getNamespace() = ns
330410
or
331-
useTreeDeclares(child.(Use).getUseTree(), name)
411+
useTreeDeclares(child.(Use).getUseTree(), name) and
412+
exists(ns) // `use foo::bar` can refer to both a value and a type
332413
)
333414
or
334415
exists(MacroCallItemNode call |
335-
declares(call, name) and
416+
declares(call, ns, name) and
336417
call.getImmediateParent() = item
337418
)
338419
}
@@ -351,19 +432,20 @@ private class RelevantPath extends Path {
351432

352433
/**
353434
* Holds if the unqualified path `p` references an item named `name`, and `name`
354-
* may be looked up inside enclosing item `encl`.
435+
* may be looked up in the `ns` namespace inside enclosing item `encl`.
355436
*/
356437
pragma[nomagic]
357-
private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode encl) {
438+
private predicate unqualifiedPathLookup(RelevantPath p, string name, Namespace ns, ItemNode encl) {
358439
exists(ItemNode encl0 |
359440
// lookup in the immediately enclosing item
360441
p.isUnqualified(name) and
361-
encl0.getADescendant() = p
442+
encl0.getADescendant() = p and
443+
exists(ns)
362444
or
363445
// lookup in an outer scope, but only if the item is not declared in inner scope
364446
exists(ItemNode mid |
365-
unqualifiedPathLookup(p, name, mid) and
366-
not declares(mid, name)
447+
unqualifiedPathLookup(p, name, ns, mid) and
448+
not declares(mid, ns, name)
367449
|
368450
// nested modules do not have unqualified access to items from outer modules,
369451
// except for items declared at top-level in the source file
@@ -374,16 +456,30 @@ private predicate unqualifiedPathLookup(RelevantPath p, string name, ItemNode en
374456
|
375457
// functions in `impl` blocks need to use explicit `Self::` to access other
376458
// functions in the `impl` block
377-
if encl0 instanceof ImplOrTraitItemNode then encl = encl0.getImmediateParent() else encl = encl0
459+
if encl0 instanceof ImplOrTraitItemNode and ns.isValue()
460+
then encl = encl0.getImmediateParent()
461+
else encl = encl0
378462
)
379463
}
380464

381-
/** Gets the item that `path` resolves to, if any. */
382-
cached
383-
ItemNode resolvePath(RelevantPath path) {
384-
exists(ItemNode encl, string name |
385-
unqualifiedPathLookup(path, name, encl) and
386-
result = encl.getASuccessor(name)
465+
pragma[nomagic]
466+
private ItemNode getASuccessor(ItemNode pred, string name, Namespace ns) {
467+
result = pred.getASuccessor(name) and
468+
ns = result.getNamespace()
469+
}
470+
471+
pragma[nomagic]
472+
private ItemNode resolvePath0(RelevantPath path) {
473+
exists(ItemNode encl, Namespace ns, string name, ItemNode res |
474+
unqualifiedPathLookup(path, name, ns, encl) and
475+
res = getASuccessor(encl, name, ns)
476+
|
477+
if
478+
not any(RelevantPath parent).getQualifier() = path and
479+
name = "Self" and
480+
res instanceof ImplItemNode
481+
then result = res.(ImplItemNode).resolveSelfTy()
482+
else result = res
387483
)
388484
or
389485
exists(ItemNode q, string name |
@@ -394,6 +490,47 @@ ItemNode resolvePath(RelevantPath path) {
394490
result = resolveUseTreeListItem(_, _, path)
395491
}
396492

493+
/** Holds if path `p` must be looked up in namespace `n`. */
494+
private predicate pathUsesNamespace(Path p, Namespace n) {
495+
n.isValue() and
496+
(
497+
p = any(PathExpr pe).getPath()
498+
or
499+
p = any(TupleStructPat tsp).getPath()
500+
)
501+
or
502+
n.isType() and
503+
(
504+
p = any(Visibility v).getPath()
505+
or
506+
p = any(RecordExpr re).getPath()
507+
or
508+
p = any(PathTypeRepr ptr).getPath()
509+
or
510+
p = any(RecordPat rp).getPath()
511+
or
512+
p =
513+
any(UseTree use |
514+
use.isGlob()
515+
or
516+
use.hasUseTreeList()
517+
).getPath()
518+
or
519+
p = any(Path parent).getQualifier()
520+
)
521+
}
522+
523+
/** Gets the item that `path` resolves to, if any. */
524+
cached
525+
ItemNode resolvePath(RelevantPath path) {
526+
result = resolvePath0(path) and
527+
(
528+
pathUsesNamespace(path, result.getNamespace())
529+
or
530+
not pathUsesNamespace(path, _)
531+
)
532+
}
533+
397534
pragma[nomagic]
398535
private ItemNode resolvePathQualifier(RelevantPath path, string name) {
399536
result = resolvePath(path.getQualifier()) and
@@ -452,14 +589,14 @@ pragma[nomagic]
452589
private predicate useImportEdge(Use use, string name, ItemNode item) {
453590
exists(UseTree tree, ItemNode used |
454591
used = resolveUseTreeListItem(use, tree) and
455-
not exists(tree.getUseTreeList()) and
592+
not tree.hasUseTreeList() and
456593
if tree.isGlob()
457594
then
458-
exists(ItemNode encl |
595+
exists(ItemNode encl, Namespace ns |
459596
encl.getADescendant() = use and
460-
item = used.getASuccessor(name) and
597+
item = getASuccessor(used, name, ns) and
461598
// glob imports can be shadowed
462-
not declares(encl, name)
599+
not declares(encl, ns, name)
463600
)
464601
else item = used
465602
|

0 commit comments

Comments
 (0)