Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion rust/ql/.generated.list

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion rust/ql/.gitattributes

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 4 additions & 10 deletions rust/ql/lib/codeql/rust/elements/internal/AstNodeImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ private import codeql.rust.controlflow.ControlFlowGraph
*/
module Impl {
private import rust
private import codeql.rust.elements.internal.ElementImpl::Impl as ElementImpl
private import codeql.rust.elements.internal.generated.ParentChild
private import codeql.rust.controlflow.ControlFlowGraph
private import codeql.rust.elements.internal.MacroCallImpl::Impl as MacroCallImpl

/**
* Gets the immediate parent of a non-`AstNode` element `e`.
Expand Down Expand Up @@ -71,21 +71,15 @@ module Impl {
}

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

/**
* Holds if this node exists only as the result of a macro expansion.
*
* This is the same as `isInMacroExpansion()`, but excludes AST nodes corresponding
* to macro arguments.
* to macro arguments, including attribute macro targets.
*/
pragma[nomagic]
predicate isFromMacroExpansion() {
exists(AstNode root |
MacroCallImpl::isInMacroExpansion(root, this) and
not this = root.(MacroCall).getATokenTreeNode()
)
}
predicate isFromMacroExpansion() { ElementImpl::MacroExpansion::isFromMacroExpansion(this) }

/**
* Gets a control flow node for this AST node, if any.
Expand Down
111 changes: 111 additions & 0 deletions rust/ql/lib/codeql/rust/elements/internal/ElementImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,118 @@ private import codeql.rust.elements.internal.generated.Element
* be referenced directly.
*/
module Impl {
private import rust
private import codeql.rust.elements.internal.generated.ParentChild
private import codeql.rust.elements.internal.generated.Synth
private import codeql.rust.elements.internal.generated.Raw
private import codeql.rust.elements.internal.LocationImpl

/**
* Provides logic for classifying elements with respect to macro expansions.
*/
cached
module MacroExpansion {
/**
* Holds if `e` is superseded by an attribute macro expansion. That is, `e` is
* a transitive child of an item with an attribute macro expansion.
*
* Since this predicate is referenced in the charpred of `Element`, we need to
* use the parent-child relation on raw elements to avoid non-monotonicity.
*/
private predicate supersededByAttributeMacroExpansionRaw(Raw::Item item, Raw::Element e) {
exists(item.getAttributeMacroExpansion()) and
e = Raw::getImmediateChild(item, _) and
not e = item.getAttributeMacroExpansion() and
// Don't consider attributes themselves to be superseded. E.g., in `#[a] fn
// f() {}` the macro expansion supersedes `fn f() {}` but not `#[a]`.
not e instanceof Raw::Attr
or
exists(Raw::Element parent |
e = Raw::getImmediateChild(parent, _) and
supersededByAttributeMacroExpansionRaw(item, parent)
)
}

private predicate isMacroExpansion(AstNode macro, AstNode expansion) {
expansion = macro.(MacroCall).getMacroCallExpansion()
or
expansion = macro.(Adt).getDeriveMacroExpansion(_)
or
expansion = macro.(Item).getAttributeMacroExpansion()
}

/**
* Gets the immediately enclosing macro invocation for element `e`, if any.
*
* The result is either a `MacroCall`, and `Adt` with a derive macro expansion, or
* an `Item` with an attribute macro expansion.
*/
cached
AstNode getImmediatelyEnclosingMacroInvocation(Element e) {
isMacroExpansion(result, e)
or
exists(Element mid |
result = getImmediatelyEnclosingMacroInvocation(mid) and
mid = getImmediateParent(e) and
not isMacroExpansion(mid, e)
)
}

pragma[nomagic]
private predicate isAttributeMacroExpansionSourceLocation(Item i, Location l) {
exists(Raw::Locatable e, @location_default loc |
supersededByAttributeMacroExpansionRaw(Synth::convertElementToRaw(i), e) and
locatable_locations(e, loc) and
l = LocationImpl::TLocationDefault(loc)
)
}

/** Gets an AST node whose location is inside the token tree belonging to `mc`. */
pragma[nomagic]
private AstNode getATokenTreeNode(MacroCall mc) {
mc = getImmediatelyEnclosingMacroInvocation(result) and
mc.getTokenTree().getLocation().contains(result.getLocation())
}

/** Holds if `n` is inside a macro expansion. */
cached
predicate isInMacroExpansion(AstNode n) { exists(getImmediatelyEnclosingMacroInvocation(n)) }

/**
* Holds if `n` exists only as the result of a macro expansion.
*
* This is the same as `isInMacroExpansion(n)`, but excludes AST nodes corresponding
* to macro arguments, including attribute macro targets.
*
* Note: This predicate is a heuristic based on location information and may not be
* accurate in all cases.
*/
cached
predicate isFromMacroExpansion(AstNode n) {
exists(AstNode macro |
macro = getImmediatelyEnclosingMacroInvocation(n) and
not n = getATokenTreeNode(macro) and
not isAttributeMacroExpansionSourceLocation(macro, n.getLocation())
)
or
isFromMacroExpansion(getImmediatelyEnclosingMacroInvocation(n))
}

cached
predicate isRelevantElement(Generated::Element e) {
exists(Raw::Element raw |
raw = Synth::convertElementToRaw(e) and
not supersededByAttributeMacroExpansionRaw(_, raw)
)
or
// Synthetic elements are relevant when their parents are
Synth::convertFormatArgsExprToRaw(_) = Synth::getSynthParent(e)
}
}

class Element extends Generated::Element {
Element() { MacroExpansion::isRelevantElement(this) }

override string toStringImpl() { result = this.getAPrimaryQlClass() }

/**
Expand Down
8 changes: 7 additions & 1 deletion rust/ql/lib/codeql/rust/elements/internal/ItemImpl.qll
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// generated by codegen, remove this comment if you wish to edit this file
/**
* This module provides a hand-modifiable wrapper around the generated class `Item`.
*
Expand All @@ -12,6 +11,7 @@ private import codeql.rust.elements.internal.generated.Item
* be referenced directly.
*/
module Impl {
// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* An item such as a function, struct, enum, etc.
*
Expand All @@ -23,4 +23,10 @@ module Impl {
* ```
*/
class Item extends Generated::Item { }

private class ItemWithAttributeMacroExpansion extends Item {
ItemWithAttributeMacroExpansion() { this.hasAttributeMacroExpansion() }

override string toStringImpl() { result = "(item with attribute macro expansion)" }
}
}
14 changes: 3 additions & 11 deletions rust/ql/lib/codeql/rust/elements/internal/LocatableImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import codeql.Locations
private import codeql.rust.elements.internal.ElementImpl::Impl as ElementImpl
private import codeql.rust.elements.internal.LocationImpl
private import codeql.rust.elements.internal.generated.Locatable
private import codeql.rust.elements.internal.generated.Synth
Expand Down Expand Up @@ -50,21 +51,12 @@ module Impl {
locatable_locations(Synth::convertLocatableToRaw(l), result)
}

private MacroCall getImmediatelyEnclosingMacroCall(AstNode n) {
result = n.getParentNode()
or
exists(AstNode mid |
result = getImmediatelyEnclosingMacroCall(mid) and
n.getParentNode() = mid and
not mid instanceof MacroCall
)
}

/** Gets the non-synthesized location of `l`, if any. */
LocationImpl::LocationDefault getLocationDefault(Locatable l) {
result = LocationImpl::TLocationDefault(getDbLocation(l))
or
not exists(getDbLocation(l)) and
result = getLocationDefault(getImmediatelyEnclosingMacroCall(l))
result =
getLocationDefault(ElementImpl::MacroExpansion::getImmediatelyEnclosingMacroInvocation(l))
}
}
22 changes: 1 addition & 21 deletions rust/ql/lib/codeql/rust/elements/internal/MacroCallImpl.qll
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,6 @@ module Impl {
private import rust
private import codeql.rust.internal.PathResolution

pragma[nomagic]
predicate isInMacroExpansion(AstNode root, AstNode n) {
n = root.(MacroCall).getMacroCallExpansion()
or
n = root.(Adt).getDeriveMacroExpansion(_)
or
n = root.(Item).getAttributeMacroExpansion()
or
isInMacroExpansion(root, n.getParentNode())
}

// the following QLdoc is generated: if you need to edit it, do it in the schema file
/**
* A macro invocation.
Expand All @@ -35,16 +24,7 @@ module Impl {
* ```
*/
class MacroCall extends Generated::MacroCall {
override string toStringImpl() {
if this.hasPath() then result = this.getPath().toAbbreviatedString() + "!..." else result = ""
}

/** Gets an AST node whose location is inside the token tree belonging to this macro call. */
pragma[nomagic]
AstNode getATokenTreeNode() {
isInMacroExpansion(this, result) and
this.getTokenTree().getLocation().contains(result.getLocation())
}
override string toStringImpl() { result = this.getPath().toAbbreviatedString() + "!..." }

/**
* Gets the macro definition that this macro call resolves to.
Expand Down
23 changes: 1 addition & 22 deletions rust/ql/lib/codeql/rust/internal/PathResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -90,24 +90,6 @@ private module UseOption = Option<Use>;

private class UseOption = UseOption::Option;

/**
* Holds if `n` is superseded by an attribute macro expansion. That is, `n` is
* an item or a transitive child of an item with an attribute macro expansion.
*/
predicate supersededByAttributeMacroExpansion(AstNode n) {
n.(Item).hasAttributeMacroExpansion()
or
exists(AstNode parent |
n.getParentNode() = parent and
supersededByAttributeMacroExpansion(parent) and
// Don't exclude expansions themselves as they supercede other nodes.
not n = parent.(Item).getAttributeMacroExpansion() and
// Don't consider attributes themselves to be superseded. E.g., in `#[a] fn
// f() {}` the macro expansion supercedes `fn f() {}` but not `#[a]`.
not n instanceof Attr
)
}

/**
* An item that may be referred to by a path, and which is a node in
* the _item graph_.
Expand Down Expand Up @@ -186,10 +168,7 @@ predicate supersededByAttributeMacroExpansion(AstNode n) {
* - https://doc.rust-lang.org/reference/names/namespaces.html
*/
abstract class ItemNode extends Locatable {
ItemNode() {
// Exclude items that are superseded by the expansion of an attribute macro.
not supersededByAttributeMacroExpansion(this)
}
ItemNode() { not this.(Item).hasAttributeMacroExpansion() }

/** Gets the (original) name of this item. */
abstract string getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ private import utils.test.InlineExpectationsTest
private module ResolveTest implements TestSig {
string getARelevantTag() { result = "item" }

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

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

private predicate item(ItemNode i, string value) {
exists(string filepath, int line, boolean inMacro | itemAt(i, filepath, line, inMacro) |
exists(string filepath, int line | itemAt(i, filepath, line) |
if i instanceof SourceFile
then value = i.getFile().getBaseName()
else (
Expand Down
2 changes: 1 addition & 1 deletion rust/ql/src/queries/unusedentities/UnreachableCode.ql
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ predicate hiddenNode(AstNode n) {
n instanceof ControlFlowGraphImpl::PostOrderTree and // location is counter-intuitive
not n instanceof MacroExpr
or
n.isInMacroExpansion()
n.isFromMacroExpansion()
}

/**
Expand Down
2 changes: 1 addition & 1 deletion rust/ql/src/queries/unusedentities/UnusedValue.ql
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ from AstNode write, Ssa::Variable v
where
variableWrite(_, write, v) and
not v instanceof DiscardVariable and
not write.isInMacroExpansion() and
not write.isFromMacroExpansion() and
not isAllowableUnused(v) and
// SSA definitions are only created for live writes
not write = any(Ssa::WriteDefinition def).getWriteAccess().getAstNode() and
Expand Down
2 changes: 1 addition & 1 deletion rust/ql/src/queries/unusedentities/UnusedVariable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class IncompleteCallable extends Callable {
*/
predicate isAllowableUnused(Variable v) {
// in a macro expansion
v.getPat().isInMacroExpansion()
v.getPat().isInMacroExpansion() // TODO: replace with `isFromMacroExpansion()` when false positives have been removed
or
// declared in an incomplete callable
v.getEnclosingCfgScope() instanceof IncompleteCallable
Expand Down
Loading