Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8976ba5

Browse files
committed
Ruby: Add CallableNode, MethodNode, and accessors
1 parent ac4cac8 commit 8976ba5

2 files changed

Lines changed: 315 additions & 12 deletions

File tree

ruby/ql/lib/codeql/ruby/ast/Module.qll

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,18 +77,46 @@ class Module extends TModule {
7777
/**
7878
* Gets a singleton method on this module, either declared as a singleton method
7979
* or an instance method on a singleton class.
80+
*
81+
* Does not take inheritance into account.
8082
*/
81-
MethodBase getASingletonMethod() {
83+
MethodBase getAnOwnSingletonMethod() {
8284
result.(SingletonMethod).getObject() = this.getAnImmediateReferenceBase()
8385
or
8486
result = this.getASingletonClass().getAMethod().(Method)
8587
}
8688

89+
/**
90+
* Gets an instance method named `name` declared in this module.
91+
*
92+
* Does not take inheritance into account.
93+
*/
94+
Method getOwnInstanceMethod(string name) { result = this.getADeclaration().getMethod(name) }
95+
96+
/**
97+
* Gets an instance method declared in this module.
98+
*
99+
* Does not take inheritance into account.
100+
*/
101+
Method getAnOwnInstanceMethod() { result = this.getADeclaration().getMethod(_) }
102+
103+
/**
104+
* Gets the instance method named `name` available in this module, including methods inherited
105+
* from ancestors.
106+
*/
107+
Method getInstanceMethod(string name) { result = lookupMethod(this, name) }
108+
109+
/**
110+
* Gets an instance method available in this module, including methods inherited
111+
* from ancestors.
112+
*/
113+
Method getAnInstanceMethod() { result = lookupMethod(this, _) }
114+
87115
/** Gets a constant or `self` access that refers to this module. */
88116
Expr getAnImmediateReference() {
89117
result = this.getAnImmediateReferenceBase()
90118
or
91-
result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getASingletonMethod()
119+
result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getAnOwnSingletonMethod()
92120
}
93121
}
94122

ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll

Lines changed: 285 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -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

202234
cached
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`. */
216268
ExprNode 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

Comments
 (0)