@@ -429,7 +429,7 @@ 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 QualifiedAccessorWrite and
433433 not this instanceof NoNodeExpr and
434434 not this instanceof SwitchExpr and
435435 not this instanceof SwitchCaseExpr and
@@ -448,6 +448,10 @@ module Expressions {
448448 /**
449449 * A qualified write access. In a qualified write access, the access itself is
450450 * not evaluated, only the qualifier and the indexer arguments (if any).
451+ *
452+ * Note that the successor declaration in `QualifiedAccessorWrite` ensures that the access itself
453+ * is evaluated after the qualifier and the indexer arguments (if any)
454+ * and the right hand side of the assignment.
451455 */
452456 private class QualifiedWriteAccess extends ControlFlowTree instanceof WriteAccess , QualifiableExpr
453457 {
@@ -470,25 +474,25 @@ module Expressions {
470474 final override predicate last ( AstNode last , Completion c ) {
471475 // Skip the access in a qualified write access
472476 last ( getLastExprChild ( this ) , last , c )
477+ or
478+ // Qualifier exits with a null completion
479+ super .isConditional ( ) and
480+ last ( super .getQualifier ( ) , last , c ) and
481+ c .( NullnessCompletion ) .isNull ( )
473482 }
474483
475484 final override predicate succ ( AstNode pred , AstNode succ , Completion c ) {
476485 exists ( int i |
477486 last ( getExprChild ( this , i ) , pred , c ) and
478487 c instanceof NormalCompletion and
488+ ( i != 0 or not c .( NullnessCompletion ) .isNull ( ) ) and
479489 first ( getExprChild ( this , i + 1 ) , succ )
480490 )
481491 }
482492 }
483493
484- private class StatOrDynAccessorCall_ =
485- @dynamic_member_access_expr or @dynamic_element_access_expr or @call_access_expr;
486-
487- /** A normal or a (potential) dynamic call to an accessor. */
488- private class StatOrDynAccessorCall extends Expr , StatOrDynAccessorCall_ { }
489-
490494 /**
491- * An expression that writes via an accessor call , for example `x.Prop = 0`,
495+ * An expression that writes via an accessor, for example `x.Prop = 0`,
492496 * where `Prop` is a property.
493497 *
494498 * Accessor writes need special attention, because we need to model the fact
@@ -498,24 +502,32 @@ module Expressions {
498502 * ```csharp
499503 * x -> 0 -> set_Prop -> x.Prop = 0
500504 * ```
505+ *
506+ * For consistency, control flow is implemented this way for all accessor writes.
507+ * For example, `x.Field = 0`, where `Field` is a field, we want a CFG that looks like
508+ *
509+ * ```csharp
510+ * x -> 0 -> x.Field -> x.Field = 0
511+ * ```
501512 */
502- class AccessorWrite extends PostOrderTree instanceof Expr {
513+ private class QualifiedAccessorWrite extends PostOrderTree instanceof Expr {
503514 AssignableDefinition def ;
504515
505- AccessorWrite ( ) {
516+ QualifiedAccessorWrite ( ) {
506517 def .getExpr ( ) = this and
507- def .getTargetAccess ( ) .( WriteAccess ) instanceof StatOrDynAccessorCall and
518+ def .getTargetAccess ( ) .( WriteAccess ) instanceof QualifiableExpr and
508519 not this instanceof AssignOperationWithExpandedAssignment
509520 }
510521
511522 /**
512523 * Gets the `i`th accessor being called in this write. More than one call
513524 * can happen in tuple assignments.
514525 */
515- StatOrDynAccessorCall getCall ( int i ) {
526+ QualifiableExpr getAccess ( int i ) {
516527 result =
517528 rank [ i + 1 ] ( AssignableDefinitions:: TupleAssignmentDefinition tdef |
518- tdef .getExpr ( ) = this and tdef .getTargetAccess ( ) instanceof StatOrDynAccessorCall
529+ tdef .getExpr ( ) = this and
530+ tdef .getTargetAccess ( ) instanceof QualifiableExpr
519531 |
520532 tdef order by tdef .getEvaluationOrder ( )
521533 ) .getTargetAccess ( )
@@ -528,7 +540,13 @@ module Expressions {
528540 final override predicate propagatesAbnormal ( AstNode child ) {
529541 child = getExprChild ( this , _)
530542 or
531- child = this .getCall ( _)
543+ child = this .getAccess ( _)
544+ }
545+
546+ final override predicate last ( AstNode last , Completion c ) {
547+ PostOrderTree .super .last ( last , c )
548+ or
549+ last ( getExprChild ( this , 0 ) , last , c ) and c .( NullnessCompletion ) .isNull ( )
532550 }
533551
534552 final override predicate first ( AstNode first ) { first ( getExprChild ( this , 0 ) , first ) }
@@ -538,24 +556,25 @@ module Expressions {
538556 exists ( int i |
539557 last ( getExprChild ( this , i ) , pred , c ) and
540558 c instanceof NormalCompletion and
559+ ( i != 0 or not c .( NullnessCompletion ) .isNull ( ) ) and
541560 first ( getExprChild ( this , i + 1 ) , succ )
542561 )
543562 or
544563 // Flow from last element of last child to first accessor call
545564 last ( getLastExprChild ( this ) , pred , c ) and
546- succ = this .getCall ( 0 ) and
565+ succ = this .getAccess ( 0 ) and
547566 c instanceof NormalCompletion
548567 or
549568 // Flow from one call to the next
550- exists ( int i | pred = this .getCall ( i ) |
551- succ = this .getCall ( i + 1 ) and
569+ exists ( int i | pred = this .getAccess ( i ) |
570+ succ = this .getAccess ( i + 1 ) and
552571 c .isValidFor ( pred ) and
553572 c instanceof NormalCompletion
554573 )
555574 or
556575 // 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
576+ exists ( int last | last = max ( int i | exists ( this .getAccess ( i ) ) ) |
577+ pred = this .getAccess ( last ) and
559578 succ = this and
560579 c .isValidFor ( pred ) and
561580 c instanceof NormalCompletion
@@ -704,7 +723,9 @@ module Expressions {
704723 private class ConditionallyQualifiedExpr extends PostOrderTree instanceof QualifiableExpr {
705724 private Expr qualifier ;
706725
707- ConditionallyQualifiedExpr ( ) { this .isConditional ( ) and qualifier = getExprChild ( this , 0 ) }
726+ ConditionallyQualifiedExpr ( ) {
727+ this .isConditional ( ) and qualifier = getExprChild ( this , 0 ) and not this instanceof WriteAccess
728+ }
708729
709730 final override predicate propagatesAbnormal ( AstNode child ) { child = qualifier }
710731
0 commit comments