@@ -113,6 +113,12 @@ module LocalFlow {
113
113
nodeFrom = TImplicitWrapNode ( cfgNode , false ) and
114
114
nodeTo = TReturnNodeImpl ( cfgNode .getScope ( ) )
115
115
)
116
+ or
117
+ exists ( CfgNode cfgNode |
118
+ cfgNode = nodeFrom .( AstNode ) .getCfgNode ( ) and
119
+ isUniqueReturned ( cfgNode ) and
120
+ nodeTo .( ReturnNodeImpl ) .getCfgScope ( ) = cfgNode .getScope ( )
121
+ )
116
122
}
117
123
118
124
predicate localMustFlowStep ( Node nodeFrom , Node nodeTo ) {
@@ -148,8 +154,8 @@ private module Cached {
148
154
or
149
155
n = any ( CfgNodes:: ExprNodes:: IndexCfgNode index ) .getBase ( )
150
156
} or
151
- TPreReturnNodeImpl ( CfgNodes:: AstCfgNode n , Boolean isArray ) { isReturned ( n ) } or
152
- TImplicitWrapNode ( CfgNodes:: AstCfgNode n , Boolean shouldWrap ) { isReturned ( n ) } or
157
+ TPreReturnNodeImpl ( CfgNodes:: AstCfgNode n , Boolean isArray ) { isMultiReturned ( n ) } or
158
+ TImplicitWrapNode ( CfgNodes:: AstCfgNode n , Boolean shouldWrap ) { isMultiReturned ( n ) } or
153
159
TReturnNodeImpl ( CfgScope scope ) or
154
160
TProcessNode ( ProcessBlock process )
155
161
@@ -564,6 +570,14 @@ private module ReturnNodes {
564
570
or
565
571
result = this .getAChild ( ) .getAReturnedNode ( )
566
572
}
573
+
574
+ /** Holds if `n` may be returned multiples times. */
575
+ predicate mayBeMultiReturned ( CfgNode n ) {
576
+ n = this .getANode ( ) and
577
+ n .getASuccessor + ( ) = n
578
+ or
579
+ this .getAChild ( ) .mayBeMultiReturned ( n )
580
+ }
567
581
}
568
582
569
583
class ScriptBlockReturnContainer extends ReturnContainer , ScriptBlock {
@@ -618,14 +632,34 @@ private module ReturnNodes {
618
632
final override ReturnContainer getAChild ( ) { none ( ) }
619
633
}
620
634
621
- /** Holds if `n` is returned from the enclosing callable. */
622
- predicate isReturned ( CfgNodes:: AstCfgNode n ) {
623
- exists ( ReturnContainer container |
624
- container = n .getScope ( ) and
625
- n = container .getAReturnedNode ( )
635
+ private predicate isReturnedImpl ( CfgNodes:: AstCfgNode n , ReturnContainer container ) {
636
+ container = n .getScope ( ) and
637
+ n = container .getAReturnedNode ( )
638
+ }
639
+
640
+ /**
641
+ * Holds if `n` may be returned, and there are possibly
642
+ * more than one return value from the function.
643
+ */
644
+ predicate isMultiReturned ( CfgNodes:: AstCfgNode n ) {
645
+ exists ( ReturnContainer container | isReturnedImpl ( n , container ) |
646
+ strictcount ( container .getAReturnedNode ( ) ) > 1
647
+ or
648
+ container .mayBeMultiReturned ( n )
626
649
)
627
650
}
628
651
652
+ /**
653
+ * Holds if `n` may be returned.
654
+ */
655
+ predicate isReturned ( CfgNodes:: AstCfgNode n ) { isReturnedImpl ( n , _) }
656
+
657
+ /**
658
+ * Holds if `n` may be returned, and this is the only value that may be
659
+ * returned from the function.
660
+ */
661
+ predicate isUniqueReturned ( CfgNodes:: AstCfgNode n ) { isReturned ( n ) and not isMultiReturned ( n ) }
662
+
629
663
class NormalReturnNode extends ReturnNode instanceof ReturnNodeImpl {
630
664
final override NormalReturnKind getKind ( ) { any ( ) }
631
665
}
0 commit comments