Skip to content

Commit fd06e4a

Browse files
hvitvedCopilot
andcommitted
Migrate Rust to shared control-flow graph
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 6dc5ec9 commit fd06e4a

28 files changed

Lines changed: 9342 additions & 6142 deletions

File tree

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,69 @@
11
private import codeql.controlflow.BasicBlock as BB
22
private import codeql.Locations
3+
private import codeql.rust.elements.internal.generated.Raw
4+
private import codeql.rust.elements.internal.generated.Synth
35
private import codeql.rust.controlflow.ControlFlowGraph as ControlFlowGraph
46
private import internal.ControlFlowGraphImpl as CfgImpl
5-
private import CfgImpl::BasicBlocks as BasicBlocksImpl
67

7-
class BasicBlock = BasicBlocksImpl::BasicBlock;
8+
class BasicBlock = CfgImpl::Cfg::BasicBlock;
89

9-
final class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock;
10+
final class EntryBasicBlock extends BasicBlock {
11+
EntryBasicBlock() { this.getFirstNode() instanceof CfgImpl::ControlFlow::EntryNode }
12+
}
13+
14+
final class ExitBasicBlock extends BasicBlock {
15+
ExitBasicBlock() { this.getLastNode() instanceof CfgImpl::ControlFlow::ExitNode }
16+
}
17+
18+
final class AnnotatedExitBasicBlock extends BasicBlock {
19+
AnnotatedExitBasicBlock() {
20+
this.getANode() instanceof CfgImpl::ControlFlow::AnnotatedExitNode
21+
}
22+
23+
/** Holds if this block represents normal exit. */
24+
predicate isNormal() {
25+
this.getANode() instanceof CfgImpl::ControlFlow::NormalExitNode
26+
}
27+
}
1028

11-
final class ExitBasicBlock = BasicBlocksImpl::ExitBasicBlock;
29+
final class ConditionBasicBlock extends BasicBlock {
30+
ConditionBasicBlock() { this.getLastNode().(ControlFlowGraph::CfgNode).isCondition() }
31+
}
32+
33+
final class JoinBasicBlock extends BasicBlock {
34+
JoinBasicBlock() { this.getFirstNode().(ControlFlowGraph::CfgNode).isJoin() }
35+
36+
/** Gets the `i`th predecessor of this join block in a stable arbitrary order. */
37+
JoinPredecessorBasicBlock getJoinBlockPredecessor(int i) {
38+
result =
39+
rank[i + 1](JoinPredecessorBasicBlock pred, int id, string tag |
40+
pred = this.getAPredecessor() and
41+
id = getNodeId(pred.getFirstNode()) and
42+
tag = pred.getFirstNode().getIdTag()
43+
|
44+
pred order by id, tag
45+
)
46+
}
47+
}
1248

13-
final class AnnotatedExitBasicBlock = BasicBlocksImpl::AnnotatedExitBasicBlock;
49+
final class JoinPredecessorBasicBlock extends BasicBlock {
50+
JoinPredecessorBasicBlock() { this.getASuccessor() instanceof JoinBasicBlock }
51+
}
1452

15-
final class ConditionBasicBlock = BasicBlocksImpl::ConditionBasicBlock;
53+
private predicate id(Raw::AstNode x, Raw::AstNode y) { x = y }
1654

17-
final class JoinBasicBlock = BasicBlocksImpl::JoinBasicBlock;
55+
private predicate idOfDbAstNode(Raw::AstNode x, int y) = equivalenceRelation(id/2)(x, y)
1856

19-
final class JoinPredecessorBasicBlock = BasicBlocksImpl::JoinPredecessorBasicBlock;
57+
private int getNodeId(CfgImpl::ControlFlowNode node) {
58+
idOfDbAstNode(Synth::convertAstNodeToRaw(node.getAstNode()), result)
59+
}
2060

2161
module Cfg implements BB::CfgSig<Location> {
22-
class ControlFlowNode = ControlFlowGraph::CfgNode;
62+
class ControlFlowNode = CfgImpl::Cfg::ControlFlowNode;
2363

24-
class BasicBlock = BasicBlocksImpl::BasicBlock;
64+
class BasicBlock = CfgImpl::Cfg::BasicBlock;
2565

26-
class EntryBasicBlock = BasicBlocksImpl::EntryBasicBlock;
66+
class EntryBasicBlock = CfgImpl::Cfg::EntryBasicBlock;
2767

28-
predicate dominatingEdge = BasicBlocksImpl::dominatingEdge/2;
68+
predicate dominatingEdge = CfgImpl::Cfg::dominatingEdge/2;
2969
}

