@@ -159,87 +159,101 @@ module AssignableInternal {
159159 }
160160
161161 /**
162- * Holds if the `ref` assignment to `aa` via call `c` is relevant.
162+ * A `ref` argument in a call.
163+ *
164+ * All predicates in this class deliberately do not use the `Call` class, or any
165+ * subclass thereof, as that results in too conservative negative recursion
166+ * compilation errors.
163167 */
164- private predicate isRelevantRefCall ( Call c , AssignableAccess aa ) {
165- isNonAnalyzableRefCall ( c , aa ) or
166- exists ( getAnAnalyzableRefDef ( c , aa , _) )
167- }
168+ private class RefArg extends AssignableAccess {
169+ private Expr call ;
168170
169- private Callable getRefCallTarget ( Call c , AssignableAccess aa , Parameter p ) {
170- exists ( Parameter parg |
171- c .getAnArgument ( ) = aa and
172- aa .isRefArgument ( ) and
173- getArgumentForParameter ( c , parg ) = aa and
174- p = parg .getSourceDeclaration ( ) and
175- result .getAParameter ( ) = p
176- )
177- }
171+ private int position ;
178172
179- /**
180- * A verbatim copy of `Call.getArgumentForParameter()` specialized to
181- * `MethodCall`/`ObjectCreation` (needed to avoid too conservative negative
182- * recursion error).
183- */
184- private Expr getArgumentForParameter ( Call c , Parameter p ) {
185- exists ( Callable callable | p = callable .getAParameter ( ) |
186- callable = c .( MethodCall ) .getTarget ( ) or
187- callable = c .( ObjectCreation ) .getTarget ( )
188- ) and
189- (
190- // Appears in the positional part of the call
191- result = c .getArgument ( p .getPosition ( ) ) and
192- not exists ( result .getExplicitArgumentName ( ) )
193- or
194- // Appears in the named part of the call
195- result = getExplicitArgument ( c , p .getName ( ) )
196- )
197- }
173+ RefArg ( ) {
174+ this .isRefArgument ( ) and
175+ this = call .getChildExpr ( position ) and
176+ (
177+ call instanceof @method_invocation_expr
178+ or
179+ call instanceof @delegate_invocation_expr
180+ or
181+ call instanceof @local_function_invocation_expr
182+ or
183+ call instanceof @object_creation_expr
184+ )
185+ }
198186
199- // predicate folding to get proper join-order
200- pragma [ noinline]
201- private Expr getExplicitArgument ( Call c , string name ) {
202- result = c .getAnArgument ( ) and
203- result .getExplicitArgumentName ( ) = name
204- }
187+ pragma [ noinline]
188+ Parameter getAParameter ( string name ) {
189+ exists ( Callable callable | result = callable .getAParameter ( ) |
190+ expr_call ( call , callable ) and
191+ result .getName ( ) = name
192+ )
193+ }
205194
206- /**
207- * Holds if the `ref` assignment to `aa` via parameter `p` is analyzable. That is,
208- * the target callable is non-overridable and from source.
209- */
210- private predicate isAnalyzableRefCall ( Call c , AssignableAccess aa , Parameter p ) {
211- exists ( Callable callable | callable = getRefCallTarget ( c , aa , p ) |
212- not callable .( Virtualizable ) .isOverridableOrImplementable ( ) and
213- callable .hasBody ( )
214- )
215- }
195+ /** Gets the parameter that this argument corresponds to. */
196+ private Parameter getParameter ( ) {
197+ exists ( string name | result = this .getAParameter ( name ) |
198+ // Appears in the positional part of the call
199+ result .getPosition ( ) = position and
200+ not exists ( this .getExplicitArgumentName ( ) )
201+ or
202+ // Appears in the named part of the call
203+ name = this .getExplicitArgumentName ( )
204+ )
205+ }
216206
217- /**
218- * Holds if the `ref` assignment to `aa` via parameter `p` is *not* analyzable.
219- * Equivalent with `not isAnalyzableRefCall(mc, aa, p)`, but avoids negative
220- * recursion.
221- */
222- private predicate isNonAnalyzableRefCall ( Call c , AssignableAccess aa ) {
223- aa = c .getAnArgument ( ) and
224- aa .isRefArgument ( ) and
225- (
226- not exists ( getRefCallTarget ( c , aa , _) )
207+ private Callable getSourceDeclarationTarget ( Parameter p ) {
208+ p = this .getParameter ( ) .getSourceDeclaration ( ) and
209+ result .getAParameter ( ) = p
210+ }
211+
212+ /**
213+ * Holds if the assignment to this `ref` argument via parameter `p` is
214+ * analyzable. That is, the target callable is non-overridable and from
215+ * source.
216+ */
217+ predicate isAnalyzable ( Parameter p ) {
218+ exists ( Callable callable | callable = this .getSourceDeclarationTarget ( p ) |
219+ not callable .( Virtualizable ) .isOverridableOrImplementable ( ) and
220+ callable .hasBody ( )
221+ )
222+ }
223+
224+ /** Gets an assignment to analyzable parameter `p`. */
225+ AssignableDefinition getAnAnalyzableRefDef ( Parameter p ) {
226+ this .isAnalyzable ( p ) and
227+ result .getTarget ( ) = p and
228+ not result = TImplicitParameterDefinition ( _)
229+ }
230+
231+ /**
232+ * Holds if this `ref` assignment is *not* analyzable. Equivalent with
233+ * `not this.isAnalyzable(_)`, but avoids negative recursion.
234+ */
235+ private predicate isNonAnalyzable ( ) {
236+ call instanceof @delegate_invocation_expr
227237 or
228- exists ( Callable callable | callable = getRefCallTarget ( c , aa , _) |
238+ exists ( Callable callable | callable = this . getSourceDeclarationTarget ( _) |
229239 callable .( Virtualizable ) .isOverridableOrImplementable ( ) or
230240 not callable .hasBody ( )
231241 )
232- )
242+ }
243+
244+ /** Holds if this `ref` access is a potential assignment. */
245+ predicate isPotentialAssignment ( ) {
246+ this .isNonAnalyzable ( ) or
247+ exists ( this .getAnAnalyzableRefDef ( _) )
248+ }
233249 }
234250
235- /**
236- * Gets an assignment to parameter `p`, where the `ref` assignment to `aa` via
237- * parameter `p` is analyzable.
238- */
239- private AssignableDefinition getAnAnalyzableRefDef ( Call c , AssignableAccess aa , Parameter p ) {
240- isAnalyzableRefCall ( c , aa , p ) and
241- result .getTarget ( ) = p and
242- not result = TImplicitParameterDefinition ( _)
251+ /** Holds if a node in basic block `bb` assigns to `ref` parameter `p` via definition `def`. */
252+ private predicate basicBlockRefParamDef (
253+ ControlFlow:: BasicBlock bb , Parameter p , AssignableDefinition def
254+ ) {
255+ def = any ( RefArg arg ) .getAnAnalyzableRefDef ( p ) and
256+ bb .getANode ( ) = def .getAControlFlowNode ( )
243257 }
244258
245259 /**
@@ -248,9 +262,11 @@ module AssignableInternal {
248262 * any assignments to `p`.
249263 */
250264 private predicate parameterReachesWithoutDef ( Parameter p , ControlFlow:: BasicBlock bb ) {
251- not basicBlockRefParamDef ( bb , p ) and
265+ forall ( AssignableDefinition def | basicBlockRefParamDef ( bb , p , def ) |
266+ isUncertainRefCall ( def .getTargetAccess ( ) )
267+ ) and
252268 (
253- isAnalyzableRefCall ( _ , _ , p ) and
269+ any ( RefArg arg ) . isAnalyzable ( p ) and
254270 p .getCallable ( ) .getEntryPoint ( ) = bb .getFirstNode ( )
255271 or
256272 exists ( ControlFlow:: BasicBlock mid | parameterReachesWithoutDef ( p , mid ) |
@@ -259,9 +275,23 @@ module AssignableInternal {
259275 )
260276 }
261277
262- /** Holds if a node in basic block `bb` assigns to `ref` parameter `p`. */
263- private predicate basicBlockRefParamDef ( ControlFlow:: BasicBlock bb , Parameter p ) {
264- bb .getANode ( ) = getAnAnalyzableRefDef ( _, _, p ) .getAControlFlowNode ( )
278+ // Not defined by dispatch in order to avoid too conservative negative recursion error
279+ Expr getExpr ( AssignableDefinition def ) {
280+ def = TAssignmentDefinition ( result )
281+ or
282+ def = TTupleAssignmentDefinition ( result , _)
283+ or
284+ def = TOutRefDefinition ( any ( AssignableAccess aa | result = aa .getParent ( ) ) )
285+ or
286+ def = TMutationDefinition ( result )
287+ or
288+ def = TLocalVariableDefinition ( result )
289+ or
290+ def = TAddressOfDefinition ( result )
291+ or
292+ def = TIsPatternDefinition ( any ( IsPatternExpr ipe | result = ipe .getVariableDeclExpr ( ) ) )
293+ or
294+ def = TTypeCasePatternDefinition ( any ( TypeCase tc | result = tc .getVariableDeclExpr ( ) ) )
265295 }
266296
267297 cached
@@ -273,7 +303,7 @@ module AssignableInternal {
273303 TOutRefDefinition ( AssignableAccess aa ) {
274304 aa .isOutArgument ( )
275305 or
276- isRelevantRefCall ( _ , aa )
306+ aa . ( RefArg ) . isPotentialAssignment ( )
277307 } or
278308 TMutationDefinition ( MutatorOperation mo ) or
279309 TLocalVariableDefinition ( LocalVariableDeclExpr lvde ) {
@@ -285,8 +315,10 @@ module AssignableInternal {
285315 } or
286316 TImplicitParameterDefinition ( Parameter p ) {
287317 exists ( Callable c | p = c .getAParameter ( ) |
288- c .hasBody ( ) or
289- c .( Constructor ) .hasInitializer ( )
318+ c .hasBody ( )
319+ or
320+ // Same as `c.(Constructor).hasInitializer()`, but avoids negative recursion warning
321+ c .getAChildExpr ( ) instanceof @constructor_init_expr
290322 )
291323 } or
292324 TAddressOfDefinition ( AddressOfExpr aoe ) or
@@ -311,9 +343,9 @@ module AssignableInternal {
311343 * Holds if the `ref` assignment to `aa` via call `c` is uncertain.
312344 */
313345 cached
314- predicate isUncertainRefCall ( Call c , AssignableAccess aa ) {
315- isRelevantRefCall ( c , aa ) and
316- exists ( ControlFlow:: BasicBlock bb , Parameter p | isAnalyzableRefCall ( c , aa , p ) |
346+ predicate isUncertainRefCall ( RefArg arg ) {
347+ arg . isPotentialAssignment ( ) and
348+ exists ( ControlFlow:: BasicBlock bb , Parameter p | arg . isAnalyzable ( p ) |
317349 parameterReachesWithoutDef ( p , bb ) and
318350 bb .getLastNode ( ) = p .getCallable ( ) .getExitPoint ( )
319351 )
@@ -360,18 +392,21 @@ module AssignableInternal {
360392 }
361393
362394 /**
363- * Gets the argument for the implicit `value` parameter in the accessor call
364- * `ac `, if any.
395+ * Gets the argument for the implicit `value` parameter in accessor access
396+ * `a `, if any.
365397 */
366398 cached
367- Expr getAccessorCallValueArgument ( AccessorCall ac ) {
368- exists ( AssignExpr ae | tupleAssignmentDefinition ( ae , ac ) |
369- tupleAssignmentPair ( ae , ac , result )
370- )
371- or
372- exists ( Assignment a | ac = a .getLValue ( ) |
373- result = a .getRValue ( ) and
374- not a .( AssignOperation ) .hasExpandedAssignment ( )
399+ Expr getAccessorCallValueArgument ( Access a ) {
400+ a .getTarget ( ) instanceof DeclarationWithAccessors and
401+ (
402+ exists ( AssignExpr ae | tupleAssignmentDefinition ( ae , a ) |
403+ tupleAssignmentPair ( ae , a , result )
404+ )
405+ or
406+ exists ( Assignment ass | a = ass .getLValue ( ) |
407+ result = ass .getRValue ( ) and
408+ not ass .( AssignOperation ) .hasExpandedAssignment ( )
409+ )
375410 )
376411 }
377412 }
@@ -410,7 +445,7 @@ class AssignableDefinition extends TAssignableDefinition {
410445 * Not all definitions have an associated expression, for example implicit
411446 * parameter definitions.
412447 */
413- Expr getExpr ( ) { none ( ) }
448+ final Expr getExpr ( ) { result = getExpr ( this ) }
414449
415450 /**
416451 * Gets the underlying element associated with this definition. This is either
@@ -509,8 +544,6 @@ module AssignableDefinitions {
509544 /** Gets the underlying assignment. */
510545 Assignment getAssignment ( ) { result = a }
511546
512- override Expr getExpr ( ) { result = a }
513-
514547 override Expr getSource ( ) {
515548 result = a .getRValue ( ) and
516549 not a instanceof AssignOperation
@@ -548,8 +581,6 @@ module AssignableDefinitions {
548581 )
549582 }
550583
551- override Expr getExpr ( ) { result = ae }
552-
553584 override Expr getSource ( ) {
554585 result = getTupleSource ( this ) // need not exist
555586 }
@@ -585,11 +616,7 @@ module AssignableDefinitions {
585616 )
586617 }
587618
588- override Expr getExpr ( ) { result = this .getCall ( ) }
589-
590- override predicate isCertain ( ) {
591- not isUncertainRefCall ( this .getCall ( ) , this .getTargetAccess ( ) )
592- }
619+ override predicate isCertain ( ) { not isUncertainRefCall ( this .getTargetAccess ( ) ) }
593620
594621 override string toString ( ) { result = aa .toString ( ) }
595622
@@ -607,8 +634,6 @@ module AssignableDefinitions {
607634 /** Gets the underlying mutator operation. */
608635 MutatorOperation getMutatorOperation ( ) { result = mo }
609636
610- override Expr getExpr ( ) { result = mo }
611-
612637 override string toString ( ) { result = mo .toString ( ) }
613638 }
614639
@@ -623,8 +648,6 @@ module AssignableDefinitions {
623648 /** Gets the underlying local variable declaration. */
624649 LocalVariableDeclExpr getDeclaration ( ) { result = lvde }
625650
626- override Expr getExpr ( ) { result = lvde }
627-
628651 override string toString ( ) { result = lvde .toString ( ) }
629652 }
630653
@@ -662,8 +685,6 @@ module AssignableDefinitions {
662685 /** Gets the underlying address-of expression. */
663686 AddressOfExpr getAddressOf ( ) { result = aoe }
664687
665- override Expr getExpr ( ) { result = aoe }
666-
667688 override string toString ( ) { result = aoe .toString ( ) }
668689 }
669690
@@ -678,8 +699,6 @@ module AssignableDefinitions {
678699 /** Gets the underlying local variable declaration. */
679700 LocalVariableDeclExpr getDeclaration ( ) { result = ipe .getVariableDeclExpr ( ) }
680701
681- override Expr getExpr ( ) { result = this .getDeclaration ( ) }
682-
683702 override Expr getSource ( ) { result = ipe .getExpr ( ) }
684703
685704 override string toString ( ) { result = this .getDeclaration ( ) .toString ( ) }
@@ -705,8 +724,6 @@ module AssignableDefinitions {
705724 /** Gets the underlying local variable declaration. */
706725 LocalVariableDeclExpr getDeclaration ( ) { result = tc .getVariableDeclExpr ( ) }
707726
708- override Expr getExpr ( ) { result = this .getDeclaration ( ) }
709-
710727 override Expr getSource ( ) {
711728 result = any ( SwitchStmt ss | ss .getATypeCase ( ) = tc ) .getCondition ( )
712729 }
0 commit comments