mirror of
https://github.com/github/codeql.git
synced 2025-12-16 00:33:11 +01:00
Rust: Auto-generate CfgNodes.qll
This commit is contained in:
@@ -64,6 +64,8 @@ def _parse_args() -> argparse.Namespace:
|
||||
help="registry file containing information about checked-in generated code. A .gitattributes"
|
||||
"file is generated besides it to mark those files with linguist-generated=true. Must"
|
||||
"be in a directory containing all generated code."),
|
||||
p.add_argument("--ql-cfg-output",
|
||||
help="output directory for QL CFG layer (optional)."),
|
||||
]
|
||||
p.add_argument("--script-name",
|
||||
help="script name to put in header comments of generated files. By default, the path of this "
|
||||
|
||||
@@ -160,6 +160,8 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
|
||||
prop = get_ql_property(cls, p, lookup, prev_child)
|
||||
if prop.is_child:
|
||||
prev_child = prop.singular
|
||||
if prop.type in lookup and lookup[prop.type].cfg:
|
||||
prop.cfg = True
|
||||
properties.append(prop)
|
||||
return ql.Class(
|
||||
name=cls.name,
|
||||
@@ -171,6 +173,16 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
|
||||
doc=cls.doc,
|
||||
hideable="ql_hideable" in cls.pragmas,
|
||||
internal="ql_internal" in cls.pragmas,
|
||||
cfg=cls.cfg,
|
||||
)
|
||||
|
||||
|
||||
def get_ql_cfg_class(cls: schema.Class, lookup: typing.Dict[str, ql.Class]) -> ql.CfgClass:
|
||||
return ql.CfgClass(
|
||||
name=cls.name,
|
||||
bases=[base for base in cls.bases if lookup[base.base].cfg],
|
||||
properties=cls.properties,
|
||||
doc=cls.doc
|
||||
)
|
||||
|
||||
|
||||
@@ -361,6 +373,7 @@ def generate(opts, renderer):
|
||||
input = opts.schema
|
||||
out = opts.ql_output
|
||||
stub_out = opts.ql_stub_output
|
||||
cfg_out = opts.ql_cfg_output
|
||||
test_out = opts.ql_test_output
|
||||
missing_test_source_filename = "MISSING_SOURCE.txt"
|
||||
include_file = stub_out.with_suffix(".qll")
|
||||
@@ -385,6 +398,7 @@ def generate(opts, renderer):
|
||||
imports = {}
|
||||
imports_impl = {}
|
||||
classes_used_by = {}
|
||||
cfg_classes = []
|
||||
generated_import_prefix = get_import(out, opts.root_dir)
|
||||
registry = opts.generated_registry or pathlib.Path(
|
||||
os.path.commonpath((out, stub_out, test_out)), ".generated.list")
|
||||
@@ -402,6 +416,8 @@ def generate(opts, renderer):
|
||||
imports[c.name] = path
|
||||
path_impl = get_import(stub_out / c.dir / "internal" / c.name, opts.root_dir)
|
||||
imports_impl[c.name + "Impl"] = path_impl + "Impl"
|
||||
if c.cfg:
|
||||
cfg_classes.append(get_ql_cfg_class(c, classes))
|
||||
|
||||
for c in classes.values():
|
||||
qll = out / c.path.with_suffix(".qll")
|
||||
@@ -411,6 +427,14 @@ def generate(opts, renderer):
|
||||
c.import_prefix = generated_import_prefix
|
||||
renderer.render(c, qll)
|
||||
|
||||
if cfg_out:
|
||||
cfg_classes_val = ql.CfgClasses(
|
||||
include_file_import=get_import(include_file, opts.root_dir),
|
||||
classes=cfg_classes
|
||||
)
|
||||
cfg_qll = cfg_out / "CfgNodes.qll"
|
||||
renderer.render(cfg_classes_val, cfg_qll)
|
||||
|
||||
for c in data.classes.values():
|
||||
path = _get_path(c)
|
||||
path_impl = _get_path_impl(c)
|
||||
|
||||
@@ -45,6 +45,7 @@ class Property:
|
||||
synth: bool = False
|
||||
type_is_hideable: bool = False
|
||||
internal: bool = False
|
||||
cfg: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
if self.tableparams:
|
||||
@@ -110,6 +111,7 @@ class Class:
|
||||
internal: bool = False
|
||||
doc: List[str] = field(default_factory=list)
|
||||
hideable: bool = False
|
||||
cfg: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
def get_bases(bases): return [Base(str(b), str(prev)) for b, prev in zip(bases, itertools.chain([""], bases))]
|
||||
@@ -333,3 +335,18 @@ class Synth:
|
||||
|
||||
cls: "Synth.FinalClass"
|
||||
import_prefix: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class CfgClass:
|
||||
name: str
|
||||
bases: List[Base] = field(default_factory=list)
|
||||
properties: List[Property] = field(default_factory=list)
|
||||
doc: List[str] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class CfgClasses:
|
||||
template: ClassVar = 'ql_cfg_nodes'
|
||||
include_file_import: Optional[str] = None
|
||||
classes: List[CfgClass] = field(default_factory=list)
|
||||
|
||||
@@ -94,6 +94,7 @@ class Class:
|
||||
properties: List[Property] = field(default_factory=list)
|
||||
pragmas: List[str] | Dict[str, object] = field(default_factory=dict)
|
||||
doc: List[str] = field(default_factory=list)
|
||||
cfg: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
if not isinstance(self.pragmas, dict):
|
||||
|
||||
@@ -279,7 +279,7 @@ _ = _PropertyAnnotation()
|
||||
drop = object()
|
||||
|
||||
|
||||
def annotate(annotated_cls: type, add_bases: _Iterable[type] | None = None, replace_bases: _Dict[type, type] | None = None) -> _Callable[[type], _PropertyAnnotation]:
|
||||
def annotate(annotated_cls: type, add_bases: _Iterable[type] | None = None, replace_bases: _Dict[type, type] | None = None, cfg: bool = False) -> _Callable[[type], _PropertyAnnotation]:
|
||||
"""
|
||||
Add or modify schema annotations after a class has been defined previously.
|
||||
|
||||
@@ -298,6 +298,7 @@ def annotate(annotated_cls: type, add_bases: _Iterable[type] | None = None, repl
|
||||
annotated_cls.__bases__ = tuple(replace_bases.get(b, b) for b in annotated_cls.__bases__)
|
||||
if add_bases:
|
||||
annotated_cls.__bases__ += tuple(add_bases)
|
||||
annotated_cls.__cfg__ = cfg
|
||||
for a in dir(cls):
|
||||
if a.startswith(_schema.inheritable_pragma_prefix):
|
||||
setattr(annotated_cls, a, getattr(cls, a))
|
||||
|
||||
@@ -53,6 +53,7 @@ def _get_class(cls: type) -> schema.Class:
|
||||
bases=[b.__name__ for b in cls.__bases__ if b is not object],
|
||||
derived=derived,
|
||||
pragmas=pragmas,
|
||||
cfg=cls.__cfg__ if hasattr(cls, "__cfg__") else False,
|
||||
# in the following we don't use `getattr` to avoid inheriting
|
||||
properties=[
|
||||
a | _PropertyNamer(n)
|
||||
|
||||
199
misc/codegen/templates/ql_cfg_nodes.mustache
Normal file
199
misc/codegen/templates/ql_cfg_nodes.mustache
Normal file
@@ -0,0 +1,199 @@
|
||||
// generated by {{generator}}, do not edit
|
||||
/**
|
||||
* This module provides generated wrappers around the `CfgNode` type.
|
||||
*
|
||||
* INTERNAL: Do not import directly.
|
||||
*/
|
||||
|
||||
private import codeql.util.Location
|
||||
private import codeql.util.Unit
|
||||
private import {{include_file_import}}
|
||||
|
||||
/** Provides the input to `MakeCfgNodes` */
|
||||
signature module InputSig<LocationSig Loc> {
|
||||
class CfgNode {
|
||||
AstNode getAstNode();
|
||||
|
||||
string toString();
|
||||
|
||||
Loc getLocation();
|
||||
}
|
||||
|
||||
AstNode getDesugared(AstNode n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a `CfgNode` implementation, provides the module `Nodes` that
|
||||
* contains wrappers around `CfgNode` for relevant classes.
|
||||
*/
|
||||
module MakeCfgNodes<LocationSig Loc, InputSig<Loc> Input> {
|
||||
private import Input
|
||||
|
||||
final private class AstNodeFinal = AstNode;
|
||||
|
||||
final private class CfgNodeFinal = CfgNode;
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not expose.
|
||||
*/
|
||||
abstract class ParentAstNode extends AstNodeFinal {
|
||||
/**
|
||||
* Holds if `child` is a (possibly nested) child of this AST node
|
||||
* for which we would like to find a matching CFG child.
|
||||
*/
|
||||
abstract predicate relevantChild(AstNode child);
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not expose.
|
||||
*/
|
||||
abstract class ChildMapping extends Unit {
|
||||
/**
|
||||
* Holds if `child` is a (possibly nested) child of AST node `parent`
|
||||
* for which we would like to find a matching CFG child.
|
||||
*/
|
||||
final predicate relevantChild(AstNode parent, AstNode child) {
|
||||
parent.(ParentAstNode).relevantChild(child)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a control-flow path from `cfn` to `cfnChild`, where `cfn`
|
||||
* is a control-flow node for this AST node, and `cfnChild` is a control-flow
|
||||
* node for `child`.
|
||||
*
|
||||
* This predicate should be implemented at the place where `MakeCfgNodes` is
|
||||
* invoked. Ideally, `MakeCfgNodes` should be a higher-order parameterized
|
||||
* module, but since that is currently not supported, we achieve the "callback"
|
||||
* effect using this `abstract` class instead.
|
||||
*/
|
||||
cached
|
||||
abstract predicate hasCfgChild(AstNode parent, AstNode child, CfgNode cfn, CfgNode cfnChild);
|
||||
}
|
||||
|
||||
/** Provides sub classes of `CfgNode`. */
|
||||
module Nodes {
|
||||
{{#classes}}
|
||||
private final class Parent{{name}} extends ParentAstNode, {{name}} {
|
||||
override predicate relevantChild(AstNode child) {
|
||||
none()
|
||||
{{#properties}}
|
||||
{{#cfg}}
|
||||
or
|
||||
child = this.{{getter}}({{#is_indexed}}_{{/is_indexed}})
|
||||
{{/cfg}}
|
||||
{{/properties}}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
{{#doc}}
|
||||
* {{.}}
|
||||
{{/doc}}
|
||||
*/
|
||||
final class {{name}}CfgNode extends CfgNodeFinal{{#bases}}, {{.}}CfgNode{{/bases}} {
|
||||
private {{name}} node;
|
||||
|
||||
{{name}}CfgNode() {
|
||||
node = this.getAstNode()
|
||||
}
|
||||
|
||||
/** Gets the underlying `{{name}}`. */
|
||||
{{name}} get{{name}}() { result = node }
|
||||
|
||||
{{#properties}}
|
||||
/**
|
||||
* {{>ql_property_doc}} *
|
||||
{{#description}}
|
||||
* {{.}}
|
||||
{{/description}}
|
||||
{{#internal}}
|
||||
* INTERNAL: Do not use.
|
||||
{{/internal}}
|
||||
*/
|
||||
{{type}}{{#cfg}}CfgNode{{/cfg}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
|
||||
{{#cfg}}
|
||||
any(ChildMapping mapping).hasCfgChild(node, node.{{getter}}({{#is_indexed}}index{{/is_indexed}}), this, result)
|
||||
{{/cfg}}
|
||||
{{^cfg}}
|
||||
{{^is_predicate}}result = {{/is_predicate}}node.{{getter}}({{#is_indexed}}index{{/is_indexed}})
|
||||
{{/cfg}}
|
||||
}
|
||||
|
||||
{{#is_optional}}
|
||||
/**
|
||||
* Holds if `{{getter}}({{#is_repeated}}index{{/is_repeated}})` exists.
|
||||
{{#internal}}
|
||||
* INTERNAL: Do not use.
|
||||
{{/internal}}
|
||||
*/
|
||||
predicate has{{singular}}({{#is_repeated}}int index{{/is_repeated}}) {
|
||||
exists(this.{{getter}}({{#is_repeated}}index{{/is_repeated}}))
|
||||
}
|
||||
{{/is_optional}}
|
||||
{{#is_indexed}}
|
||||
|
||||
/**
|
||||
* Gets any of the {{doc_plural}}.
|
||||
{{#internal}}
|
||||
* INTERNAL: Do not use.
|
||||
{{/internal}}
|
||||
*/
|
||||
{{type}}{{#cfg}}CfgNode{{/cfg}} {{indefinite_getter}}() {
|
||||
result = this.{{getter}}(_)
|
||||
}
|
||||
{{^is_optional}}
|
||||
|
||||
/**
|
||||
* Gets the number of {{doc_plural}}.
|
||||
{{#internal}}
|
||||
* INTERNAL: Do not use.
|
||||
{{/internal}}
|
||||
*/
|
||||
int getNumberOf{{plural}}() {
|
||||
result = count(int i | exists(this.{{getter}}(i)))
|
||||
}
|
||||
{{/is_optional}}
|
||||
{{/is_indexed}}
|
||||
{{#is_unordered}}
|
||||
/**
|
||||
* Gets the number of {{doc_plural}}.
|
||||
{{#internal}}
|
||||
* INTERNAL: Do not use.
|
||||
{{/internal}}
|
||||
*/
|
||||
int getNumberOf{{plural}}() {
|
||||
result = count(this.{{getter}}())
|
||||
}
|
||||
{{/is_unordered}}
|
||||
{{/properties}}
|
||||
}
|
||||
{{/classes}}
|
||||
}
|
||||
|
||||
module Consistency {
|
||||
private predicate hasCfgNode(AstNode astNode) {
|
||||
astNode = any(CfgNode cfgNode).getAstNode()
|
||||
}
|
||||
|
||||
query predicate missingCfgChild(CfgNode parent, string pred, int i, AstNode child) {
|
||||
none()
|
||||
{{#classes}}
|
||||
{{#properties}}
|
||||
{{#cfg}}
|
||||
or
|
||||
pred = "{{getter}}" and
|
||||
parent = any(Nodes::{{name}}CfgNode cfgNode, {{name}} astNode |
|
||||
astNode = cfgNode.get{{name}}() and
|
||||
child = getDesugared(astNode.{{getter}}({{#is_indexed}}i{{/is_indexed}}))
|
||||
{{^is_indexed}}and i = -1{{/is_indexed}} and
|
||||
hasCfgNode(child) and
|
||||
not child = cfgNode.{{getter}}({{#is_indexed}}i{{/is_indexed}}).getAstNode()
|
||||
|
|
||||
cfgNode
|
||||
)
|
||||
{{/cfg}}
|
||||
{{/properties}}
|
||||
{{/classes}}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
--dbscheme=ql/lib/rust.dbscheme
|
||||
--ql-output=ql/lib/codeql/rust/elements/internal/generated
|
||||
--ql-stub-output=ql/lib/codeql/rust/elements
|
||||
--ql-cfg-output=ql/lib/codeql/rust/controlflow/internal/generated
|
||||
--ql-test-output=ql/test/extractor-tests/generated
|
||||
--rust-output=extractor/src/generated
|
||||
--script-name=codegen
|
||||
|
||||
1
rust/ql/.generated.list
generated
1
rust/ql/.generated.list
generated
@@ -1,3 +1,4 @@
|
||||
lib/codeql/rust/controlflow/internal/generated/CfgNodes.qll 55c20303ad50f17ddea728eb840304e0a634821adb7b13471a9ce55b71ed3886 13509512ffb59a9634ed2dc3c4969af26d5ba2d1502d98dc0c107a99392ce892
|
||||
lib/codeql/rust/elements/Abi.qll 4c973d28b6d628f5959d1f1cc793704572fd0acaae9a97dfce82ff9d73f73476 250f68350180af080f904cd34cb2af481c5c688dc93edf7365fd0ae99855e893
|
||||
lib/codeql/rust/elements/ArgList.qll 661f5100f5d3ef8351452d9058b663a2a5c720eea8cf11bedd628969741486a2 28e424aac01a90fb58cd6f9f83c7e4cf379eea39e636bc0ba07efc818be71c71
|
||||
lib/codeql/rust/elements/ArrayExpr.qll a3e6e122632f4011644ec31b37f88b32fe3f2b7e388e7e878a6883309937049f 12ccb5873d95c433da5606fd371d182ef2f71b78d0c53c2d6dec10fa45852bdc
|
||||
|
||||
1
rust/ql/.gitattributes
generated
vendored
1
rust/ql/.gitattributes
generated
vendored
@@ -1,5 +1,6 @@
|
||||
/.generated.list linguist-generated
|
||||
/.gitattributes linguist-generated
|
||||
/lib/codeql/rust/controlflow/internal/generated/CfgNodes.qll linguist-generated
|
||||
/lib/codeql/rust/elements/Abi.qll linguist-generated
|
||||
/lib/codeql/rust/elements/ArgList.qll linguist-generated
|
||||
/lib/codeql/rust/elements/ArrayExpr.qll linguist-generated
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
|
||||
private import rust
|
||||
private import ControlFlowGraph
|
||||
private import internal.ControlFlowGraphImpl
|
||||
private import internal.ControlFlowGraphImpl as CfgImpl
|
||||
private import internal.CfgNodes
|
||||
import Nodes
|
||||
|
||||
/** A CFG node that corresponds to an element in the AST. */
|
||||
class AstCfgNode extends CfgNode {
|
||||
@@ -57,4 +59,4 @@ class MethodCallExprCfgNode extends ExprCfgNode {
|
||||
MethodCallExpr getMethodCallExpr() { result = node }
|
||||
}
|
||||
|
||||
final class ExitCfgNode = ExitNode;
|
||||
final class ExitCfgNode = CfgImpl::ExitNode;
|
||||
|
||||
@@ -8,6 +8,8 @@ import Consistency
|
||||
private import codeql.rust.controlflow.ControlFlowGraph
|
||||
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
|
||||
private import codeql.rust.controlflow.internal.Completion
|
||||
private import codeql.rust.controlflow.internal.CfgNodes::Consistency as CfgNodes
|
||||
private import codeql.rust.Diagnostics
|
||||
|
||||
private predicate nonPostOrderExpr(Expr e) {
|
||||
not e instanceof LetExpr and
|
||||
@@ -54,6 +56,24 @@ query predicate deadEnd(CfgImpl::Node node) {
|
||||
not letElsePanic(node.getAstNode())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate successfullyExtractedFile(File f) {
|
||||
not exists(ExtractionWarning ee | ee.getLocation().getFile() = f)
|
||||
}
|
||||
|
||||
query predicate missingCfgChild(CfgNode parent, string pred, int i, AstNode child) {
|
||||
CfgNodes::missingCfgChild(parent, pred, i, child) and
|
||||
successfullyExtractedFile(child.getLocation().getFile()) and
|
||||
not exists(AstNode last, CfgImpl::Completion c | CfgImpl::last(child, last, c) |
|
||||
// In for example `if (a && true) ...` there is no RHS CFG node going into the
|
||||
// `[false] a && true` operation
|
||||
strictcount(ConditionalSuccessor cs | exists(last.getACfgNode().getASuccessor(cs)) | cs) = 1
|
||||
or
|
||||
// In for example `x && return`, there is no RHS CFG node going into the `&&` operation
|
||||
not c instanceof CfgImpl::NormalCompletion
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets counts of control flow graph inconsistencies of each type.
|
||||
*/
|
||||
@@ -99,4 +119,7 @@ int getCfgInconsistencyCounts(string type) {
|
||||
or
|
||||
type = "Non-PostOrderTree Expr node" and
|
||||
result = count(Expr e | nonPostOrderExpr(e) | e)
|
||||
or
|
||||
type = "Missing CFG child" and
|
||||
result = count(CfgNode parent, string pred, int child | missingCfgChild(parent, pred, child, _))
|
||||
}
|
||||
|
||||
116
rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll
Normal file
116
rust/ql/lib/codeql/rust/controlflow/internal/CfgNodes.qll
Normal file
@@ -0,0 +1,116 @@
|
||||
private import rust
|
||||
private import codeql.rust.controlflow.internal.generated.CfgNodes
|
||||
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
|
||||
private import codeql.rust.controlflow.ControlFlowGraph
|
||||
private import codeql.rust.controlflow.BasicBlocks
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
private import codeql.rust.internal.CachedStages
|
||||
|
||||
private predicate isPostOrder(AstNode n) {
|
||||
n instanceof Expr and
|
||||
not n instanceof LetExpr
|
||||
or
|
||||
n instanceof OrPat
|
||||
or
|
||||
n instanceof IdentPat
|
||||
or
|
||||
n instanceof LiteralPat
|
||||
or
|
||||
n instanceof Param
|
||||
}
|
||||
|
||||
private module CfgNodesInput implements InputSig<Location> {
|
||||
private import codeql.rust.controlflow.ControlFlowGraph as Cfg
|
||||
|
||||
class CfgNode = AstCfgNode;
|
||||
|
||||
private AstNode desugar(AstNode n) {
|
||||
result = n.(ParenPat).getPat()
|
||||
or
|
||||
result = n.(ParenExpr).getExpr()
|
||||
}
|
||||
|
||||
AstNode getDesugared(AstNode n) {
|
||||
result = getDesugared(desugar(n))
|
||||
or
|
||||
not exists(desugar(n)) and
|
||||
result = n
|
||||
}
|
||||
}
|
||||
|
||||
import MakeCfgNodes<Location, CfgNodesInput>
|
||||
|
||||
private class ChildMappingImpl extends ChildMapping {
|
||||
/** Gets a CFG node for `child`, where `child` is a relevant child node of `parent`. */
|
||||
private CfgNode getRelevantChildCfgNode(AstNode parent, AstNode child) {
|
||||
this.relevantChild(parent, child) and
|
||||
result = CfgNodesInput::getDesugared(child).getACfgNode()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private BasicBlock getARelevantBasicBlock(AstNode parent) {
|
||||
result.getANode().getAstNode() = parent or
|
||||
result.getANode() = this.getRelevantChildCfgNode(parent, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CFG node `cfnChild` can reach basic block `bb`, without going
|
||||
* through an intermediate block that contains a CFG node for `parent` or
|
||||
* any other relevant child of `parent`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate childNodeReachesBasicBlock(
|
||||
AstNode parent, AstNode child, CfgNode cfnChild, BasicBlock bb
|
||||
) {
|
||||
exists(BasicBlock bb0 |
|
||||
cfnChild = this.getRelevantChildCfgNode(parent, child) and
|
||||
bb0.getANode() = cfnChild
|
||||
|
|
||||
bb = bb0
|
||||
or
|
||||
not bb0.getANode().getAstNode() = parent and
|
||||
if isPostOrder(parent) then bb = bb0.getASuccessor() else bb = bb0.getAPredecessor()
|
||||
)
|
||||
or
|
||||
exists(BasicBlock mid |
|
||||
this.childNodeReachesBasicBlock(parent, child, cfnChild, mid) and
|
||||
not mid = this.getARelevantBasicBlock(parent) and
|
||||
if isPostOrder(parent) then bb = mid.getASuccessor() else bb = mid.getAPredecessor()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if CFG node `cfnChild` can reach CFG node `cfnParent`, without going
|
||||
* through an intermediate block that contains a CFG node for `parent`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate childNodeReachesParentNode(
|
||||
AstNode parent, CfgNode cfnParent, AstNode child, CfgNode cfnChild
|
||||
) {
|
||||
// `cfnChild` can reach `cfnParent` directly
|
||||
exists(BasicBlock bb |
|
||||
this.childNodeReachesBasicBlock(parent, child, cfnChild, bb) and
|
||||
cfnParent.getAstNode() = parent
|
||||
|
|
||||
cfnParent = bb.getANode()
|
||||
or
|
||||
if isPostOrder(parent)
|
||||
then cfnParent = bb.getASuccessor().getANode()
|
||||
else cfnParent = bb.getAPredecessor().getANode()
|
||||
)
|
||||
or
|
||||
// `cfnChild` can reach `cfnParent` by going via another relevant child
|
||||
exists(CfgNode cfnOtherChild |
|
||||
this.childNodeReachesParentNode(parent, cfnParent, _, cfnOtherChild) and
|
||||
exists(BasicBlock bb |
|
||||
this.childNodeReachesBasicBlock(parent, child, cfnChild, bb) and
|
||||
bb.getANode() = cfnOtherChild
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasCfgChild(AstNode parent, AstNode child, AstCfgNode cfn, AstCfgNode cfnChild) {
|
||||
Stages::CfgStage::ref() and
|
||||
this.childNodeReachesParentNode(parent, cfn, child, cfnChild)
|
||||
}
|
||||
}
|
||||
3657
rust/ql/lib/codeql/rust/controlflow/internal/generated/CfgNodes.qll
generated
Normal file
3657
rust/ql/lib/codeql/rust/controlflow/internal/generated/CfgNodes.qll
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,7 @@ private import codeql.rust.controlflow.ControlFlowGraph
|
||||
module Impl {
|
||||
private import rust
|
||||
private import codeql.rust.elements.internal.generated.ParentChild
|
||||
private import codeql.rust.controlflow.ControlFlowGraph
|
||||
|
||||
/**
|
||||
* Gets the immediate parent of a non-`AstNode` element `e`.
|
||||
@@ -62,5 +63,37 @@ module Impl {
|
||||
or
|
||||
this.getParentNode().isInMacroExpansion()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a control-flow node for this AST node, if any.
|
||||
*
|
||||
* Note that because of _control-flow splitting_, one `AstNode` node may correspond
|
||||
* to multiple `CfgNode`s. Example:
|
||||
*
|
||||
* ```rust
|
||||
* if a && b {
|
||||
* // ...
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The CFG for the condition above looks like
|
||||
*
|
||||
* ```mermaid
|
||||
* flowchart TD
|
||||
* 1["a"]
|
||||
* 2["b"]
|
||||
* 3["[false] a && b"]
|
||||
* 4["[true] a && b"]
|
||||
*
|
||||
* 1 -- false --> 3
|
||||
* 1 -- true --> 2
|
||||
* 2 -- false --> 3
|
||||
* 2 -- true --> 4
|
||||
* ```
|
||||
*
|
||||
* That is, the AST node for `a && b` corresponds to _two_ CFG nodes (it is
|
||||
* split into two).
|
||||
*/
|
||||
CfgNode getACfgNode() { this = result.getAstNode() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ module Stages {
|
||||
private import codeql.rust.controlflow.internal.Splitting
|
||||
private import codeql.rust.controlflow.internal.SuccessorType
|
||||
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl
|
||||
private import codeql.rust.controlflow.CfgNodes
|
||||
|
||||
/**
|
||||
* Always holds.
|
||||
@@ -93,6 +94,8 @@ module Stages {
|
||||
exists(TNormalSuccessor())
|
||||
or
|
||||
exists(AstCfgNode n)
|
||||
or
|
||||
exists(CallExprCfgNode n | exists(n.getExpr()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
| CFG scope lacks initial AST node | 0 |
|
||||
| Dead end | 0 |
|
||||
| Missing CFG child | 0 |
|
||||
| Multiple successors of the same type | 0 |
|
||||
| Multiple toStrings | 0 |
|
||||
| Non-PostOrderTree Expr node | 0 |
|
||||
|
||||
@@ -18,14 +18,14 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(Expr)
|
||||
@annotate(Expr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
The base class for expressions.
|
||||
"""
|
||||
|
||||
|
||||
@annotate(Pat)
|
||||
@annotate(Pat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
The base class for patterns.
|
||||
@@ -106,7 +106,7 @@ class PathExprBase(Expr):
|
||||
"""
|
||||
|
||||
|
||||
@annotate(PathExpr, replace_bases={Expr: PathExprBase})
|
||||
@annotate(PathExpr, replace_bases={Expr: PathExprBase}, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A path expression. For example:
|
||||
@@ -119,7 +119,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(IfExpr)
|
||||
@annotate(IfExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An `if` expression. For example:
|
||||
@@ -138,7 +138,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(LetExpr)
|
||||
@annotate(LetExpr, cfg = True)
|
||||
@rust.doc_test_signature("(maybe_some: Option<String>) -> ()")
|
||||
class _:
|
||||
"""
|
||||
@@ -151,7 +151,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(BlockExpr)
|
||||
@annotate(BlockExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A block expression. For example:
|
||||
@@ -169,7 +169,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(LoopExpr)
|
||||
@annotate(LoopExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A loop expression. For example:
|
||||
@@ -205,7 +205,7 @@ class CallExprBase(Expr):
|
||||
attrs: list["Attr"] | child
|
||||
|
||||
|
||||
@annotate(CallExpr, replace_bases={Expr: CallExprBase})
|
||||
@annotate(CallExpr, replace_bases={Expr: CallExprBase}, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A function call expression. For example:
|
||||
@@ -220,7 +220,7 @@ class _:
|
||||
attrs: drop
|
||||
|
||||
|
||||
@annotate(MethodCallExpr, replace_bases={Expr: CallExprBase}, add_bases=(Resolvable,))
|
||||
@annotate(MethodCallExpr, replace_bases={Expr: CallExprBase}, add_bases=(Resolvable,), cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A method call expression. For example:
|
||||
@@ -253,7 +253,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(MatchExpr)
|
||||
@annotate(MatchExpr, cfg = True)
|
||||
@rust.doc_test_signature("(x: i32) -> i32")
|
||||
class _:
|
||||
"""
|
||||
@@ -273,7 +273,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(ContinueExpr)
|
||||
@annotate(ContinueExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A continue expression. For example:
|
||||
@@ -294,7 +294,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(BreakExpr)
|
||||
@annotate(BreakExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A break expression. For example:
|
||||
@@ -323,7 +323,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(ReturnExpr)
|
||||
@annotate(ReturnExpr, cfg = True)
|
||||
@rust.doc_test_signature(None)
|
||||
class _:
|
||||
"""
|
||||
@@ -341,7 +341,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(BecomeExpr)
|
||||
@annotate(BecomeExpr, cfg = True)
|
||||
@rust.doc_test_signature(None)
|
||||
class _:
|
||||
"""
|
||||
@@ -358,7 +358,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(YieldExpr)
|
||||
@annotate(YieldExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A `yield` expression. For example:
|
||||
@@ -371,7 +371,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(YeetExpr)
|
||||
@annotate(YeetExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A `yeet` expression. For example:
|
||||
@@ -393,7 +393,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RecordExpr)
|
||||
@annotate(RecordExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A record expression. For example:
|
||||
@@ -406,7 +406,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(FieldExpr)
|
||||
@annotate(FieldExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A field access expression. For example:
|
||||
@@ -416,7 +416,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(AwaitExpr)
|
||||
@annotate(AwaitExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An `await` expression. For example:
|
||||
@@ -439,7 +439,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RefExpr)
|
||||
@annotate(RefExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A reference expression. For example:
|
||||
@@ -452,7 +452,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(PrefixExpr)
|
||||
@annotate(PrefixExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A unary operation expression. For example:
|
||||
@@ -464,7 +464,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(BinaryExpr)
|
||||
@annotate(BinaryExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A binary operation expression. For example:
|
||||
@@ -478,7 +478,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RangeExpr)
|
||||
@annotate(RangeExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A range expression. For example:
|
||||
@@ -493,7 +493,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(IndexExpr)
|
||||
@annotate(IndexExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An index expression. For example:
|
||||
@@ -520,7 +520,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(TupleExpr)
|
||||
@annotate(TupleExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A tuple expression. For example:
|
||||
@@ -531,7 +531,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(ArrayExpr)
|
||||
@annotate(ArrayExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An array expression. For example:
|
||||
@@ -542,7 +542,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(LiteralExpr)
|
||||
@annotate(LiteralExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A literal expression. For example:
|
||||
@@ -559,7 +559,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(UnderscoreExpr)
|
||||
@annotate(UnderscoreExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An underscore expression. For example:
|
||||
@@ -569,7 +569,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(OffsetOfExpr)
|
||||
@annotate(OffsetOfExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An `offset_of` expression. For example:
|
||||
@@ -579,7 +579,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(AsmExpr)
|
||||
@annotate(AsmExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An inline assembly expression. For example:
|
||||
@@ -591,7 +591,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(LetStmt)
|
||||
@annotate(LetStmt, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A let statement. For example:
|
||||
@@ -620,7 +620,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(WildcardPat)
|
||||
@annotate(WildcardPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A wildcard pattern. For example:
|
||||
@@ -630,7 +630,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(TuplePat)
|
||||
@annotate(TuplePat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A tuple pattern. For example:
|
||||
@@ -641,7 +641,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(OrPat)
|
||||
@annotate(OrPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
An or pattern. For example:
|
||||
@@ -663,7 +663,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RecordPat)
|
||||
@annotate(RecordPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A record pattern. For example:
|
||||
@@ -676,7 +676,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RangePat)
|
||||
@annotate(RangePat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A range pattern. For example:
|
||||
@@ -690,7 +690,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(SlicePat)
|
||||
@annotate(SlicePat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A slice pattern. For example:
|
||||
@@ -704,7 +704,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(PathPat)
|
||||
@annotate(PathPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A path pattern. For example:
|
||||
@@ -717,7 +717,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(LiteralPat)
|
||||
@annotate(LiteralPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A literal pattern. For example:
|
||||
@@ -730,7 +730,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(IdentPat)
|
||||
@annotate(IdentPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A binding pattern. For example:
|
||||
@@ -749,7 +749,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(TupleStructPat)
|
||||
@annotate(TupleStructPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A tuple struct pattern. For example:
|
||||
@@ -763,7 +763,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RefPat)
|
||||
@annotate(RefPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A reference pattern. For example:
|
||||
@@ -776,7 +776,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(BoxPat)
|
||||
@annotate(BoxPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A box pattern. For example:
|
||||
@@ -789,7 +789,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(ConstBlockPat)
|
||||
@annotate(ConstBlockPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A const block pattern. For example:
|
||||
@@ -990,7 +990,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(ForExpr)
|
||||
@annotate(ForExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A ForExpr. For example:
|
||||
@@ -1020,7 +1020,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(FormatArgsExpr)
|
||||
@annotate(FormatArgsExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A FormatArgsExpr. For example:
|
||||
@@ -1150,7 +1150,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(MacroCall)
|
||||
@annotate(MacroCall, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A MacroCall. For example:
|
||||
@@ -1171,7 +1171,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(MacroExpr)
|
||||
@annotate(MacroExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A MacroExpr. For example:
|
||||
@@ -1194,7 +1194,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(MacroPat)
|
||||
@annotate(MacroPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A MacroPat. For example:
|
||||
@@ -1297,7 +1297,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(Param)
|
||||
@annotate(Param, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A Param. For example:
|
||||
@@ -1437,7 +1437,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(RestPat)
|
||||
@annotate(RestPat, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A RestPat. For example:
|
||||
@@ -1564,7 +1564,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(TryExpr)
|
||||
@annotate(TryExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A TryExpr. For example:
|
||||
@@ -1744,7 +1744,7 @@ class _:
|
||||
"""
|
||||
|
||||
|
||||
@annotate(WhileExpr)
|
||||
@annotate(WhileExpr, cfg = True)
|
||||
class _:
|
||||
"""
|
||||
A WhileExpr. For example:
|
||||
|
||||
Reference in New Issue
Block a user