Skip to content

Commit f7f434b

Browse files
authored
Merge pull request #19519 from hvitved/rust-extract-libs-follow-up
Rust: Follow-up work to make path resolution and type inference tests pass again
2 parents 7227701 + 766af35 commit f7f434b

File tree

15 files changed

+198
-111
lines changed

15 files changed

+198
-111
lines changed

rust/ql/lib/codeql/files/FileSystem.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ module Folder = Impl::Folder;
4040
class File extends Container, Impl::File {
4141
/** Holds if this file was extracted from ordinary source code. */
4242
predicate fromSource() {
43-
exists(ExtractorStep s | s.getAction() = "Extract" and s.getFile() = this)
43+
exists(ExtractorStep s | s.getAction() = "Extract" and s.getFile() = this) and
44+
// instead use special extractor step for dependencies?
45+
exists(this.getRelativePath())
4446
}
4547

4648
/**

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module Impl {
1515
private import rust
1616
private import codeql.rust.elements.internal.generated.ParentChild
1717
private import codeql.rust.controlflow.ControlFlowGraph
18+
private import codeql.rust.elements.internal.MacroCallImpl::Impl as MacroCallImpl
1819

1920
/**
2021
* Gets the immediate parent of a non-`AstNode` element `e`.
@@ -59,10 +60,20 @@ module Impl {
5960
}
6061

6162
/** Holds if this node is inside a macro expansion. */
62-
predicate isInMacroExpansion() {
63-
this = any(MacroCall mc).getMacroCallExpansion()
64-
or
65-
this.getParentNode().isInMacroExpansion()
63+
predicate isInMacroExpansion() { MacroCallImpl::isInMacroExpansion(_, this) }
64+
65+
/**
66+
* Holds if this node exists only as the result of a macro expansion.
67+
*
68+
* This is the same as `isInMacroExpansion()`, but excludes AST nodes corresponding
69+
* to macro arguments.
70+
*/
71+
pragma[nomagic]
72+
predicate isFromMacroExpansion() {
73+
exists(MacroCall mc |
74+
MacroCallImpl::isInMacroExpansion(mc, this) and
75+
not this = mc.getATokenTreeNode()
76+
)
6677
}
6778

6879
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ module Impl {
4343
File getFile() { result = this.getLocation().getFile() }
4444

4545
/** Holds if this element is from source code. */
46-
predicate fromSource() { exists(this.getFile().getRelativePath()) }
46+
predicate fromSource() { this.getFile().fromSource() }
4747
}
4848

4949
private @location_default getDbLocation(Locatable l) {

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

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,13 +77,76 @@ module LocationImpl {
7777
)
7878
}
7979

80-
/** Holds if this location starts strictly before the specified location. */
80+
/** Holds if this location starts before location `that`. */
8181
pragma[inline]
82-
predicate strictlyBefore(Location other) {
83-
this.getStartLine() < other.getStartLine()
84-
or
85-
this.getStartLine() = other.getStartLine() and this.getStartColumn() < other.getStartColumn()
82+
predicate startsBefore(Location that) {
83+
exists(string f, int sl1, int sc1, int sl2, int sc2 |
84+
this.hasLocationInfo(f, sl1, sc1, _, _) and
85+
that.hasLocationInfo(f, sl2, sc2, _, _)
86+
|
87+
sl1 < sl2
88+
or
89+
sl1 = sl2 and sc1 <= sc2
90+
)
91+
}
92+
93+
/** Holds if this location starts strictly before location `that`. */
94+
pragma[inline]
95+
predicate startsStrictlyBefore(Location that) {
96+
exists(string f, int sl1, int sc1, int sl2, int sc2 |
97+
this.hasLocationInfo(f, sl1, sc1, _, _) and
98+
that.hasLocationInfo(f, sl2, sc2, _, _)
99+
|
100+
sl1 < sl2
101+
or
102+
sl1 = sl2 and sc1 < sc2
103+
)
104+
}
105+
106+
/** Holds if this location ends after location `that`. */
107+
pragma[inline]
108+
predicate endsAfter(Location that) {
109+
exists(string f, int el1, int ec1, int el2, int ec2 |
110+
this.hasLocationInfo(f, _, _, el1, ec1) and
111+
that.hasLocationInfo(f, _, _, el2, ec2)
112+
|
113+
el1 > el2
114+
or
115+
el1 = el2 and ec1 >= ec2
116+
)
86117
}
118+
119+
/** Holds if this location ends strictly after location `that`. */
120+
pragma[inline]
121+
predicate endsStrictlyAfter(Location that) {
122+
exists(string f, int el1, int ec1, int el2, int ec2 |
123+
this.hasLocationInfo(f, _, _, el1, ec1) and
124+
that.hasLocationInfo(f, _, _, el2, ec2)
125+
|
126+
el1 > el2
127+
or
128+
el1 = el2 and ec1 > ec2
129+
)
130+
}
131+
132+
/**
133+
* Holds if this location contains location `that`, meaning that it starts
134+
* before and ends after it.
135+
*/
136+
pragma[inline]
137+
predicate contains(Location that) { this.startsBefore(that) and this.endsAfter(that) }
138+
139+
/**
140+
* Holds if this location strictlycontains location `that`, meaning that it starts
141+
* strictly before and ends strictly after it.
142+
*/
143+
pragma[inline]
144+
predicate strictlyContains(Location that) {
145+
this.startsStrictlyBefore(that) and this.endsStrictlyAfter(that)
146+
}
147+
148+
/** Holds if this location is from source code. */
149+
predicate fromSource() { this.getFile().fromSource() }
87150
}
88151

89152
class LocationDefault extends Location, TLocationDefault {

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ private import codeql.rust.elements.internal.generated.MacroCall
1111
* be referenced directly.
1212
*/
1313
module Impl {
14+
private import rust
15+
16+
pragma[nomagic]
17+
predicate isInMacroExpansion(MacroCall mc, AstNode n) {
18+
n = mc.getMacroCallExpansion()
19+
or
20+
isInMacroExpansion(mc, n.getParentNode())
21+
}
22+
1423
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1524
/**
1625
* A MacroCall. For example:
@@ -20,5 +29,12 @@ module Impl {
2029
*/
2130
class MacroCall extends Generated::MacroCall {
2231
override string toStringImpl() { result = this.getPath().toAbbreviatedString() + "!..." }
32+
33+
/** Gets an AST node whose location is inside the token tree belonging to this macro call. */
34+
pragma[nomagic]
35+
AstNode getATokenTreeNode() {
36+
isInMacroExpansion(this, result) and
37+
this.getTokenTree().getLocation().contains(result.getLocation())
38+
}
2339
}
2440
}

rust/ql/lib/codeql/rust/frameworks/stdlib/Stdlib.qll

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ private import codeql.rust.Concepts
77
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
88
private import codeql.rust.controlflow.CfgNodes as CfgNodes
99
private import codeql.rust.dataflow.DataFlow
10+
private import codeql.rust.internal.PathResolution
1011

1112
/**
1213
* A call to the `starts_with` method on a `Path`.
@@ -32,10 +33,8 @@ class OptionEnum extends Enum {
3233
// todo: replace with canonical path, once calculated in QL
3334
exists(Crate core, Module m |
3435
core.getName() = "core" and
35-
m = core.getSourceFile().getAnItem() and
36-
m.getName().getText() = "option" and
37-
this = m.getItemList().getAnItem() and
38-
this.getName().getText() = "Option"
36+
m = core.getSourceFile().(ItemNode).getASuccessor("option") and
37+
this = m.(ItemNode).getASuccessor("Option")
3938
)
4039
}
4140

@@ -53,10 +52,8 @@ class ResultEnum extends Enum {
5352
// todo: replace with canonical path, once calculated in QL
5453
exists(Crate core, Module m |
5554
core.getName() = "core" and
56-
m = core.getSourceFile().getAnItem() and
57-
m.getName().getText() = "result" and
58-
this = m.getItemList().getAnItem() and
59-
this.getName().getText() = "Result"
55+
m = core.getSourceFile().(ItemNode).getASuccessor("result") and
56+
this = m.(ItemNode).getASuccessor("Result")
6057
)
6158
}
6259

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

Lines changed: 19 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,11 @@ abstract class ItemNode extends Locatable {
194194
this = result.(ImplOrTraitItemNode).getAnItemInSelfScope()
195195
or
196196
name = "crate" and
197-
this = result.(CrateItemNode).getARootModuleNode()
197+
this = result.(CrateItemNode).getASourceFile()
198198
or
199199
// todo: implement properly
200200
name = "$crate" and
201-
result = any(CrateItemNode crate | this = crate.getARootModuleNode()).(Crate).getADependency*() and
201+
result = any(CrateItemNode crate | this = crate.getASourceFile()).(Crate).getADependency*() and
202202
result.(CrateItemNode).isPotentialDollarCrateTarget()
203203
}
204204

@@ -240,12 +240,6 @@ abstract private class ModuleLikeNode extends ItemNode {
240240
not mid instanceof ModuleLikeNode
241241
)
242242
}
243-
244-
/**
245-
* Holds if this is a root module, meaning either a source file or
246-
* the entry module of a crate.
247-
*/
248-
predicate isRoot() { this instanceof SourceFileItemNode }
249243
}
250244

251245
private class SourceFileItemNode extends ModuleLikeNode, SourceFile {
@@ -267,16 +261,13 @@ private class SourceFileItemNode extends ModuleLikeNode, SourceFile {
267261

268262
class CrateItemNode extends ItemNode instanceof Crate {
269263
/**
270-
* Gets the module node that defines this crate.
271-
*
272-
* This is either a source file, when the crate is defined in source code,
273-
* or a module, when the crate is defined in a dependency.
264+
* Gets the source file that defines this crate.
274265
*/
275266
pragma[nomagic]
276-
ModuleLikeNode getModuleNode() { result = super.getSourceFile() }
267+
SourceFileItemNode getSourceFile() { result = super.getSourceFile() }
277268

278269
/**
279-
* Gets a source file that belongs to this crate, if any.
270+
* Gets a source file that belongs to this crate.
280271
*
281272
* This is calculated as those source files that can be reached from the entry
282273
* file of this crate using zero or more `mod` imports, without going through
@@ -294,11 +285,6 @@ class CrateItemNode extends ItemNode instanceof Crate {
294285
)
295286
}
296287

297-
/**
298-
* Gets a root module node belonging to this crate.
299-
*/
300-
ModuleLikeNode getARootModuleNode() { result = this.getASourceFile() }
301-
302288
pragma[nomagic]
303289
predicate isPotentialDollarCrateTarget() {
304290
exists(string name, RelevantPath p |
@@ -710,7 +696,7 @@ private predicate modImport0(Module m, string name, Folder lookup) {
710696
// sibling import
711697
lookup = parent and
712698
(
713-
m.getFile() = any(CrateItemNode c).getModuleNode().(SourceFileItemNode).getFile()
699+
m.getFile() = any(CrateItemNode c).getSourceFile().getFile()
714700
or
715701
m.getFile().getBaseName() = "mod.rs"
716702
)
@@ -798,25 +784,18 @@ private predicate fileImportEdge(Module mod, string name, ItemNode item) {
798784
*/
799785
pragma[nomagic]
800786
private predicate crateDefEdge(CrateItemNode c, string name, ItemNode i) {
801-
i = c.getModuleNode().getASuccessorRec(name) and
787+
i = c.getSourceFile().getASuccessorRec(name) and
802788
not i instanceof Crate
803789
}
804790

805791
/**
806792
* Holds if `m` depends on crate `dep` named `name`.
807793
*/
808794
private predicate crateDependencyEdge(ModuleLikeNode m, string name, CrateItemNode dep) {
809-
exists(CrateItemNode c | dep = c.(Crate).getDependency(name) |
810-
// entry module/entry source file
811-
m = c.getModuleNode()
812-
or
813-
// entry/transitive source file
795+
exists(CrateItemNode c |
796+
dep = c.(Crate).getDependency(name) and
814797
m = c.getASourceFile()
815798
)
816-
or
817-
// paths inside the crate graph use the name of the crate itself as prefix,
818-
// although that is not valid in Rust
819-
dep = any(Crate c | name = c.getName() and m = c.getSourceFile())
820799
}
821800

822801
private predicate useTreeDeclares(UseTree tree, string name) {
@@ -884,9 +863,9 @@ class RelevantPath extends Path {
884863

885864
private predicate isModule(ItemNode m) { m instanceof Module }
886865

887-
/** Holds if root module `root` contains the module `m`. */
888-
private predicate rootHasModule(ItemNode root, ItemNode m) =
889-
doublyBoundedFastTC(hasChild/2, isRoot/1, isModule/1)(root, m)
866+
/** Holds if source file `source` contains the module `m`. */
867+
private predicate rootHasModule(SourceFileItemNode source, ItemNode m) =
868+
doublyBoundedFastTC(hasChild/2, isSourceFile/1, isModule/1)(source, m)
890869

891870
pragma[nomagic]
892871
private ItemNode getOuterScope(ItemNode i) {
@@ -939,14 +918,14 @@ private ItemNode getASuccessorFull(ItemNode pred, string name, Namespace ns) {
939918
ns = result.getNamespace()
940919
}
941920

942-
private predicate isRoot(ItemNode root) { root.(ModuleLikeNode).isRoot() }
921+
private predicate isSourceFile(ItemNode source) { source instanceof SourceFileItemNode }
943922

944923
private predicate hasCratePath(ItemNode i) { any(RelevantPath path).isCratePath(_, i) }
945924

946925
private predicate hasChild(ItemNode parent, ItemNode child) { child.getImmediateParent() = parent }
947926

948-
private predicate rootHasCratePathTc(ItemNode i1, ItemNode i2) =
949-
doublyBoundedFastTC(hasChild/2, isRoot/1, hasCratePath/1)(i1, i2)
927+
private predicate sourceFileHasCratePathTc(ItemNode i1, ItemNode i2) =
928+
doublyBoundedFastTC(hasChild/2, isSourceFile/1, hasCratePath/1)(i1, i2)
950929

951930
/**
952931
* Holds if the unqualified path `p` references a keyword item named `name`, and
@@ -956,10 +935,10 @@ pragma[nomagic]
956935
private predicate keywordLookup(ItemNode encl, string name, Namespace ns, RelevantPath p) {
957936
// For `($)crate`, jump directly to the root module
958937
exists(ItemNode i | p.isCratePath(name, i) |
959-
encl.(ModuleLikeNode).isRoot() and
938+
encl instanceof SourceFile and
960939
encl = i
961940
or
962-
rootHasCratePathTc(encl, i)
941+
sourceFileHasCratePathTc(encl, i)
963942
)
964943
or
965944
name = ["super", "self"] and
@@ -1176,8 +1155,8 @@ private module Debug {
11761155
private Locatable getRelevantLocatable() {
11771156
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
11781157
result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
1179-
filepath.matches("%/test_logging.rs") and
1180-
startline = 163
1158+
filepath.matches("%/main.rs") and
1159+
startline = 284
11811160
)
11821161
}
11831162

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,7 @@ private module Debug {
10421042
exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
10431043
result.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and
10441044
filepath.matches("%/main.rs") and
1045-
startline = 28
1045+
startline = 948
10461046
)
10471047
}
10481048

rust/ql/lib/utils/test/PathResolutionInlineExpectationsTest.qll

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ private module ResolveTest implements TestSig {
1818
private predicate commmentAt(string text, string filepath, int line) {
1919
exists(Comment c |
2020
c.getLocation().hasLocationInfo(filepath, line, _, _, _) and
21-
c.getCommentText().trim() = text
21+
c.getCommentText().trim() = text and
22+
c.fromSource()
2223
)
2324
}
2425

@@ -35,6 +36,8 @@ private module ResolveTest implements TestSig {
3536
exists(AstNode n |
3637
not n = any(Path parent).getQualifier() and
3738
location = n.getLocation() and
39+
n.fromSource() and
40+
not n.isFromMacroExpansion() and
3841
element = n.toString() and
3942
tag = "item"
4043
|

0 commit comments

Comments
 (0)