Skip to content

Commit 01871a8

Browse files
committed
Overlay.qll: Streamline discarding
- Unify discardable entities under one Discardable superclass. - Reduce discard predicates down to two depending on TRAP ID type.
1 parent fa2bb8c commit 01871a8

File tree

1 file changed

+214
-74
lines changed

1 file changed

+214
-74
lines changed

python/ql/lib/semmle/python/Overlay.qll

Lines changed: 214 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,54 @@ overlay[local]
1414
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
1515

1616
overlay[local]
17-
private string getRawPathForLocation(@location loc) {
17+
private string getPathForLocation(@location loc) {
1818
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
1919
or
20-
exists(@py_Module mod | locations_ast(loc, mod, _, _, _, _) | result = getRawPathForModule(mod))
20+
exists(@py_Module mod | locations_ast(loc, mod, _, _, _, _) | result = getPathForModule(mod))
2121
}
2222

2323
overlay[local]
24-
private string getRawPathForModule(@py_Module mod) {
24+
private string getPathForModule(@py_Module mod) {
2525
exists(@container fileOrFolder | py_module_path(mod, fileOrFolder) |
26-
result = getRawPathForContainer(fileOrFolder)
26+
result = getPathForContainer(fileOrFolder)
2727
)
2828
}
2929

3030
overlay[local]
31-
private string getRawPathForContainer(@container fileOrFolder) {
31+
private string getPathForContainer(@container fileOrFolder) {
3232
files(fileOrFolder, result) or folders(fileOrFolder, result)
3333
}
3434

35-
/*- Source elements -*/
35+
/*- Discardable entities and their discard predicates -*/
36+
/** Python database entities that use named TRAP IDs; the rest use *-ids. */
37+
overlay[local]
38+
private class NamedEntity = @py_Module or @container or @py_cobject;
39+
40+
overlay[discard_entity]
41+
private predicate discardNamedEntity(@top el) {
42+
el instanceof NamedEntity and
43+
// Entities with named IDs can exist both in base, overlay, or both.
44+
exists(Discardable d | d = el |
45+
overlayChangedFiles(d.getPath()) and
46+
not d.existsInOverlay()
47+
)
48+
}
49+
50+
overlay[discard_entity]
51+
private predicate discardStarEntity(@top el) {
52+
not el instanceof NamedEntity and
53+
// Entities with *-ids can exist either in base or overlay, but not both.
54+
exists(Discardable d | d = el |
55+
overlayChangedFiles(d.getPath()) and
56+
d.existsInBase()
57+
)
58+
}
59+
3660
/**
3761
* An abstract base class for all elements that can be discarded from the base.
3862
*/
3963
overlay[local]
40-
abstract private class Discardable extends @py_source_element {
64+
abstract private class Discardable extends @top {
4165
/** Gets the path to the file in which this element occurs. */
4266
abstract string getPath();
4367

@@ -51,98 +75,51 @@ abstract private class Discardable extends @py_source_element {
5175
string toString() { none() }
5276
}
5377

54-
overlay[discard_entity]
55-
private predicate discardEntity(@py_source_element el) {
56-
exists(Discardable d | d = el |
57-
overlayChangedFiles(d.getPath()) and
58-
d.existsInBase() and
59-
not d.existsInOverlay()
60-
)
61-
}
62-
6378
/**
64-
* Discard all locatable AST nodes (`@py_location_parent`) in modified files
65-
* since they use *-ids and hence cannot be referenced across TRAP files.
79+
* Discardable locatable AST nodes (`@py_location_parent`).
6680
*/
6781
overlay[local]
6882
final private class DiscardableLocatable extends Discardable instanceof @py_location_parent {
6983
override string getPath() {
70-
exists(@location loc | py_locations(loc, this) | result = getRawPathForLocation(loc))
84+
exists(@location loc | py_locations(loc, this) | result = getPathForLocation(loc))
7185
}
7286
}
7387

7488
/**
75-
* Discard scopes (classes, functions, modules) that were deleted in the overlay.
89+
* Discardable scopes (classes, functions, modules).
7690
*/
7791
overlay[local]
7892
final private class DiscardableScope extends Discardable instanceof @py_scope {
7993
override string getPath() {
80-
exists(@location loc | py_scope_location(loc, this) | result = getRawPathForLocation(loc))
94+
exists(@location loc | py_scope_location(loc, this) | result = getPathForLocation(loc))
8195
or
82-
result = getRawPathForModule(this)
96+
result = getPathForModule(this)
8397
}
8498
}
8599

86100
/**
87-
* Discard files and folders that were deleted in the overlay.
101+
* Discardable files and folders.
88102
*/
89103
overlay[local]
90104
final private class DiscardableContainer extends Discardable instanceof @container {
91-
override string getPath() { result = getRawPathForContainer(this) }
105+
override string getPath() { result = getPathForContainer(this) }
92106
}
93107

94-
/*- CFG Nodes -*/
95108
/** Discardable control flow nodes */
96109
overlay[local]
97-
final private class DiscardableCfgNode instanceof @py_flow_node {
98-
string getPath() {
110+
final private class DiscardableCfgNode extends Discardable instanceof @py_flow_node {
111+
override string getPath() {
99112
exists(Discardable d | result = d.getPath() |
100113
py_flow_bb_node(this, d.(@py_ast_node), _, _)
101114
or
102115
py_scope_flow(this, d.(@py_scope), _)
103116
)
104117
}
105-
106-
predicate existsInBase() { not isOverlay() and exists(this) }
107-
108-
predicate existsInOverlay() { isOverlay() and exists(this) }
109-
110-
string toString() { none() }
111-
}
112-
113-
overlay[discard_entity]
114-
private predicate discardCfgNode(@py_flow_node n) {
115-
exists(DiscardableCfgNode d | d = n |
116-
overlayChangedFiles(d.getPath()) and
117-
d.existsInBase() and
118-
not d.existsInOverlay()
119-
)
120118
}
121119

122-
/*- Variables -*/
123-
/** Discardable (normal and SSA) variables */
120+
/** Discardable Python variables. */
124121
overlay[local]
125-
abstract private class DiscardableBaseVar instanceof @py_base_var {
126-
abstract string getPath();
127-
128-
predicate existsInBase() { not isOverlay() and exists(this) }
129-
130-
predicate existsInOverlay() { isOverlay() and exists(this) }
131-
132-
string toString() { none() }
133-
}
134-
135-
overlay[discard_entity]
136-
private predicate discardVar(@py_base_var n) {
137-
exists(DiscardableVar d | d = n |
138-
overlayChangedFiles(d.getPath()) and
139-
d.existsInBase() and
140-
not d.existsInOverlay()
141-
)
142-
}
143-
144-
overlay[local]
145-
final private class DiscardableVar extends DiscardableBaseVar instanceof @py_variable {
122+
final private class DiscardableVar extends Discardable instanceof @py_variable {
146123
override string getPath() {
147124
exists(Discardable parent | result = parent.getPath() |
148125
variable(this, parent.(@py_scope), _)
@@ -152,8 +129,9 @@ final private class DiscardableVar extends DiscardableBaseVar instanceof @py_var
152129
}
153130
}
154131

132+
/** Discardable SSA variables. */
155133
overlay[local]
156-
final private class DiscardableSsaVar extends DiscardableBaseVar instanceof @py_ssa_var {
134+
final private class DiscardableSsaVar extends Discardable instanceof @py_ssa_var {
157135
override string getPath() {
158136
exists(DiscardableSsaVar other | result = other.getPath() |
159137
py_ssa_phi(this, other.(@py_ssa_var))
@@ -169,18 +147,180 @@ final private class DiscardableSsaVar extends DiscardableBaseVar instanceof @py_
169147
}
170148
}
171149

172-
/*- Locations -*/
150+
/** Discardable locations. */
173151
overlay[local]
174-
private predicate locationExistsInBase(@location loc) { not isOverlay() and exists(loc) }
152+
final private class DiscardableLocation extends Discardable instanceof @location {
153+
override string getPath() { result = getPathForLocation(this) }
154+
}
175155

156+
/** Discardable lines. */
176157
overlay[local]
177-
private predicate locationExistsInOverlay(@location loc) { isOverlay() and exists(loc) }
158+
final private class DiscardableLine extends Discardable instanceof @py_line {
159+
override string getPath() {
160+
exists(Discardable d | result = d.getPath() | py_line_lengths(this, d.(@py_Module), _, _))
161+
}
162+
}
178163

179-
overlay[discard_entity]
180-
private predicate discardLocation(@location loc) {
181-
overlayChangedFiles(getRawPathForLocation(loc)) and
182-
locationExistsInBase(loc) and
183-
not locationExistsInOverlay(loc)
164+
/** Discardable string part lists. */
165+
overlay[local]
166+
final private class DiscardableStringPartList extends Discardable instanceof @py_StringPart_list {
167+
override string getPath() {
168+
exists(Discardable d | result = d.getPath() | py_StringPart_lists(this, d.(@py_Bytes_or_Str)))
169+
}
170+
}
171+
172+
/** Discardable alias */
173+
overlay[local]
174+
final private class DiscardableAlias extends Discardable instanceof @py_alias {
175+
override string getPath() {
176+
exists(DiscardableAliasList d | result = d.getPath() | py_aliases(this, d, _))
177+
}
178+
}
179+
180+
/** Discardable alias list */
181+
overlay[local]
182+
final private class DiscardableAliasList extends Discardable instanceof @py_alias_list {
183+
override string getPath() {
184+
exists(Discardable d | result = d.getPath() | py_alias_lists(this, d.(@py_Import)))
185+
}
186+
}
187+
188+
/** Discardable arguments */
189+
overlay[local]
190+
final private class DiscardableArguments extends Discardable instanceof @py_arguments {
191+
override string getPath() {
192+
exists(Discardable d | result = d.getPath() | py_arguments(this, d.(@py_arguments_parent)))
193+
}
194+
}
195+
196+
/** Discardable boolop */
197+
overlay[local]
198+
final private class DiscardableBoolOp extends Discardable instanceof @py_boolop {
199+
override string getPath() {
200+
exists(Discardable d | result = d.getPath() | py_boolops(this, _, d.(@py_BoolExpr)))
201+
}
202+
}
203+
204+
/** Discardable cmpop */
205+
overlay[local]
206+
final private class DiscardableCmpOp extends Discardable instanceof @py_cmpop {
207+
override string getPath() {
208+
exists(DiscardableCmpOpList d | result = d.getPath() | py_cmpops(this, _, d, _))
209+
}
210+
}
211+
212+
/** Discardable cmpop list */
213+
overlay[local]
214+
final private class DiscardableCmpOpList extends Discardable instanceof @py_cmpop_list {
215+
override string getPath() {
216+
exists(Discardable d | result = d.getPath() | py_cmpop_lists(this, d.(@py_Compare)))
217+
}
218+
}
219+
220+
/** Discardable comprehension list */
221+
overlay[local]
222+
final private class DiscardableComprehensionList extends Discardable instanceof @py_comprehension_list
223+
{
224+
override string getPath() {
225+
exists(Discardable d | result = d.getPath() | py_comprehension_lists(this, d.(@py_ListComp)))
226+
}
227+
}
228+
229+
/** Discardable dict item list */
230+
overlay[local]
231+
final private class DiscardableDictItemList extends Discardable instanceof @py_dict_item_list {
232+
override string getPath() {
233+
exists(Discardable d | result = d.getPath() |
234+
py_dict_item_lists(this, d.(@py_dict_item_list_parent))
235+
)
236+
}
237+
}
238+
239+
/** Discardable expr context */
240+
overlay[local]
241+
final private class DiscardableExprContext extends Discardable instanceof @py_expr_context {
242+
override string getPath() {
243+
exists(Discardable d | result = d.getPath() |
244+
py_expr_contexts(this, _, d.(@py_expr_context_parent))
245+
)
246+
}
247+
}
248+
249+
/** Discardable expr list */
250+
overlay[local]
251+
final private class DiscardableExprList extends Discardable instanceof @py_expr_list {
252+
override string getPath() {
253+
exists(Discardable d | result = d.getPath() | py_expr_lists(this, d.(@py_expr_list_parent), _))
254+
}
255+
}
256+
257+
/** Discardable operator */
258+
overlay[local]
259+
final private class DiscardableOperator extends Discardable instanceof @py_operator {
260+
override string getPath() {
261+
exists(Discardable d | result = d.getPath() | py_operators(this, _, d.(@py_BinaryExpr)))
262+
}
263+
}
264+
265+
/** Discardable parameter list */
266+
overlay[local]
267+
final private class DiscardableParameterList extends Discardable instanceof @py_parameter_list {
268+
override string getPath() {
269+
exists(Discardable d | result = d.getPath() | py_parameter_lists(this, d.(@py_Function)))
270+
}
271+
}
272+
273+
/** Discardable pattern list */
274+
overlay[local]
275+
final private class DiscardablePatternList extends Discardable instanceof @py_pattern_list {
276+
override string getPath() {
277+
exists(Discardable d | result = d.getPath() |
278+
py_pattern_lists(this, d.(@py_pattern_list_parent), _)
279+
)
280+
}
281+
}
282+
283+
/** Discardable stmt list */
284+
overlay[local]
285+
final private class DiscardableStmtList extends Discardable instanceof @py_stmt_list {
286+
override string getPath() {
287+
exists(Discardable d | result = d.getPath() | py_stmt_lists(this, d.(@py_stmt_list_parent), _))
288+
}
289+
}
290+
291+
/** Discardable str list */
292+
overlay[local]
293+
final private class DiscardableStrList extends Discardable instanceof @py_str_list {
294+
override string getPath() {
295+
exists(Discardable d | result = d.getPath() | py_str_lists(this, d.(@py_str_list_parent)))
296+
}
297+
}
298+
299+
/** Discardable type parameter list */
300+
overlay[local]
301+
final private class DiscardableTypeParameterList extends Discardable instanceof @py_type_parameter_list
302+
{
303+
override string getPath() {
304+
exists(Discardable d | result = d.getPath() |
305+
py_type_parameter_lists(this, d.(@py_type_parameter_list_parent))
306+
)
307+
}
308+
}
309+
310+
/** Discardable unaryop */
311+
overlay[local]
312+
final private class DiscardableUnaryOp extends Discardable instanceof @py_unaryop {
313+
override string getPath() {
314+
exists(Discardable d | result = d.getPath() | py_unaryops(this, _, d.(@py_UnaryExpr)))
315+
}
316+
}
317+
318+
/** Discardable comment */
319+
overlay[local]
320+
final private class DiscardableComment extends Discardable instanceof @py_comment {
321+
override string getPath() {
322+
exists(DiscardableLocation d | result = d.getPath() | py_comments(this, _, d))
323+
}
184324
}
185325

186326
/*- XML -*/

0 commit comments

Comments
 (0)