Skip to content

Commit cee7088

Browse files
authored
Merge pull request #12964 from hvitved/ruby/remove-synth-returns
Ruby: Remove canonical return nodes
2 parents fd8112f + 48ac3e5 commit cee7088

File tree

13 files changed

+1046
-773
lines changed

13 files changed

+1046
-773
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll

Lines changed: 155 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ private import DataFlowPrivate
3838
private import FlowSummaryImpl as FlowSummaryImpl
3939
private import FlowSummaryImplSpecific as FlowSummaryImplSpecific
4040
private import semmle.python.internal.CachedStages
41+
private import semmle.python.dataflow.new.internal.TypeTracker::CallGraphConstruction as CallGraphConstruction
4142

4243
newtype TParameterPosition =
4344
/** Used for `self` in methods, and `cls` in classmethods. */
@@ -464,137 +465,138 @@ private predicate ignoreForCallGraph(File f) {
464465
f.getAbsolutePath().matches("%/site-packages/sympy/%")
465466
}
466467

467-
/**
468-
* Gets a reference to the function `func`.
469-
*/
470-
private TypeTrackingNode functionTracker(TypeTracker t, Function func) {
471-
not ignoreForCallGraph(result.getLocation().getFile()) and
472-
t.start() and
473-
(
474-
result.asExpr() = func.getDefinition()
468+
private module TrackFunctionInput implements CallGraphConstruction::Simple::InputSig {
469+
class State = Function;
470+
471+
predicate start(Node start, Function func) {
472+
start.asExpr() = func.getDefinition()
475473
or
476474
// when a function is decorated, it's the result of the (last) decorator call that
477475
// is used
478-
result.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
479-
)
480-
or
481-
not ignoreForCallGraph(result.getLocation().getFile()) and
482-
exists(TypeTracker t2 | result = functionTracker(t2, func).track(t2, t))
476+
start.asExpr() = func.getDefinition().(FunctionExpr).getADecoratorCall()
477+
}
478+
479+
predicate filter(Node n) { ignoreForCallGraph(n.getLocation().getFile()) }
483480
}
484481

485482
/**
486483
* Gets a reference to the function `func`.
487484
*/
488-
Node functionTracker(Function func) { functionTracker(TypeTracker::end(), func).flowsTo(result) }
485+
Node functionTracker(Function func) {
486+
CallGraphConstruction::Simple::Make<TrackFunctionInput>::track(func)
487+
.(LocalSourceNode)
488+
.flowsTo(result)
489+
}
489490

490-
/**
491-
* Gets a reference to the class `cls`.
492-
*/
493-
private TypeTrackingNode classTracker(TypeTracker t, Class cls) {
494-
not ignoreForCallGraph(result.getLocation().getFile()) and
495-
t.start() and
496-
(
497-
result.asExpr() = cls.getParent()
491+
private module TrackClassInput implements CallGraphConstruction::Simple::InputSig {
492+
class State = Class;
493+
494+
predicate start(Node start, Class cls) {
495+
start.asExpr() = cls.getParent()
498496
or
499497
// when a class is decorated, it's the result of the (last) decorator call that
500498
// is used
501-
result.asExpr() = cls.getParent().getADecoratorCall()
499+
start.asExpr() = cls.getParent().getADecoratorCall()
502500
or
503501
// `type(obj)`, where obj is an instance of this class
504-
result = getTypeCall() and
505-
result.(CallCfgNode).getArg(0) = classInstanceTracker(cls)
506-
)
507-
or
508-
not ignoreForCallGraph(result.getLocation().getFile()) and
509-
exists(TypeTracker t2 | result = classTracker(t2, cls).track(t2, t)) and
510-
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
502+
start = getTypeCall() and
503+
start.(CallCfgNode).getArg(0) = classInstanceTracker(cls)
504+
}
505+
506+
predicate filter(Node n) {
507+
ignoreForCallGraph(n.getLocation().getFile())
508+
or
509+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
510+
}
511511
}
512512

513513
/**
514514
* Gets a reference to the class `cls`.
515515
*/
516-
Node classTracker(Class cls) { classTracker(TypeTracker::end(), cls).flowsTo(result) }
516+
Node classTracker(Class cls) {
517+
CallGraphConstruction::Simple::Make<TrackClassInput>::track(cls).(LocalSourceNode).flowsTo(result)
518+
}
517519

518-
/**
519-
* Gets a reference to an instance of the class `cls`.
520-
*/
521-
private TypeTrackingNode classInstanceTracker(TypeTracker t, Class cls) {
522-
not ignoreForCallGraph(result.getLocation().getFile()) and
523-
t.start() and
524-
resolveClassCall(result.(CallCfgNode).asCfgNode(), cls)
525-
or
526-
// result of `super().__new__` as used in a `__new__` method implementation
527-
not ignoreForCallGraph(result.getLocation().getFile()) and
528-
t.start() and
529-
exists(Class classUsedInSuper |
530-
fromSuperNewCall(result.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
531-
classUsedInSuper = getADirectSuperclass*(cls)
532-
)
533-
or
534-
not ignoreForCallGraph(result.getLocation().getFile()) and
535-
exists(TypeTracker t2 | result = classInstanceTracker(t2, cls).track(t2, t)) and
536-
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
520+
private module TrackClassInstanceInput implements CallGraphConstruction::Simple::InputSig {
521+
class State = Class;
522+
523+
predicate start(Node start, Class cls) {
524+
resolveClassCall(start.(CallCfgNode).asCfgNode(), cls)
525+
or
526+
// result of `super().__new__` as used in a `__new__` method implementation
527+
exists(Class classUsedInSuper |
528+
fromSuperNewCall(start.(CallCfgNode).asCfgNode(), classUsedInSuper, _, _) and
529+
classUsedInSuper = getADirectSuperclass*(cls)
530+
)
531+
}
532+
533+
predicate filter(Node n) {
534+
ignoreForCallGraph(n.getLocation().getFile())
535+
or
536+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
537+
}
537538
}
538539

539540
/**
540541
* Gets a reference to an instance of the class `cls`.
541542
*/
542543
Node classInstanceTracker(Class cls) {
543-
classInstanceTracker(TypeTracker::end(), cls).flowsTo(result)
544+
CallGraphConstruction::Simple::Make<TrackClassInstanceInput>::track(cls)
545+
.(LocalSourceNode)
546+
.flowsTo(result)
544547
}
545548

546-
/**
547-
* Gets a reference to the `self` argument of a method on class `classWithMethod`.
548-
* The method cannot be a `staticmethod` or `classmethod`.
549-
*/
550-
private TypeTrackingNode selfTracker(TypeTracker t, Class classWithMethod) {
551-
not ignoreForCallGraph(result.getLocation().getFile()) and
552-
t.start() and
553-
exists(Function func |
554-
func = classWithMethod.getAMethod() and
555-
not isStaticmethod(func) and
556-
not isClassmethod(func)
557-
|
558-
result.asExpr() = func.getArg(0)
559-
)
560-
or
561-
not ignoreForCallGraph(result.getLocation().getFile()) and
562-
exists(TypeTracker t2 | result = selfTracker(t2, classWithMethod).track(t2, t)) and
563-
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
549+
private module TrackSelfInput implements CallGraphConstruction::Simple::InputSig {
550+
class State = Class;
551+
552+
predicate start(Node start, Class classWithMethod) {
553+
exists(Function func |
554+
func = classWithMethod.getAMethod() and
555+
not isStaticmethod(func) and
556+
not isClassmethod(func)
557+
|
558+
start.asExpr() = func.getArg(0)
559+
)
560+
}
561+
562+
predicate filter(Node n) {
563+
ignoreForCallGraph(n.getLocation().getFile())
564+
or
565+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
566+
}
564567
}
565568

566569
/**
567570
* Gets a reference to the `self` argument of a method on class `classWithMethod`.
568571
* The method cannot be a `staticmethod` or `classmethod`.
569572
*/
570573
Node selfTracker(Class classWithMethod) {
571-
selfTracker(TypeTracker::end(), classWithMethod).flowsTo(result)
574+
CallGraphConstruction::Simple::Make<TrackSelfInput>::track(classWithMethod)
575+
.(LocalSourceNode)
576+
.flowsTo(result)
572577
}
573578

574-
/**
575-
* Gets a reference to the enclosing class `classWithMethod` from within one of its
576-
* methods, either through the `cls` argument from a `classmethod` or from `type(self)`
577-
* from a normal method.
578-
*/
579-
private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod) {
580-
not ignoreForCallGraph(result.getLocation().getFile()) and
581-
t.start() and
582-
(
579+
private module TrackClsArgumentInput implements CallGraphConstruction::Simple::InputSig {
580+
class State = Class;
581+
582+
predicate start(Node start, Class classWithMethod) {
583583
exists(Function func |
584584
func = classWithMethod.getAMethod() and
585585
isClassmethod(func)
586586
|
587-
result.asExpr() = func.getArg(0)
587+
start.asExpr() = func.getArg(0)
588588
)
589589
or
590590
// type(self)
591-
result = getTypeCall() and
592-
result.(CallCfgNode).getArg(0) = selfTracker(classWithMethod)
593-
)
594-
or
595-
not ignoreForCallGraph(result.getLocation().getFile()) and
596-
exists(TypeTracker t2 | result = clsArgumentTracker(t2, classWithMethod).track(t2, t)) and
597-
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
591+
start = getTypeCall() and
592+
start.(CallCfgNode).getArg(0) = selfTracker(classWithMethod)
593+
}
594+
595+
predicate filter(Node n) {
596+
ignoreForCallGraph(n.getLocation().getFile())
597+
or
598+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
599+
}
598600
}
599601

600602
/**
@@ -603,60 +605,72 @@ private TypeTrackingNode clsArgumentTracker(TypeTracker t, Class classWithMethod
603605
* from a normal method.
604606
*/
605607
Node clsArgumentTracker(Class classWithMethod) {
606-
clsArgumentTracker(TypeTracker::end(), classWithMethod).flowsTo(result)
608+
CallGraphConstruction::Simple::Make<TrackClsArgumentInput>::track(classWithMethod)
609+
.(LocalSourceNode)
610+
.flowsTo(result)
607611
}
608612

609-
/**
610-
* Gets a reference to the result of calling `super` without any argument, where the
611-
* call happened in the method `func` (either a method or a classmethod).
612-
*/
613-
private TypeTrackingNode superCallNoArgumentTracker(TypeTracker t, Function func) {
614-
not ignoreForCallGraph(result.getLocation().getFile()) and
615-
t.start() and
616-
not isStaticmethod(func) and
617-
exists(CallCfgNode call | result = call |
618-
call = getSuperCall() and
619-
not exists(call.getArg(_)) and
620-
call.getScope() = func
621-
)
622-
or
623-
not ignoreForCallGraph(result.getLocation().getFile()) and
624-
exists(TypeTracker t2 | result = superCallNoArgumentTracker(t2, func).track(t2, t)) and
625-
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
613+
private module TrackSuperCallNoArgumentInput implements CallGraphConstruction::Simple::InputSig {
614+
class State = Function;
615+
616+
predicate start(Node start, Function func) {
617+
not isStaticmethod(func) and
618+
exists(CallCfgNode call | start = call |
619+
call = getSuperCall() and
620+
not exists(call.getArg(_)) and
621+
call.getScope() = func
622+
)
623+
}
624+
625+
predicate filter(Node n) {
626+
ignoreForCallGraph(n.getLocation().getFile())
627+
or
628+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
629+
}
626630
}
627631

628632
/**
629633
* Gets a reference to the result of calling `super` without any argument, where the
630634
* call happened in the method `func` (either a method or a classmethod).
631635
*/
632636
Node superCallNoArgumentTracker(Function func) {
633-
superCallNoArgumentTracker(TypeTracker::end(), func).flowsTo(result)
637+
CallGraphConstruction::Simple::Make<TrackSuperCallNoArgumentInput>::track(func)
638+
.(LocalSourceNode)
639+
.flowsTo(result)
634640
}
635641

636-
/**
637-
* Gets a reference to the result of calling `super` with 2 arguments, where the
638-
* first is a reference to the class `cls`, and the second argument is `obj`.
639-
*/
640-
private TypeTrackingNode superCallTwoArgumentTracker(TypeTracker t, Class cls, Node obj) {
641-
not ignoreForCallGraph(result.getLocation().getFile()) and
642-
t.start() and
643-
exists(CallCfgNode call | result = call |
642+
private module TrackSuperCallTwoArgumentInput implements CallGraphConstruction::Simple::InputSig {
643+
additional predicate superCall(CallCfgNode call, Class cls, Node obj) {
644644
call = getSuperCall() and
645645
call.getArg(0) = classTracker(cls) and
646646
call.getArg(1) = obj
647-
)
648-
or
649-
not ignoreForCallGraph(result.getLocation().getFile()) and
650-
exists(TypeTracker t2 | result = superCallTwoArgumentTracker(t2, cls, obj).track(t2, t)) and
651-
not result.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
647+
}
648+
649+
class State = CallCfgNode;
650+
651+
predicate start(Node start, CallCfgNode call) {
652+
superCall(call, _, _) and
653+
start = call
654+
}
655+
656+
predicate filter(Node n) {
657+
ignoreForCallGraph(n.getLocation().getFile())
658+
or
659+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
660+
}
652661
}
653662

654663
/**
655664
* Gets a reference to the result of calling `super` with 2 arguments, where the
656665
* first is a reference to the class `cls`, and the second argument is `obj`.
657666
*/
658667
Node superCallTwoArgumentTracker(Class cls, Node obj) {
659-
superCallTwoArgumentTracker(TypeTracker::end(), cls, obj).flowsTo(result)
668+
exists(CallCfgNode call |
669+
TrackSuperCallTwoArgumentInput::superCall(call, cls, obj) and
670+
CallGraphConstruction::Simple::Make<TrackSuperCallTwoArgumentInput>::track(call)
671+
.(LocalSourceNode)
672+
.flowsTo(result)
673+
)
660674
}
661675

662676
// =============================================================================
@@ -800,20 +814,30 @@ Function findFunctionAccordingToMroKnownStartingClass(Class startingClass, strin
800814
// =============================================================================
801815
// attribute trackers
802816
// =============================================================================
803-
/** Gets a reference to the attribute read `attr` */
804-
private TypeTrackingNode attrReadTracker(TypeTracker t, AttrRead attr) {
805-
t.start() and
806-
result = attr and
807-
attr.getObject() in [
808-
classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_),
809-
superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _)
810-
]
811-
or
812-
exists(TypeTracker t2 | result = attrReadTracker(t2, attr).track(t2, t))
817+
private module TrackAttrReadInput implements CallGraphConstruction::Simple::InputSig {
818+
class State = AttrRead;
819+
820+
predicate start(Node start, AttrRead attr) {
821+
start = attr and
822+
attr.getObject() in [
823+
classTracker(_), classInstanceTracker(_), selfTracker(_), clsArgumentTracker(_),
824+
superCallNoArgumentTracker(_), superCallTwoArgumentTracker(_, _)
825+
]
826+
}
827+
828+
predicate filter(Node n) {
829+
ignoreForCallGraph(n.getLocation().getFile())
830+
or
831+
n.(ParameterNodeImpl).isParameterOf(_, any(ParameterPosition pp | pp.isSelf()))
832+
}
813833
}
814834

815835
/** Gets a reference to the attribute read `attr` */
816-
Node attrReadTracker(AttrRead attr) { attrReadTracker(TypeTracker::end(), attr).flowsTo(result) }
836+
Node attrReadTracker(AttrRead attr) {
837+
CallGraphConstruction::Simple::Make<TrackAttrReadInput>::track(attr)
838+
.(LocalSourceNode)
839+
.flowsTo(result)
840+
}
817841

818842
// =============================================================================
819843
// call and argument resolution

0 commit comments

Comments
 (0)