@@ -28,15 +28,15 @@ private AstNode publicApi() {
28
28
* Gets any AstNode that directly computes a result of a query.
29
29
* I.e. a query predicate or the from-where-select.
30
30
*/
31
- private AstNode queryable ( ) {
31
+ private AstNode queryPredicate ( ) {
32
32
// result = query relation that is "transitively" imported by a .ql file.
33
33
PathProblemQuery:: importsQueryRelation ( result ) .asFile ( ) .getExtension ( ) = "ql"
34
34
or
35
35
// the from-where-select
36
36
result instanceof Select
37
37
or
38
38
// child of the above.
39
- result = queryable ( ) .getAChild ( )
39
+ result = queryPredicate ( ) .getAChild ( )
40
40
}
41
41
42
42
AstNode hackyShouldBeTreatedAsAlive ( ) {
@@ -64,7 +64,7 @@ private AstNode alive() {
64
64
result = publicApi ( )
65
65
or
66
66
// 2) everything that can be an output when running a query
67
- result = queryable ( )
67
+ result = queryPredicate ( )
68
68
or
69
69
// 3) A module with an import that imports another file, the import can activate a file.
70
70
result .( Module ) .getAMember ( ) .( Import ) .getResolvedModule ( ) .getFile ( ) !=
@@ -73,90 +73,97 @@ private AstNode alive() {
73
73
// 4) Things that aren't really alive, but that this query treats as live.
74
74
result = hackyShouldBeTreatedAsAlive ( )
75
75
or
76
+ result instanceof TopLevel // toplevel is always alive.
77
+ or
78
+ // recurisve cases
79
+ result = aliveStep ( alive ( ) )
80
+ }
81
+
82
+ private AstNode aliveStep ( AstNode prev ) {
76
83
//
77
84
// The recursive cases.
78
85
//
79
- result .getEnclosingPredicate ( ) = alive ( )
86
+ result .getEnclosingPredicate ( ) = prev
80
87
or
81
- result = alive ( ) .( Call ) .getTarget ( )
88
+ result = prev .( Call ) .getTarget ( )
82
89
or
83
- alive ( ) .( ClassPredicate ) .overrides ( result )
90
+ prev .( ClassPredicate ) .overrides ( result )
84
91
or
85
- result .( ClassPredicate ) .overrides ( alive ( ) )
92
+ result .( ClassPredicate ) .overrides ( prev )
86
93
or
87
- result = alive ( ) .( PredicateExpr ) .getResolvedPredicate ( )
94
+ result = prev .( PredicateExpr ) .getResolvedPredicate ( )
88
95
or
89
96
// if a sub-class is alive, then the super-class is alive.
90
- result = alive ( ) .( Class ) .getASuperType ( ) .getResolvedType ( ) .( ClassType ) .getDeclaration ( )
97
+ result = prev .( Class ) .getASuperType ( ) .getResolvedType ( ) .( ClassType ) .getDeclaration ( )
91
98
or
92
99
// if the super class is alive and abstract, then any sub-class is alive.
93
- exists ( Class sup | sup = alive ( ) and sup .isAbstract ( ) |
100
+ exists ( Class sup | sup = prev and sup .isAbstract ( ) |
94
101
sup = result .( Class ) .getASuperType ( ) .getResolvedType ( ) .( ClassType ) .getDeclaration ( )
95
102
)
96
103
or
97
- result = alive ( ) .( Class ) .getAChild ( ) and
104
+ result = prev .( Class ) .getAChild ( ) and
98
105
not result .hasAnnotation ( "private" )
99
106
or
100
- result = alive ( ) .getAnAnnotation ( )
107
+ result = prev .getAnAnnotation ( )
101
108
or
102
- result = alive ( ) .getQLDoc ( )
109
+ result = prev .getQLDoc ( )
103
110
or
104
111
// any imported module is alive. We don't have to handle the "import a file"-case, those are treated as public APIs.
105
- result = alive ( ) .( Import ) .getResolvedModule ( ) .asModule ( )
112
+ result = prev .( Import ) .getResolvedModule ( ) .asModule ( )
106
113
or
107
- result = alive ( ) .( VarDecl ) .getType ( ) .getDeclaration ( )
114
+ result = prev .( VarDecl ) .getType ( ) .getDeclaration ( )
108
115
or
109
- result = alive ( ) .( FieldDecl ) .getVarDecl ( )
116
+ result = prev .( FieldDecl ) .getVarDecl ( )
110
117
or
111
- result = alive ( ) .( InlineCast ) .getType ( ) .getDeclaration ( )
118
+ result = prev .( InlineCast ) .getType ( ) .getDeclaration ( )
112
119
or
113
120
// a class overrides some predicate, is the super-predicate is alive.
114
121
exists ( ClassPredicate pred , ClassPredicate sup |
115
122
pred .hasAnnotation ( "override" ) and
116
123
pred .overrides ( sup ) and
117
124
result = pred .getParent ( ) and
118
- sup .getParent ( ) = alive ( )
125
+ sup .getParent ( ) = prev
119
126
)
120
127
or
121
128
// if a class is alive, so is it's super-class
122
129
result =
123
- [ alive ( ) .( Class ) .getASuperType ( ) , alive ( ) .( Class ) .getAnInstanceofType ( ) ]
130
+ [ prev .( Class ) .getASuperType ( ) , prev .( Class ) .getAnInstanceofType ( ) ]
124
131
.getResolvedType ( )
125
132
.getDeclaration ( )
126
133
or
127
134
// if a class is alive and abstract, then any sub-class is alive.
128
135
exists ( Class clz , Class sup | result = clz |
129
136
clz .getASuperType ( ) .getResolvedType ( ) .getDeclaration ( ) = sup and
130
137
sup .isAbstract ( ) and
131
- sup = alive ( )
138
+ sup = prev
132
139
)
133
140
or
134
141
// a module containing something live, is also alive.
135
- result .( Module ) .getAMember ( ) = alive ( )
142
+ result .( Module ) .getAMember ( ) = prev
136
143
or
137
- result = alive ( ) .( Module ) .getAlias ( )
144
+ result = prev .( Module ) .getAlias ( )
138
145
or
139
- result .( NewType ) .getABranch ( ) = alive ( )
146
+ result .( NewType ) .getABranch ( ) = prev
140
147
or
141
- result = alive ( ) .( TypeExpr ) .getAChild ( )
148
+ result = prev .( TypeExpr ) .getAChild ( )
142
149
or
143
- result = alive ( ) .( FieldAccess ) .getDeclaration ( )
150
+ result = prev .( FieldAccess ) .getDeclaration ( )
144
151
or
145
- result = alive ( ) .( VarDecl ) .getTypeExpr ( )
152
+ result = prev .( VarDecl ) .getTypeExpr ( )
146
153
or
147
- result .( Import ) .getParent ( ) = alive ( )
154
+ result .( Import ) .getParent ( ) = prev
148
155
or
149
- result = alive ( ) .( NewType ) .getABranch ( )
156
+ result = prev .( NewType ) .getABranch ( )
150
157
or
151
- result = alive ( ) .( ModuleExpr ) .getAChild ( )
158
+ result = prev .( ModuleExpr ) .getAChild ( )
152
159
or
153
- result = alive ( ) .( ModuleExpr ) .getResolvedModule ( ) .asModule ( )
160
+ result = prev .( ModuleExpr ) .getResolvedModule ( ) .asModule ( )
154
161
or
155
- result = alive ( ) .( InstanceOf ) .getType ( ) .getResolvedType ( ) .getDeclaration ( )
162
+ result = prev .( InstanceOf ) .getType ( ) .getResolvedType ( ) .getDeclaration ( )
156
163
or
157
- result = alive ( ) .( Annotation ) .getAChild ( )
164
+ result = prev .( Annotation ) .getAChild ( )
158
165
or
159
- result = alive ( ) .( Predicate ) .getReturnType ( ) .getDeclaration ( )
166
+ result = prev .( Predicate ) .getReturnType ( ) .getDeclaration ( )
160
167
}
161
168
162
169
private AstNode deprecated ( ) {
@@ -188,21 +195,60 @@ private AstNode classUnion() {
188
195
result = classUnion ( ) .( ModuleExpr ) .getAChild ( )
189
196
}
190
197
198
+ private AstNode benign ( ) {
199
+ not result .getLocation ( ) .getFile ( ) .getExtension ( ) = [ "ql" , "qll" ] or // ignore dbscheme files
200
+ result instanceof BlockComment or
201
+ not exists ( result .toString ( ) ) or // <- invalid code
202
+ // cached-stages pattern
203
+ result .( Module ) .getAMember ( ) .( ClasslessPredicate ) .getName ( ) = "forceStage" or
204
+ result .( ClasslessPredicate ) .getName ( ) = "forceStage" or
205
+ result .getLocation ( ) .getFile ( ) .getBaseName ( ) = "Caching.qll" or
206
+ // sometimes contains dead code - ignore
207
+ result .getLocation ( ) .getFile ( ) .getRelativePath ( ) .matches ( "%/tutorials/%" ) or
208
+ result = classUnion ( )
209
+ }
210
+
191
211
private predicate isDeadInternal ( AstNode node ) {
192
212
not node = alive ( ) and
193
- not node = deprecated ( ) and
194
- not node = classUnion ( )
213
+ not node = deprecated ( )
195
214
}
196
215
197
216
predicate isDead ( AstNode node ) {
198
217
isDeadInternal ( node ) and
199
218
not isDeadInternal ( node .getParent ( ) ) and
200
- not node instanceof BlockComment and
201
- exists ( node .toString ( ) ) and // <- invalid code
202
- node .getLocation ( ) .getFile ( ) .getExtension ( ) = [ "ql" , "qll" ] and // ignore dbscheme files
203
- // cached-stages pattern
204
- not node .( Module ) .getAMember ( ) .( ClasslessPredicate ) .getName ( ) = "forceStage" and
205
- not node .( ClasslessPredicate ) .getName ( ) = "forceStage" and
206
- not node .getLocation ( ) .getFile ( ) .getBaseName ( ) = "Caching.qll" and
207
- not node .getLocation ( ) .getFile ( ) .getRelativePath ( ) .matches ( "%/tutorials/%" ) // sometimes contains dead code - ignore
219
+ not node = benign ( )
220
+ }
221
+
222
+ /**
223
+ * Gets an AST node that affects a query.
224
+ */
225
+ private AstNode queryable ( ) {
226
+ //
227
+ // The base cases.
228
+ //
229
+ // everything that can be an output when running a query
230
+ result = queryPredicate ( )
231
+ or
232
+ // A module with an import that imports another file, the import can activate a file.
233
+ result .( Module ) .getAMember ( ) .( Import ) .getResolvedModule ( ) .getFile ( ) !=
234
+ result .getLocation ( ) .getFile ( )
235
+ or
236
+ result instanceof TopLevel // toplevel is always alive.
237
+ or
238
+ // recurisve cases
239
+ result = aliveStep ( queryable ( ) )
240
+ }
241
+
242
+ /**
243
+ * Gets an AstNode that does not affect any query result.
244
+ * Is interresting as an quick-eval target to investigate dead code.
245
+ * (It is intentional that this predicate is a result of this predicate).
246
+ */
247
+ AstNode unQueryable ( string msg ) {
248
+ not result = queryable ( ) and
249
+ not result = deprecated ( ) and
250
+ not result = benign ( ) and
251
+ not result .getParent ( ) = any ( AstNode node | not node = queryable ( ) ) and
252
+ msg = result .getLocation ( ) .getFile ( ) .getBaseName ( ) and
253
+ result .getLocation ( ) .getFile ( ) .getAbsolutePath ( ) .matches ( "%/javascript/%" )
208
254
}
0 commit comments