Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
70 changes: 70 additions & 0 deletions misc/codegen/templates/ql_db.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,79 @@ module Raw {
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
{{tablename}}({{#tableparams}}{{^first}}, {{/first}}{{param}}{{/tableparams}})
}
{{#is_indexed}}
/**
* Gets the number of {{doc_plural}}.
{{#internal}}
* INTERNAL: Do not use.
{{/internal}}
*/
int getNumberOf{{plural}}() {
result = count(int i | {{tablename}}(this, i, _))
}
{{/is_indexed}}
{{/synth}}
{{/properties}}
}


{{#final}}
private Element getImmediateChildOf{{name}}({{name}} e, int index) {
{{^has_children}}none(){{/has_children}}
{{#has_children}}
{{! n is the base offset 0, for ease of generation }}
{{! n<child> is constructed to be strictly greater than the indexes for <child> children }}
exists(int n{{#all_children}}, int n{{property.singular}}{{/all_children}} |
n = 0
{{#all_children}}
{{#property}}
{{#synth}}
and
n{{singular}} = n{{prev}}
{{/synth}}
{{^synth}}
{{! n<child> is defined on top of the previous definition }}
{{! for single and optional properties it adds 1 (regardless of whether the optional property exists) }}
{{! for repeated it adds 1 + the maximum index (which works for repeated optional as well) }}
and
n{{singular}} = n{{prev}} {{^is_repeated}}+ 1{{/is_repeated}}{{#is_repeated}}+ e.getNumberOf{{plural}}(){{/is_repeated}}
{{/synth}}
{{/property}}
{{/all_children}} and (
none()
{{#all_children}}
{{#property}}
{{^synth}}
or
{{#is_repeated}}
result = e.get{{singular}}(index - n{{prev}})
{{/is_repeated}}
{{^is_repeated}}
index = n{{prev}} and result = e.get{{singular}}()
{{/is_repeated}}
{{/synth}}
{{/property}}
{{/all_children}}
))
{{/has_children}}
}
{{/final}}

{{/classes}}

/**
* Gets the immediate child indexed at `index`. Indexes are not guaranteed to be contiguous, but are guaranteed to be distinct.
*/
pragma[nomagic]
Element getImmediateChild(Element e, int index) {
// why does this look more complicated than it should?
// * none() simplifies generation, as we can append `or ...` without a special case for the first item
none()
{{#classes}}
{{#final}}
or
result = getImmediateChildOf{{name}}(e, index)
{{/final}}
{{/classes}}
}
}
7 changes: 6 additions & 1 deletion misc/codegen/templates/ql_parent.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ private module Impl {
{{! for single and optional properties it adds 1 (regardless of whether the optional property exists) }}
{{! for repeated it adds 1 + the maximum index (which works for repeated optional as well) }}
and
n{{singular}} = n{{prev}} + 1{{#is_repeated}}+ max(int i | i = -1 or exists(e.get{{#type_is_hideable}}Immediate{{/type_is_hideable}}{{singular}}(i)) | i){{/is_repeated}}
{{#type_is_hideable}}
n{{singular}} = n{{prev}} + 1{{#is_repeated}}+ max(int i | i = -1 or exists(e.getImmediate{{singular}}(i)) | i){{/is_repeated}}
{{/type_is_hideable}}
{{^type_is_hideable}}
n{{singular}} = n{{prev}} {{^is_repeated}}+ 1{{/is_repeated}}{{#is_repeated}}+ e.getNumberOf{{plural}}(){{/is_repeated}}
{{/type_is_hideable}}
{{/property}}
{{/all_children}} and (
none()
Expand Down
19 changes: 19 additions & 0 deletions misc/codegen/templates/ql_synth_types.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,28 @@ module Synth {
{{/root}}
{{/non_final_classes}}

/**
* INTERNAL: Do not use.
*
* Gets the parent of synthetic element `e`.
*/
Raw::{{root}} getSynthParent(T{{root}} e) {
none()
{{#final_classes}}
{{#is_fresh_synth}}
{{#has_params}}
or
e = T{{name}}({{#params}}{{#first}}result{{/first}}{{^first}}, _{{/first}}{{/params}})
{{/has_params}}
{{/is_fresh_synth}}
{{/final_classes}}
}


{{#final_classes}}
/**
* INTERNAL: Do not use.
*
* Converts a raw element to a synthesized `T{{name}}`, if possible.
*/
T{{name}} convert{{name}}FromRaw(Raw::Element e) {
Expand Down
7 changes: 3 additions & 4 deletions 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
108 changes: 108 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,115 @@ 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.
*/
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 parent is
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))
}
}
Loading
Loading