@@ -40,6 +40,14 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
4040 override DataFlowCallable getEnclosingCallable ( ) { result = TDataFlowFunc ( n .getScope ( ) ) }
4141}
4242
43+ private class PatternNodeImpl extends PatternNode , NodeImpl {
44+ override Location getLocationImpl ( ) { result = pattern .getLocation ( ) }
45+
46+ override string toStringImpl ( ) { result = pattern .toString ( ) }
47+
48+ override DataFlowCallable getEnclosingCallable ( ) { result = TDataFlowFunc ( n .getScope ( ) ) }
49+ }
50+
4351private class SsaDefinitionNodeImpl extends SsaDefinitionNode , NodeImpl {
4452 override Location getLocationImpl ( ) { result = def .getLocation ( ) }
4553
@@ -63,6 +71,7 @@ private module Cached {
6371 cached
6472 newtype TNode =
6573 TExprNode ( CfgNode n , Expr e ) { hasExprNode ( n , e ) } or
74+ TPatternNode ( CfgNode n , Pattern p ) { hasPatternNode ( n , p ) } or
6675 TSsaDefinitionNode ( Ssa:: Definition def ) or
6776 TInoutReturnNode ( ParamDecl param ) { modifiableParam ( param ) } or
6877 TSummaryNode ( FlowSummary:: SummarizedCallable c , FlowSummaryImpl:: Private:: SummaryNodeState state ) {
@@ -175,6 +184,20 @@ private module Cached {
175184 nodeFrom .asExpr ( ) = ie .getBranch ( _)
176185 )
177186 or
187+ // flow from Expr to Pattern
188+ exists ( Expr e , Pattern p |
189+ nodeFrom .asExpr ( ) = e and
190+ nodeTo .asPattern ( ) = p and
191+ p .getImmediateMatchingExpr ( ) = e
192+ )
193+ or
194+ // flow from Pattern to an identity-preserving sub-Pattern:
195+ nodeTo .asPattern ( ) =
196+ [
197+ nodeFrom .asPattern ( ) .( IsPattern ) .getSubPattern ( ) ,
198+ nodeFrom .asPattern ( ) .( TypedPattern ) .getSubPattern ( )
199+ ]
200+ or
178201 // flow through a flow summary (extension of `SummaryModelCsv`)
179202 FlowSummaryImpl:: Private:: Steps:: summaryLocalStep ( nodeFrom , nodeTo , true )
180203 }
@@ -201,7 +224,8 @@ private module Cached {
201224 cached
202225 newtype TContent =
203226 TFieldContent ( FieldDecl f ) or
204- TTupleContent ( int index ) { exists ( any ( TupleExpr te ) .getElement ( index ) ) }
227+ TTupleContent ( int index ) { exists ( any ( TupleExpr te ) .getElement ( index ) ) } or
228+ TEnumContent ( ParamDecl f ) { exists ( EnumElementDecl d | d .getAParam ( ) = f ) }
205229}
206230
207231/**
@@ -230,6 +254,11 @@ private predicate hasExprNode(CfgNode n, Expr e) {
230254 n .( PropertyObserverCfgNode ) .getAssignExpr ( ) = e
231255}
232256
257+ private predicate hasPatternNode ( PatternCfgNode n , Pattern p ) {
258+ n .getPattern ( ) = p and
259+ p = p .resolve ( ) // no need to turn hidden-AST patterns (`let`s, parens) into data flow nodes
260+ }
261+
233262import Cached
234263
235264/** Holds if `n` should be hidden from path explanations. */
@@ -558,6 +587,29 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
558587 c .isSingleton ( any ( Content:: TupleContent tc | tc .getIndex ( ) = tuple .getIndex ( ) ) )
559588 )
560589 or
590+ // creation of an enum `.variant(v1, v2)`
591+ exists ( EnumElementExpr enum , int pos |
592+ node1 .asExpr ( ) = enum .getArgument ( pos ) .getExpr ( ) and
593+ node2 .asExpr ( ) = enum and
594+ c .isSingleton ( any ( Content:: EnumContent ec | ec .getParam ( ) = enum .getElement ( ) .getParam ( pos ) ) )
595+ )
596+ or
597+ // creation of an optional via implicit conversion,
598+ // i.e. from `f(x)` where `x: T` into `f(.some(x))` where the context `f` expects a `T?`.
599+ exists ( InjectIntoOptionalExpr e |
600+ e .convertsFrom ( node1 .asExpr ( ) ) and
601+ node2 = node1 and // HACK: we should ideally have a separate Node case for the (hidden) InjectIntoOptionalExpr
602+ c instanceof OptionalSomeContentSet
603+ )
604+ or
605+ // creation of an optional by returning from a failable initializer (`init?`)
606+ exists ( ConstructorDecl init |
607+ node1 .asExpr ( ) .( CallExpr ) .getStaticTarget ( ) = init and
608+ node2 = node1 and // HACK: again, we should ideally have a separate Node case here, and not reuse the CallExpr
609+ c instanceof OptionalSomeContentSet and
610+ init .isFailable ( )
611+ )
612+ or
561613 FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 , c , node2 )
562614}
563615
@@ -578,6 +630,34 @@ predicate readStep(Node node1, ContentSet c, Node node2) {
578630 node2 .asExpr ( ) = tuple and
579631 c .isSingleton ( any ( Content:: TupleContent tc | tc .getIndex ( ) = tuple .getIndex ( ) ) )
580632 )
633+ or
634+ // read of an enum member via `case let .variant(v1, v2)` pattern matching
635+ exists ( EnumElementPattern enumPat , ParamDecl enumParam , Pattern subPat |
636+ node1 .asPattern ( ) = enumPat and
637+ node2 .asPattern ( ) = subPat and
638+ c .isSingleton ( any ( Content:: EnumContent ec | ec .getParam ( ) = enumParam ) )
639+ |
640+ exists ( int idx |
641+ enumPat .getElement ( ) .getParam ( idx ) = enumParam and
642+ enumPat .getSubPattern ( idx ) = subPat
643+ )
644+ )
645+ or
646+ // read of a tuple member via `case let (v1, v2)` pattern matching
647+ exists ( TuplePattern tupPat , int idx , Pattern subPat |
648+ node1 .asPattern ( ) = tupPat and
649+ node2 .asPattern ( ) = subPat and
650+ c .isSingleton ( any ( Content:: TupleContent tc | tc .getIndex ( ) = idx ) )
651+ |
652+ tupPat .getElement ( idx ) = subPat
653+ )
654+ or
655+ // read of an optional .some member via `let x: T = y: T?` pattern matching
656+ exists ( OptionalSomePattern pat |
657+ node1 .asPattern ( ) = pat and
658+ node2 .asPattern ( ) = pat .getSubPattern ( ) and
659+ c instanceof OptionalSomeContentSet
660+ )
581661}
582662
583663/**
@@ -595,6 +675,22 @@ predicate clearsContent(Node n, ContentSet c) {
595675 */
596676predicate expectsContent ( Node n , ContentSet c ) { none ( ) }
597677
678+ /**
679+ * The global singleton `Optional.some` content set.
680+ */
681+ private class OptionalSomeContentSet extends ContentSet {
682+ OptionalSomeContentSet ( ) {
683+ exists ( EnumDecl optional , EnumElementDecl some |
684+ some .getDeclaringDecl ( ) = optional and
685+ some .getName ( ) = "some" and
686+ optional .getName ( ) = "Optional" and
687+ optional .getModule ( ) .getName ( ) = "Swift"
688+ |
689+ this .isSingleton ( any ( Content:: EnumContent ec | ec .getParam ( ) = some .getParam ( 0 ) ) )
690+ )
691+ }
692+ }
693+
598694private newtype TDataFlowType = TODO_DataFlowType ( )
599695
600696class DataFlowType extends TDataFlowType {
0 commit comments