@@ -115,6 +115,11 @@ module Node {
115115 */
116116 ExprCfgNode asExpr ( ) { none ( ) }
117117
118+ /**
119+ * Gets the pattern that corresponds to this node, if any.
120+ */
121+ PatCfgNode asPat ( ) { none ( ) }
122+
118123 /** Gets the enclosing callable. */
119124 DataFlowCallable getEnclosingCallable ( ) { result = TCfgScope ( this .getCfgScope ( ) ) }
120125
@@ -177,8 +182,7 @@ module Node {
177182
178183 PatNode ( ) { this = TPatNode ( n ) }
179184
180- /** Gets the `PatCfgNode` in the CFG that this node corresponds to. */
181- PatCfgNode getPat ( ) { result = n }
185+ override PatCfgNode asPat ( ) { result = n }
182186 }
183187
184188 abstract class ParameterNode extends AstCfgFlowNode { }
@@ -333,18 +337,137 @@ module LocalFlow {
333337 nodeFrom .( Node:: AstCfgFlowNode ) .getCfgNode ( ) =
334338 nodeTo .( Node:: SsaNode ) .getDefinitionExt ( ) .( Ssa:: WriteDefinition ) .getControlFlowNode ( )
335339 or
336- nodeFrom .( Node:: PositionalParameterNode ) .getParameter ( ) .getPat ( ) =
337- nodeTo .( Node:: PatNode ) .getPat ( )
340+ nodeFrom .( Node:: PositionalParameterNode ) .getParameter ( ) .getPat ( ) = nodeTo .asPat ( )
338341 or
339342 SsaFlow:: localFlowStep ( _, nodeFrom , nodeTo , _)
340343 or
341344 exists ( AssignmentExprCfgNode a |
342345 a .getRhs ( ) = nodeFrom .getCfgNode ( ) and
343346 a .getLhs ( ) = nodeTo .getCfgNode ( )
344347 )
348+ or
349+ exists ( MatchExprCfgNode match |
350+ nodeFrom .asExpr ( ) = match .getExpr ( ) and
351+ nodeTo .asPat ( ) = match .getArmPat ( _)
352+ )
345353 }
346354}
347355
356+ abstract class Content extends TContent {
357+ abstract string toString ( ) ;
358+ }
359+
360+ abstract private class VariantContent extends Content {
361+ string name ;
362+
363+ bindingset [ this , name]
364+ VariantContent ( ) { exists ( name ) }
365+ }
366+
367+ private class VariantTupleContent extends VariantContent , TVariantTupleContent {
368+ private CrateOriginOption crate ;
369+ private string path ;
370+ private int i ;
371+
372+ VariantTupleContent ( ) { this = TVariantTupleContent ( crate , path , name , i ) }
373+
374+ final override string toString ( ) {
375+ if exists ( TVariantTupleContent ( crate , path , name , 1 ) )
376+ then result = name + "(" + i + ")"
377+ else result = name
378+ }
379+ }
380+
381+ abstract class ContentSet extends TContentSet {
382+ /** Gets a textual representation of this element. */
383+ abstract string toString ( ) ;
384+
385+ /** Gets a content that may be stored into when storing into this set. */
386+ abstract Content getAStoreContent ( ) ;
387+
388+ /** Gets a content that may be read from when reading from this set. */
389+ abstract Content getAReadContent ( ) ;
390+ }
391+
392+ private class SingletonContentSet extends ContentSet , TSingletonContentSet {
393+ private Content c ;
394+
395+ SingletonContentSet ( ) { this = TSingletonContentSet ( c ) }
396+
397+ Content getContent ( ) { result = c }
398+
399+ override string toString ( ) { result = c .toString ( ) }
400+
401+ override Content getAStoreContent ( ) { result = c }
402+
403+ override Content getAReadContent ( ) { result = c }
404+ }
405+
406+ private import codeql.util.Option
407+
408+ private class CrateOrigin extends string {
409+ CrateOrigin ( ) {
410+ this = [ any ( Item i ) .getCrateOrigin ( ) , any ( Resolvable r ) .getResolvedCrateOrigin ( ) ]
411+ }
412+ }
413+
414+ private class CrateOriginOption = Option< CrateOrigin > :: Option ;
415+
416+ pragma [ nomagic]
417+ private predicate hasExtendedCanonicalPath ( Item i , CrateOriginOption crate , string path ) {
418+ path = i .getExtendedCanonicalPath ( ) and
419+ (
420+ crate .asSome ( ) = i .getCrateOrigin ( )
421+ or
422+ crate .isNone ( ) and
423+ not i .hasCrateOrigin ( )
424+ )
425+ }
426+
427+ pragma [ nomagic]
428+ private predicate resolvesExtendedCanonicalPath ( Resolvable r , CrateOriginOption crate , string path ) {
429+ path = r .getResolvedPath ( ) and
430+ (
431+ crate .asSome ( ) = r .getResolvedCrateOrigin ( )
432+ or
433+ crate .isNone ( ) and
434+ not r .hasResolvedCrateOrigin ( )
435+ )
436+ }
437+
438+ pragma [ nomagic]
439+ private predicate callResolvesExtendedCanonicalPath (
440+ CallExprBase call , CrateOriginOption crate , string path
441+ ) {
442+ exists ( Resolvable r | resolvesExtendedCanonicalPath ( r , crate , path ) |
443+ r = call .( MethodCallExpr )
444+ or
445+ r = call .( CallExpr ) .getExpr ( ) .( PathExpr ) .getPath ( )
446+ )
447+ }
448+
449+ /** Holds if qualified path `p` resolves to variant `c`. */
450+ private predicate pathResolvesToVariant ( Path p , VariantContent c , int i ) {
451+ exists ( CrateOriginOption crate , string path |
452+ resolvesExtendedCanonicalPath ( p .getQualifier ( ) , crate , path ) and
453+ c = TVariantTupleContent ( crate , path , p .getPart ( ) .getNameRef ( ) .getText ( ) , i )
454+ )
455+ or
456+ // TODO: Remove once library types are extracted
457+ not p .hasQualifier ( ) and
458+ c = TVariantTupleContent ( _, "crate::std::option::Option" , p .getPart ( ) .getNameRef ( ) .getText ( ) , i )
459+ }
460+
461+ /** Holds if `ce` constructs an enum value of type `c`. */
462+ private predicate variantConstructor ( CallExpr ce , VariantContent c , int i ) {
463+ pathResolvesToVariant ( ce .getExpr ( ) .( PathExpr ) .getPath ( ) , c , i )
464+ }
465+
466+ /** Holds if `p` destructs an enum value of type `c`. */
467+ private predicate variantDestructor ( TupleStructPat p , VariantContent c , int i ) {
468+ pathResolvesToVariant ( p .getPath ( ) , c , i )
469+ }
470+
348471private class DataFlowCallableAlias = DataFlowCallable ;
349472
350473private class ReturnKindAlias = ReturnKind ;
@@ -353,6 +476,10 @@ private class DataFlowCallAlias = DataFlowCall;
353476
354477private class ParameterPositionAlias = ParameterPosition ;
355478
479+ private class ContentAlias = Content ;
480+
481+ private class ContentSetAlias = ContentSet ;
482+
356483module RustDataFlow implements InputSig< Location > {
357484 /**
358485 * An element, viewed as a node in a data flow graph. Either an expression
@@ -399,55 +526,11 @@ module RustDataFlow implements InputSig<Location> {
399526
400527 final class ReturnKind = ReturnKindAlias ;
401528
402- private import codeql.util.Option
403-
404- private class CrateOrigin extends string {
405- CrateOrigin ( ) {
406- this = [ any ( Item i ) .getCrateOrigin ( ) , any ( Resolvable r ) .getResolvedCrateOrigin ( ) ]
407- }
408- }
409-
410- private class CrateOriginOption = Option< CrateOrigin > :: Option ;
411-
412- pragma [ nomagic]
413- private predicate hasExtendedCanonicalPath (
414- DataFlowCallable c , CrateOriginOption crate , string path
415- ) {
416- exists ( Item i |
417- i = c .asCfgScope ( ) and
418- path = i .getExtendedCanonicalPath ( )
419- |
420- crate .asSome ( ) = i .getCrateOrigin ( )
421- or
422- crate .isNone ( ) and
423- not i .hasCrateOrigin ( )
424- )
425- }
426-
427- pragma [ nomagic]
428- private predicate resolvesExtendedCanonicalPath (
429- DataFlowCall c , CrateOriginOption crate , string path
430- ) {
431- exists ( Resolvable r |
432- path = r .getResolvedPath ( ) and
433- (
434- r = c .asMethodCallExprCfgNode ( ) .getExpr ( )
435- or
436- r = c .asCallExprCfgNode ( ) .getExpr ( ) .( PathExprCfgNode ) .getPath ( )
437- )
438- |
439- crate .asSome ( ) = r .getResolvedCrateOrigin ( )
440- or
441- crate .isNone ( ) and
442- not r .hasResolvedCrateOrigin ( )
443- )
444- }
445-
446529 /** Gets a viable implementation of the target of the given `Call`. */
447530 DataFlowCallable viableCallable ( DataFlowCall call ) {
448531 exists ( string path , CrateOriginOption crate |
449- hasExtendedCanonicalPath ( result , crate , path ) and
450- resolvesExtendedCanonicalPath ( call , crate , path )
532+ hasExtendedCanonicalPath ( result . asCfgScope ( ) , crate , path ) and
533+ callResolvesExtendedCanonicalPath ( call . asCallBaseExprCfgNode ( ) . getExpr ( ) , crate , path )
451534 )
452535 }
453536
@@ -469,24 +552,15 @@ module RustDataFlow implements InputSig<Location> {
469552
470553 predicate typeStrongerThan ( DataFlowType t1 , DataFlowType t2 ) { none ( ) }
471554
472- final class Content = Void ;
555+ class Content = ContentAlias ;
473556
474- predicate forceHighPrecision ( Content c ) { none ( ) }
475-
476- class ContentSet extends TContentSet {
477- /** Gets a textual representation of this element. */
478- string toString ( ) { result = "ContentSet" }
479-
480- /** Gets a content that may be stored into when storing into this set. */
481- Content getAStoreContent ( ) { none ( ) }
557+ class ContentSet = ContentSetAlias ;
482558
483- /** Gets a content that may be read from when reading from this set. */
484- Content getAReadContent ( ) { none ( ) }
485- }
559+ predicate forceHighPrecision ( Content c ) { none ( ) }
486560
487- final class ContentApprox = Void ;
561+ final class ContentApprox = Content ; // todo
488562
489- ContentApprox getContentApprox ( Content c ) { any ( ) }
563+ ContentApprox getContentApprox ( Content c ) { result = c }
490564
491565 class ParameterPosition = ParameterPositionAlias ;
492566
@@ -519,14 +593,31 @@ module RustDataFlow implements InputSig<Location> {
519593 * `node1` references an object with a content `c.getAReadContent()` whose
520594 * value ends up in `node2`.
521595 */
522- predicate readStep ( Node node1 , ContentSet c , Node node2 ) { none ( ) }
596+ predicate readStep ( Node node1 , ContentSet c , Node node2 ) {
597+ node1 .asPat ( ) =
598+ any ( TupleStructPatCfgNode pat , int i |
599+ variantDestructor ( pat .getPat ( ) , c .( SingletonContentSet ) .getContent ( ) , i ) and
600+ node2 .asPat ( ) = pat .getField ( i )
601+ |
602+ pat
603+ )
604+ }
523605
524606 /**
525607 * Holds if data can flow from `node1` to `node2` via a store into `c`. Thus,
526608 * `node2` references an object with a content `c.getAStoreContent()` that
527609 * contains the value of `node1`.
528610 */
529- predicate storeStep ( Node node1 , ContentSet c , Node node2 ) { none ( ) }
611+ predicate storeStep ( Node node1 , ContentSet c , Node node2 ) {
612+ // todo: use post-update
613+ node2 .asExpr ( ) =
614+ any ( CallExprCfgNode call , int i |
615+ variantConstructor ( call .getCallExpr ( ) , c .( SingletonContentSet ) .getContent ( ) , i ) and
616+ node1 .asExpr ( ) = call .getArgument ( i )
617+ |
618+ call
619+ )
620+ }
530621
531622 /**
532623 * Holds if values stored inside content `c` are cleared at node `n`. For example,
@@ -593,8 +684,6 @@ module RustDataFlow implements InputSig<Location> {
593684 class DataFlowSecondLevelScope = Void ;
594685}
595686
596- final class ContentSet = RustDataFlow:: ContentSet ;
597-
598687import MakeImpl< Location , RustDataFlow >
599688
600689/** A collection of cached types and predicates to be evaluated in the same stage. */
@@ -612,14 +701,6 @@ private module Cached {
612701 cached
613702 newtype TDataFlowCall = TCall ( CallExprBaseCfgNode c )
614703
615- cached
616- newtype TOptionalContentSet =
617- TAnyElementContent ( ) or
618- TAnyContent ( )
619-
620- cached
621- class TContentSet = TAnyElementContent or TAnyContent ;
622-
623704 cached
624705 newtype TDataFlowCallable = TCfgScope ( CfgScope scope )
625706
@@ -635,6 +716,27 @@ private module Cached {
635716 i in [ 0 .. max ( [ any ( ParamList l ) .getNumberOfParams ( ) , any ( ArgList l ) .getNumberOfArgs ( ) ] ) - 1 ]
636717 } or
637718 TSelfParameterPosition ( )
719+
720+ cached
721+ newtype TContent =
722+ TVariantTupleContent ( CrateOriginOption crate , string path , string name , int i ) {
723+ exists ( Enum e , Variant v |
724+ hasExtendedCanonicalPath ( e , crate , path ) and
725+ v = e .getVariantList ( ) .getAVariant ( ) and
726+ name = v .getName ( ) .getText ( ) and
727+ i in [ 0 .. v .getFieldList ( ) .( TupleFieldList ) .getNumberOfFields ( ) - 1 ]
728+ )
729+ or
730+ // TODO: Remove once library types are extracted
731+ crate .isNone ( ) and
732+ path = "crate::std::option::Option" and
733+ name = "Some" and
734+ i = 0
735+ }
736+
737+ // todo: add TVariantRecordContent
738+ cached
739+ newtype TContentSet = TSingletonContentSet ( Content c )
638740}
639741
640742import Cached
0 commit comments