Skip to content

Commit 69d8d2a

Browse files
committed
Rust: Path resolution before variable resolution
1 parent 47bb9d7 commit 69d8d2a

File tree

12 files changed

+132
-93
lines changed

12 files changed

+132
-93
lines changed

rust/ql/integration-tests/hello-workspace/path-resolution.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import rust
22
import codeql.rust.internal.PathResolution
33
import utils.test.PathResolutionInlineExpectationsTest
44

5-
query predicate resolveDollarCrate(RelevantPath p, Crate c) {
5+
query predicate resolveDollarCrate(PathExt p, Crate c) {
66
c = resolvePath(p) and
77
p.isDollarCrate() and
88
p.fromSource() and

rust/ql/lib/codeql/rust/controlflow/internal/Completion.qll

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
private import codeql.util.Boolean
22
private import codeql.rust.controlflow.ControlFlowGraph
3+
private import codeql.rust.elements.internal.VariableImpl::Impl as VariableImpl
34
private import rust
45

56
newtype TCompletion =
@@ -123,13 +124,7 @@ class BooleanCompletion extends ConditionalCompletion, TBooleanCompletion {
123124
*/
124125
private predicate cannotCauseMatchFailure(Pat pat) {
125126
pat instanceof RangePat or
126-
// Identifier patterns that are in fact path patterns can cause failures. For
127-
// instance `None`. Only if an `@ ...` part is present can we be sure that
128-
// it's an actual identifier pattern. As a heuristic, if the identifier starts
129-
// with a lower case letter, then we assume that it's an identifier. This
130-
// works for code that follows the Rust naming convention for enums and
131-
// constants.
132-
pat = any(IdentPat p | p.hasPat() or p.getName().getText().charAt(0).isLowercase()) or
127+
pat = any(IdentPat p | p.hasPat() or VariableImpl::variableDecl(_, p.getName(), _)) or
133128
pat instanceof WildcardPat or
134129
pat instanceof RestPat or
135130
pat instanceof RefPat or

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ module Impl {
8282
}
8383

8484
private predicate callHasTraitQualifier(CallExpr call, Trait qualifier) {
85-
exists(RelevantPath qualifierPath |
85+
exists(PathExt qualifierPath |
8686
callHasQualifier(call, _, qualifierPath) and
8787
qualifier = resolvePath(qualifierPath) and
8888
// When the qualifier is `Self` and resolves to a trait, it's inside a

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
private import rust
22
private import codeql.rust.controlflow.ControlFlowGraph
3+
private import codeql.rust.internal.PathResolution as PathResolution
34
private import codeql.rust.elements.internal.generated.ParentChild as ParentChild
45
private import codeql.rust.elements.internal.PathImpl::Impl as PathImpl
56
private import codeql.rust.elements.internal.PathExprBaseImpl::Impl as PathExprBaseImpl
@@ -101,7 +102,7 @@ module Impl {
101102
* pattern.
102103
*/
103104
cached
104-
private predicate variableDecl(AstNode definingNode, Name name, string text) {
105+
predicate variableDecl(AstNode definingNode, Name name, string text) {
105106
Cached::ref() and
106107
exists(SelfParam sp |
107108
name = sp.getName() and
@@ -120,11 +121,7 @@ module Impl {
120121
not exists(getOutermostEnclosingOrPat(pat)) and definingNode = name
121122
) and
122123
text = name.getText() and
123-
// exclude for now anything starting with an uppercase character, which may be a reference to
124-
// an enum constant (e.g. `None`). This excludes static and constant variables (UPPERCASE),
125-
// which we don't appear to recognize yet anyway. This also assumes programmers follow the
126-
// naming guidelines, which they generally do, but they're not enforced.
127-
not text.charAt(0).isUppercase() and
124+
not PathResolution::identPatIsResolvable(pat) and
128125
// exclude parameters from functions without a body as these are trait method declarations
129126
// without implementations
130127
not exists(Function f | not f.hasBody() and f.getAParam().getPat() = pat) and

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

Lines changed: 87 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ abstract class ItemNode extends Locatable {
205205
pragma[nomagic]
206206
final Attr getAttr(string name) {
207207
result = this.getAnAttr() and
208-
result.getMeta().getPath().(RelevantPath).isUnqualified(name)
208+
result.getMeta().getPath().(PathExt).isUnqualified(name)
209209
}
210210

211211
final predicate hasAttr(string name) { exists(this.getAttr(name)) }
@@ -1521,20 +1521,22 @@ private predicate declares(ItemNode item, Namespace ns, string name) {
15211521
)
15221522
}
15231523

1524-
/** A path that does not access a local variable. */
1525-
class RelevantPath extends Path {
1526-
RelevantPath() { not this = any(VariableAccess va).(PathExpr).getPath() }
1524+
/**
1525+
* A `Path` or an `IdentPat`.
1526+
*
1527+
* `IdentPat`s are included in order to resolve unqualified references
1528+
* to constructors in patterns.
1529+
*/
1530+
abstract class PathExt extends AstNode {
1531+
abstract string getText();
15271532

15281533
/** Holds if this is an unqualified path with the textual value `name`. */
15291534
pragma[nomagic]
1530-
predicate isUnqualified(string name) {
1531-
not exists(this.getQualifier()) and
1532-
not exists(UseTree tree |
1533-
tree.hasPath() and
1534-
this = getAUseTreeUseTree(tree).getPath().getQualifier*()
1535-
) and
1536-
name = this.getText()
1537-
}
1535+
abstract predicate isUnqualified(string name);
1536+
1537+
abstract Path getQualifier();
1538+
1539+
abstract string toStringDebug();
15381540

15391541
/**
15401542
* Holds if this is an unqualified path with the textual value `name` and
@@ -1556,6 +1558,33 @@ class RelevantPath extends Path {
15561558
predicate isDollarCrate() { this.isUnqualified("$crate", _) }
15571559
}
15581560

1561+
private class PathExtPath extends PathExt instanceof Path {
1562+
override string getText() { result = Path.super.getText() }
1563+
1564+
override predicate isUnqualified(string name) {
1565+
not exists(Path.super.getQualifier()) and
1566+
not exists(UseTree tree |
1567+
tree.hasPath() and
1568+
this = getAUseTreeUseTree(tree).getPath().getQualifier*()
1569+
) and
1570+
name = Path.super.getText()
1571+
}
1572+
1573+
override Path getQualifier() { result = Path.super.getQualifier() }
1574+
1575+
override string toStringDebug() { result = Path.super.toStringDebug() }
1576+
}
1577+
1578+
private class PathExtIdentPat extends PathExt, IdentPat {
1579+
override string getText() { result = this.getName().getText() }
1580+
1581+
override predicate isUnqualified(string name) { name = this.getText() }
1582+
1583+
override Path getQualifier() { none() }
1584+
1585+
override string toStringDebug() { result = this.getText() }
1586+
}
1587+
15591588
private predicate isModule(ItemNode m) { m instanceof Module }
15601589

15611590
/** Holds if source file `source` contains the module `m`. */
@@ -1579,7 +1608,7 @@ private ItemNode getOuterScope(ItemNode i) {
15791608
pragma[nomagic]
15801609
private predicate unqualifiedPathLookup(ItemNode ancestor, string name, Namespace ns, ItemNode encl) {
15811610
// lookup in the immediately enclosing item
1582-
exists(RelevantPath path |
1611+
exists(PathExt path |
15831612
path.isUnqualified(name, encl) and
15841613
ancestor = encl and
15851614
not name = ["crate", "$crate", "super", "self"]
@@ -1615,7 +1644,7 @@ private ItemNode getASuccessor(
16151644

16161645
private predicate isSourceFile(ItemNode source) { source instanceof SourceFileItemNode }
16171646

1618-
private predicate hasCratePath(ItemNode i) { any(RelevantPath path).isCratePath(_, i) }
1647+
private predicate hasCratePath(ItemNode i) { any(PathExt path).isCratePath(_, i) }
16191648

16201649
private predicate hasChild(ItemNode parent, ItemNode child) { child.getImmediateParent() = parent }
16211650

@@ -1627,7 +1656,7 @@ private predicate sourceFileHasCratePathTc(ItemNode i1, ItemNode i2) =
16271656
* `name` may be looked up inside `ancestor`.
16281657
*/
16291658
pragma[nomagic]
1630-
private predicate keywordLookup(ItemNode ancestor, string name, RelevantPath p) {
1659+
private predicate keywordLookup(ItemNode ancestor, string name, PathExt p) {
16311660
// For `crate`, jump directly to the root module
16321661
exists(ItemNode i | p.isCratePath(name, i) |
16331662
ancestor instanceof SourceFile and
@@ -1641,7 +1670,7 @@ private predicate keywordLookup(ItemNode ancestor, string name, RelevantPath p)
16411670
}
16421671

16431672
pragma[nomagic]
1644-
private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKind kind) {
1673+
private ItemNode unqualifiedPathLookup(PathExt p, Namespace ns, SuccessorKind kind) {
16451674
exists(ItemNode ancestor, string name |
16461675
result = getASuccessor(ancestor, pragma[only_bind_into](name), ns, kind, _) and
16471676
kind.isInternalOrBoth()
@@ -1656,7 +1685,7 @@ private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKi
16561685
}
16571686

16581687
pragma[nomagic]
1659-
private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") }
1688+
private predicate isUnqualifiedSelfPath(PathExt path) { path.isUnqualified("Self") }
16601689

16611690
/** Provides the input to `TraitIsVisible`. */
16621691
signature predicate relevantTraitVisibleSig(Element element, Trait trait);
@@ -1739,14 +1768,14 @@ private module DollarCrateResolution {
17391768
isDollarCrateSupportedMacroExpansion(_, expansion)
17401769
}
17411770

1742-
private predicate isDollarCratePath(RelevantPath p) { p.isDollarCrate() }
1771+
private predicate isDollarCratePath(PathExt p) { p.isDollarCrate() }
17431772

1744-
private predicate isInDollarCrateMacroExpansion(RelevantPath p, AstNode expansion) =
1773+
private predicate isInDollarCrateMacroExpansion(PathExt p, AstNode expansion) =
17451774
doublyBoundedFastTC(hasParent/2, isDollarCratePath/1, isDollarCrateSupportedMacroExpansion/1)(p,
17461775
expansion)
17471776

17481777
pragma[nomagic]
1749-
private predicate isInDollarCrateMacroExpansionFromFile(File macroDefFile, RelevantPath p) {
1778+
private predicate isInDollarCrateMacroExpansionFromFile(File macroDefFile, PathExt p) {
17501779
exists(Path macroDefPath, AstNode expansion |
17511780
isDollarCrateSupportedMacroExpansion(macroDefPath, expansion) and
17521781
isInDollarCrateMacroExpansion(p, expansion) and
@@ -1761,17 +1790,17 @@ private module DollarCrateResolution {
17611790
* calls.
17621791
*/
17631792
pragma[nomagic]
1764-
predicate resolveDollarCrate(RelevantPath p, CrateItemNode crate) {
1793+
predicate resolveDollarCrate(PathExt p, CrateItemNode crate) {
17651794
isInDollarCrateMacroExpansionFromFile(crate.getASourceFile().getFile(), p)
17661795
}
17671796
}
17681797

17691798
pragma[nomagic]
1770-
private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) {
1799+
private ItemNode resolvePathCand0(PathExt path, Namespace ns) {
17711800
exists(ItemNode res |
17721801
res = unqualifiedPathLookup(path, ns, _) and
17731802
if
1774-
not any(RelevantPath parent).getQualifier() = path and
1803+
not any(PathExt parent).getQualifier() = path and
17751804
isUnqualifiedSelfPath(path) and
17761805
res instanceof ImplItemNode
17771806
then result = res.(ImplItemNodeImpl).resolveSelfTyCand()
@@ -1788,7 +1817,7 @@ private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) {
17881817
}
17891818

17901819
pragma[nomagic]
1791-
private ItemNode resolvePathCandQualifier(RelevantPath qualifier, RelevantPath path, string name) {
1820+
private ItemNode resolvePathCandQualifier(PathExt qualifier, PathExt path, string name) {
17921821
qualifier = path.getQualifier() and
17931822
result = resolvePathCand(qualifier) and
17941823
name = path.getText()
@@ -1836,9 +1865,7 @@ private predicate checkQualifiedVisibility(
18361865
* qualifier of `path` and `qualifier` resolves to `q`, if any.
18371866
*/
18381867
pragma[nomagic]
1839-
private ItemNode resolvePathCandQualified(
1840-
RelevantPath qualifier, ItemNode q, RelevantPath path, Namespace ns
1841-
) {
1868+
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
18421869
exists(string name, SuccessorKind kind, UseOption useOpt |
18431870
q = resolvePathCandQualifier(qualifier, path, name) and
18441871
result = getASuccessor(q, name, ns, kind, useOpt) and
@@ -1847,12 +1874,14 @@ private ItemNode resolvePathCandQualified(
18471874
}
18481875

18491876
/** Holds if path `p` must be looked up in namespace `n`. */
1850-
private predicate pathUsesNamespace(Path p, Namespace n) {
1877+
private predicate pathUsesNamespace(PathExt p, Namespace n) {
18511878
n.isValue() and
18521879
(
18531880
p = any(PathExpr pe).getPath()
18541881
or
18551882
p = any(TupleStructPat tsp).getPath()
1883+
or
1884+
p instanceof PathExtIdentPat
18561885
)
18571886
or
18581887
n.isType() and
@@ -1928,7 +1957,7 @@ private predicate macroUseEdge(
19281957
* result in non-monotonic recursion.
19291958
*/
19301959
pragma[nomagic]
1931-
private ItemNode resolvePathCand(RelevantPath path) {
1960+
private ItemNode resolvePathCand(PathExt path) {
19321961
exists(Namespace ns |
19331962
result = resolvePathCand0(path, ns) and
19341963
if path = any(ImplItemNode i).getSelfPath()
@@ -1941,7 +1970,10 @@ private ItemNode resolvePathCand(RelevantPath path) {
19411970
else
19421971
if path = any(PathTypeRepr p).getPath()
19431972
then result instanceof TypeItemNode
1944-
else any()
1973+
else
1974+
if path instanceof IdentPat
1975+
then result instanceof VariantItemNode or result instanceof StructItemNode
1976+
else any()
19451977
|
19461978
pathUsesNamespace(path, ns)
19471979
or
@@ -1958,7 +1990,7 @@ private ItemNode resolvePathCand(RelevantPath path) {
19581990
}
19591991

19601992
/** Get a trait that should be visible when `path` resolves to `node`, if any. */
1961-
private Trait getResolvePathTraitUsed(RelevantPath path, AssocItemNode node) {
1993+
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
19621994
exists(TypeItemNode type, ImplItemNodeImpl impl |
19631995
node = resolvePathCandQualified(_, type, path, _) and
19641996
typeImplEdge(type, impl, _, _, node, _) and
@@ -1970,9 +2002,8 @@ private predicate pathTraitUsed(Element path, Trait trait) {
19702002
trait = getResolvePathTraitUsed(path, _)
19712003
}
19722004

1973-
/** Gets the item that `path` resolves to, if any. */
19742005
cached
1975-
ItemNode resolvePath(RelevantPath path) {
2006+
private ItemNode resolvePath0(PathExt path) {
19762007
result = resolvePathCand(path) and
19772008
not path = any(Path parent | exists(resolvePathCand(parent))).getQualifier() and
19782009
(
@@ -1985,29 +2016,40 @@ ItemNode resolvePath(RelevantPath path) {
19852016
or
19862017
// if `path` is the qualifier of a resolvable `parent`, then we should
19872018
// resolve `path` to something consistent with what `parent` resolves to
1988-
exists(RelevantPath parent |
1989-
resolvePathCandQualified(path, result, parent, _) = resolvePath(parent)
1990-
)
2019+
exists(PathExt parent | resolvePathCandQualified(path, result, parent, _) = resolvePath0(parent))
19912020
}
19922021

1993-
private predicate isUseTreeSubPath(UseTree tree, RelevantPath path) {
2022+
/**
2023+
* Holds if `ip` resolves to some constructor.
2024+
*/
2025+
// use `forceLocal` once we implement overlay support
2026+
predicate identPatIsResolvable(IdentPat ip) { exists(resolvePath0(ip)) }
2027+
2028+
/** Gets the item that `path` resolves to, if any. */
2029+
pragma[nomagic]
2030+
ItemNode resolvePath(PathExt path) {
2031+
result = resolvePath0(path) and
2032+
not path = any(VariableAccess va).(PathExpr).getPath()
2033+
}
2034+
2035+
private predicate isUseTreeSubPath(UseTree tree, PathExt path) {
19942036
path = tree.getPath()
19952037
or
1996-
exists(RelevantPath mid |
2038+
exists(PathExt mid |
19972039
isUseTreeSubPath(tree, mid) and
19982040
path = mid.getQualifier()
19992041
)
20002042
}
20012043

20022044
pragma[nomagic]
2003-
private predicate isUseTreeSubPathUnqualified(UseTree tree, RelevantPath path, string name) {
2045+
private predicate isUseTreeSubPathUnqualified(UseTree tree, PathExt path, string name) {
20042046
isUseTreeSubPath(tree, path) and
20052047
not exists(path.getQualifier()) and
20062048
name = path.getText()
20072049
}
20082050

20092051
pragma[nomagic]
2010-
private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path, SuccessorKind kind) {
2052+
private ItemNode resolveUseTreeListItem(Use use, UseTree tree, PathExt path, SuccessorKind kind) {
20112053
exists(UseOption useOpt | checkQualifiedVisibility(use, result, kind, useOpt) |
20122054
exists(UseTree midTree, ItemNode mid, string name |
20132055
mid = resolveUseTreeListItem(use, midTree) and
@@ -2024,9 +2066,7 @@ private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path
20242066
}
20252067

20262068
pragma[nomagic]
2027-
private ItemNode resolveUseTreeListItemQualifier(
2028-
Use use, UseTree tree, RelevantPath path, string name
2029-
) {
2069+
private ItemNode resolveUseTreeListItemQualifier(Use use, UseTree tree, PathExt path, string name) {
20302070
result = resolveUseTreeListItem(use, tree, path.getQualifier(), _) and
20312071
name = path.getText()
20322072
}
@@ -2175,12 +2215,12 @@ private module Debug {
21752215
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
21762216
result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
21772217
filepath.matches("%/main.rs") and
2178-
startline = 52
2218+
startline = 800
21792219
)
21802220
}
21812221

21822222
predicate debugUnqualifiedPathLookup(
2183-
RelevantPath p, string name, Namespace ns, ItemNode ancestor, string path
2223+
PathExt p, string name, Namespace ns, ItemNode ancestor, string path
21842224
) {
21852225
p = getRelevantLocatable() and
21862226
exists(ItemNode encl |
@@ -2192,12 +2232,12 @@ private module Debug {
21922232

21932233
predicate debugItemNode(ItemNode item) { item = getRelevantLocatable() }
21942234

2195-
ItemNode debugResolvePath(RelevantPath path) {
2235+
ItemNode debugResolvePath(PathExt path) {
21962236
path = getRelevantLocatable() and
21972237
result = resolvePath(path)
21982238
}
21992239

2200-
ItemNode debugResolveUseTreeListItem(Use use, UseTree tree, RelevantPath path, SuccessorKind kind) {
2240+
ItemNode debugResolveUseTreeListItem(Use use, UseTree tree, PathExt path, SuccessorKind kind) {
22012241
use = getRelevantLocatable() and
22022242
result = resolveUseTreeListItem(use, tree, path, kind)
22032243
}

0 commit comments

Comments
 (0)