@@ -429,7 +429,8 @@ module Expressions {
429429 not this instanceof ObjectCreation and
430430 not this instanceof ArrayCreation and
431431 not this instanceof QualifiedWriteAccess and
432- not this instanceof AccessorWrite and
432+ not this instanceof QualifiedAssignments and
433+ not this instanceof QualifiedAccessorWrite and
433434 not this instanceof NoNodeExpr and
434435 not this instanceof SwitchExpr and
435436 not this instanceof SwitchCaseExpr and
@@ -481,11 +482,35 @@ module Expressions {
481482 }
482483 }
483484
484- private class StatOrDynAccessorCall_ =
485- @dynamic_member_access_expr or @dynamic_element_access_expr or @call_access_expr;
485+ private class QualifiedAssignments extends PostOrderTree instanceof Assignment {
486+ QualifiedWriteAccess left ;
487+ Expr right ;
486488
487- /** A normal or a (potential) dynamic call to an accessor. */
488- private class StatOrDynAccessorCall extends Expr , StatOrDynAccessorCall_ { }
489+ QualifiedAssignments ( ) {
490+ left = this .getLValue ( ) and
491+ right = this .getRValue ( )
492+ }
493+
494+ final override predicate propagatesAbnormal ( AstNode child ) { child = [ left , right ] }
495+
496+ final override predicate first ( AstNode first ) { first ( left , first ) }
497+
498+ final override predicate last ( AstNode last , Completion c ) {
499+ PostOrderTree .super .last ( last , c )
500+ or
501+ last ( left , last , c ) and
502+ left .( QualifiableExpr ) .isConditional ( ) and
503+ c .( NullnessCompletion ) .isNull ( )
504+ }
505+
506+ final override predicate succ ( AstNode pred , AstNode succ , Completion c ) {
507+ // Standard left-to-right evaluation
508+ last ( left , pred , c ) and
509+ c instanceof NormalCompletion and
510+ not c .( NullnessCompletion ) .isNull ( ) and
511+ first ( right , succ )
512+ }
513+ }
489514
490515 /**
491516 * An expression that writes via an accessor call, for example `x.Prop = 0`,
@@ -499,23 +524,23 @@ module Expressions {
499524 * x -> 0 -> set_Prop -> x.Prop = 0
500525 * ```
501526 */
502- class AccessorWrite extends PostOrderTree instanceof Expr {
527+ private class QualifiedAccessorWrite extends PostOrderTree instanceof Expr {
503528 AssignableDefinition def ;
504529
505- AccessorWrite ( ) {
530+ QualifiedAccessorWrite ( ) {
506531 def .getExpr ( ) = this and
507- def .getTargetAccess ( ) . ( WriteAccess ) instanceof StatOrDynAccessorCall and
532+ def .getTargetAccess ( ) instanceof QualifiedWriteAccess and
508533 not this instanceof AssignOperationWithExpandedAssignment
509534 }
510535
511536 /**
512537 * Gets the `i`th accessor being called in this write. More than one call
513538 * can happen in tuple assignments.
514539 */
515- StatOrDynAccessorCall getCall ( int i ) {
540+ WriteAccess getAccess ( int i ) {
516541 result =
517542 rank [ i + 1 ] ( AssignableDefinitions:: TupleAssignmentDefinition tdef |
518- tdef .getExpr ( ) = this and tdef . getTargetAccess ( ) instanceof StatOrDynAccessorCall
543+ tdef .getExpr ( ) = this
519544 |
520545 tdef order by tdef .getEvaluationOrder ( )
521546 ) .getTargetAccess ( )
@@ -528,7 +553,13 @@ module Expressions {
528553 final override predicate propagatesAbnormal ( AstNode child ) {
529554 child = getExprChild ( this , _)
530555 or
531- child = this .getCall ( _)
556+ child = this .getAccess ( _)
557+ }
558+
559+ final override predicate last ( AstNode last , Completion c ) {
560+ PostOrderTree .super .last ( last , c )
561+ or
562+ last ( getExprChild ( this , 0 ) , last , c ) and c .( NullnessCompletion ) .isNull ( )
532563 }
533564
534565 final override predicate first ( AstNode first ) { first ( getExprChild ( this , 0 ) , first ) }
@@ -538,24 +569,25 @@ module Expressions {
538569 exists ( int i |
539570 last ( getExprChild ( this , i ) , pred , c ) and
540571 c instanceof NormalCompletion and
572+ ( i != 0 or not c .( NullnessCompletion ) .isNull ( ) ) and
541573 first ( getExprChild ( this , i + 1 ) , succ )
542574 )
543575 or
544576 // Flow from last element of last child to first accessor call
545577 last ( getLastExprChild ( this ) , pred , c ) and
546- succ = this .getCall ( 0 ) and
578+ succ = this .getAccess ( 0 ) and
547579 c instanceof NormalCompletion
548580 or
549581 // Flow from one call to the next
550- exists ( int i | pred = this .getCall ( i ) |
551- succ = this .getCall ( i + 1 ) and
582+ exists ( int i | pred = this .getAccess ( i ) |
583+ succ = this .getAccess ( i + 1 ) and
552584 c .isValidFor ( pred ) and
553585 c instanceof NormalCompletion
554586 )
555587 or
556588 // Post-order: flow from last call to element itself
557- exists ( int last | last = max ( int i | exists ( this .getCall ( i ) ) ) |
558- pred = this .getCall ( last ) and
589+ exists ( int last | last = max ( int i | exists ( this .getAccess ( i ) ) ) |
590+ pred = this .getAccess ( last ) and
559591 succ = this and
560592 c .isValidFor ( pred ) and
561593 c instanceof NormalCompletion
@@ -704,7 +736,9 @@ module Expressions {
704736 private class ConditionallyQualifiedExpr extends PostOrderTree instanceof QualifiableExpr {
705737 private Expr qualifier ;
706738
707- ConditionallyQualifiedExpr ( ) { this .isConditional ( ) and qualifier = getExprChild ( this , 0 ) }
739+ ConditionallyQualifiedExpr ( ) {
740+ this .isConditional ( ) and qualifier = getExprChild ( this , 0 ) and not this instanceof WriteAccess
741+ }
708742
709743 final override predicate propagatesAbnormal ( AstNode child ) { child = qualifier }
710744
0 commit comments