Skip to content

Commit f9eca42

Browse files
authored
Merge pull request github#17763 from geoffw0/cfgcount3
Rust: Count number of CFG inconsistencies
2 parents 196baa9 + 79c5adf commit f9eca42

File tree

8 files changed

+145
-34
lines changed

8 files changed

+145
-34
lines changed
Lines changed: 5 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,8 @@
1-
import rust
2-
import codeql.rust.controlflow.internal.ControlFlowGraphImpl::Consistency as Consistency
3-
import Consistency
4-
import codeql.rust.controlflow.ControlFlowGraph
5-
import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
6-
import codeql.rust.controlflow.internal.Completion
7-
81
/**
9-
* All `Expr` nodes are `PostOrderTree`s
2+
* @name Control flow graph inconsistencies
3+
* @description Lists the control flow graph inconsistencies in the database. This query is intended for internal use.
4+
* @kind table
5+
* @id rust/diagnostics/cfg-consistency
106
*/
11-
query predicate nonPostOrderExpr(Expr e, string cls) {
12-
cls = e.getPrimaryQlClasses() and
13-
not e instanceof LetExpr and
14-
not e instanceof ParenExpr and
15-
exists(AstNode last, Completion c |
16-
CfgImpl::last(e, last, c) and
17-
last != e and
18-
c instanceof NormalCompletion
19-
)
20-
}
21-
22-
query predicate scopeNoFirst(CfgScope scope) {
23-
Consistency::scopeNoFirst(scope) and
24-
not scope = any(Function f | not exists(f.getBody())) and
25-
not scope = any(ClosureExpr c | not exists(c.getBody()))
26-
}
27-
28-
/** Holds if `be` is the `else` branch of a `let` statement that results in a panic. */
29-
private predicate letElsePanic(BlockExpr be) {
30-
be = any(LetStmt let).getLetElse().getBlockExpr() and
31-
exists(Completion c | CfgImpl::last(be, _, c) | completionIsNormal(c))
32-
}
337