‎rust/ql/lib/codeql/rust/controlflow/CfgNodes.qll‎

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,24 @@
55

66
private import rust
77
private import ControlFlowGraph
8-
private import internal.ControlFlowGraphImpl as CfgImpl
98
private import internal.CfgNodes
109
import Nodes
1110

12-
class AstCfgNode = CfgImpl::AstCfgNode;
11+
/**
12+
* A CFG node that uniquely represents an AST node.
13+
*/
14+
class AstCfgNode extends CfgNode {
15+
AstCfgNode() { this.injects(_) }
16+
17+
/** Gets the represented AST node. */
18+
AstNode getAstNode() { this.injects(result) }
19+
}
1320

14-
class ExitCfgNode = CfgImpl::ExitNode;
21+
class ExitCfgNode extends CfgNode instanceof ControlFlow::ExitNode {
22+
}
1523

16-
class AnnotatedExitCfgNode = CfgImpl::AnnotatedExitNode;
24+
class AnnotatedExitCfgNode extends CfgNode instanceof ControlFlow::AnnotatedExitNode {
25+
}
1726

1827
/** A variable access. */
1928
final class VariableAccessCfgNode extends PathExprBaseCfgNode {
Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
11
/** Provides classes representing the control flow graph. */
22

3-
private import internal.ControlFlowGraphImpl
4-
private import internal.Scope as Scope
3+
private import internal.ControlFlowGraphImpl as CfgImpl
54
import codeql.controlflow.SuccessorType
65

7-
final class CfgScope = Scope::CfgScope;
6+
final class CfgScope = CfgImpl::CfgScope;
87

9-
final class CfgNode = Node;
8+
/**
9+
* A node in the control flow graph.
10+
*/
11+
class CfgNode extends CfgImpl::ControlFlowNode {
12+
/** Gets the CFG scope containing this node. */
13+
CfgScope getScope() { result = this.getEnclosingCallable() }
14+
15+
/** Holds if this node has conditional successors. */
16+
predicate isCondition() {
17+
exists(this.getASuccessor(any(ConditionalSuccessor t)))
18+
}
19+
20+
/** Holds if this node has more than one predecessor. */
21+
predicate isJoin() { strictcount(this.getAPredecessor()) > 1 }
22+
23+
/** Holds if this node has more than one successor. */
24+
predicate isBranch() { strictcount(this.getASuccessor()) > 1 }
25+
}
26+
27+
module ControlFlow = CfgImpl::ControlFlow;

‎rust/ql/lib/codeql/rust/controlflow/internal/CfgConsistency.qll‎

Lines changed: 11 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,124 +3,32 @@
33
*/
44

55
private import rust
6-
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl::Consistency as Consistency
7-
import Consistency
8-
private import codeql.rust.controlflow.ControlFlowGraph
96
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
10-
private import codeql.rust.controlflow.internal.Completion
117
private import codeql.rust.controlflow.internal.CfgNodes::Consistency as CfgNodes
128
private import codeql.rust.Diagnostics
13-
14-
private predicate nonPostOrderExpr(Expr e) {
15-
not e instanceof LetExpr and
16-
not e instanceof ParenExpr and
17-
exists(AstNode last, Completion c |
18-
CfgImpl::last(e, last, c) and
19-
last != e and
20-
c instanceof NormalCompletion
21-
)
22-
}
23-
24-
/**
25-
* All `Expr` nodes are `PostOrderTree`s
26-
*/
27-
query predicate nonPostOrderExpr(Expr e, string cls) {
28-
nonPostOrderExpr(e) and
29-
cls = e.getPrimaryQlClasses()
30-
}
31-
32-
/**
33-
* Holds if CFG scope `scope` lacks an initial AST node. Overrides shared consistency predicate.
34-
*/
35-
query predicate scopeNoFirst(CfgScope scope) {
36-
Consistency::scopeNoFirst(scope) and
37-
not scope =
38-
[
39-
any(AstNode f | not f.(Callable).hasBody()),
40-
any(AsyncBlockExpr b | not b.hasStmtList())
41-
]
42-
}
43-
44-
/** Holds if `be` is the `else` branch of a `let` statement that results in a panic. */
45-
private predicate letElsePanic(BlockExpr be) {
46-
be = any(LetStmt let).getLetElse().getBlockExpr() and
47-
exists(Completion c | CfgImpl::last(be, _, c) | completionIsNormal(c))
48-
}
49-
50-
/**
51-
* Holds if `node` is lacking a successor. Overrides shared consistency predicate.
52-
*/
53-
query predicate deadEnd(CfgImpl::Node node) {
54-
Consistency::deadEnd(node) and
55-
successfullyExtractedFile(node.getLocation().getFile()) and
56-
not letElsePanic(node.getAstNode())
57-
}
9+
import CfgImpl::ControlFlow::Consistency
5810

5911
pragma[nomagic]
6012
private predicate successfullyExtractedFile(File f) {
61-
not exists(ExtractionWarning ee | ee.getLocation().getFile() = f)
13+
not exists(ExtractionWarning warning | warning.getLocation().getFile() = f)
6214
}
6315

64-
query predicate missingCfgChild(CfgNode parent, string pred, int i, AstNode child) {
16+
query predicate missingCfgChild(
17+
CfgImpl::ControlFlowNode parent, string pred, int i, AstNode child
18+
) {
6519
CfgNodes::missingCfgChild(parent, pred, i, child) and
66-
successfullyExtractedFile(child.getLocation().getFile()) and
67-
not exists(AstNode last, CfgImpl::Completion c | CfgImpl::last(child, last, c) |
68-
// In for example `if (a && true) ...`, there is no edge from the CFG node
69-
// for `true` into the `[false] a && true` node.
70-
strictcount(ConditionalSuccessor cs | exists(last.getACfgNode().getASuccessor(cs)) | cs) = 1
71-
or
72-
// In for example `x && return`, there is no edge from the node for
73-
// `return` into the `&&` node.
74-
not c instanceof CfgImpl::NormalCompletion
75-
)
20+
successfullyExtractedFile(child.getLocation().getFile())
7621
}
7722

7823
/**
7924
* Gets counts of control flow graph inconsistencies of each type.
8025
*/
8126
int getCfgInconsistencyCounts(string type) {
82-
// total results from all the CFG consistency query predicates in:
83-
// - `codeql.rust.controlflow.internal.CfgConsistency` (this file)
84-
// - `shared.controlflow.codeql.controlflow.Cfg`
85-
type = "Non-unique set representation" and
86-
result = count(CfgImpl::Splits ss | nonUniqueSetRepresentation(ss, _) | ss)
87-
or
88-
type = "Splitting invariant 2" and
89-
result = count(AstNode n | breakInvariant2(n, _, _, _, _, _) | n)
90-
or
91-
type = "Splitting invariant 3" and
92-
result = count(AstNode n | breakInvariant3(n, _, _, _, _, _) | n)
93-
or
94-
type = "Splitting invariant 4" and
95-
result = count(AstNode n | breakInvariant4(n, _, _, _, _, _) | n)
96-
or
97-
type = "Splitting invariant 5" and
98-
result = count(AstNode n | breakInvariant5(n, _, _, _, _, _) | n)
99-
or
100-
type = "Multiple successors of the same type" and
101-
result = count(CfgNode n | multipleSuccessors(n, _, _) | n)
102-
or
103-
type = "Simple and normal successors" and
104-
result = count(CfgNode n | simpleAndNormalSuccessors(n, _, _, _, _) | n)
105-
or
106-
type = "Dead end" and
107-
result = count(CfgNode n | deadEnd(n) | n)
108-
or
109-
type = "Non-unique split kind" and
110-
result = count(CfgImpl::SplitImpl si | nonUniqueSplitKind(si, _) | si)
111-
or
112-
type = "Non-unique list order" and
113-
result = count(CfgImpl::SplitKind sk | nonUniqueListOrder(sk, _) | sk)
114-
or
115-
type = "Multiple toStrings" and
116-
result = count(CfgNode n | multipleToString(n) | n)
117-
or
118-
type = "CFG scope lacks initial AST node" and
119-
result = count(CfgScope s | scopeNoFirst(s) | s)
120-
or
121-
type = "Non-PostOrderTree Expr node" and
122-
result = count(Expr e | nonPostOrderExpr(e) | e)
27+
CfgImpl::ControlFlow::Consistency::consistencyOverview(type, result)
12328
or
12429
type = "Missing CFG child" and
125-
result = count(CfgNode parent, string pred, int child | missingCfgChild(parent, pred, child, _))
30+
result =
31+
count(CfgImpl::ControlFlowNode parent, string pred, int child |
32+
missingCfgChild(parent, pred, child, _)
33+
)
12634
}

0 commit comments

Comments
 (0)