@@ -691,19 +691,22 @@ module LocalFlow {
691691 )
692692 }
693693
694- /** Gets a node for which to construct a post-update node for argument `arg`. */
695- ControlFlow:: Nodes:: ExprNode getAPostUpdateNodeForArg ( ControlFlow:: Nodes:: ExprNode arg ) {
696- arg .getExpr ( ) instanceof Argument and
697- result = getALastEvalNode * ( arg ) and
698- exists ( Expr e , Type t | result .getExpr ( ) = e and t = e .stripCasts ( ) .getType ( ) |
699- t instanceof RefType and
700- not t instanceof NullType
701- or
702- t = any ( TypeParameter tp | not tp .isValueType ( ) )
703- or
704- t .isRefLikeType ( )
705- ) and
706- not exists ( getALastEvalNode ( result ) )
694+ /**
695+ * Holds if a reverse local flow step should be added from the post-update node
696+ * for `e` to the post-update node for the result.
697+ *
698+ * This is needed to allow for side-effects on compound expressions to propagate
699+ * to sub components. For example, in
700+ *
701+ * ```csharp
702+ * m(b ? x : y)
703+ * ```
704+ *
705+ * we add a reverse flow step from `[post] b ? x : y` to `[post] x` and to
706+ * `[post] y`, in order for the side-effect of `m` to reach both `x` and `y`.
707+ */
708+ ControlFlow:: Nodes:: ExprNode getPostUpdateReverseStep ( ControlFlow:: Nodes:: ExprNode e ) {
709+ result = getALastEvalNode ( e )
707710 }
708711
709712 /**
@@ -763,6 +766,13 @@ predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo, string model) {
763766 VariableCapture:: valueStep ( nodeFrom , nodeTo )
764767 or
765768 nodeTo = nodeFrom .( LocalFunctionCreationNode ) .getAnAccess ( true )
769+ or
770+ nodeTo .( PostUpdateNode ) .getPreUpdateNode ( ) .( ExprNode ) .getControlFlowNode ( ) =
771+ LocalFlow:: getPostUpdateReverseStep ( nodeFrom
772+ .( PostUpdateNode )
773+ .getPreUpdateNode ( )
774+ .( ExprNode )
775+ .getControlFlowNode ( ) )
766776 ) and
767777 model = ""
768778 or
@@ -1061,6 +1071,20 @@ private class FieldOrPropertyUsedInSource extends FieldOrProperty {
10611071 }
10621072}
10631073
1074+ /**
1075+ * Hold if `e` has a type that allows for it to have a post-update node.
1076+ */
1077+ predicate exprMayHavePostUpdateNode ( Expr e ) {
1078+ exists ( Type t | t = e .stripCasts ( ) .getType ( ) |
1079+ t instanceof RefType and
1080+ not t instanceof NullType
1081+ or
1082+ t = any ( TypeParameter tp | not tp .isValueType ( ) )
1083+ or
1084+ t .isRefLikeType ( )
1085+ )
1086+ }
1087+
10641088/** A collection of cached types and predicates to be evaluated in the same stage. */
10651089cached
10661090private module Cached {
@@ -1106,7 +1130,15 @@ private module Cached {
11061130 cfn .getAstNode ( ) .( ObjectCreation ) .hasInitializer ( )
11071131 } or
11081132 TExprPostUpdateNode ( ControlFlow:: Nodes:: ExprNode cfn ) {
1109- cfn = LocalFlow:: getAPostUpdateNodeForArg ( _)
1133+ (
1134+ cfn .getExpr ( ) instanceof Argument
1135+ or
1136+ cfn =
1137+ LocalFlow:: getPostUpdateReverseStep ( any ( ControlFlow:: Nodes:: ExprNode e |
1138+ exists ( any ( SourcePostUpdateNode p ) .getPreUpdateNode ( ) .asExprAtNode ( e ) )
1139+ ) )
1140+ ) and
1141+ exprMayHavePostUpdateNode ( cfn .getExpr ( ) )
11101142 or
11111143 exists ( Expr e | e = cfn .getExpr ( ) |
11121144 fieldOrPropertyStore ( _, _, _, e , true )
@@ -2722,17 +2754,23 @@ abstract class PostUpdateNode extends Node {
27222754}
27232755
27242756module PostUpdateNodes {
2725- class ObjectCreationNode extends PostUpdateNode , ExprNode , TExprNode {
2757+ abstract class SourcePostUpdateNode extends PostUpdateNode {
2758+ abstract Node getPreUpdateSourceNode ( ) ;
2759+
2760+ final override Node getPreUpdateNode ( ) { result = this .getPreUpdateSourceNode ( ) }
2761+ }
2762+
2763+ class ObjectCreationNode extends SourcePostUpdateNode , ExprNode , TExprNode {
27262764 private ObjectCreation oc ;
27272765
27282766 ObjectCreationNode ( ) { this = TExprNode ( oc .getAControlFlowNode ( ) ) }
27292767
2730- override Node getPreUpdateNode ( ) {
2768+ override Node getPreUpdateSourceNode ( ) {
27312769 exists ( ControlFlow:: Nodes:: ElementNode cfn | this = TExprNode ( cfn ) |
2732- result . ( ObjectInitializerNode ) . getControlFlowNode ( ) = cfn
2770+ result = TObjectInitializerNode ( cfn )
27332771 or
27342772 not oc .hasInitializer ( ) and
2735- result . ( MallocNode ) . getControlFlowNode ( ) = cfn
2773+ result = TMallocNode ( cfn )
27362774 )
27372775 }
27382776 }
@@ -2744,7 +2782,7 @@ module PostUpdateNodes {
27442782 * Such a node acts as both a post-update node for the `MallocNode`, as well as
27452783 * a pre-update node for the `ObjectCreationNode`.
27462784 */
2747- class ObjectInitializerNode extends PostUpdateNode , NodeImpl , ArgumentNodeImpl ,
2785+ class ObjectInitializerNode extends SourcePostUpdateNode , NodeImpl , ArgumentNodeImpl ,
27482786 TObjectInitializerNode
27492787 {
27502788 private ObjectCreation oc ;
@@ -2758,7 +2796,7 @@ module PostUpdateNodes {
27582796 /** Gets the initializer to which this initializer node belongs. */
27592797 ObjectOrCollectionInitializer getInitializer ( ) { result = oc .getInitializer ( ) }
27602798
2761- override MallocNode getPreUpdateNode ( ) { result . getControlFlowNode ( ) = cfn }
2799+ override MallocNode getPreUpdateSourceNode ( ) { result = TMallocNode ( cfn ) }
27622800
27632801 override predicate argumentOf ( DataFlowCall call , ArgumentPosition pos ) {
27642802 pos .isQualifier ( ) and
@@ -2781,23 +2819,12 @@ module PostUpdateNodes {
27812819 override string toStringImpl ( ) { result = "[pre-initializer] " + cfn }
27822820 }
27832821
2784- class ExprPostUpdateNode extends PostUpdateNode , NodeImpl , TExprPostUpdateNode {
2822+ class ExprPostUpdateNode extends SourcePostUpdateNode , NodeImpl , TExprPostUpdateNode {
27852823 private ControlFlow:: Nodes:: ElementNode cfn ;
27862824
27872825 ExprPostUpdateNode ( ) { this = TExprPostUpdateNode ( cfn ) }
27882826
2789- override ExprNode getPreUpdateNode ( ) {
2790- // For compound arguments, such as `m(b ? x : y)`, we want the leaf nodes
2791- // `[post] x` and `[post] y` to have two pre-update nodes: (1) the compound argument,
2792- // `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
2793- // respectively.
2794- //
2795- // This ensures that we get flow out of the call into both leafs (1), while still
2796- // maintaining the invariant that the underlying expression is a pre-update node (2).
2797- cfn = LocalFlow:: getAPostUpdateNodeForArg ( result .getControlFlowNode ( ) )
2798- or
2799- cfn = result .getControlFlowNode ( )
2800- }
2827+ override ExprNode getPreUpdateSourceNode ( ) { result = TExprNode ( cfn ) }
28012828
28022829 override DataFlowCallable getEnclosingCallableImpl ( ) {
28032830 result .getAControlFlowNode ( ) = cfn
@@ -2825,49 +2852,49 @@ module PostUpdateNodes {
28252852 override Node getPreUpdateNode ( ) { result .( FlowSummaryNode ) .getSummaryNode ( ) = preUpdateNode }
28262853 }
28272854
2828- private class InstanceParameterAccessPostUpdateNode extends PostUpdateNode ,
2855+ private class InstanceParameterAccessPostUpdateNode extends SourcePostUpdateNode ,
28292856 InstanceParameterAccessNode
28302857 {
28312858 InstanceParameterAccessPostUpdateNode ( ) { isPostUpdate = true }
28322859
2833- override InstanceParameterAccessPreNode getPreUpdateNode ( ) {
2860+ override InstanceParameterAccessPreNode getPreUpdateSourceNode ( ) {
28342861 result = TInstanceParameterAccessNode ( cfn , false )
28352862 }
28362863
28372864 override string toStringImpl ( ) { result = "[post] this" }
28382865 }
28392866
2840- private class PrimaryConstructorThisAccessPostUpdateNode extends PostUpdateNode ,
2867+ private class PrimaryConstructorThisAccessPostUpdateNode extends SourcePostUpdateNode ,
28412868 PrimaryConstructorThisAccessNode
28422869 {
28432870 PrimaryConstructorThisAccessPostUpdateNode ( ) { isPostUpdate = true }
28442871
2845- override PrimaryConstructorThisAccessPreNode getPreUpdateNode ( ) {
2872+ override PrimaryConstructorThisAccessPreNode getPreUpdateSourceNode ( ) {
28462873 result = TPrimaryConstructorThisAccessNode ( p , false , callable )
28472874 }
28482875
28492876 override string toStringImpl ( ) { result = "[post] this" }
28502877 }
28512878
2852- class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode , PostUpdateNode {
2879+ class LocalFunctionCreationPostUpdateNode extends LocalFunctionCreationNode , SourcePostUpdateNode {
28532880 LocalFunctionCreationPostUpdateNode ( ) { isPostUpdate = true }
28542881
2855- override LocalFunctionCreationPreNode getPreUpdateNode ( ) {
2882+ override LocalFunctionCreationPreNode getPreUpdateSourceNode ( ) {
28562883 result = TLocalFunctionCreationNode ( cfn , false )
28572884 }
28582885
28592886 override string toStringImpl ( ) { result = "[post] " + cfn }
28602887 }
28612888
2862- private class CapturePostUpdateNode extends PostUpdateNode , CaptureNode {
2889+ private class CapturePostUpdateNode extends SourcePostUpdateNode , CaptureNode {
28632890 private CaptureNode pre ;
28642891
28652892 CapturePostUpdateNode ( ) {
28662893 VariableCapture:: Flow:: capturePostUpdateNode ( this .getSynthesizedCaptureNode ( ) ,
28672894 pre .getSynthesizedCaptureNode ( ) )
28682895 }
28692896
2870- override CaptureNode getPreUpdateNode ( ) { result = pre }
2897+ override CaptureNode getPreUpdateSourceNode ( ) { result = pre }
28712898
28722899 override string toStringImpl ( ) { result = "[post] " + cn }
28732900 }
0 commit comments