@@ -54,6 +54,32 @@ class Node extends TNode {
5454 * Gets a data flow node to which data may flow from this node in one local step.
5555 */
5656 Node getASuccessor ( ) { localFlowStep ( this , result ) }
57+
58+ /** Gets the constant value of this expression, if any. */
59+ ConstantValue getConstantValue ( ) { result = asExpr ( ) .getExpr ( ) .getConstantValue ( ) }
60+
61+ /**
62+ * Gets the callable corresponding to this block, lambda expression, or call to `proc` or `lambda`.
63+ *
64+ * For example, gets the callable in each of the following cases:
65+ * ```rb
66+ * { |x| x } # block expression
67+ * ->(x) { x } # lambda expression
68+ * proc { |x| x } # call to 'proc'
69+ * lambda { |x| x } # call to 'lambda'
70+ * ```
71+ */
72+ pragma [ noinline]
73+ CallableNode asCallable ( ) {
74+ result = this
75+ or
76+ exists ( CallNode call |
77+ call .getReceiver ( ) .asExpr ( ) .getExpr ( ) instanceof SelfVariableAccess and
78+ call .getMethodName ( ) = [ "proc" , "lambda" ] and
79+ call .getBlock ( ) = result and
80+ this = call
81+ )
82+ }
5783}
5884
5985/** A data-flow node corresponding to a call in the control-flow graph. */
@@ -181,6 +207,12 @@ class LocalSourceNode extends Node {
181207 */
182208 pragma [ inline]
183209 Node getALocalUse ( ) { hasLocalSource ( result , this ) }
210+
211+ /** Gets a method call where this node flows to the receiver. */
212+ CallNode getAMethodCall ( ) { Cached:: hasMethodCall ( this , result , _) }
213+
214+ /** Gets a call to a method named `name`, where this node flows to the receiver. */
215+ CallNode getAMethodCall ( string name ) { Cached:: hasMethodCall ( this , result , name ) }
184216}
185217
186218/**
@@ -200,18 +232,38 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
200232}
201233
202234cached
203- private predicate hasLocalSource ( Node sink , Node source ) {
204- // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
205- // recursive case, so instead we check it explicitly here.
206- source = sink and
207- source instanceof LocalSourceNode
208- or
209- exists ( Node mid |
210- hasLocalSource ( mid , source ) and
211- localFlowStepTypeTracker ( mid , sink )
212- )
235+ private module Cached {
236+ cached
237+ predicate hasLocalSource ( Node sink , Node source ) {
238+ // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
239+ // recursive case, so instead we check it explicitly here.
240+ source = sink and
241+ source instanceof LocalSourceNode
242+ or
243+ exists ( Node mid |
244+ hasLocalSource ( mid , source ) and
245+ localFlowStepTypeTracker ( mid , sink )
246+ )
247+ }
248+
249+ cached
250+ predicate hasMethodCall ( LocalSourceNode source , CallNode call , string name ) {
251+ source .flowsTo ( call .getReceiver ( ) ) and
252+ call .getMethodName ( ) = name
253+ }
254+
255+ cached
256+ predicate hasYieldCall ( BlockParameterNode block , CallNode yield ) {
257+ exists ( MethodBase method , YieldCall call |
258+ block .getMethod ( ) = method and
259+ call .getEnclosingMethod ( ) = method and
260+ yield .asExpr ( ) .getExpr ( ) = call
261+ )
262+ }
213263}
214264
265+ private import Cached
266+
215267/** Gets a node corresponding to expression `e`. */
216268ExprNode exprNode ( CfgNodes:: ExprCfgNode e ) { result .getExprNode ( ) = e }
217269
@@ -604,8 +656,231 @@ class ModuleNode instanceof Module {
604656 /** Gets the location of this module. */
605657 final Location getLocation ( ) { result = super .getLocation ( ) }
606658
659+ /**
660+ * Gets `self` in a declaration of this module.
661+ *
662+ * This only gets `self` at the module level, not inside any (singleton) method.
663+ */
664+ LocalSourceNode getModuleLevelSelf ( ) {
665+ result .( SsaDefinitionNode ) .getVariable ( ) = super .getADeclaration ( ) .getModuleSelfVariable ( )
666+ }
667+
668+ /**
669+ * Gets `self` in the module declaration or in one of its singleton methods.
670+ *
671+ * Does not take inheritance into account.
672+ */
673+ LocalSourceNode getAnOwnModuleSelf ( ) {
674+ result = this .getModuleLevelSelf ( )
675+ or
676+ result = this .getAnOwnSingletonMethod ( ) .getSelfParameter ( )
677+ }
678+
679+ /**
680+ * Gets a call to method `name` on `self` in the module-level scope of this module.
681+ *
682+ * For example,
683+ * ```rb
684+ * module M
685+ * include A # getAModuleLevelCall("include")
686+ * foo :bar # getAModuleLevelCall("foo")
687+ * end
688+ * ```
689+ */
690+ CallNode getAModuleLevelCall ( string name ) {
691+ result = this .getModuleLevelSelf ( ) .getAMethodCall ( name )
692+ }
693+
607694 /** Gets a constant or `self` variable that refers to this module. */
608695 LocalSourceNode getAnImmediateReference ( ) {
609696 result .asExpr ( ) .getExpr ( ) = super .getAnImmediateReference ( )
610697 }
698+
699+ /**
700+ * Gets a singleton method declared in this module (or in a singleton class
701+ * augmenting this module).
702+ *
703+ * Does not take inheritance into account.
704+ */
705+ MethodNode getAnOwnSingletonMethod ( ) { result .asMethod ( ) = super .getAnOwnSingletonMethod ( ) }
706+
707+ /**
708+ * Gets the singleton method named `name` declared in this module (or in a singleton class
709+ * augmenting this module).
710+ *
711+ * Does not take inheritance into account.
712+ */
713+ MethodNode getOwnSingletonMethod ( string name ) {
714+ result = this .getAnOwnSingletonMethod ( ) and
715+ result .getMethodName ( ) = name
716+ }
717+
718+ /**
719+ * Gets an instance method declared in this module.
720+ *
721+ * Does not take inheritance into account.
722+ */
723+ MethodNode getAnOwnInstanceMethod ( ) {
724+ result .asMethod ( ) = this .getADeclaration ( ) .getAMethod ( ) .( Method )
725+ }
726+
727+ /**
728+ * Gets an instance method named `name` declared in this module.
729+ *
730+ * Does not take inheritance into account.
731+ */
732+ MethodNode getOwnInstanceMethod ( string name ) {
733+ result = this .getAnOwnInstanceMethod ( ) and
734+ result .getMethodName ( ) = name
735+ }
736+
737+ /**
738+ * Gets the `self` parameter of an instance method declared in this module.
739+ *
740+ * Does not take inheritance into account.
741+ */
742+ ParameterNode getAnOwnInstanceSelf ( ) {
743+ result = TSelfParameterNode ( this .getAnOwnInstanceMethod ( ) .asMethod ( ) )
744+ }
745+
746+ /**
747+ * Gets the `self` parameter of an instance method available in this module,
748+ * including those inherited from ancestors.
749+ */
750+ ParameterNode getAnInstanceSelf ( ) {
751+ result = TSelfParameterNode ( this .getAnInstanceMethod ( ) .asMethod ( ) )
752+ }
753+
754+ private InstanceVariableAccess getAnOwnInstanceVariableAccess ( string name ) {
755+ exists ( InstanceVariable v |
756+ v .getDeclaringScope ( ) = this .getADeclaration ( ) and
757+ v .getName ( ) = name and
758+ result .getVariable ( ) = v
759+ )
760+ }
761+
762+ /**
763+ * Gets an access to the instance variable `name` in this module.
764+ *
765+ * Does not take inheritance into account.
766+ */
767+ LocalSourceNode getAnOwnInstanceVariableRead ( string name ) {
768+ result .asExpr ( ) .getExpr ( ) =
769+ this .getAnOwnInstanceVariableAccess ( name ) .( InstanceVariableReadAccess )
770+ }
771+
772+ /**
773+ * Gets the right-hand side of an assignment to the instance variable `name` in this module.
774+ *
775+ * Does not take inheritance into account.
776+ */
777+ Node getAnOwnInstanceVariableWriteValue ( string name ) {
778+ exists ( Assignment assignment |
779+ assignment .getLeftOperand ( ) = this .getAnOwnInstanceVariableAccess ( name ) and
780+ result .asExpr ( ) .getExpr ( ) = assignment .getRightOperand ( )
781+ )
782+ }
783+
784+ /**
785+ * Gets the instance method named `name` available in this module, including methods inherited
786+ * from ancestors.
787+ */
788+ MethodNode getInstanceMethod ( string name ) {
789+ result .asCallableAstNode ( ) = super .getInstanceMethod ( name )
790+ }
791+
792+ /**
793+ * Gets an instance method named available in this module, including methods inherited
794+ * from ancestors.
795+ */
796+ MethodNode getAnInstanceMethod ( ) { result = this .getInstanceMethod ( _) }
797+ }
798+
799+ /**
800+ * A representation of a run-time class.
801+ */
802+ class ClassNode extends ModuleNode {
803+ ClassNode ( ) { isClass ( ) }
804+ }
805+
806+ /**
807+ * A data flow node corresponding to a method, block, or lambda expression.
808+ */
809+ class CallableNode extends ExprNode {
810+ private Callable callable ;
811+
812+ CallableNode ( ) { this .asExpr ( ) .getExpr ( ) = callable }
813+
814+ /** Gets the underlying AST node as a `Callable`. */
815+ Callable asCallableAstNode ( ) { result = callable }
816+
817+ private ParameterPosition getParameterPosition ( ParameterNodeImpl node ) {
818+ node .isSourceParameterOf ( callable , result )
819+ }
820+
821+ /** Gets the `n`th positional parameter. */
822+ ParameterNode getParameter ( int n ) { getParameterPosition ( result ) .isPositional ( n ) }
823+
824+ /** Gets the keyword parameter of the given name. */
825+ ParameterNode getKeywordParameter ( string name ) { getParameterPosition ( result ) .isKeyword ( name ) }
826+
827+ /** Gets the `self` parameter of this callable, if any. */
828+ ParameterNode getSelfParameter ( ) { getParameterPosition ( result ) .isSelf ( ) }
829+
830+ /**
831+ * Gets the `hash-splat` parameter. This is a synthetic parameter holding
832+ * a hash object with entries for each keyword argument passed to the function.
833+ */
834+ ParameterNode getHashSplatParameter ( ) { getParameterPosition ( result ) .isHashSplat ( ) }
835+
836+ /**
837+ * Gets the block parameter of this method, if any.
838+ */
839+ ParameterNode getBlockParameter ( ) { getParameterPosition ( result ) .isBlock ( ) }
840+
841+ /**
842+ * Gets a `yield` in this method call or `.call` on the block parameter.
843+ */
844+ CallNode getABlockCall ( ) {
845+ hasYieldCall ( getBlockParameter ( ) , result )
846+ or
847+ result = getBlockParameter ( ) .getAMethodCall ( "call" )
848+ }
849+
850+ /**
851+ * Gets the canonical return node from this callable.
852+ *
853+ * Each callable has exactly one such node, and its location may not correspond
854+ * to any particular return site - consider using `getAReturningNode` to get nodes
855+ * whose locations correspond to return sites.
856+ */
857+ Node getReturn ( ) { result .( SynthReturnNode ) .getCfgScope ( ) = callable }
858+
859+ /**
860+ * Gets a data flow node whose value is about to be returned by this callable.
861+ */
862+ Node getAReturningNode ( ) { result = this .getReturn ( ) .( SynthReturnNode ) .getAnInput ( ) }
863+ }
864+
865+ /**
866+ * A data flow node corresponding to a method (possibly a singleton method).
867+ */
868+ class MethodNode extends CallableNode {
869+ MethodNode ( ) { super .asCallableAstNode ( ) instanceof MethodBase }
870+
871+ /** Gets the underlying AST node for this method. */
872+ MethodBase asMethod ( ) { result = this .asCallableAstNode ( ) }
873+
874+ /** Gets the name of this method. */
875+ string getMethodName ( ) { result = asMethod ( ) .getName ( ) }
876+ }
877+
878+ /**
879+ * A data flow node corresponding to a block argument.
880+ */
881+ class BlockNode extends CallableNode {
882+ BlockNode ( ) { super .asCallableAstNode ( ) instanceof Block }
883+
884+ /** Gets the underlying AST node for this block. */
885+ Block asBlock ( ) { result = this .asCallableAstNode ( ) }
611886}
0 commit comments