Skip to content

Commit 989432c

Browse files
committed
Rust: Path resolution before variable resolution
1 parent 33c7de8 commit 989432c

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)) }
@@ -1524,20 +1524,22 @@ private predicate declares(ItemNode item, Namespace ns, string name) {
15241524
)
15251525
}
15261526

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

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

15421544
/**
15431545
* Holds if this is an unqualified path with the textual value `name` and
@@ -1559,6 +1561,33 @@ class RelevantPath extends Path {
15591561
predicate isDollarCrate() { this.isUnqualified("$crate", _) }
15601562
}
15611563

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

15641593
/** Holds if source file `source` contains the module `m`. */
@@ -1582,7 +1611,7 @@ private ItemNode getOuterScope(ItemNode i) {
15821611
pragma[nomagic]
15831612
private predicate unqualifiedPathLookup(ItemNode ancestor, string name, Namespace ns, ItemNode encl) {
15841613
// lookup in the immediately enclosing item
1585-
exists(RelevantPath path |
1614+
exists(PathExt path |
15861615
path.isUnqualified(name, encl) and
15871616
ancestor = encl and
15881617
not name = ["crate", "$crate", "super", "self"]
@@ -1618,7 +1647,7 @@ private ItemNode getASuccessor(
16181647

16191648
private predicate isSourceFile(ItemNode source) { source instanceof SourceFileItemNode }
16201649

1621-
private predicate hasCratePath(ItemNode i) { any(RelevantPath path).isCratePath(_, i) }
1650+
private predicate hasCratePath(ItemNode i) { any(PathExt path).isCratePath(_, i) }
16221651

16231652
private predicate hasChild(ItemNode parent, ItemNode child) { child.getImmediateParent() = parent }
16241653

@@ -1630,7 +1659,7 @@ private predicate sourceFileHasCratePathTc(ItemNode i1, ItemNode i2) =
16301659
* `name` may be looked up inside `ancestor`.
16311660
*/
16321661
pragma[nomagic]
1633-
private predicate keywordLookup(ItemNode ancestor, string name, RelevantPath p) {
1662+
private predicate keywordLookup(ItemNode ancestor, string name, PathExt p) {
16341663
// For `crate`, jump directly to the root module
16351664
exists(ItemNode i | p.isCratePath(name, i) |
16361665
ancestor instanceof SourceFile and
@@ -1644,7 +1673,7 @@ private predicate keywordLookup(ItemNode ancestor, string name, RelevantPath p)
16441673
}
16451674

16461675
pragma[nomagic]
1647-
private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKind kind) {
1676+
private ItemNode unqualifiedPathLookup(PathExt p, Namespace ns, SuccessorKind kind) {
16481677
exists(ItemNode ancestor, string name |
16491678
result = getASuccessor(ancestor, pragma[only_bind_into](name), ns, kind, _) and
16501679
kind.isInternalOrBoth()
@@ -1659,7 +1688,7 @@ private ItemNode unqualifiedPathLookup(RelevantPath p, Namespace ns, SuccessorKi
16591688
}
16601689

16611690
pragma[nomagic]
1662-
private predicate isUnqualifiedSelfPath(RelevantPath path) { path.isUnqualified("Self") }
1691+
private predicate isUnqualifiedSelfPath(PathExt path) { path.isUnqualified("Self") }
16631692

16641693
/** Provides the input to `TraitIsVisible`. */
16651694
signature predicate relevantTraitVisibleSig(Element element, Trait trait);
@@ -1742,14 +1771,14 @@ private module DollarCrateResolution {
17421771
isDollarCrateSupportedMacroExpansion(_, expansion)
17431772
}
17441773

1745-
private predicate isDollarCratePath(RelevantPath p) { p.isDollarCrate() }
1774+
private predicate isDollarCratePath(PathExt p) { p.isDollarCrate() }
17461775

1747-
private predicate isInDollarCrateMacroExpansion(RelevantPath p, AstNode expansion) =
1776+
private predicate isInDollarCrateMacroExpansion(PathExt p, AstNode expansion) =
17481777
doublyBoundedFastTC(hasParent/2, isDollarCratePath/1, isDollarCrateSupportedMacroExpansion/1)(p,
17491778
expansion)
17501779

17511780
pragma[nomagic]
1752-
private predicate isInDollarCrateMacroExpansionFromFile(File macroDefFile, RelevantPath p) {
1781+
private predicate isInDollarCrateMacroExpansionFromFile(File macroDefFile, PathExt p) {
17531782
exists(Path macroDefPath, AstNode expansion |
17541783
isDollarCrateSupportedMacroExpansion(macroDefPath, expansion) and
17551784
isInDollarCrateMacroExpansion(p, expansion) and
@@ -1764,17 +1793,17 @@ private module DollarCrateResolution {
17641793
* calls.
17651794
*/
17661795
pragma[nomagic]
1767-
predicate resolveDollarCrate(RelevantPath p, CrateItemNode crate) {
1796+
predicate resolveDollarCrate(PathExt p, CrateItemNode crate) {
17681797
isInDollarCrateMacroExpansionFromFile(crate.getASourceFile().getFile(), p)
17691798
}
17701799
}
17711800

17721801
pragma[nomagic]
1773-
private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) {
1802+
private ItemNode resolvePathCand0(PathExt path, Namespace ns) {
17741803
exists(ItemNode res |
17751804
res = unqualifiedPathLookup(path, ns, _) and
17761805
if
1777-
not any(RelevantPath parent).getQualifier() = path and
1806+
not any(PathExt parent).getQualifier() = path and
17781807
isUnqualifiedSelfPath(path) and
17791808
res instanceof ImplItemNode
17801809
then result = res.(ImplItemNodeImpl).resolveSelfTyCand()
@@ -1791,7 +1820,7 @@ private ItemNode resolvePathCand0(RelevantPath path, Namespace ns) {
17911820
}
17921821

17931822
pragma[nomagic]
1794-
private ItemNode resolvePathCandQualifier(RelevantPath qualifier, RelevantPath path, string name) {
1823+
private ItemNode resolvePathCandQualifier(PathExt qualifier, PathExt path, string name) {
17951824
qualifier = path.getQualifier() and
17961825
result = resolvePathCand(qualifier) and
17971826
name = path.getText()
@@ -1839,9 +1868,7 @@ private predicate checkQualifiedVisibility(
18391868
* qualifier of `path` and `qualifier` resolves to `q`, if any.
18401869
*/
18411870
pragma[nomagic]
1842-
private ItemNode resolvePathCandQualified(
1843-
RelevantPath qualifier, ItemNode q, RelevantPath path, Namespace ns
1844-
) {
1871+
private ItemNode resolvePathCandQualified(PathExt qualifier, ItemNode q, PathExt path, Namespace ns) {
18451872
exists(string name, SuccessorKind kind, UseOption useOpt |
18461873
q = resolvePathCandQualifier(qualifier, path, name) and
18471874
result = getASuccessor(q, name, ns, kind, useOpt) and
@@ -1850,12 +1877,14 @@ private ItemNode resolvePathCandQualified(
18501877
}
18511878

18521879
/** Holds if path `p` must be looked up in namespace `n`. */
1853-
private predicate pathUsesNamespace(Path p, Namespace n) {
1880+
private predicate pathUsesNamespace(PathExt p, Namespace n) {
18541881
n.isValue() and
18551882
(
18561883
p = any(PathExpr pe).getPath()
18571884
or
18581885
p = any(TupleStructPat tsp).getPath()
1886+
or
1887+
p instanceof PathExtIdentPat
18591888
)
18601889
or
18611890
n.isType() and
@@ -1931,7 +1960,7 @@ private predicate macroUseEdge(
19311960
* result in non-monotonic recursion.
19321961
*/
19331962
pragma[nomagic]
1934-
private ItemNode resolvePathCand(RelevantPath path) {
1963+
private ItemNode resolvePathCand(PathExt path) {
19351964
exists(Namespace ns |
19361965
result = resolvePathCand0(path, ns) and
19371966
if path = any(ImplItemNode i).getSelfPath()
@@ -1944,7 +1973,10 @@ private ItemNode resolvePathCand(RelevantPath path) {
19441973
else
19451974
if path = any(PathTypeRepr p).getPath()
19461975
then result instanceof TypeItemNode
1947-
else any()
1976+
else
1977+
if path instanceof IdentPat
1978+
then result instanceof VariantItemNode or result instanceof StructItemNode
1979+
else any()
19481980
|
19491981
pathUsesNamespace(path, ns)
19501982
or
@@ -1961,7 +1993,7 @@ private ItemNode resolvePathCand(RelevantPath path) {
19611993
}
19621994

19631995
/** Get a trait that should be visible when `path` resolves to `node`, if any. */
1964-
private Trait getResolvePathTraitUsed(RelevantPath path, AssocItemNode node) {
1996+
private Trait getResolvePathTraitUsed(PathExt path, AssocItemNode node) {
19651997
exists(TypeItemNode type, ImplItemNodeImpl impl |
19661998
node = resolvePathCandQualified(_, type, path, _) and
19671999
typeImplEdge(type, impl, _, _, node, _) and
@@ -1973,9 +2005,8 @@ private predicate pathTraitUsed(Element path, Trait trait) {
19732005
trait = getResolvePathTraitUsed(path, _)
19742006
}
19752007

1976-
/** Gets the item that `path` resolves to, if any. */
19772008
cached
1978-
ItemNode resolvePath(RelevantPath path) {
2009+
private ItemNode resolvePath0(PathExt path) {
19792010
result = resolvePathCand(path) and
19802011
not path = any(Path parent | exists(resolvePathCand(parent))).getQualifier() and
19812012
(
@@ -1988,29 +2019,40 @@ ItemNode resolvePath(RelevantPath path) {
19882019
or
19892020
// if `path` is the qualifier of a resolvable `parent`, then we should
19902021
// resolve `path` to something consistent with what `parent` resolves to
1991-
exists(RelevantPath parent |
1992-
resolvePathCandQualified(path, result, parent, _) = resolvePath(parent)
1993-
)
2022+
exists(PathExt parent | resolvePathCandQualified(path, result, parent, _) = resolvePath0(parent))
19942023
}
19952024

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

20052047
pragma[nomagic]
2006-
private predicate isUseTreeSubPathUnqualified(UseTree tree, RelevantPath path, string name) {
2048+
private predicate isUseTreeSubPathUnqualified(UseTree tree, PathExt path, string name) {
20072049
isUseTreeSubPath(tree, path) and
20082050
not exists(path.getQualifier()) and
20092051
name = path.getText()
20102052
}
20112053

20122054
pragma[nomagic]
2013-
private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path, SuccessorKind kind) {
2055+
private ItemNode resolveUseTreeListItem(Use use, UseTree tree, PathExt path, SuccessorKind kind) {
20142056
exists(UseOption useOpt | checkQualifiedVisibility(use, result, kind, useOpt) |
20152057
exists(UseTree midTree, ItemNode mid, string name |
20162058
mid = resolveUseTreeListItem(use, midTree) and
@@ -2027,9 +2069,7 @@ private ItemNode resolveUseTreeListItem(Use use, UseTree tree, RelevantPath path
20272069
}
20282070

20292071
pragma[nomagic]
2030-
private ItemNode resolveUseTreeListItemQualifier(
2031-
Use use, UseTree tree, RelevantPath path, string name
2032-
) {
2072+
private ItemNode resolveUseTreeListItemQualifier(Use use, UseTree tree, PathExt path, string name) {
20332073
result = resolveUseTreeListItem(use, tree, path.getQualifier(), _) and
20342074
name = path.getText()
20352075
}
@@ -2178,12 +2218,12 @@ private module Debug {
21782218
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
21792219
result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
21802220
filepath.matches("%/main.rs") and
2181-
startline = 52
2221+
startline = 800
21822222
)
21832223
}
21842224

21852225
predicate debugUnqualifiedPathLookup(
2186-
RelevantPath p, string name, Namespace ns, ItemNode ancestor, string path
2226+
PathExt p, string name, Namespace ns, ItemNode ancestor, string path
21872227
) {
21882228
p = getRelevantLocatable() and
21892229
exists(ItemNode encl |
@@ -2195,12 +2235,12 @@ private module Debug {
21952235

21962236
predicate debugItemNode(ItemNode item) { item = getRelevantLocatable() }
21972237

2198-
ItemNode debugResolvePath(RelevantPath path) {
2238+
ItemNode debugResolvePath(PathExt path) {
21992239
path = getRelevantLocatable() and
22002240
result = resolvePath(path)
22012241
}
22022242

2203-
ItemNode debugResolveUseTreeListItem(Use use, UseTree tree, RelevantPath path, SuccessorKind kind) {
2243+
ItemNode debugResolveUseTreeListItem(Use use, UseTree tree, PathExt path, SuccessorKind kind) {
22042244
use = getRelevantLocatable() and
22052245
result = resolveUseTreeListItem(use, tree, path, kind)
22062246
}

0 commit comments

Comments
 (0)