Skip to content

Commit 3e14e38

Browse files
committed
Rust: Remove elements superseded by attribute macro expansions
1 parent 7034daf commit 3e14e38

File tree

25 files changed

+161
-307
lines changed

25 files changed

+161
-307
lines changed

rust/ql/.generated.list

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/ql/.gitattributes

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ private import codeql.rust.controlflow.ControlFlowGraph
1313
*/
1414
module Impl {
1515
private import rust
16+
private import codeql.rust.elements.internal.ElementImpl::Impl as ElementImpl
1617
private import codeql.rust.elements.internal.generated.ParentChild
1718
private import codeql.rust.controlflow.ControlFlowGraph
18-
private import codeql.rust.elements.internal.MacroCallImpl::Impl as MacroCallImpl
1919

2020
/**
2121
* Gets the immediate parent of a non-`AstNode` element `e`.
@@ -71,21 +71,15 @@ module Impl {
7171
}
7272

7373
/** Holds if this node is inside a macro expansion. */
74-
predicate isInMacroExpansion() { MacroCallImpl::isInMacroExpansion(_, this) }
74+
predicate isInMacroExpansion() { ElementImpl::MacroExpansion::isInMacroExpansion(this) }
7575

7676
/**
7777
* Holds if this node exists only as the result of a macro expansion.
7878
*
7979
* This is the same as `isInMacroExpansion()`, but excludes AST nodes corresponding
80-
* to macro arguments.
80+
* to macro arguments, including attribute macro targets.
8181
*/
82-
pragma[nomagic]
83-
predicate isFromMacroExpansion() {
84-
exists(AstNode root |
85-
MacroCallImpl::isInMacroExpansion(root, this) and
86-
not this = root.(MacroCall).getATokenTreeNode()
87-
)
88-
}
82+
predicate isFromMacroExpansion() { ElementImpl::MacroExpansion::isFromMacroExpansion(this) }
8983

9084
/**
9185
* Gets a control flow node for this AST node, if any.

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

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,115 @@ private import codeql.rust.elements.internal.generated.Element
1111
* be referenced directly.
1212
*/
1313
module Impl {
14+
private import rust
15+
private import codeql.rust.elements.internal.generated.ParentChild
16+
private import codeql.rust.elements.internal.generated.Synth
17+
private import codeql.rust.elements.internal.generated.Raw
18+
private import codeql.rust.elements.internal.LocationImpl
19+
20+
/**
21+
* Provides logic for classifying elements with respect to macro expansions.
22+
*/
23+
cached
24+
module MacroExpansion {
25+
/**
26+
* Holds if `e` is superseded by an attribute macro expansion. That is, `e` is
27+
* a transitive child of an item with an attribute macro expansion.
28+
*/
29+
private predicate supersededByAttributeMacroExpansionRaw(Raw::Item item, Raw::Element e) {
30+
exists(item.getAttributeMacroExpansion()) and
31+
e = Raw::getImmediateChild(item, _) and
32+
not e = item.getAttributeMacroExpansion() and
33+
// Don't consider attributes themselves to be superseded. E.g., in `#[a] fn
34+
// f() {}` the macro expansion supersedes `fn f() {}` but not `#[a]`.
35+
not e instanceof Raw::Attr
36+
or
37+
exists(Raw::Element parent |
38+
e = Raw::getImmediateChild(parent, _) and
39+
supersededByAttributeMacroExpansionRaw(item, parent)
40+
)
41+
}
42+
43+
private predicate isMacroExpansion(AstNode macro, AstNode expansion) {
44+
expansion = macro.(MacroCall).getMacroCallExpansion()
45+
or
46+
expansion = macro.(Adt).getDeriveMacroExpansion(_)
47+
or
48+
expansion = macro.(Item).getAttributeMacroExpansion()
49+
}
50+
51+
/**
52+
* Gets the immediately enclosing macro invocation for element `e`, if any.
53+
*
54+
* The result is either a `MacroCall`, and `Adt` with a derive macro expansion, or
55+
* an `Item` with an attribute macro expansion.
56+
*/
57+
cached
58+
AstNode getImmediatelyEnclosingMacroInvocation(Element e) {
59+
isMacroExpansion(result, e)
60+
or
61+
exists(Element mid |
62+
result = getImmediatelyEnclosingMacroInvocation(mid) and
63+
mid = getImmediateParent(e) and
64+
not isMacroExpansion(mid, e)
65+
)
66+
}
67+
68+
pragma[nomagic]
69+
private predicate isAttributeMacroExpansionSourceLocation(Item i, Location l) {
70+
exists(Raw::Locatable e, @location_default loc |
71+
supersededByAttributeMacroExpansionRaw(Synth::convertElementToRaw(i), e) and
72+
locatable_locations(e, loc) and
73+
l = LocationImpl::TLocationDefault(loc)
74+
)
75+
}
76+
77+
/** Gets an AST node whose location is inside the token tree belonging to `mc`. */
78+
pragma[nomagic]
79+
private AstNode getATokenTreeNode(MacroCall mc) {
80+
mc = getImmediatelyEnclosingMacroInvocation(result) and
81+
mc.getTokenTree().getLocation().contains(result.getLocation())
82+
}
83+
84+
/** Holds if `n` is inside a macro expansion. */
85+
cached
86+
predicate isInMacroExpansion(AstNode n) { exists(getImmediatelyEnclosingMacroInvocation(n)) }
87+
88+
/**
89+
* Holds if `n` exists only as the result of a macro expansion.
90+
*
91+
* This is the same as `isInMacroExpansion(n)`, but excludes AST nodes corresponding
92+
* to macro arguments, including attribute macro targets.
93+
*
94+
* Note: This predicate is a heuristic based on location information and may not be
95+
* accurate in all cases.
96+
*/
97+
cached
98+
predicate isFromMacroExpansion(AstNode n) {
99+
exists(AstNode macro |
100+
macro = getImmediatelyEnclosingMacroInvocation(n) and
101+
not n = getATokenTreeNode(macro) and
102+
not isAttributeMacroExpansionSourceLocation(macro, n.getLocation())
103+
)
104+
or
105+
isFromMacroExpansion(getImmediatelyEnclosingMacroInvocation(n))
106+
}
107+
108+
cached
109+
predicate isRelevantElement(Generated::Element e) {
110+
exists(Raw::Element raw |
111+
raw = Synth::convertElementToRaw(e) and
112+
not supersededByAttributeMacroExpansionRaw(_, raw)
113+
)
114+
or
115+
// Synthetic elements are relevant when their parent is
116+
Synth::convertFormatArgsExprToRaw(_) = Synth::getSynthParent(e)
117+
}
118+
}
119+
14120
class Element extends Generated::Element {
121+
Element() { MacroExpansion::isRelevantElement(this) }
122+
15123
override string toStringImpl() { result = this.getAPrimaryQlClass() }
16124

17125
/**

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// generated by codegen, remove this comment if you wish to edit this file
21
/**
32
* This module provides a hand-modifiable wrapper around the generated class `Item`.
43
*
@@ -12,6 +11,7 @@ private import codeql.rust.elements.internal.generated.Item
1211
* be referenced directly.
1312
*/
1413
module Impl {
14+
// the following QLdoc is generated: if you need to edit it, do it in the schema file
1515
/**
1616
* An item such as a function, struct, enum, etc.
1717
*
@@ -23,4 +23,10 @@ module Impl {
2323
* ```
2424
*/
2525
class Item extends Generated::Item { }
26+
27+
private class ItemWithAttributeMacroExpansion extends Item {
28+
ItemWithAttributeMacroExpansion() { this.hasAttributeMacroExpansion() }
29+
30+
override string toStringImpl() { result = "(item with attribute macro expansion)" }
31+
}
2632
}

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

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
import codeql.Locations
8+
private import codeql.rust.elements.internal.ElementImpl::Impl as ElementImpl
89
private import codeql.rust.elements.internal.LocationImpl
910
private import codeql.rust.elements.internal.generated.Locatable
1011
private import codeql.rust.elements.internal.generated.Synth
@@ -50,21 +51,12 @@ module Impl {
5051
locatable_locations(Synth::convertLocatableToRaw(l), result)
5152
}
5253

53-
private MacroCall getImmediatelyEnclosingMacroCall(AstNode n) {
54-
result = n.getParentNode()
55-
or
56-
exists(AstNode mid |
57-
result = getImmediatelyEnclosingMacroCall(mid) and
58-
n.getParentNode() = mid and
59-
not mid instanceof MacroCall
60-
)
61-
}
62-
6354
/** Gets the non-synthesized location of `l`, if any. */
6455
LocationImpl::LocationDefault getLocationDefault(Locatable l) {
6556
result = LocationImpl::TLocationDefault(getDbLocation(l))
6657
or
6758
not exists(getDbLocation(l)) and
68-
result = getLocationDefault(getImmediatelyEnclosingMacroCall(l))
59+
result =
60+
getLocationDefault(ElementImpl::MacroExpansion::getImmediatelyEnclosingMacroInvocation(l))
6961
}
7062
}

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

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,6 @@ module Impl {
1414
private import rust
1515
private import codeql.rust.internal.PathResolution
1616

17-
pragma[nomagic]
18-
predicate isInMacroExpansion(AstNode root, AstNode n) {
19-
n = root.(MacroCall).getMacroCallExpansion()
20-
or
21-
n = root.(Adt).getDeriveMacroExpansion(_)
22-
or
23-
n = root.(Item).getAttributeMacroExpansion()
24-
or
25-
isInMacroExpansion(root, n.getParentNode())
26-
}
27-
2817
// the following QLdoc is generated: if you need to edit it, do it in the schema file
2918
/**
3019
* A macro invocation.
@@ -35,16 +24,7 @@ module Impl {
3524
* ```
3625
*/
3726
class MacroCall extends Generated::MacroCall {
38-
override string toStringImpl() {
39-
if this.hasPath() then result = this.getPath().toAbbreviatedString() + "!..." else result = ""
40-
}
41-
42-
/** Gets an AST node whose location is inside the token tree belonging to this macro call. */
43-
pragma[nomagic]
44-
AstNode getATokenTreeNode() {
45-
isInMacroExpansion(this, result) and
46-
this.getTokenTree().getLocation().contains(result.getLocation())
47-
}
27+
override string toStringImpl() { result = this.getPath().toAbbreviatedString() + "!..." }
4828

4929
/**
5030
* Gets the macro definition that this macro call resolves to.

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

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,6 @@ private module UseOption = Option<Use>;
9090

9191
private class UseOption = UseOption::Option;
9292

93-
/**
94-
* Holds if `n` is superseded by an attribute macro expansion. That is, `n` is
95-
* an item or a transitive child of an item with an attribute macro expansion.
96-
*/
97-
predicate supersededByAttributeMacroExpansion(AstNode n) {
98-
n.(Item).hasAttributeMacroExpansion()
99-
or
100-
exists(AstNode parent |
101-
n.getParentNode() = parent and
102-
supersededByAttributeMacroExpansion(parent) and
103-
// Don't exclude expansions themselves as they supercede other nodes.
104-
not n = parent.(Item).getAttributeMacroExpansion() and
105-
// Don't consider attributes themselves to be superseded. E.g., in `#[a] fn
106-
// f() {}` the macro expansion supercedes `fn f() {}` but not `#[a]`.
107-
not n instanceof Attr
108-
)
109-
}
110-
11193
/**
11294
* An item that may be referred to by a path, and which is a node in
11395
* the _item graph_.
@@ -186,10 +168,7 @@ predicate supersededByAttributeMacroExpansion(AstNode n) {
186168
* - https://doc.rust-lang.org/reference/names/namespaces.html
187169
*/
188170
abstract class ItemNode extends Locatable {
189-
ItemNode() {
190-
// Exclude items that are superseded by the expansion of an attribute macro.
191-
not supersededByAttributeMacroExpansion(this)
192-
}
171+
ItemNode() { not this.(Item).hasAttributeMacroExpansion() }
193172

194173
/** Gets the (original) name of this item. */
195174
abstract string getName();

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ private import utils.test.InlineExpectationsTest
1010
private module ResolveTest implements TestSig {
1111
string getARelevantTag() { result = "item" }
1212

13-
private predicate itemAt(ItemNode i, string filepath, int line, boolean inMacro) {
14-
i.getLocation().hasLocationInfo(filepath, _, _, line, _) and
15-
if i.(AstNode).isInMacroExpansion() then inMacro = true else inMacro = false
13+
private predicate itemAt(ItemNode i, string filepath, int line) {
14+
i.getLocation().hasLocationInfo(filepath, _, _, line, _)
1615
}
1716

1817
private predicate commmentAt(string text, string filepath, int line) {
@@ -25,7 +24,7 @@ private module ResolveTest implements TestSig {
2524
}
2625

2726
private predicate item(ItemNode i, string value) {
28-
exists(string filepath, int line, boolean inMacro | itemAt(i, filepath, line, inMacro) |
27+
exists(string filepath, int line | itemAt(i, filepath, line) |
2928
if i instanceof SourceFile
3029
then value = i.getFile().getBaseName()
3130
else (

rust/ql/src/queries/unusedentities/UnreachableCode.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ predicate hiddenNode(AstNode n) {
3333
n instanceof ControlFlowGraphImpl::PostOrderTree and // location is counter-intuitive
3434
not n instanceof MacroExpr
3535
or
36-
n.isInMacroExpansion()
36+
n.isFromMacroExpansion()
3737
}
3838

3939
/**

0 commit comments

Comments
 (0)