@@ -277,6 +277,20 @@ private predicate hasAdjacentTypeCheckedReads(
277277 )
278278}
279279
280+ /** Holds if `call` may resolve to the returned source-code method. */
281+ private DataFlowCallable viableSourceCallable ( DataFlowCall call ) {
282+ result = TCfgScope ( getTarget ( call .asCall ( ) ) ) and
283+ not call .asCall ( ) .getExpr ( ) instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
284+ }
285+
286+ /** Holds if `call` may resolve to the returned summarized library method. */
287+ private DataFlowCallable viableLibraryCallable ( DataFlowCall call ) {
288+ exists ( LibraryCallable callable |
289+ result = TLibraryCallable ( callable ) and
290+ call .asCall ( ) .getExpr ( ) = callable .getACall ( )
291+ )
292+ }
293+
280294cached
281295private module Cached {
282296 cached
@@ -366,13 +380,9 @@ private module Cached {
366380 /** Gets a viable run-time target for the call `call`. */
367381 cached
368382 DataFlowCallable viableCallable ( DataFlowCall call ) {
369- result = TCfgScope ( getTarget ( call .asCall ( ) ) ) and
370- not call .asCall ( ) .getExpr ( ) instanceof YieldCall // handled by `lambdaCreation`/`lambdaCall`
383+ result = viableSourceCallable ( call )
371384 or
372- exists ( LibraryCallable callable |
373- result = TLibraryCallable ( callable ) and
374- call .asCall ( ) .getExpr ( ) = callable .getACall ( )
375- )
385+ result = viableLibraryCallable ( call )
376386 }
377387
378388 cached
@@ -438,90 +448,103 @@ private DataFlow::LocalSourceNode trackModuleAccess(Module m) {
438448 result = trackModuleAccess ( m , TypeTracker:: end ( ) )
439449}
440450
441- pragma [ nomagic]
442- private DataFlow:: Node trackInstance ( Module tp , boolean exact , TypeTracker t ) {
443- t .start ( ) and
444- (
445- result .asExpr ( ) .getExpr ( ) instanceof NilLiteral and
446- tp = TResolved ( "NilClass" ) and
447- exact = true
448- or
449- result .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isFalse ( ) and
450- tp = TResolved ( "FalseClass" ) and
451- exact = true
452- or
453- result .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isTrue ( ) and
454- tp = TResolved ( "TrueClass" ) and
455- exact = true
456- or
457- result .asExpr ( ) .getExpr ( ) instanceof IntegerLiteral and
458- tp = TResolved ( "Integer" ) and
459- exact = true
460- or
461- result .asExpr ( ) .getExpr ( ) instanceof FloatLiteral and
462- tp = TResolved ( "Float" ) and
463- exact = true
464- or
465- result .asExpr ( ) .getExpr ( ) instanceof RationalLiteral and
466- tp = TResolved ( "Rational" ) and
467- exact = true
468- or
469- result .asExpr ( ) .getExpr ( ) instanceof ComplexLiteral and
470- tp = TResolved ( "Complex" ) and
471- exact = true
472- or
473- result .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
474- tp = TResolved ( "String" ) and
475- exact = true
476- or
477- result .asExpr ( ) instanceof CfgNodes:: ExprNodes:: ArrayLiteralCfgNode and
478- tp = TResolved ( "Array" ) and
479- exact = true
480- or
481- result .asExpr ( ) instanceof CfgNodes:: ExprNodes:: HashLiteralCfgNode and
482- tp = TResolved ( "Hash" ) and
483- exact = true
484- or
485- result .asExpr ( ) .getExpr ( ) instanceof MethodBase and
486- tp = TResolved ( "Symbol" ) and
487- exact = true
488- or
489- result .asParameter ( ) instanceof BlockParameter and
490- tp = TResolved ( "Proc" ) and
491- exact = true
451+ /** Holds if `n` is an instance of type `tp`. */
452+ private predicate isInstance ( DataFlow:: Node n , Module tp , boolean exact ) {
453+ n .asExpr ( ) .getExpr ( ) instanceof NilLiteral and
454+ tp = TResolved ( "NilClass" ) and
455+ exact = true
456+ or
457+ n .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isFalse ( ) and
458+ tp = TResolved ( "FalseClass" ) and
459+ exact = true
460+ or
461+ n .asExpr ( ) .getExpr ( ) .( BooleanLiteral ) .isTrue ( ) and
462+ tp = TResolved ( "TrueClass" ) and
463+ exact = true
464+ or
465+ n .asExpr ( ) .getExpr ( ) instanceof IntegerLiteral and
466+ tp = TResolved ( "Integer" ) and
467+ exact = true
468+ or
469+ n .asExpr ( ) .getExpr ( ) instanceof FloatLiteral and
470+ tp = TResolved ( "Float" ) and
471+ exact = true
472+ or
473+ n .asExpr ( ) .getExpr ( ) instanceof RationalLiteral and
474+ tp = TResolved ( "Rational" ) and
475+ exact = true
476+ or
477+ n .asExpr ( ) .getExpr ( ) instanceof ComplexLiteral and
478+ tp = TResolved ( "Complex" ) and
479+ exact = true
480+ or
481+ n .asExpr ( ) .getExpr ( ) instanceof StringlikeLiteral and
482+ tp = TResolved ( "String" ) and
483+ exact = true
484+ or
485+ n .asExpr ( ) instanceof CfgNodes:: ExprNodes:: ArrayLiteralCfgNode and
486+ tp = TResolved ( "Array" ) and
487+ exact = true
488+ or
489+ n .asExpr ( ) instanceof CfgNodes:: ExprNodes:: HashLiteralCfgNode and
490+ tp = TResolved ( "Hash" ) and
491+ exact = true
492+ or
493+ n .asExpr ( ) .getExpr ( ) instanceof MethodBase and
494+ tp = TResolved ( "Symbol" ) and
495+ exact = true
496+ or
497+ n .asParameter ( ) instanceof BlockParameter and
498+ tp = TResolved ( "Proc" ) and
499+ exact = true
500+ or
501+ n .asExpr ( ) .getExpr ( ) instanceof Lambda and
502+ tp = TResolved ( "Proc" ) and
503+ exact = true
504+ or
505+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode |
506+ flowsToMethodCall ( call , sourceNode , "new" ) and
507+ exact = true and
508+ n .asExpr ( ) = call
509+ |
510+ // `C.new`
511+ sourceNode = trackModuleAccess ( tp )
492512 or
493- result .asExpr ( ) .getExpr ( ) instanceof Lambda and
494- tp = TResolved ( "Proc" ) and
495- exact = true
513+ // `self.new` inside a module
514+ selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , tp )
496515 or
497- exists ( CfgNodes:: ExprNodes:: CallCfgNode call , DataFlow:: LocalSourceNode sourceNode |
498- flowsToMethodCall ( call , sourceNode , "new" ) and
499- exact = true and
500- result .asExpr ( ) = call
501- |
502- // `C.new`
503- sourceNode = trackModuleAccess ( tp )
504- or
505- // `self.new` inside a module
506- selfInModule ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , tp )
516+ // `self.new` inside a singleton method
517+ selfInMethod ( sourceNode .( SsaSelfDefinitionNode ) .getVariable ( ) , any ( SingletonMethod sm ) , tp )
518+ )
519+ or
520+ // `self` reference in method or top-level (but not in module or singleton method,
521+ // where instance methods cannot be called; only singleton methods)
522+ n =
523+ any ( SsaSelfDefinitionNode self |
524+ exists ( MethodBase m |
525+ selfInMethod ( self .getVariable ( ) , m , tp ) and
526+ not m instanceof SingletonMethod and
527+ if m .getEnclosingModule ( ) instanceof Toplevel then exact = true else exact = false
528+ )
507529 or
508- // ` self.new` inside a singleton method
509- selfInMethod ( sourceNode . ( SsaSelfDefinitionNode ) . getVariable ( ) , any ( SingletonMethod sm ) , tp )
530+ selfInToplevel ( self .getVariable ( ) , tp ) and
531+ exact = true
510532 )
511- or
512- // `self` reference in method or top-level (but not in module or singleton method,
513- // where instance methods cannot be called; only singleton methods)
514- result =
515- any ( SsaSelfDefinitionNode self |
516- exists ( MethodBase m |
517- selfInMethod ( self .getVariable ( ) , m , tp ) and
518- not m instanceof SingletonMethod and
519- if m .getEnclosingModule ( ) instanceof Toplevel then exact = true else exact = false
520- )
521- or
522- selfInToplevel ( self .getVariable ( ) , tp ) and
523- exact = true
524- )
533+ or
534+ // `in C => c then c.foo`
535+ asModulePattern ( n , tp ) and
536+ exact = false
537+ or
538+ // `case object when C then object.foo`
539+ hasAdjacentTypeCheckedReads ( _, _, n .asExpr ( ) , tp ) and
540+ exact = false
541+ }
542+
543+ pragma [ nomagic]
544+ private DataFlow:: Node trackInstance ( Module tp , boolean exact , TypeTracker t ) {
545+ t .start ( ) and
546+ (
547+ isInstance ( result , tp , exact )
525548 or
526549 exists ( Module m |
527550 ( if m .isClass ( ) then tp = TResolved ( "Class" ) else tp = TResolved ( "Module" ) ) and
@@ -536,14 +559,6 @@ private DataFlow::Node trackInstance(Module tp, boolean exact, TypeTracker t) {
536559 // needed for e.g. `self.puts`
537560 selfInMethod ( result .( SsaSelfDefinitionNode ) .getVariable ( ) , any ( SingletonMethod sm ) , m )
538561 )
539- or
540- // `in C => c then c.foo`
541- asModulePattern ( result , tp ) and
542- exact = false
543- or
544- // `case object when C then object.foo`
545- hasAdjacentTypeCheckedReads ( _, _, result .asExpr ( ) , tp ) and
546- exact = false
547562 )
548563 or
549564 exists ( TypeTracker t2 , StepSummary summary |
@@ -778,19 +793,112 @@ private DataFlow::Node trackSingletonMethodOnInstance(MethodBase method, string
778793 result = trackSingletonMethodOnInstance ( method , name , TypeTracker:: end ( ) )
779794}
780795
796+ /** Same as `isInstance`, but includes local must-flow through SSA definitions. */
797+ private predicate isInstanceLocalMustFlow ( DataFlow:: Node n , Module tp , boolean exact ) {
798+ isInstance ( n , tp , exact )
799+ or
800+ exists ( DataFlow:: Node mid | isInstanceLocalMustFlow ( mid , tp , exact ) |
801+ n .asExpr ( ) = mid .( SsaDefinitionNode ) .getDefinition ( ) .getARead ( )
802+ or
803+ n .( SsaDefinitionNode ) .getDefinition ( ) .( Ssa:: WriteDefinition ) .assigns ( mid .asExpr ( ) )
804+ )
805+ }
806+
807+ /**
808+ * Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, the receiver
809+ * of `call` is a parameter access, where the corresponding argument of `ctx` is `arg`.
810+ *
811+ * `name` is the name of the method being called by `call`.
812+ */
813+ pragma [ nomagic]
814+ private predicate mayBenefitFromCallContext0 (
815+ CfgNodes:: ExprNodes:: CallCfgNode ctx , ArgumentNode arg , CfgNodes:: ExprNodes:: CallCfgNode call ,
816+ Callable encl , string name
817+ ) {
818+ exists (
819+ ParameterNodeImpl p , SsaDefinitionNode ssaNode , ParameterPosition ppos , ArgumentPosition apos
820+ |
821+ // the receiver of `call` references `p`
822+ ssaNode = trackInstance ( _, _) and
823+ LocalFlow:: localFlowSsaParamInput ( p , ssaNode ) and
824+ flowsToMethodCall ( pragma [ only_bind_into ] ( call ) , pragma [ only_bind_into ] ( ssaNode ) ,
825+ pragma [ only_bind_into ] ( name ) ) and
826+ // `p` is a parameter of `encl`,
827+ encl = call .getScope ( ) and
828+ p .isParameterOf ( TCfgScope ( encl ) , ppos ) and
829+ // `ctx` targets `encl`
830+ getTarget ( ctx ) = encl and
831+ // `arg` is the argument for `p` in the call `ctx`
832+ arg .sourceArgumentOf ( ctx , apos ) and
833+ parameterMatch ( ppos , apos )
834+ )
835+ }
836+
837+ /**
838+ * Holds if `ctx` targets `encl`, which is the enclosing callable of `call`, and
839+ * the receiver of `call` is a parameter access, where the corresponding argument
840+ * of `ctx` has type `tp`.
841+ *
842+ * `name` is the name of the method being called by `call`, and `exact` is pertaining
843+ * to the type of the argument.
844+ */
845+ pragma [ nomagic]
846+ private predicate mayBenefitFromCallContext1 (
847+ CfgNodes:: ExprNodes:: CallCfgNode ctx , CfgNodes:: ExprNodes:: CallCfgNode call , Callable encl ,
848+ Module tp , boolean exact , string name
849+ ) {
850+ exists ( ArgumentNode arg |
851+ mayBenefitFromCallContext0 ( ctx , arg , call , encl , name ) and
852+ // `arg` has a relevant instance type
853+ isInstanceLocalMustFlow ( arg , pragma [ only_bind_out ] ( tp ) , exact ) and
854+ exists ( lookupMethod ( tp , pragma [ only_bind_into ] ( name ) ) )
855+ )
856+ }
857+
781858/**
782859 * Holds if the set of viable implementations that can be called by `call`
783860 * might be improved by knowing the call context. This is the case if the
784- * qualifier accesses a parameter of the enclosing callable `c` (including
861+ * receiver accesses a parameter of the enclosing callable `c` (including
785862 * the implicit `self` parameter).
786863 */
787- predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) { none ( ) }
864+ predicate mayBenefitFromCallContext ( DataFlowCall call , DataFlowCallable c ) {
865+ mayBenefitFromCallContext1 ( _, call .asCall ( ) , c .asCallable ( ) , _, _, _)
866+ }
788867
789868/**
790869 * Gets a viable dispatch target of `call` in the context `ctx`. This is
791870 * restricted to those `call`s for which a context might make a difference.
792871 */
793- DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) { none ( ) }
872+ pragma [ nomagic]
873+ DataFlowCallable viableImplInCallContext ( DataFlowCall call , DataFlowCall ctx ) {
874+ // `ctx` can provide a potentially better type bound
875+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call0 , Callable res |
876+ call0 = call .asCall ( ) and
877+ res = result .asCallable ( ) and
878+ res = getTarget ( call0 ) and // make sure to not include e.g. private methods
879+ exists ( Module tp , Module m , boolean exact , string name |
880+ res = lookupMethod ( tp , name ) and
881+ mayBenefitFromCallContext1 ( ctx .asCall ( ) , pragma [ only_bind_into ] ( call0 ) , _,
882+ pragma [ only_bind_into ] ( m ) , exact , pragma [ only_bind_into ] ( name ) )
883+ |
884+ tp = m
885+ or
886+ exact = false and
887+ tp .getSuperClass + ( ) = m
888+ )
889+ )
890+ or
891+ // `ctx` cannot provide a type bound
892+ exists ( ArgumentNode arg |
893+ mayBenefitFromCallContext0 ( ctx .asCall ( ) , arg , call .asCall ( ) , _, _) and
894+ not isInstanceLocalMustFlow ( arg , _, _) and
895+ result = viableSourceCallable ( call )
896+ )
897+ or
898+ // library calls should always be able to resolve
899+ mayBenefitFromCallContext0 ( ctx .asCall ( ) , _, call .asCall ( ) , _, _) and
900+ result = viableLibraryCallable ( call )
901+ }
794902
795903predicate exprNodeReturnedFrom = exprNodeReturnedFromCached / 2 ;
796904
0 commit comments