34-
query predicate deadEnd(CfgImpl::Node node) {
35-
Consistency::deadEnd(node) and
36-
not letElsePanic(node.getAstNode())
37-
}
8+
import codeql.rust.controlflow.internal.CfgConsistency
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/**
2+
* Provides classes for recognizing control flow graph inconsistencies.
3+
*/
4+
5+
private import rust
6+
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl::Consistency as Consistency
7+
import Consistency
8+
private import codeql.rust.controlflow.ControlFlowGraph
9+
private import codeql.rust.controlflow.internal.ControlFlowGraphImpl as CfgImpl
10+
private import codeql.rust.controlflow.internal.Completion
11+
12+
/**
13+
* All `Expr` nodes are `PostOrderTree`s
14+
*/
15+
query predicate nonPostOrderExpr(Expr e, string cls) {
16+
cls = e.getPrimaryQlClasses() and
17+
not e instanceof LetExpr and
18+
not e instanceof ParenExpr and
19+
exists(AstNode last, Completion c |
20+
CfgImpl::last(e, last, c) and
21+
last != e and
22+
c instanceof NormalCompletion
23+
)
24+
}
25+
26+
/**
27+
* Holds if CFG scope `scope` lacks an initial AST node. Overrides shared consistency predicate.
28+
*/
29+
query predicate scopeNoFirst(CfgScope scope) {
30+
Consistency::scopeNoFirst(scope) and
31+
not scope = any(Function f | not exists(f.getBody())) and
32+
not scope = any(ClosureExpr c | not exists(c.getBody()))
33+
}
34+
35+
/** Holds if `be` is the `else` branch of a `let` statement that results in a panic. */
36+
private predicate letElsePanic(BlockExpr be) {
37+
be = any(LetStmt let).getLetElse().getBlockExpr() and
38+
exists(Completion c | CfgImpl::last(be, _, c) | completionIsNormal(c))
39+
}
40+
41+
/**
42+
* Holds if `node` is lacking a successor. Overrides shared consistency predicate.
43+
*/
44+
query predicate deadEnd(CfgImpl::Node node) {
45+
Consistency::deadEnd(node) and
46+
not letElsePanic(node.getAstNode())
47+
}
48+
49+
/**
50+
* Gets counts of control flow graph inconsistencies of each type.
51+
*/
52+
int getCfgInconsistencyCounts(string type) {
53+
// total results from all the CFG consistency query predicates in:
54+
// - `codeql.rust.controlflow.internal.CfgConsistency` (this file)
55+
// - `shared.controlflow.codeql.controlflow.Cfg`
56+
type = "Non-unique set representation" and
57+
result = count(CfgImpl::Splits ss | nonUniqueSetRepresentation(ss, _) | ss)
58+
or
59+
type = "Splitting invariant 2" and
60+
result = count(AstNode n | breakInvariant2(n, _, _, _, _, _) | n)
61+
or
62+
type = "Splitting invariant 3" and
63+
result = count(AstNode n | breakInvariant3(n, _, _, _, _, _) | n)
64+
or
65+
type = "Splitting invariant 4" and
66+
result = count(AstNode n | breakInvariant4(n, _, _, _, _, _) | n)
67+
or
68+
type = "Splitting invariant 5" and
69+
result = count(AstNode n | breakInvariant5(n, _, _, _, _, _) | n)
70+
or
71+
type = "Multiple successors of the same type" and
72+
result = count(CfgNode n | multipleSuccessors(n, _, _) | n)
73+
or
74+
type = "Simple and normal successors" and
75+
result = count(CfgNode n | simpleAndNormalSuccessors(n, _, _, _, _) | n)
76+
or
77+
type = "Dead end" and
78+
result = count(CfgNode n | deadEnd(n) | n)
79+
or
80+
type = "Non-unique split kind" and
81+
result = count(CfgImpl::SplitImpl si | nonUniqueSplitKind(si, _) | si)
82+
or
83+
type = "Non-unique list order" and
84+
result = count(CfgImpl::SplitKind sk | nonUniqueListOrder(sk, _) | sk)
85+
or
86+
type = "Multiple toStrings" and
87+
result = count(CfgNode n | multipleToString(n, _) | n)
88+
or
89+
type = "CFG scope lacks initial AST node" and
90+
result = count(CfgScope s | scopeNoFirst(s) | s)
91+
or
92+
type = "Non-PostOrderTree Expr node" and
93+
result = count(Expr e | nonPostOrderExpr(e, _) | e)
94+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @name Control flow graph inconsistency counts
3+
* @description Counts the number of control flow graph inconsistencies of each type. This query is intended for internal use.
4+
* @kind diagnostic
5+
* @id rust/diagnostics/cfg-consistency-counts
6+
*/
7+
8+
import rust
9+
import codeql.rust.controlflow.internal.CfgConsistency as Consistency
10+
11+
// see also `rust/diagnostics/cfg-consistency`, which lists the
12+
// individual inconsistency results.
13+
from string type, int num
14+
where num = Consistency::getCfgInconsistencyCounts(type)
15+
select type, num

rust/ql/src/queries/summary/Stats.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,23 @@
33
*/
44

55
import rust
6+
private import codeql.rust.controlflow.internal.CfgConsistency as CfgConsistency
67

8+
/**
9+
* Gets a count of the total number of lines of code in the database.
10+
*/
711
int getLinesOfCode() { result = sum(File f | | f.getNumberOfLinesOfCode()) }
812

13+
/**
14+
* Gets a count of the total number of lines of code from the source code directory in the database.
15+
*/
916
int getLinesOfUserCode() {
1017
result = sum(File f | exists(f.getRelativePath()) | f.getNumberOfLinesOfCode())
1118
}
19+
20+
/**
21+
* Gets a count of the total number of control flow graph inconsistencies in the database.
22+
*/
23+
int getTotalCfgInconsistencies() {
24+
result = sum(string type | | CfgConsistency::getCfgInconsistencyCounts(type))
25+
}

rust/ql/src/queries/summary/SummaryStats.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,6 @@ where
3333
key = "Lines of code extracted" and value = getLinesOfCode().toString()
3434
or
3535
key = "Lines of user code extracted" and value = getLinesOfUserCode().toString()
36+
or
37+
key = "Inconsistencies - CFG" and value = getTotalCfgInconsistencies().toString()
3638
select key, value
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
| CFG scope lacks initial AST node | 0 |
2+
| Dead end | 0 |
3+
| Multiple successors of the same type | 0 |
4+
| Multiple toStrings | 0 |
5+
| Non-PostOrderTree Expr node | 0 |
6+
| Non-unique list order | 0 |
7+
| Non-unique set representation | 0 |
8+
| Non-unique split kind | 0 |
9+
| Simple and normal successors | 0 |
10+
| Splitting invariant 2 | 0 |
11+
| Splitting invariant 3 | 0 |
12+
| Splitting invariant 4 | 0 |
13+
| Splitting invariant 5 | 0 |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
queries/diagnostics/CfgConsistencyCounts.ql

rust/ql/test/query-tests/diagnostics/SummaryStats.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
| Files extracted - total | 7 |
66
| Files extracted - with errors | 2 |
77
| Files extracted - without errors | 5 |
8+
| Inconsistencies - CFG | 0 |
89
| Lines of code extracted | 59 |
910
| Lines of user code extracted | 59 |

0 commit comments

Comments
 (0)