@@ -159,87 +159,104 @@ 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 deliberatly 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+ exists ( Parameter parg |
209+ parg = this .getParameter ( ) and
210+ p = parg .getSourceDeclaration ( ) and
211+ result .getAParameter ( ) = p
212+ )
213+ }
214+
215+ /**
216+ * Holds if the assignment to this `ref` argument via parameter `p` is
217+ * analyzable. That is, the target callable is non-overridable and from
218+ * source.
219+ */
220+ predicate isAnalyzable ( Parameter p ) {
221+ exists ( Callable callable | callable = this .getSourceDeclarationTarget ( p ) |
222+ not callable .( Virtualizable ) .isOverridableOrImplementable ( ) and
223+ callable .hasBody ( )
224+ )
225+ }
226+
227+ /** Gets an assignment to analyzable parameter `p`. */
228+ AssignableDefinition getAnAnalyzableRefDef ( Parameter p ) {
229+ this .isAnalyzable ( p ) and
230+ result .getTarget ( ) = p and
231+ not result = TImplicitParameterDefinition ( _)
232+ }
233+
234+ /**
235+ * Holds if this `ref` assignment is *not* analyzable. Equivalent with
236+ * `not this.isAnalyzable(_)`, but avoids negative recursion.
237+ */
238+ private predicate isNonAnalyzable ( ) {
239+ call instanceof @delegate_invocation_expr
227240 or
228- exists ( Callable callable | callable = getRefCallTarget ( c , aa , _) |
241+ exists ( Callable callable | callable = this . getSourceDeclarationTarget ( _) |
229242 callable .( Virtualizable ) .isOverridableOrImplementable ( ) or
230243 not callable .hasBody ( )
231244 )
232- )
245+ }
246+
247+ /** Holds if this `ref` assignment is relevant. */
248+ predicate isRelevant ( ) {
249+ this .isNonAnalyzable ( ) or
250+ exists ( this .getAnAnalyzableRefDef ( _) )
251+ }
233252 }
234253
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 ( _)
254+ /** Holds if a node in basic block `bb` assigns to `ref` parameter `p` via definition `def`. */
255+ private predicate basicBlockRefParamDef (
256+ ControlFlow:: BasicBlock bb , Parameter p , AssignableDefinition def
257+ ) {
258+ def = any ( RefArg arg ) .getAnAnalyzableRefDef ( p ) and
259+ bb .getANode ( ) = def .getAControlFlowNode ( )
243260 }
244261
245262 /**
@@ -248,9 +265,11 @@ module AssignableInternal {
248265 * any assignments to `p`.
249266 */
250267 private predicate parameterReachesWithoutDef ( Parameter p , ControlFlow:: BasicBlock bb ) {
251- not basicBlockRefParamDef ( bb , p ) and
268+ forall ( AssignableDefinition def | basicBlockRefParamDef ( bb , p , def ) |
269+ isUncertainRefCall ( def .getTargetAccess ( ) )
270+ ) and
252271 (
253- isAnalyzableRefCall ( _ , _ , p ) and
272+ any ( RefArg arg ) . isAnalyzable ( p ) and
254273 p .getCallable ( ) .getEntryPoint ( ) = bb .getFirstNode ( )
255274 or
256275 exists ( ControlFlow:: BasicBlock mid | parameterReachesWithoutDef ( p , mid ) |
@@ -259,9 +278,23 @@ module AssignableInternal {
259278 )
260279 }
261280
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 ( )
281+ // Not defined by dispatch in order to avoid too conservative negative recursion error
282+ Expr getExpr ( AssignableDefinition def ) {
283+ def = TAssignmentDefinition ( result )
284+ or
285+ def = TTupleAssignmentDefinition ( result , _)
286+ or
287+ def = TOutRefDefinition ( any ( AssignableAccess aa | result = aa .getParent ( ) ) )
288+ or
289+ def = TMutationDefinition ( result )
290+ or
291+ def = TLocalVariableDefinition ( result )
292+ or
293+ def = TAddressOfDefinition ( result )
294+ or
295+ def = TIsPatternDefinition ( any ( IsPatternExpr ipe | result = ipe .getVariableDeclExpr ( ) ) )
296+ or
297+ def = TTypeCasePatternDefinition ( any ( TypeCase tc | result = tc .getVariableDeclExpr ( ) ) )
265298 }
266299
267300 cached
@@ -273,7 +306,7 @@ module AssignableInternal {
273306 TOutRefDefinition ( AssignableAccess aa ) {
274307 aa .isOutArgument ( )
275308 or
276- isRelevantRefCall ( _ , aa )
309+ aa . ( RefArg ) . isRelevant ( )
277310 } or
278311 TMutationDefinition ( MutatorOperation mo ) or
279312 TLocalVariableDefinition ( LocalVariableDeclExpr lvde ) {
@@ -285,8 +318,10 @@ module AssignableInternal {
285318 } or
286319 TImplicitParameterDefinition ( Parameter p ) {
287320 exists ( Callable c | p = c .getAParameter ( ) |
288- c .hasBody ( ) or
289- c .( Constructor ) .hasInitializer ( )
321+ c .hasBody ( )
322+ or
323+ // Same as `c.(Constructor).hasInitializer()`, but avoids negative recursion warning
324+ c .getAChildExpr ( ) instanceof @constructor_init_expr
290325 )
291326 } or
292327 TAddressOfDefinition ( AddressOfExpr aoe ) or
@@ -311,9 +346,9 @@ module AssignableInternal {
311346 * Holds if the `ref` assignment to `aa` via call `c` is uncertain.
312347 */
313348 cached
314- predicate isUncertainRefCall ( Call c , AssignableAccess aa ) {
315- isRelevantRefCall ( c , aa ) and
316- exists ( ControlFlow:: BasicBlock bb , Parameter p | isAnalyzableRefCall ( c , aa , p ) |
349+ predicate isUncertainRefCall ( RefArg arg ) {
350+ arg . isRelevant ( ) and
351+ exists ( ControlFlow:: BasicBlock bb , Parameter p | arg . isAnalyzable ( p ) |
317352 parameterReachesWithoutDef ( p , bb ) and
318353 bb .getLastNode ( ) = p .getCallable ( ) .getExitPoint ( )
319354 )
@@ -360,18 +395,21 @@ module AssignableInternal {
360395 }
361396
362397 /**
363- * Gets the argument for the implicit `value` parameter in the accessor call
364- * `ac `, if any.
398+ * Gets the argument for the implicit `value` parameter in accessor access
399+ * `a `, if any.
365400 */
366401 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 ( )
402+ Expr getAccessorCallValueArgument ( Access a ) {
403+ a .getTarget ( ) instanceof DeclarationWithAccessors and
404+ (
405+ exists ( AssignExpr ae | tupleAssignmentDefinition ( ae , a ) |
406+ tupleAssignmentPair ( ae , a , result )
407+ )
408+ or
409+ exists ( Assignment ass | a = ass .getLValue ( ) |
410+ result = ass .getRValue ( ) and
411+ not ass .( AssignOperation ) .hasExpandedAssignment ( )
412+ )
375413 )
376414 }
377415 }
@@ -410,7 +448,7 @@ class AssignableDefinition extends TAssignableDefinition {
410448 * Not all definitions have an associated expression, for example implicit
411449 * parameter definitions.
412450 */
413- Expr getExpr ( ) { none ( ) }
451+ final Expr getExpr ( ) { result = getExpr ( this ) }
414452
415453 /**
416454 * Gets the underlying element associated with this definition. This is either
@@ -509,8 +547,6 @@ module AssignableDefinitions {
509547 /** Gets the underlying assignment. */
510548 Assignment getAssignment ( ) { result = a }
511549
512- override Expr getExpr ( ) { result = a }
513-
514550 override Expr getSource ( ) {
515551 result = a .getRValue ( ) and
516552 not a instanceof AssignOperation
@@ -548,8 +584,6 @@ module AssignableDefinitions {
548584 )
549585 }
550586
551- override Expr getExpr ( ) { result = ae }
552-
553587 override Expr getSource ( ) {
554588 result = getTupleSource ( this ) // need not exist
555589 }
@@ -585,11 +619,7 @@ module AssignableDefinitions {
585619 )
586620 }
587621
588- override Expr getExpr ( ) { result = this .getCall ( ) }
589-
590- override predicate isCertain ( ) {
591- not isUncertainRefCall ( this .getCall ( ) , this .getTargetAccess ( ) )
592- }
622+ override predicate isCertain ( ) { not isUncertainRefCall ( this .getTargetAccess ( ) ) }
593623
594624 override string toString ( ) { result = aa .toString ( ) }
595625
@@ -607,8 +637,6 @@ module AssignableDefinitions {
607637 /** Gets the underlying mutator operation. */
608638 MutatorOperation getMutatorOperation ( ) { result = mo }
609639
610- override Expr getExpr ( ) { result = mo }
611-
612640 override string toString ( ) { result = mo .toString ( ) }
613641 }
614642
@@ -623,8 +651,6 @@ module AssignableDefinitions {
623651 /** Gets the underlying local variable declaration. */
624652 LocalVariableDeclExpr getDeclaration ( ) { result = lvde }
625653
626- override Expr getExpr ( ) { result = lvde }
627-
628654 override string toString ( ) { result = lvde .toString ( ) }
629655 }
630656
@@ -662,8 +688,6 @@ module AssignableDefinitions {
662688 /** Gets the underlying address-of expression. */
663689 AddressOfExpr getAddressOf ( ) { result = aoe }
664690
665- override Expr getExpr ( ) { result = aoe }
666-
667691 override string toString ( ) { result = aoe .toString ( ) }
668692 }
669693
@@ -678,8 +702,6 @@ module AssignableDefinitions {
678702 /** Gets the underlying local variable declaration. */
679703 LocalVariableDeclExpr getDeclaration ( ) { result = ipe .getVariableDeclExpr ( ) }
680704
681- override Expr getExpr ( ) { result = this .getDeclaration ( ) }
682-
683705 override Expr getSource ( ) { result = ipe .getExpr ( ) }
684706
685707 override string toString ( ) { result = this .getDeclaration ( ) .toString ( ) }
@@ -705,8 +727,6 @@ module AssignableDefinitions {
705727 /** Gets the underlying local variable declaration. */
706728 LocalVariableDeclExpr getDeclaration ( ) { result = tc .getVariableDeclExpr ( ) }
707729
708- override Expr getExpr ( ) { result = this .getDeclaration ( ) }
709-
710730 override Expr getSource ( ) {
711731 result = any ( SwitchStmt ss | ss .getATypeCase ( ) = tc ) .getCondition ( )
712732 }
0 commit comments