@@ -442,90 +442,103 @@ private DataFlow::LocalSourceNode trackModuleAccess(Module m) {
442
442
result = trackModuleAccess ( m , TypeTracker:: end ( ) )
443
443
}
444
444
445
- pragma [ nomagic]
446
- private DataFlow:: Node trackInstance ( Module tp , boolean exact , TypeTracker t ) {
447
- t .start ( ) and
448
- (
449
- result .asExpr ( ) .getExpr ( ) instanceof NilLiteral and
450
- tp = TResolved ( "NilClass" ) and
451
- exact = true
452
- or
453
- result .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isFalse ( ) and
454
- tp = TResolved ( "FalseClass" ) and
455
- exact = true
456
- or
457
- result .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isTrue ( ) and
458
- tp = TResolved ( "TrueClass" ) and
459
- exact = true
460
- or
461
- result .asExpr ( ) .getExpr ( ) instanceof IntegerLiteral and
462
- tp = TResolved ( "Integer" ) and
463
- exact = true
464
- or
465
- result .asExpr ( ) .getExpr ( ) instanceof FloatLiteral and
466
- tp = TResolved ( "Float" ) and
467
- exact = true
468
- or
469
- result .asExpr ( ) .getExpr ( ) instanceof RationalLiteral and
470
- tp = TResolved ( "Rational" ) and
471
- exact = true
472
- or
473
- result .asExpr ( ) .getExpr ( ) instanceof ComplexLiteral and
474
- tp = TResolved ( "Complex" ) and
475
- exact = true
476
- or
477
- result .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
478
- tp = TResolved ( "String" ) and
479
- exact = true
480
- or
481
- result .asExpr ( ) instanceof CfgNodes:: ExprNodes:: ArrayLiteralCfgNode and
482
- tp = TResolved ( "Array" ) and
483
- exact = true
484
- or
485
- result .asExpr ( ) instanceof CfgNodes:: ExprNodes:: HashLiteralCfgNode and
486
- tp = TResolved ( "Hash" ) and
487
- exact = true
488
- or
489
- result .asExpr ( ) .getExpr ( ) instanceof MethodBase and
490
- tp = TResolved ( "Symbol" ) and
491
- exact = true
492
- or
493
- result .asParameter ( ) instanceof BlockParameter and
494
- tp = TResolved ( "Proc" ) and
495
- exact = true
445
+ /** Holds if `n` is an instance of type `tp`. */
446
+ private predicate isInstance ( DataFlow:: Node n , Module tp , boolean exact ) {
447
+ n .asExpr ( ) .getExpr ( ) instanceof NilLiteral and
448
+ tp = TResolved ( "NilClass" ) and
449
+ exact = true
450
+ or
451
+ n .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isFalse ( ) and
452
+ tp = TResolved ( "FalseClass" ) and
453
+ exact = true
454
+ or
455
+ n .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isTrue ( ) and
456
+ tp = TResolved ( "TrueClass" ) and
457
+ exact = true
458
+ or
459
+ n .asExpr ( ) .getExpr ( ) instanceof IntegerLiteral and
460
+ tp = TResolved ( "Integer" ) and
461
+ exact = true
462
+ or
463
+ n .asExpr ( ) .getExpr ( ) instanceof FloatLiteral and
464
+ tp = TResolved ( "Float" ) and
465
+ exact = true
466
+ or
467
+ n .asExpr ( ) .getExpr ( ) instanceof RationalLiteral and
468
+ tp = TResolved ( "Rational" ) and
469
+ exact = true
470
+ or
471
+ n .asExpr ( ) .getExpr ( ) instanceof ComplexLiteral and
472
+ tp = TResolved ( "Complex" ) and
473
+ exact = true
474
+ or
475
+ n .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
476
+ tp = TResolved ( "String" ) and
477
+ exact = true
478
+ or
479
+ n .asExpr ( ) instanceof CfgNodes:: ExprNodes:: ArrayLiteralCfgNode and
480
+ tp = TResolved ( "Array" ) and
481
+ exact = true
482
+ or
483
+ n .asExpr ( ) instanceof CfgNodes:: ExprNodes:: HashLiteralCfgNode and
484
+ tp = TResolved ( "Hash" ) and
485
+ exact = true
486
+ or
487
+ n .asExpr ( ) .getExpr ( ) instanceof MethodBase and
488
+ tp = TResolved ( "Symbol" ) and
489
+ exact = true
490
+ or
491
+ n .asParameter ( ) instanceof BlockParameter and
492
+ tp = TResolved ( "Proc" ) and
493
+ exact = true
494
+ or
495
+ n .asExpr ( ) .getExpr ( ) instanceof Lambda and
496
+ tp = TResolved ( "Proc" ) and
497
+ exact = true
498
+ or
499
+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode |
500
+ flowsToMethodCall ( call , sourceNode , "new" ) and
501
+ exact = true and
502
+ n .asExpr ( ) = call
503
+ |
504
+ // `C.new`
505
+ sourceNode = trackModuleAccess ( tp )
496
506
or
497
- result .asExpr ( ) .getExpr ( ) instanceof Lambda and
498
- tp = TResolved ( "Proc" ) and
499
- exact = true
507
+ // `self.new` inside a module
508
+ selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , tp )
500
509
or
501
- exists ( CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode |
502
- flowsToMethodCall ( call , sourceNode , "new" ) and
503
- exact = true and
504
- result .asExpr ( ) = call
505
- |
506
- // `C.new`
507
- sourceNode = trackModuleAccess ( tp )
508
- or
509
- // `self.new` inside a module
510
- selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , tp )
510
+ // `self.new` inside a singleton method
511
+ selfInMethod ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , any ( SingletonMethod sm ) , tp )
512
+ )
513
+ or
514
+ // `self` reference in method or top-level (but not in module or singleton method,
515
+ // where instance methods cannot be called; only singleton methods)
516
+ n =
517
+ any ( SsaSelfDefinitionNode self |
518
+ exists ( MethodBase m |
519
+ selfInMethod ( self .getVariable ( ) , m , tp ) and
520
+ not m instanceof SingletonMethod and
521
+ if m .getEnclosingModule ( ) instanceof Toplevel then exact = true else exact = false
522
+ )
511
523
or
512
- // ` self.new` inside a singleton method
513
- selfInMethod ( sourceNode . ( SsaSelfDefinitionNode ) . getVariable ( ) , any ( SingletonMethod sm ) , tp )
524
+ selfInToplevel ( self .getVariable ( ) , tp ) and
525
+ exact = true
514
526
)
515
- or
516
- // `self` reference in method or top-level (but not in module or singleton method,
517
- // where instance methods cannot be called; only singleton methods)
518
- result =
519
- any ( SsaSelfDefinitionNode self |
520
- exists ( MethodBase m |
521
- selfInMethod ( self .getVariable ( ) , m , tp ) and
522
- not m instanceof SingletonMethod and
523
- if m .getEnclosingModule ( ) instanceof Toplevel then exact = true else exact = false
524
- )
525
- or
526
- selfInToplevel ( self .getVariable ( ) , tp ) and
527
- exact = true
528
- )
527
+ or
528
+ // `in C => c then c.foo`
529
+ asModulePattern ( n , tp ) and
530
+ exact = false
531
+ or
532
+ // `case object when C then object.foo`
533
+ hasAdjacentTypeCheckedReads ( _, _, n .asExpr ( ) , tp ) and
534
+ exact = false
535
+ }
536
+
537
+ pragma [ nomagic]
538
+ private DataFlow:: Node trackInstance ( Module tp , boolean exact , TypeTracker t ) {
539
+ t .start ( ) and
540
+ (
541
+ isInstance ( result , tp , exact )
529
542
or
530
543
exists ( Module m |
531
544
( if m .isClass ( ) then tp = TResolved ( "Class" ) else tp = TResolved ( "Module" ) ) and
@@ -540,14 +553,6 @@ private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
540
553
// needed for e.g. `self.puts`
541
554
selfInMethod ( result .( SsaSelfDefinitionNode ) .getVariable ( ) , any ( SingletonMethod sm ) , m )
542
555
)
543
- or
544
- // `in C => c then c.foo`
545
- asModulePattern ( result , tp ) and
546
- exact = false
547
- or
548
- // `case object when C then object.foo`
549
- hasAdjacentTypeCheckedReads ( _, _, result .asExpr ( ) , tp ) and
550
- exact = false
551
556
)
552
557
or
553
558
exists ( TypeTracker t2 , StepSummary summary |
@@ -782,19 +787,85 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
782
787
result = trackSingletonMethodOnInstance ( method , name , TypeTracker:: end ( ) )
783
788
}
784
789
790
+ /** Same as `isInstance`, but includes local must-flow through SSA definitions. */
791
+ private predicate isInstanceLocalMustFlow ( DataFlow:: Node n , Module tp , boolean exact ) {
792
+ isInstance ( n , tp , exact )
793
+ or
794
+ exists ( DataFlow:: Node mid | isInstanceLocalMustFlow ( mid , tp , exact ) |
795
+ n .asExpr ( ) = mid .( SsaDefinitionNode ) .getDefinition ( ) .getARead ( )
796
+ or
797
+ n .( SsaDefinitionNode ) .getDefinition ( ) .( Ssa:: WriteDefinition ) .assigns ( mid .asExpr ( ) )
798
+ )
799
+ }
800
+
801
+ /**
802
+ * Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, and
803
+ * the receiver of `call` is a parameter access, where the corresponding argument
804
+ * of `ctx` has type `tp`.
805
+ *
806
+ * `name` is the name of the method being called by `call`, and `exact` is pertaining
807
+ * to the type of the argument.
808
+ */
809
+ pragma [ nomagic]
810
+ private predicate mayBenefitFromCallContext0 (
811
+ CfgNodes:: ExprNodes:: CallCfgNode ctx , CfgNodes:: ExprNodes:: CallCfgNode call , Callable encl ,
812
+ Module tp , boolean exact , string name
813
+ ) {
814
+ exists (
815
+ ParameterNodeImpl p , SsaDefinitionNode ssaNode , ParameterPosition ppos , ArgumentPosition apos ,
816
+ ArgumentNode arg
817
+ |
818
+ // the receiver of `call` references `p`
819
+ ssaNode = trackInstance ( _, _) and
820
+ LocalFlow:: localFlowSsaParamInput ( p , ssaNode ) and
821
+ flowsToMethodCall ( pragma [ only_bind_into ] ( call ) , pragma [ only_bind_into ] ( ssaNode ) ,
822
+ pragma [ only_bind_into ] ( name ) ) and
823
+ // `p` is a parameter of `encl`,
824
+ encl = call .getScope ( ) and
825
+ p .isParameterOf ( TCfgScope ( encl ) , ppos ) and
826
+ // `ctx` targets `encl`
827
+ getTarget ( ctx ) = encl and
828
+ // `arg` is the argument for `p` in the call `ctx`
829
+ arg .sourceArgumentOf ( ctx , apos ) and
830
+ parameterMatch ( ppos , apos ) and
831
+ // `arg` has a relevant instance type
832
+ isInstanceLocalMustFlow ( arg , pragma [ only_bind_out ] ( tp ) , exact ) and
833
+ exists ( lookupMethod ( tp , pragma [ only_bind_into ] ( name ) ) )
834
+ )
835
+ }
836
+
785
837
/**
786
838
* Holds if the set of viable implementations that can be called by `call`
787
839
* might be improved by knowing the call context. This is the case if the
788
- * qualifier accesses a parameter of the enclosing callable `c` (including
840
+ * receiver accesses a parameter of the enclosing callable `c` (including
789
841
* the implicit `self` parameter).
790
842
*/
791
- predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) { none ( ) }
843
+ predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) {
844
+ mayBenefitFromCallContext0 ( _, call .asCall ( ) , c .asCallable ( ) , _, _, _)
845
+ }
792
846
793
847
/**
794
848
* Gets a viable dispatch target of `call` in the context `ctx`. This is
795
849
* restricted to those `call`s for which a context might make a difference.
796
850
*/
797
- DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) { none ( ) }
851
+ pragma [ nomagic]
852
+ DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
853
+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call0 , Callable res |
854
+ call0 = call .asCall ( ) and
855
+ res = result .asCallable ( ) and
856
+ res = getTarget ( call0 ) and // make sure to not include e.g. private methods
857
+ exists ( Module tp , Module m , boolean exact , string name |
858
+ res = lookupMethod ( tp , name ) and
859
+ mayBenefitFromCallContext0 ( ctx .asCall ( ) , pragma [ only_bind_into ] ( call0 ) , _,
860
+ pragma [ only_bind_into ] ( m ) , exact , pragma [ only_bind_into ] ( name ) )
861
+ |
862
+ tp = m
863
+ or
864
+ exact = false and
865
+ tp .getSuperClass + ( ) = m
866
+ )
867
+ )
868
+ }
798
869
799
870
predicate exprNodeReturnedFrom = exprNodeReturnedFromCached / 2 ;
800
871
0 commit comments