@@ -430,6 +430,7 @@ module Expressions {
430430 not this instanceof ArrayCreation and
431431 not this instanceof QualifiedWriteAccess and
432432 not this instanceof QualifiedAccessorWrite and
433+ not this instanceof QualifiedWriteAccessOutRef and
433434 not this instanceof NoNodeExpr and
434435 not this instanceof SwitchExpr and
435436 not this instanceof SwitchCaseExpr and
@@ -491,6 +492,86 @@ module Expressions {
491492 }
492493 }
493494
495+ /**
496+ * An expression that writes via an accessor in an out/ref parameter, for example `s = M(out x.Field)`,
497+ * where `Field` is a field.
498+ *
499+ * Accessor writes need special attention, because we need to model that the
500+ * access is written after the method call.
501+ *
502+ * In the example above, this means we want a CFG that looks like
503+ *
504+ * ```csharp
505+ * x -> call M -> x.Field -> s = M(out x.Field)
506+ * ```
507+ */
508+ private class QualifiedWriteAccessOutRef extends PostOrderTree instanceof Expr {
509+ final override predicate propagatesAbnormal ( AstNode child ) { child = getExprChild ( this , _) }
510+
511+ QualifiedWriteAccessOutRef ( ) {
512+ exists ( AssignableDefinitions:: OutRefDefinition def |
513+ def .getExpr ( ) = this and
514+ def .getTargetAccess ( ) instanceof QualifiableExpr
515+ )
516+ }
517+
518+ final override predicate first ( AstNode first ) {
519+ first ( getExprChild ( this , 0 ) , first )
520+ or
521+ not exists ( getExprChild ( this , 0 ) ) and
522+ first = this
523+ }
524+
525+ private QualifiableExpr getOutRefAccess ( int i ) {
526+ result =
527+ rank [ i + 1 ] ( AssignableDefinitions:: OutRefDefinition def |
528+ def .getExpr ( ) = this and
529+ def .getTargetAccess ( ) instanceof QualifiableExpr
530+ |
531+ def order by def .getIndex ( )
532+ ) .getTargetAccess ( )
533+ }
534+
535+ private QualifiableExpr getLastOutRefAccess ( ) {
536+ exists ( int last |
537+ result = this .getOutRefAccess ( last ) and
538+ not exists ( this .getOutRefAccess ( last + 1 ) )
539+ )
540+ }
541+
542+ final override predicate last ( AstNode last , Completion c ) {
543+ last = this .getLastOutRefAccess ( ) and
544+ c .isValidFor ( last )
545+ }
546+
547+ final override predicate succ ( AstNode pred , AstNode succ , Completion c ) {
548+ exists ( int i |
549+ last ( getExprChild ( this , i ) , pred , c ) and
550+ c instanceof NormalCompletion
551+ |
552+ // Post-order: flow from last element of last child to element itself
553+ i = max ( int j | exists ( getExprChild ( this , j ) ) ) and
554+ succ = this
555+ or
556+ // Standard left-to-right evaluation
557+ first ( getExprChild ( this , i + 1 ) , succ )
558+ )
559+ or
560+ // Flow from this element to the first write access.
561+ pred = this and
562+ succ = this .getOutRefAccess ( 0 ) and
563+ c .isValidFor ( pred ) and
564+ c instanceof NormalCompletion
565+ or
566+ // Flow from one access to the next
567+ exists ( int i | pred = this .getOutRefAccess ( i ) |
568+ succ = this .getOutRefAccess ( i + 1 ) and
569+ c .isValidFor ( pred ) and
570+ c instanceof NormalCompletion
571+ )
572+ }
573+ }
574+
494575 /**
495576 * An expression that writes via an accessor, for example `x.Prop = 0`,
496577 * where `Prop` is a property.
@@ -516,6 +597,7 @@ module Expressions {
516597 QualifiedAccessorWrite ( ) {
517598 def .getExpr ( ) = this and
518599 def .getTargetAccess ( ) .( WriteAccess ) instanceof QualifiableExpr and
600+ not def instanceof AssignableDefinitions:: OutRefDefinition and
519601 not this instanceof AssignOperationWithExpandedAssignment
520602 }
521603
0 commit comments