@@ -28,15 +28,15 @@ private AstNode publicApi() {
2828 * Gets any AstNode that directly computes a result of a query.
2929 * I.e. a query predicate or the from-where-select.
3030 */
31- private AstNode queryable ( ) {
31+ private AstNode queryPredicate ( ) {
3232 // result = query relation that is "transitively" imported by a .ql file.
3333 PathProblemQuery:: importsQueryRelation ( result ) .asFile ( ) .getExtension ( ) = "ql"
3434 or
3535 // the from-where-select
3636 result instanceof Select
3737 or
3838 // child of the above.
39- result = queryable ( ) .getAChild ( )
39+ result = queryPredicate ( ) .getAChild ( )
4040}
4141
4242AstNode hackyShouldBeTreatedAsAlive ( ) {
@@ -64,7 +64,7 @@ private AstNode alive() {
6464 result = publicApi ( )
6565 or
6666 // 2) everything that can be an output when running a query
67- result = queryable ( )
67+ result = queryPredicate ( )
6868 or
6969 // 3) A module with an import that imports another file, the import can activate a file.
7070 result .( Module ) .getAMember ( ) .( Import ) .getResolvedModule ( ) .getFile ( ) !=
@@ -73,90 +73,97 @@ private AstNode alive() {
7373 // 4) Things that aren't really alive, but that this query treats as live.
7474 result = hackyShouldBeTreatedAsAlive ( )
7575 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 ) {
7683 //
7784 // The recursive cases.
7885 //
79- result .getEnclosingPredicate ( ) = alive ( )
86+ result .getEnclosingPredicate ( ) = prev
8087 or
81- result = alive ( ) .( Call ) .getTarget ( )
88+ result = prev .( Call ) .getTarget ( )
8289 or
83- alive ( ) .( ClassPredicate ) .overrides ( result )
90+ prev .( ClassPredicate ) .overrides ( result )
8491 or
85- result .( ClassPredicate ) .overrides ( alive ( ) )
92+ result .( ClassPredicate ) .overrides ( prev )
8693 or
87- result = alive ( ) .( PredicateExpr ) .getResolvedPredicate ( )
94+ result = prev .( PredicateExpr ) .getResolvedPredicate ( )
8895 or
8996 // 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 ( )
9198 or
9299 // 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 ( ) |
94101 sup = result .( Class ) .getASuperType ( ) .getResolvedType ( ) .( ClassType ) .getDeclaration ( )
95102 )
96103 or
97- result = alive ( ) .( Class ) .getAChild ( ) and
104+ result = prev .( Class ) .getAChild ( ) and
98105 not result .hasAnnotation ( "private" )
99106 or
100- result = alive ( ) .getAnAnnotation ( )
107+ result = prev .getAnAnnotation ( )
101108 or
102- result = alive ( ) .getQLDoc ( )
109+ result = prev .getQLDoc ( )
103110 or
104111 // 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 ( )
106113 or
107- result = alive ( ) .( VarDecl ) .getType ( ) .getDeclaration ( )
114+ result = prev .( VarDecl ) .getType ( ) .getDeclaration ( )
108115 or
109- result = alive ( ) .( FieldDecl ) .getVarDecl ( )
116+ result = prev .( FieldDecl ) .getVarDecl ( )
110117 or
111- result = alive ( ) .( InlineCast ) .getType ( ) .getDeclaration ( )
118+ result = prev .( InlineCast ) .getType ( ) .getDeclaration ( )
112119 or
113120 // a class overrides some predicate, is the super-predicate is alive.
114121 exists ( ClassPredicate pred , ClassPredicate sup |
115122 pred .hasAnnotation ( "override" ) and
116123 pred .overrides ( sup ) and
117124 result = pred .getParent ( ) and
118- sup .getParent ( ) = alive ( )
125+ sup .getParent ( ) = prev
119126 )
120127 or
121128 // if a class is alive, so is it's super-class
122129 result =
123- [ alive ( ) .( Class ) .getASuperType ( ) , alive ( ) .( Class ) .getAnInstanceofType ( ) ]
130+ [ prev .( Class ) .getASuperType ( ) , prev .( Class ) .getAnInstanceofType ( ) ]
124131 .getResolvedType ( )
125132 .getDeclaration ( )
126133 or
127134 // if a class is alive and abstract, then any sub-class is alive.
128135 exists ( Class clz , Class sup | result = clz |
129136 clz .getASuperType ( ) .getResolvedType ( ) .getDeclaration ( ) = sup and
130137 sup .isAbstract ( ) and
131- sup = alive ( )
138+ sup = prev
132139 )
133140 or
134141 // a module containing something live, is also alive.
135- result .( Module ) .getAMember ( ) = alive ( )
142+ result .( Module ) .getAMember ( ) = prev
136143 or
137- result = alive ( ) .( Module ) .getAlias ( )
144+ result = prev .( Module ) .getAlias ( )
138145 or
139- result .( NewType ) .getABranch ( ) = alive ( )
146+ result .( NewType ) .getABranch ( ) = prev
140147 or
141- result = alive ( ) .( TypeExpr ) .getAChild ( )
148+ result = prev .( TypeExpr ) .getAChild ( )
142149 or
143- result = alive ( ) .( FieldAccess ) .getDeclaration ( )
150+ result = prev .( FieldAccess ) .getDeclaration ( )
144151 or
145- result = alive ( ) .( VarDecl ) .getTypeExpr ( )
152+ result = prev .( VarDecl ) .getTypeExpr ( )
146153 or
147- result .( Import ) .getParent ( ) = alive ( )
154+ result .( Import ) .getParent ( ) = prev
148155 or
149- result = alive ( ) .( NewType ) .getABranch ( )
156+ result = prev .( NewType ) .getABranch ( )
150157 or
151- result = alive ( ) .( ModuleExpr ) .getAChild ( )
158+ result = prev .( ModuleExpr ) .getAChild ( )
152159 or
153- result = alive ( ) .( ModuleExpr ) .getResolvedModule ( ) .asModule ( )
160+ result = prev .( ModuleExpr ) .getResolvedModule ( ) .asModule ( )
154161 or
155- result = alive ( ) .( InstanceOf ) .getType ( ) .getResolvedType ( ) .getDeclaration ( )
162+ result = prev .( InstanceOf ) .getType ( ) .getResolvedType ( ) .getDeclaration ( )
156163 or
157- result = alive ( ) .( Annotation ) .getAChild ( )
164+ result = prev .( Annotation ) .getAChild ( )
158165 or
159- result = alive ( ) .( Predicate ) .getReturnType ( ) .getDeclaration ( )
166+ result = prev .( Predicate ) .getReturnType ( ) .getDeclaration ( )
160167}
161168
162169private AstNode deprecated ( ) {
@@ -188,21 +195,60 @@ private AstNode classUnion() {
188195 result = classUnion ( ) .( ModuleExpr ) .getAChild ( )
189196}
190197
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+
191211private predicate isDeadInternal ( AstNode node ) {
192212 not node = alive ( ) and
193- not node = deprecated ( ) and
194- not node = classUnion ( )
213+ not node = deprecated ( )
195214}
196215
197216predicate isDead ( AstNode node ) {
198217 isDeadInternal ( node ) and
199218 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/%" )
208254}
0 commit comments