Skip to content

Commit eba5208

Browse files
authored
Merge pull request #304 from microsoft/macro-flow
C++: Add a new library for constructing macro flow
2 parents 2515b12 + 3c8d07b commit eba5208

File tree

1 file changed

+182
-0
lines changed

1 file changed

+182
-0
lines changed
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
private import cpp
2+
3+
/**
4+
* A module for reasoning about macro expansion in C/C++ code.
5+
*
6+
* The library allows one to construct path-problem queries that
7+
* display paths through nested macro invocations to make
8+
* alerts in macro expansions easier to understand.
9+
*/
10+
module MacroFlow {
11+
/**
12+
* Configuration for defining which expressions are interesting for paths from
13+
* an inner-most macro expansion to an outer-most macro expansion that results
14+
* in an expression.
15+
*
16+
* For example, to find paths from macro invocations that expand to
17+
* `sizeof` expressions with literal arguments, one could do:
18+
* ```
19+
* module MyConfig implements ConfigSig {
20+
* predicate isSink(Expr e) { e.(SizeofExprOperator).getExprOperand() instanceof Literal }
21+
* }
22+
*
23+
* module Flow = Make<MyConfig>;
24+
* import Flow::PathGraph
25+
*
26+
* from Flow::Node n1, Flow::Node n2, Expr e
27+
* where Flow::flowsTo(n1, n2, e)
28+
* select e, n1, n2, "Use of sizeof with literal argument."
29+
*
30+
* ```
31+
*/
32+
signature module ConfigSig {
33+
predicate isSink(Expr e);
34+
}
35+
36+
/**
37+
* Constructs a successor relation over macro invocations and expressions
38+
* based on the given configuration.
39+
*/
40+
module Make<ConfigSig Config> {
41+
private predicate rev(MacroInvocation mi) {
42+
Config::isSink(mi.getExpr())
43+
or
44+
exists(MacroInvocation mi1 | rev(mi1) | mi.getParentInvocation() = mi1)
45+
}
46+
47+
private predicate isSource(MacroInvocation mi) {
48+
rev(mi) and
49+
not exists(MacroInvocation mi0 | rev(mi0) | mi0.getParentInvocation() = mi)
50+
}
51+
52+
private predicate fwd(MacroInvocation mi) {
53+
rev(mi) and
54+
(
55+
isSource(mi)
56+
or
57+
exists(MacroInvocation mi0 | fwd(mi0) | mi0.getParentInvocation() = mi)
58+
)
59+
}
60+
61+
private newtype TNode =
62+
TMacroInvocationNode(MacroInvocation mi) { fwd(mi) } or
63+
TExprNode(Expr e) {
64+
Config::isSink(e) and
65+
(
66+
exists(MacroInvocation mi |
67+
fwd(mi) and
68+
mi.getExpr() = e
69+
)
70+
or
71+
// Handle empty paths (i.e., the expression does not have any macros)
72+
not exists(MacroInvocation mi | mi.getExpr() = e)
73+
)
74+
}
75+
76+
/**
77+
* A node in the path graph. A node is either a macro invocation or a sink
78+
* expression.
79+
*/
80+
abstract private class NodeImpl extends TNode {
81+
/** Gets the macro invocation associated with this node, if any. */
82+
abstract MacroInvocation getMacroInvocation();
83+
84+
/** Gets the expression associated with this node, if any. */
85+
abstract Expr getExpr();
86+
87+
/** Gets the string representation of this node. */
88+
abstract string toString();
89+
90+
/** Gets the location of this node. */
91+
abstract Location getLocation();
92+
}
93+
94+
final class Node = NodeImpl;
95+
96+
private class MacroInvocationNode extends NodeImpl, TMacroInvocationNode {
97+
MacroInvocation mi;
98+
99+
MacroInvocationNode() { this = TMacroInvocationNode(mi) }
100+
101+
override MacroInvocation getMacroInvocation() { result = mi }
102+
103+
override Expr getExpr() { none() }
104+
105+
override Location getLocation() { result = mi.getMacro().getLocation() }
106+
107+
final override string toString() { result = this.getMacroInvocation().toString() }
108+
}
109+
110+
private class ExprNode extends NodeImpl, TExprNode {
111+
Expr e;
112+
113+
ExprNode() { this = TExprNode(e) }
114+
115+
override Expr getExpr() { result = e }
116+
117+
override MacroInvocation getMacroInvocation() {
118+
result = any(MacroInvocation mi | mi.getExpr() = e)
119+
}
120+
121+
override Location getLocation() { result = e.getLocation() }
122+
123+
final override string toString() { result = e.toString() }
124+
}
125+
126+
private predicate steps(Node n1, Node n2) {
127+
exists(MacroInvocation mi | n1.(MacroInvocationNode).getMacroInvocation() = mi |
128+
mi.getParentInvocation() = n2.(MacroInvocationNode).getMacroInvocation()
129+
or
130+
not exists(mi.getParentInvocation()) and
131+
exists(ExprNode en | en = n2 |
132+
mi = en.getMacroInvocation()
133+
or
134+
// Handle empty paths
135+
not exists(en.getMacroInvocation()) and
136+
en.getExpr() = mi.getExpr()
137+
)
138+
)
139+
}
140+
141+
private predicate isSinkNode(Node n) { Config::isSink(n.(ExprNode).getExpr()) }
142+
143+
private predicate isSourceNode(Node n) {
144+
isSource(n.(MacroInvocationNode).getMacroInvocation())
145+
or
146+
not exists(any(MacroInvocationNode invocation).getMacroInvocation()) and
147+
isSinkNode(n)
148+
}
149+
150+
private predicate stepsPlus(Node n1, Node n2) =
151+
doublyBoundedFastTC(steps/2, isSourceNode/1, isSinkNode/1)(n1, n2)
152+
153+
private predicate stepsStar(Node n1, Node n2) {
154+
stepsPlus(n1, n2)
155+
or
156+
// Handle empty paths
157+
n1 = n2 and
158+
isSourceNode(n1) and
159+
isSinkNode(n2)
160+
}
161+
162+
predicate flowsTo(Node n, ExprNode n2, Expr e) {
163+
stepsStar(n, n2) and
164+
n2.getExpr() = e
165+
}
166+
167+
/**
168+
* Provides the query predicates needed to include a graph in a path-problem
169+
* query.
170+
*/
171+
module PathGraph {
172+
query predicate edges(Node n1, Node n2) {
173+
steps(n1, n2)
174+
or
175+
// Handle empty paths
176+
isSourceNode(n1) and
177+
isSinkNode(n2) and
178+
n1 = n2
179+
}
180+
}
181+
}
182+
}

0 commit comments

Comments
 (0)