mirror of
https://github.com/github/codeql.git
synced 2026-05-05 05:35:13 +02:00
Merge remote-tracking branch 'origin/main' into ruby/clear-text-logging
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
name: codeql/ruby-consistency-queries
|
||||
version: 0.0.1
|
||||
groups: [ruby, test, consistency-queries]
|
||||
dependencies:
|
||||
codeql/ruby-all: 0.0.1
|
||||
|
||||
codeql/ruby-all: "*"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
## 0.0.9
|
||||
|
||||
## 0.0.8
|
||||
|
||||
## 0.0.7
|
||||
|
||||
## 0.0.6
|
||||
|
||||
1
ruby/ql/lib/change-notes/released/0.0.8.md
Normal file
1
ruby/ql/lib/change-notes/released/0.0.8.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.0.8
|
||||
1
ruby/ql/lib/change-notes/released/0.0.9.md
Normal file
1
ruby/ql/lib/change-notes/released/0.0.9.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.0.9
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.7
|
||||
lastReleaseVersion: 0.0.9
|
||||
|
||||
@@ -126,7 +126,7 @@ class AstNode extends TAstNode {
|
||||
|
||||
/** A Ruby source file */
|
||||
class RubyFile extends File {
|
||||
RubyFile() { ruby_ast_node_parent(_, this, _) }
|
||||
RubyFile() { ruby_ast_node_info(_, this, _, _) }
|
||||
|
||||
/** Gets a token in this file. */
|
||||
private Ruby::Token getAToken() { result.getLocation().getFile() = this }
|
||||
|
||||
@@ -11,6 +11,8 @@ import codeql.ruby.DataFlow
|
||||
import codeql.ruby.typetracking.TypeTracker
|
||||
import codeql.ruby.ast.internal.Module
|
||||
private import codeql.ruby.controlflow.CfgNodes
|
||||
private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate
|
||||
private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch
|
||||
|
||||
/**
|
||||
* Provides classes and predicates for working with APIs used in a database.
|
||||
@@ -43,6 +45,16 @@ module API {
|
||||
*/
|
||||
DataFlow::LocalSourceNode getAnImmediateUse() { Impl::use(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node corresponding the value flowing into this API component.
|
||||
*/
|
||||
DataFlow::Node getARhs() { Impl::def(this, result) }
|
||||
|
||||
/**
|
||||
* Gets a data-flow node that may interprocedurally flow to the value escaping into this API component.
|
||||
*/
|
||||
DataFlow::Node getAValueReachingRhs() { result = Impl::trackDefNode(this.getARhs()) }
|
||||
|
||||
/**
|
||||
* Gets a call to a method on the receiver represented by this API component.
|
||||
*/
|
||||
@@ -88,12 +100,37 @@ module API {
|
||||
* This predicate may have multiple results when there are multiple constructor calls invoking this API component.
|
||||
* Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls.
|
||||
*/
|
||||
Node getInstance() { result = this.getASuccessor(Label::instance()) }
|
||||
Node getInstance() { result = this.getASubclass().getReturn("new") }
|
||||
|
||||
/**
|
||||
* Gets a node representing a call to `method` on the receiver represented by this node.
|
||||
*/
|
||||
Node getMethod(string method) {
|
||||
result = this.getASubclass().getASuccessor(Label::method(method))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node representing the result of this call.
|
||||
*/
|
||||
Node getReturn() { result = this.getASuccessor(Label::return()) }
|
||||
|
||||
/**
|
||||
* Gets a node representing the result of calling a method on the receiver represented by this node.
|
||||
*/
|
||||
Node getReturn(string method) { result = this.getASuccessor(Label::return(method)) }
|
||||
Node getReturn(string method) { result = this.getMethod(method).getReturn() }
|
||||
|
||||
/** Gets an API node representing the `n`th positional parameter. */
|
||||
pragma[nomagic]
|
||||
Node getParameter(int n) { result = this.getASuccessor(Label::parameter(n)) }
|
||||
|
||||
/** Gets an API node representing the given keyword parameter. */
|
||||
pragma[nomagic]
|
||||
Node getKeywordParameter(string name) {
|
||||
result = this.getASuccessor(Label::keywordParameter(name))
|
||||
}
|
||||
|
||||
/** Gets an API node representing the block parameter. */
|
||||
Node getBlock() { result = this.getASuccessor(Label::blockParameter()) }
|
||||
|
||||
/**
|
||||
* Gets a `new` call to the function represented by this API component.
|
||||
@@ -101,9 +138,26 @@ module API {
|
||||
DataFlow::ExprNode getAnInstantiation() { result = this.getInstance().getAnImmediateUse() }
|
||||
|
||||
/**
|
||||
* Gets a node representing a subclass of the class represented by this node.
|
||||
* Gets a node representing a (direct or indirect) subclass of the class represented by this node.
|
||||
* ```rb
|
||||
* class A; end
|
||||
* class B < A; end
|
||||
* class C < B; end
|
||||
* ```
|
||||
* In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`.
|
||||
*/
|
||||
Node getASubclass() { result = this.getASuccessor(Label::subclass()) }
|
||||
Node getASubclass() { result = this.getAnImmediateSubclass*() }
|
||||
|
||||
/**
|
||||
* Gets a node representing a direct subclass of the class represented by this node.
|
||||
* ```rb
|
||||
* class A; end
|
||||
* class B < A; end
|
||||
* class C < B; end
|
||||
* ```
|
||||
* In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only.
|
||||
*/
|
||||
Node getAnImmediateSubclass() { result = this.getASuccessor(Label::subclass()) }
|
||||
|
||||
/**
|
||||
* Gets a string representation of the lexicographically least among all shortest access paths
|
||||
@@ -117,13 +171,13 @@ module API {
|
||||
* Gets a node such that there is an edge in the API graph between this node and the other
|
||||
* one, and that edge is labeled with `lbl`.
|
||||
*/
|
||||
Node getASuccessor(string lbl) { Impl::edge(this, lbl, result) }
|
||||
Node getASuccessor(Label::ApiLabel lbl) { Impl::edge(this, lbl, result) }
|
||||
|
||||
/**
|
||||
* Gets a node such that there is an edge in the API graph between that other node and
|
||||
* this one, and that edge is labeled with `lbl`
|
||||
*/
|
||||
Node getAPredecessor(string lbl) { this = result.getASuccessor(lbl) }
|
||||
Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) }
|
||||
|
||||
/**
|
||||
* Gets a node such that there is an edge in the API graph between this node and the other
|
||||
@@ -140,7 +194,13 @@ module API {
|
||||
/**
|
||||
* Gets the data-flow node that gives rise to this node, if any.
|
||||
*/
|
||||
DataFlow::Node getInducingNode() { this = Impl::MkUse(result) }
|
||||
DataFlow::Node getInducingNode() {
|
||||
this = Impl::MkUse(result)
|
||||
or
|
||||
this = Impl::MkDef(result)
|
||||
or
|
||||
this = Impl::MkMethodAccessNode(result)
|
||||
}
|
||||
|
||||
/** Gets the location of this node. */
|
||||
Location getLocation() {
|
||||
@@ -165,9 +225,8 @@ module API {
|
||||
length = 0 and
|
||||
result = ""
|
||||
or
|
||||
exists(Node pred, string lbl, string predpath |
|
||||
exists(Node pred, Label::ApiLabel lbl, string predpath |
|
||||
Impl::edge(pred, lbl, this) and
|
||||
lbl != "" and
|
||||
predpath = pred.getAPath(length - 1) and
|
||||
exists(string dot | if length = 1 then dot = "" else dot = "." |
|
||||
result = predpath + dot + lbl and
|
||||
@@ -187,15 +246,26 @@ module API {
|
||||
override string toString() { result = "root" }
|
||||
}
|
||||
|
||||
private string tryGetPath(Node node) {
|
||||
result = node.getPath()
|
||||
or
|
||||
not exists(node.getPath()) and
|
||||
result = "with no path"
|
||||
}
|
||||
|
||||
/** A node corresponding to the use of an API component. */
|
||||
class Use extends Node, Impl::MkUse {
|
||||
override string toString() {
|
||||
exists(string type | type = "Use " |
|
||||
result = type + this.getPath()
|
||||
or
|
||||
not exists(this.getPath()) and result = type + "with no path"
|
||||
)
|
||||
}
|
||||
override string toString() { result = "Use " + tryGetPath(this) }
|
||||
}
|
||||
|
||||
/** A node corresponding to a value escaping into an API component. */
|
||||
class Def extends Node, Impl::MkDef {
|
||||
override string toString() { result = "Def " + tryGetPath(this) }
|
||||
}
|
||||
|
||||
/** A node corresponding to the method being invoked at a method call. */
|
||||
class MethodAccessNode extends Node, Impl::MkMethodAccessNode {
|
||||
override string toString() { result = "MethodAccessNode " + tryGetPath(this) }
|
||||
}
|
||||
|
||||
/** Gets the root node. */
|
||||
@@ -240,8 +310,12 @@ module API {
|
||||
newtype TApiNode =
|
||||
/** The root of the API graph. */
|
||||
MkRoot() or
|
||||
/** The method accessed at `call`, synthetically treated as a separate object. */
|
||||
MkMethodAccessNode(DataFlow::CallNode call) { isUse(call) } or
|
||||
/** A use of an API member at the node `nd`. */
|
||||
MkUse(DataFlow::Node nd) { isUse(nd) }
|
||||
MkUse(DataFlow::Node nd) { isUse(nd) } or
|
||||
/** A value that escapes into an external library at the node `nd` */
|
||||
MkDef(DataFlow::Node nd) { isDef(nd) }
|
||||
|
||||
private string resolveTopLevel(ConstantReadAccess read) {
|
||||
TResolved(result) = resolveConstantReadAccess(read) and
|
||||
@@ -253,11 +327,10 @@ module API {
|
||||
* node labeled `lbl` in the API graph.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate useRoot(string lbl, DataFlow::Node ref) {
|
||||
exists(string name, ExprNodes::ConstantAccessCfgNode access, ConstantReadAccess read |
|
||||
access = ref.asExpr() and
|
||||
lbl = Label::member(read.getName()) and
|
||||
read = access.getExpr()
|
||||
private predicate useRoot(Label::ApiLabel lbl, DataFlow::Node ref) {
|
||||
exists(string name, ConstantReadAccess read |
|
||||
read = ref.asExpr().getExpr() and
|
||||
lbl = Label::member(read.getName())
|
||||
|
|
||||
name = resolveTopLevel(read)
|
||||
or
|
||||
@@ -271,46 +344,33 @@ module API {
|
||||
* Holds if `ref` is a use of a node that should have an incoming edge labeled `lbl`,
|
||||
* from a use node that flows to `node`.
|
||||
*/
|
||||
private predicate useStep(string lbl, ExprCfgNode node, DataFlow::Node ref) {
|
||||
private predicate useStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node ref) {
|
||||
// // Referring to an attribute on a node that is a use of `base`:
|
||||
// pred = `Rails` part of `Rails::Whatever`
|
||||
// lbl = `Whatever`
|
||||
// ref = `Rails::Whatever`
|
||||
exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read |
|
||||
not exists(resolveTopLevel(read)) and
|
||||
node = c.getScopeExpr() and
|
||||
node.asExpr() = c.getScopeExpr() and
|
||||
lbl = Label::member(read.getName()) and
|
||||
ref.asExpr() = c and
|
||||
read = c.getExpr()
|
||||
)
|
||||
or
|
||||
// Calling a method on a node that is a use of `base`
|
||||
exists(ExprNodes::MethodCallCfgNode call, string name |
|
||||
node = call.getReceiver() and
|
||||
name = call.getExpr().getMethodName() and
|
||||
lbl = Label::return(name) and
|
||||
name != "new" and
|
||||
ref.asExpr() = call
|
||||
)
|
||||
or
|
||||
// Calling the `new` method on a node that is a use of `base`, which creates a new instance
|
||||
exists(ExprNodes::MethodCallCfgNode call |
|
||||
node = call.getReceiver() and
|
||||
lbl = Label::instance() and
|
||||
call.getExpr().getMethodName() = "new" and
|
||||
ref.asExpr() = call
|
||||
)
|
||||
// note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isUse(DataFlow::Node nd) {
|
||||
useRoot(_, nd)
|
||||
or
|
||||
exists(ExprCfgNode node, DataFlow::LocalSourceNode pred |
|
||||
pred = useCandFwd() and
|
||||
pred.flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node)) and
|
||||
exists(DataFlow::Node node |
|
||||
useCandFwd().flowsTo(node) and
|
||||
useStep(_, node, nd)
|
||||
)
|
||||
or
|
||||
useCandFwd().flowsTo(nd.(DataFlow::CallNode).getReceiver())
|
||||
or
|
||||
parameterStep(_, defCand(), nd)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,6 +379,13 @@ module API {
|
||||
cached
|
||||
predicate use(TApiNode nd, DataFlow::Node ref) { nd = MkUse(ref) }
|
||||
|
||||
/**
|
||||
* Holds if `rhs` is a RHS of node `nd`.
|
||||
*/
|
||||
cached
|
||||
predicate def(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) }
|
||||
|
||||
/** Gets a node reachable from a use-node. */
|
||||
private DataFlow::LocalSourceNode useCandFwd(TypeTracker t) {
|
||||
t.start() and
|
||||
isUse(result)
|
||||
@@ -326,6 +393,7 @@ module API {
|
||||
exists(TypeTracker t2 | result = useCandFwd(t2).track(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a node reachable from a use-node. */
|
||||
private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) }
|
||||
|
||||
private DataFlow::Node useCandRev(TypeBackTracker tb) {
|
||||
@@ -345,6 +413,81 @@ module API {
|
||||
isUse(result)
|
||||
}
|
||||
|
||||
private predicate isDef(DataFlow::Node rhs) {
|
||||
// If a call node is relevant as a use-node, treat its arguments as def-nodes
|
||||
argumentStep(_, useCandFwd(), rhs)
|
||||
}
|
||||
|
||||
/** Gets a data flow node that flows to the RHS of a def-node. */
|
||||
private DataFlow::LocalSourceNode defCand(TypeBackTracker t) {
|
||||
t.start() and
|
||||
exists(DataFlow::Node rhs |
|
||||
isDef(rhs) and
|
||||
result = rhs.getALocalSource()
|
||||
)
|
||||
or
|
||||
exists(TypeBackTracker t2 | result = defCand(t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node that flows to the RHS of a def-node. */
|
||||
private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) }
|
||||
|
||||
private Label::ApiLabel getLabelFromArgumentPosition(DataFlowDispatch::ArgumentPosition pos) {
|
||||
exists(int n |
|
||||
pos.isPositional(n) and
|
||||
result = Label::parameter(n)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
pos.isKeyword(name) and
|
||||
result = Label::keywordParameter(name)
|
||||
)
|
||||
or
|
||||
pos.isBlock() and
|
||||
result = Label::blockParameter()
|
||||
}
|
||||
|
||||
private Label::ApiLabel getLabelFromParameterPosition(DataFlowDispatch::ParameterPosition pos) {
|
||||
exists(int n |
|
||||
pos.isPositional(n) and
|
||||
result = Label::parameter(n)
|
||||
)
|
||||
or
|
||||
exists(string name |
|
||||
pos.isKeyword(name) and
|
||||
result = Label::keywordParameter(name)
|
||||
)
|
||||
or
|
||||
pos.isBlock() and
|
||||
result = Label::blockParameter()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there should be a `lbl`-edge from the given call to an argument.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate argumentStep(
|
||||
Label::ApiLabel lbl, DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument
|
||||
) {
|
||||
exists(DataFlowDispatch::ArgumentPosition argPos |
|
||||
argument.sourceArgumentOf(call.asExpr(), argPos) and
|
||||
lbl = getLabelFromArgumentPosition(argPos)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there should be a `lbl`-edge from the given callable to a parameter.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private predicate parameterStep(
|
||||
Label::ApiLabel lbl, DataFlow::Node callable, DataFlowPrivate::ParameterNodeImpl paramNode
|
||||
) {
|
||||
exists(DataFlowDispatch::ParameterPosition paramPos |
|
||||
paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and
|
||||
lbl = getLabelFromParameterPosition(paramPos)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a data-flow node to which `src`, which is a use of an API-graph node, flows.
|
||||
*
|
||||
@@ -373,21 +516,79 @@ module API {
|
||||
result = trackUseNode(src, TypeTracker::end())
|
||||
}
|
||||
|
||||
/** Gets a data flow node reaching the RHS of the given def node. */
|
||||
private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs, TypeBackTracker t) {
|
||||
t.start() and
|
||||
isDef(rhs) and
|
||||
result = rhs.getALocalSource()
|
||||
or
|
||||
exists(TypeBackTracker t2 | result = trackDefNode(rhs, t2).backtrack(t2, t))
|
||||
}
|
||||
|
||||
/** Gets a data flow node reaching the RHS of the given def node. */
|
||||
cached
|
||||
DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs) {
|
||||
result = trackDefNode(rhs, TypeBackTracker::end())
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate useNodeReachesReceiver(DataFlow::Node use, DataFlow::CallNode call) {
|
||||
trackUseNode(use).flowsTo(call.getReceiver())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`.
|
||||
*/
|
||||
cached
|
||||
predicate edge(TApiNode pred, string lbl, TApiNode succ) {
|
||||
predicate edge(TApiNode pred, Label::ApiLabel lbl, TApiNode succ) {
|
||||
/* Every node that is a use of an API component is itself added to the API graph. */
|
||||
exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) |
|
||||
pred = MkRoot() and
|
||||
useRoot(lbl, ref)
|
||||
or
|
||||
exists(ExprCfgNode node, DataFlow::Node src |
|
||||
exists(DataFlow::Node node, DataFlow::Node src |
|
||||
pred = MkUse(src) and
|
||||
trackUseNode(src).flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node)) and
|
||||
trackUseNode(src).flowsTo(node) and
|
||||
useStep(lbl, node, ref)
|
||||
)
|
||||
or
|
||||
exists(DataFlow::Node callback |
|
||||
pred = MkDef(callback) and
|
||||
parameterStep(lbl, trackDefNode(callback), ref)
|
||||
)
|
||||
)
|
||||
or
|
||||
// `pred` is a use of class A
|
||||
// `succ` is a use of class B
|
||||
// there exists a class declaration B < A
|
||||
exists(ClassDeclaration c, DataFlow::Node a, DataFlow::Node b |
|
||||
use(pred, a) and
|
||||
use(succ, b) and
|
||||
resolveConstant(b.asExpr().getExpr()) = resolveConstantWriteAccess(c) and
|
||||
c.getSuperclassExpr() = a.asExpr().getExpr() and
|
||||
lbl = Label::subclass()
|
||||
)
|
||||
or
|
||||
exists(DataFlow::CallNode call |
|
||||
// from receiver to method call node
|
||||
exists(DataFlow::Node receiver |
|
||||
pred = MkUse(receiver) and
|
||||
useNodeReachesReceiver(receiver, call) and
|
||||
lbl = Label::method(call.getMethodName()) and
|
||||
succ = MkMethodAccessNode(call)
|
||||
)
|
||||
or
|
||||
// from method call node to return and arguments
|
||||
pred = MkMethodAccessNode(call) and
|
||||
(
|
||||
lbl = Label::return() and
|
||||
succ = MkUse(call)
|
||||
or
|
||||
exists(DataFlow::Node rhs |
|
||||
argumentStep(lbl, call, rhs) and
|
||||
succ = MkDef(rhs)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -399,25 +600,140 @@ module API {
|
||||
/** Gets the shortest distance from the root to `nd` in the API graph. */
|
||||
cached
|
||||
int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result)
|
||||
|
||||
/** All the possible labels in the API graph. */
|
||||
cached
|
||||
newtype TLabel =
|
||||
MkLabelMember(string member) { member = any(ConstantReadAccess a).getName() } or
|
||||
MkLabelUnknownMember() or
|
||||
MkLabelMethod(string m) { m = any(DataFlow::CallNode c).getMethodName() } or
|
||||
MkLabelReturn() or
|
||||
MkLabelSubclass() or
|
||||
MkLabelKeywordParameter(string name) {
|
||||
any(DataFlowDispatch::ArgumentPosition arg).isKeyword(name)
|
||||
or
|
||||
any(DataFlowDispatch::ParameterPosition arg).isKeyword(name)
|
||||
} or
|
||||
MkLabelParameter(int n) {
|
||||
any(DataFlowDispatch::ArgumentPosition c).isPositional(n)
|
||||
or
|
||||
any(DataFlowDispatch::ParameterPosition c).isPositional(n)
|
||||
} or
|
||||
MkLabelBlockParameter()
|
||||
}
|
||||
|
||||
/** Provides classes modeling the various edges (labels) in the API graph. */
|
||||
module Label {
|
||||
/** A label in the API-graph */
|
||||
class ApiLabel extends Impl::TLabel {
|
||||
/** Gets a string representation of this label. */
|
||||
string toString() { result = "???" }
|
||||
}
|
||||
|
||||
private import LabelImpl
|
||||
|
||||
private module LabelImpl {
|
||||
private import Impl
|
||||
|
||||
/** A label for a member, for example a constant. */
|
||||
class LabelMember extends ApiLabel {
|
||||
private string member;
|
||||
|
||||
LabelMember() { this = MkLabelMember(member) }
|
||||
|
||||
/** Gets the member name associated with this label. */
|
||||
string getMember() { result = member }
|
||||
|
||||
override string toString() { result = "getMember(\"" + member + "\")" }
|
||||
}
|
||||
|
||||
/** A label for a member with an unknown name. */
|
||||
class LabelUnknownMember extends ApiLabel {
|
||||
LabelUnknownMember() { this = MkLabelUnknownMember() }
|
||||
|
||||
override string toString() { result = "getUnknownMember()" }
|
||||
}
|
||||
|
||||
/** A label for a method. */
|
||||
class LabelMethod extends ApiLabel {
|
||||
private string method;
|
||||
|
||||
LabelMethod() { this = MkLabelMethod(method) }
|
||||
|
||||
/** Gets the method name associated with this label. */
|
||||
string getMethod() { result = method }
|
||||
|
||||
override string toString() { result = "getMethod(\"" + method + "\")" }
|
||||
}
|
||||
|
||||
/** A label for the return value of a method. */
|
||||
class LabelReturn extends ApiLabel {
|
||||
LabelReturn() { this = MkLabelReturn() }
|
||||
|
||||
override string toString() { result = "getReturn()" }
|
||||
}
|
||||
|
||||
/** A label for the subclass relationship. */
|
||||
class LabelSubclass extends ApiLabel {
|
||||
LabelSubclass() { this = MkLabelSubclass() }
|
||||
|
||||
override string toString() { result = "getASubclass()" }
|
||||
}
|
||||
|
||||
/** A label for a keyword parameter. */
|
||||
class LabelKeywordParameter extends ApiLabel {
|
||||
private string name;
|
||||
|
||||
LabelKeywordParameter() { this = MkLabelKeywordParameter(name) }
|
||||
|
||||
/** Gets the name of the keyword parameter associated with this label. */
|
||||
string getName() { result = name }
|
||||
|
||||
override string toString() { result = "getKeywordParameter(\"" + name + "\")" }
|
||||
}
|
||||
|
||||
/** A label for a parameter. */
|
||||
class LabelParameter extends ApiLabel {
|
||||
private int n;
|
||||
|
||||
LabelParameter() { this = MkLabelParameter(n) }
|
||||
|
||||
/** Gets the parameter number associated with this label. */
|
||||
int getIndex() { result = n }
|
||||
|
||||
override string toString() { result = "getParameter(" + n + ")" }
|
||||
}
|
||||
|
||||
/** A label for a block parameter. */
|
||||
class LabelBlockParameter extends ApiLabel {
|
||||
LabelBlockParameter() { this = MkLabelBlockParameter() }
|
||||
|
||||
override string toString() { result = "getBlock()" }
|
||||
}
|
||||
}
|
||||
|
||||
/** Gets the `member` edge label for member `m`. */
|
||||
LabelMember member(string m) { result.getMember() = m }
|
||||
|
||||
/** Gets the `member` edge label for the unknown member. */
|
||||
LabelUnknownMember unknownMember() { any() }
|
||||
|
||||
/** Gets the `method` edge label. */
|
||||
LabelMethod method(string m) { result.getMethod() = m }
|
||||
|
||||
/** Gets the `return` edge label. */
|
||||
LabelReturn return() { any() }
|
||||
|
||||
/** Gets the `subclass` edge label. */
|
||||
LabelSubclass subclass() { any() }
|
||||
|
||||
/** Gets the label representing the given keword argument/parameter. */
|
||||
LabelKeywordParameter keywordParameter(string name) { result.getName() = name }
|
||||
|
||||
/** Gets the label representing the `n`th positional argument/parameter. */
|
||||
LabelParameter parameter(int n) { result.getIndex() = n }
|
||||
|
||||
/** Gets the label representing the block argument/parameter. */
|
||||
LabelBlockParameter blockParameter() { any() }
|
||||
}
|
||||
}
|
||||
|
||||
private module Label {
|
||||
/** Gets the `member` edge label for member `m`. */
|
||||
bindingset[m]
|
||||
bindingset[result]
|
||||
string member(string m) { result = "getMember(\"" + m + "\")" }
|
||||
|
||||
/** Gets the `member` edge label for the unknown member. */
|
||||
string unknownMember() { result = "getUnknownMember()" }
|
||||
|
||||
/** Gets the `instance` edge label. */
|
||||
string instance() { result = "instance" }
|
||||
|
||||
/** Gets the `return` edge label. */
|
||||
bindingset[m]
|
||||
bindingset[result]
|
||||
string return(string m) { result = "getReturn(\"" + m + "\")" }
|
||||
|
||||
string subclass() { result = "getASubclass()" }
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ private import codeql.ruby.frameworks.ActionController
|
||||
private import codeql.ruby.frameworks.ActiveRecord
|
||||
private import codeql.ruby.frameworks.ActiveStorage
|
||||
private import codeql.ruby.frameworks.ActionView
|
||||
private import codeql.ruby.frameworks.ActiveSupport
|
||||
private import codeql.ruby.frameworks.GraphQL
|
||||
private import codeql.ruby.frameworks.Rails
|
||||
private import codeql.ruby.frameworks.StandardLibrary
|
||||
private import codeql.ruby.frameworks.Files
|
||||
private import codeql.ruby.frameworks.HttpClients
|
||||
private import codeql.ruby.frameworks.XmlParsing
|
||||
private import codeql.ruby.frameworks.ActionDispatch
|
||||
|
||||
@@ -187,7 +187,10 @@ class BlockArgument extends Expr, TBlockArgument {
|
||||
* foo(&bar)
|
||||
* ```
|
||||
*/
|
||||
final Expr getValue() { toGenerated(result) = g.getChild() }
|
||||
final Expr getValue() {
|
||||
toGenerated(result) = g.getChild() or
|
||||
synthChild(this, 0, result)
|
||||
}
|
||||
|
||||
final override string toString() { result = "&..." }
|
||||
|
||||
|
||||
@@ -157,6 +157,7 @@ class ErbDirective extends TDirectiveNode, ErbAstNode {
|
||||
* Gets a statement that starts in directive that is not a child of any other
|
||||
* statement starting in this directive.
|
||||
*/
|
||||
cached
|
||||
Stmt getAChildStmt() {
|
||||
this.containsAstNodeStart(result) and
|
||||
not this.containsAstNodeStart(result.getParent())
|
||||
|
||||
@@ -293,13 +293,16 @@ class Pair extends Expr, TPair {
|
||||
final Expr getKey() { toGenerated(result) = g.getKey() }
|
||||
|
||||
/**
|
||||
* Gets the value expression of this pair. For example, the `InteralLiteral`
|
||||
* Gets the value expression of this pair. For example, the `IntegerLiteral`
|
||||
* 123 in the following hash pair:
|
||||
* ```rb
|
||||
* { 'foo' => 123 }
|
||||
* ```
|
||||
*/
|
||||
final Expr getValue() { toGenerated(result) = g.getValue() }
|
||||
final Expr getValue() {
|
||||
toGenerated(result) = g.getValue() or
|
||||
synthChild(this, 0, result)
|
||||
}
|
||||
|
||||
final override string toString() { result = "Pair" }
|
||||
|
||||
|
||||
@@ -148,11 +148,19 @@ class BlockParameter extends NamedParameter, TBlockParameter {
|
||||
|
||||
BlockParameter() { this = TBlockParameter(g) }
|
||||
|
||||
/** Gets the name of this parameter, if any. */
|
||||
final override string getName() { result = g.getName().getValue() }
|
||||
|
||||
final override LocalVariable getVariable() { result = TLocalVariableReal(_, _, g.getName()) }
|
||||
final override LocalVariable getVariable() {
|
||||
result = TLocalVariableReal(_, _, g.getName()) or
|
||||
result = TLocalVariableSynth(this, 0)
|
||||
}
|
||||
|
||||
final override string toString() { result = "&" + this.getName() }
|
||||
final override string toString() {
|
||||
result = "&" + this.getName()
|
||||
or
|
||||
not exists(this.getName()) and result = "&"
|
||||
}
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "BlockParameter" }
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ deprecated class TuplePattern extends Pattern, TTuplePattern {
|
||||
|
||||
private class TPatternNode =
|
||||
TArrayPattern or TFindPattern or THashPattern or TAlternativePattern or TAsPattern or
|
||||
TParenthesizedPattern or TVariableReferencePattern;
|
||||
TParenthesizedPattern or TExpressionReferencePattern or TVariableReferencePattern;
|
||||
|
||||
private class TPattern =
|
||||
TPatternNode or TLiteral or TLambda or TConstantAccess or TLocalVariableAccess or
|
||||
@@ -404,6 +404,42 @@ class ParenthesizedPattern extends CasePattern, TParenthesizedPattern {
|
||||
}
|
||||
|
||||
/**
|
||||
* A variable or value reference in a pattern, i.e. `^x`, and `^(2 * x)` in the following example:
|
||||
* ```rb
|
||||
* x = 10
|
||||
* case expr
|
||||
* in ^x then puts "ok"
|
||||
* in ^(2 * x) then puts "ok"
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class ReferencePattern extends CasePattern, TReferencePattern {
|
||||
private Ruby::AstNode value;
|
||||
|
||||
ReferencePattern() {
|
||||
value = any(Ruby::VariableReferencePattern g | this = TVariableReferencePattern(g)).getName()
|
||||
or
|
||||
value =
|
||||
any(Ruby::ExpressionReferencePattern g | this = TExpressionReferencePattern(g)).getValue()
|
||||
}
|
||||
|
||||
/** Gets the value this reference pattern matches against. For example `2 * x` in `^(2 * x)` */
|
||||
final Expr getExpr() { toGenerated(result) = value }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ReferencePattern" }
|
||||
|
||||
final override string toString() { result = "^..." }
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = super.getAChild(pred)
|
||||
or
|
||||
pred = "getExpr" and result = this.getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED: Use `ReferencePattern` instead.
|
||||
*
|
||||
* A variable reference in a pattern, i.e. `^x` in the following example:
|
||||
* ```rb
|
||||
* x = 10
|
||||
@@ -412,21 +448,7 @@ class ParenthesizedPattern extends CasePattern, TParenthesizedPattern {
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class VariableReferencePattern extends CasePattern, TVariableReferencePattern {
|
||||
private Ruby::VariableReferencePattern g;
|
||||
|
||||
VariableReferencePattern() { this = TVariableReferencePattern(g) }
|
||||
|
||||
deprecated class VariableReferencePattern extends ReferencePattern, TVariableReferencePattern {
|
||||
/** Gets the variable access corresponding to this variable reference pattern. */
|
||||
LocalVariableReadAccess getVariableAccess() { toGenerated(result) = g.getName() }
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "VariableReferencePattern" }
|
||||
|
||||
final override string toString() { result = "^..." }
|
||||
|
||||
final override AstNode getAChild(string pred) {
|
||||
result = super.getAChild(pred)
|
||||
or
|
||||
pred = "getVariableAccess" and result = this.getVariableAccess()
|
||||
}
|
||||
final VariableReadAccess getVariableAccess() { result = this.getExpr() }
|
||||
}
|
||||
|
||||
@@ -35,7 +35,10 @@ class LocalVariable extends Variable, TLocalVariable {
|
||||
override LocalVariableAccess getAnAccess() { result.getVariable() = this }
|
||||
|
||||
/** Gets the access where this local variable is first introduced. */
|
||||
VariableAccess getDefiningAccess() { result = this.(LocalVariableReal).getDefiningAccessImpl() }
|
||||
VariableAccess getDefiningAccess() {
|
||||
result = this.(LocalVariableReal).getDefiningAccessImpl() or
|
||||
synthChild(any(BlockParameter p | this = p.getVariable()), 0, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this variable is captured. For example in
|
||||
@@ -117,6 +120,8 @@ class VariableAccess extends Expr instanceof VariableAccessImpl {
|
||||
this = any(SimpleParameterSynthImpl p).getDefininingAccess()
|
||||
or
|
||||
this = any(HashPattern p).getValue(_)
|
||||
or
|
||||
synthChild(any(BlockParameter p), 0, this)
|
||||
}
|
||||
|
||||
final override string toString() { result = VariableAccessImpl.super.toString() }
|
||||
|
||||
@@ -320,11 +320,14 @@ private module Cached {
|
||||
TUntilExpr(Ruby::Until g) or
|
||||
TUntilModifierExpr(Ruby::UntilModifier g) or
|
||||
TVariableReferencePattern(Ruby::VariableReferencePattern g) or
|
||||
TExpressionReferencePattern(Ruby::ExpressionReferencePattern g) or
|
||||
TWhenClause(Ruby::When g) or
|
||||
TWhileExpr(Ruby::While g) or
|
||||
TWhileModifierExpr(Ruby::WhileModifier g) or
|
||||
TYieldCall(Ruby::Yield g)
|
||||
|
||||
class TReferencePattern = TVariableReferencePattern or TExpressionReferencePattern;
|
||||
|
||||
class TAstNodeReal =
|
||||
TAddExprReal or TAliasStmt or TAlternativePattern or TArgumentList or TArrayPattern or
|
||||
TAsPattern or TAssignAddExpr or TAssignBitwiseAndExpr or TAssignBitwiseOrExpr or
|
||||
@@ -359,7 +362,7 @@ private module Cached {
|
||||
TTernaryIfExpr or TThen or TTokenConstantAccess or TTokenMethodName or TTokenSuperCall or
|
||||
TToplevel or TTrueLiteral or TUnaryMinusExpr or TUnaryPlusExpr or TUndefStmt or
|
||||
TUnlessExpr or TUnlessModifierExpr or TUntilExpr or TUntilModifierExpr or
|
||||
TVariableReferencePattern or TWhenClause or TWhileExpr or TWhileModifierExpr or TYieldCall;
|
||||
TReferencePattern or TWhenClause or TWhileExpr or TWhileModifierExpr or TYieldCall;
|
||||
|
||||
class TAstNodeSynth =
|
||||
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
|
||||
@@ -529,6 +532,7 @@ private module Cached {
|
||||
n = TUntilExpr(result) or
|
||||
n = TUntilModifierExpr(result) or
|
||||
n = TVariableReferencePattern(result) or
|
||||
n = TExpressionReferencePattern(result) or
|
||||
n = TWhenClause(result) or
|
||||
n = TWhileExpr(result) or
|
||||
n = TWhileModifierExpr(result) or
|
||||
|
||||
@@ -233,12 +233,17 @@ private module ResolveImpl {
|
||||
|
||||
pragma[nomagic]
|
||||
private string resolveConstantReadAccessNonRec(ConstantReadAccess c, int priority) {
|
||||
// ::B
|
||||
c.hasGlobalScope() and result = c.getName() and priority = 0
|
||||
or
|
||||
// A::B
|
||||
exists(string name, string s | result = isDefinedConstantNonRec(s, name) |
|
||||
s = resolveConstantReadAccessScopeNonRec(c, priority, name)
|
||||
)
|
||||
or
|
||||
// module A
|
||||
// B
|
||||
// end
|
||||
exists(string name |
|
||||
exists(Namespace n, string qname |
|
||||
n = constantReadAccessEnclosingNameSpace(c, priority, name) and
|
||||
|
||||
@@ -975,24 +975,107 @@ private module ForLoopDesugar {
|
||||
* ```
|
||||
*/
|
||||
private module ImplicitHashValueSynthesis {
|
||||
private Ruby::AstNode keyWithoutValue(HashPattern parent, int i) {
|
||||
private Ruby::AstNode keyWithoutValue(AstNode parent, int i) {
|
||||
exists(Ruby::KeywordPattern pair |
|
||||
result = pair.getKey() and
|
||||
result = toGenerated(parent.getKey(i)) and
|
||||
result = toGenerated(parent.(HashPattern).getKey(i)) and
|
||||
not exists(pair.getValue())
|
||||
)
|
||||
or
|
||||
exists(Ruby::Pair pair |
|
||||
i = 0 and
|
||||
result = pair.getKey() and
|
||||
pair = toGenerated(parent) and
|
||||
not exists(pair.getValue())
|
||||
)
|
||||
}
|
||||
|
||||
private string keyName(Ruby::AstNode key) {
|
||||
result = key.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
|
||||
result = key.(Ruby::HashKeySymbol).getValue()
|
||||
}
|
||||
|
||||
private class ImplicitHashValueSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
exists(TVariableReal variable |
|
||||
access(keyWithoutValue(parent, i), variable) and
|
||||
child = SynthChild(LocalVariableAccessRealKind(variable))
|
||||
exists(Ruby::AstNode key | key = keyWithoutValue(parent, i) |
|
||||
exists(TVariableReal variable |
|
||||
access(key, variable) and
|
||||
child = SynthChild(LocalVariableAccessRealKind(variable))
|
||||
)
|
||||
or
|
||||
not access(key, _) and
|
||||
exists(string name | name = keyName(key) |
|
||||
child = SynthChild(ConstantReadAccessKind(name)) or
|
||||
child = SynthChild(MethodCallKind(name, false, 0))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate methodCall(string name, boolean setter, int arity) {
|
||||
setter = false and
|
||||
arity = 0 and
|
||||
name = keyName(keyWithoutValue(_, _)) and
|
||||
not name.charAt(0).isUppercase()
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) {
|
||||
name = keyName(keyWithoutValue(_, _)) and
|
||||
name.charAt(0).isUppercase()
|
||||
}
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(HashPattern p, int i | n = p.getValue(i) and l = keyWithoutValue(p, i).getLocation())
|
||||
exists(AstNode p, int i | l = keyWithoutValue(p, i).getLocation() |
|
||||
n = p.(HashPattern).getValue(i)
|
||||
or
|
||||
i = 0 and n = p.(Pair).getValue()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* def foo(&)
|
||||
* bar(&)
|
||||
* end
|
||||
* ```
|
||||
* desugars to,
|
||||
* ```rb
|
||||
* def foo(&__synth_0)
|
||||
* bar(&__synth_0)
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
private module AnonymousBlockParameterSynth {
|
||||
private BlockParameter anonymousBlockParameter() {
|
||||
exists(Ruby::BlockParameter p | not exists(p.getName()) and toGenerated(result) = p)
|
||||
}
|
||||
|
||||
private BlockArgument anonymousBlockArgument() {
|
||||
exists(Ruby::BlockArgument p | not exists(p.getChild()) and toGenerated(result) = p)
|
||||
}
|
||||
|
||||
private class AnonymousBlockParameterSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
i = 0 and
|
||||
parent = anonymousBlockParameter() and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(parent, 0)))
|
||||
}
|
||||
|
||||
final override predicate localVariable(AstNode n, int i) {
|
||||
n = anonymousBlockParameter() and i = 0
|
||||
}
|
||||
}
|
||||
|
||||
private class AnonymousBlockArgumentSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
i = 0 and
|
||||
parent = anonymousBlockArgument() and
|
||||
exists(BlockParameter param |
|
||||
param = anonymousBlockParameter() and
|
||||
scopeOf(toGenerated(parent)).getEnclosingMethod() = scopeOf(toGenerated(param)) and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(param, 0)))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -116,6 +116,11 @@ private string variableNameInScope(Ruby::AstNode i, Scope::Range scope) {
|
||||
result = i.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
|
||||
result = i.(Ruby::HashKeySymbol).getValue()
|
||||
)
|
||||
or
|
||||
exists(Ruby::Pair p | i = p.getKey() and not exists(p.getValue()) |
|
||||
result = i.(Ruby::String).getChild(0).(Ruby::StringContent).getValue() or
|
||||
result = i.(Ruby::HashKeySymbol).getValue()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -307,6 +312,8 @@ private module Cached {
|
||||
i = any(Ruby::WhileModifier x).getCondition()
|
||||
or
|
||||
i = any(Ruby::WhileModifier x).getBody()
|
||||
or
|
||||
i = any(Ruby::ExpressionReferencePattern x).getValue()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
@@ -414,10 +421,10 @@ class TVariableReal =
|
||||
class TLocalVariable = TLocalVariableReal or TLocalVariableSynth or TSelfVariable;
|
||||
|
||||
/**
|
||||
* This class only exists to avoid negative recursion warnings. Ideally,
|
||||
* we would use `VariableImpl` directly, but that results in incorrect
|
||||
* negative recursion warnings. Adding new root-defs for the predicates
|
||||
* below works around this.
|
||||
* A "real" (i.e. non-synthesized) variable. This class only exists to
|
||||
* avoid negative recursion warnings. Ideally, we would use `VariableImpl`
|
||||
* directly, but that results in incorrect negative recursion warnings.
|
||||
* Adding new root-defs for the predicates below works around this.
|
||||
*/
|
||||
abstract class VariableReal extends TVariableReal {
|
||||
abstract string getNameImpl();
|
||||
|
||||
@@ -309,6 +309,17 @@ private module Cached {
|
||||
jbp order by JoinBlockPredecessors::getId(jbp), JoinBlockPredecessors::getSplitString(jbp)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate immediatelyControls(ConditionBlock cb, BasicBlock succ, BooleanSuccessor s) {
|
||||
succ = cb.getASuccessor(s) and
|
||||
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != cb | succ.dominates(pred))
|
||||
}
|
||||
|
||||
cached
|
||||
predicate controls(ConditionBlock cb, BasicBlock controlled, BooleanSuccessor s) {
|
||||
exists(BasicBlock succ | cb.immediatelyControls(succ, s) | succ.dominates(controlled))
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
@@ -395,10 +406,8 @@ class ConditionBlock extends BasicBlock {
|
||||
* successor of this block, and `succ` can only be reached from
|
||||
* the callable entry point by going via the `s` edge out of this basic block.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate immediatelyControls(BasicBlock succ, BooleanSuccessor s) {
|
||||
succ = this.getASuccessor(s) and
|
||||
forall(BasicBlock pred | pred = succ.getAPredecessor() and pred != this | succ.dominates(pred))
|
||||
immediatelyControls(this, succ, s)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -406,7 +415,5 @@ class ConditionBlock extends BasicBlock {
|
||||
* conditional value `s`. That is, `controlled` can only be reached from
|
||||
* the callable entry point by going via the `s` edge out of this basic block.
|
||||
*/
|
||||
predicate controls(BasicBlock controlled, BooleanSuccessor s) {
|
||||
exists(BasicBlock succ | this.immediatelyControls(succ, s) | succ.dominates(controlled))
|
||||
}
|
||||
predicate controls(BasicBlock controlled, BooleanSuccessor s) { controls(this, controlled, s) }
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ private predicate inMatchingContext(AstNode n) {
|
||||
or
|
||||
n instanceof CasePattern
|
||||
or
|
||||
n = any(VariableReferencePattern p).getVariableAccess()
|
||||
n = any(ReferencePattern p).getExpr()
|
||||
or
|
||||
n.(Trees::DefaultValueParameterTree).hasDefaultValue()
|
||||
}
|
||||
|
||||
@@ -731,8 +731,8 @@ module Trees {
|
||||
override ControlFlowTree getChildElement(int i) { result = this.getPattern() and i = 0 }
|
||||
}
|
||||
|
||||
private class VariableReferencePatternTree extends StandardPreOrderTree, VariableReferencePattern {
|
||||
override ControlFlowTree getChildElement(int i) { result = this.getVariableAccess() and i = 0 }
|
||||
private class ReferencePatternTree extends StandardPreOrderTree, ReferencePattern {
|
||||
override ControlFlowTree getChildElement(int i) { result = this.getExpr() and i = 0 }
|
||||
}
|
||||
|
||||
private class InClauseTree extends PreOrderTree, InClause {
|
||||
|
||||
@@ -214,7 +214,7 @@ abstract class SplitKind extends SplitKindBase {
|
||||
abstract string toString();
|
||||
}
|
||||
|
||||
/** Provides the interface for implementing an entity to split on. */
|
||||
/** An interface for implementing an entity to split on. */
|
||||
abstract class SplitImpl extends Split {
|
||||
/** Gets the kind of this split. */
|
||||
abstract SplitKind getKind();
|
||||
@@ -900,10 +900,25 @@ module TestOutput {
|
||||
}
|
||||
|
||||
query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) {
|
||||
attr = "semmle.label" and
|
||||
exists(SuccessorType t | succ = getASuccessor(pred, t) |
|
||||
attr = "semmle.label" and
|
||||
if successorTypeIsSimple(t) then val = "" else val = t.toString()
|
||||
)
|
||||
or
|
||||
attr = "semmle.order" and
|
||||
val =
|
||||
any(int i |
|
||||
succ =
|
||||
rank[i](RelevantNode s, SuccessorType t, Location l |
|
||||
s = getASuccessor(pred, t) and
|
||||
l = s.getLocation()
|
||||
|
|
||||
s
|
||||
order by
|
||||
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
|
||||
l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString()
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -134,10 +134,12 @@ abstract class SummarizedCallable extends LibraryCallable {
|
||||
* calls to a method with the same name are considered relevant.
|
||||
*/
|
||||
abstract class SimpleSummarizedCallable extends SummarizedCallable {
|
||||
bindingset[this]
|
||||
SimpleSummarizedCallable() { any() }
|
||||
MethodCall mc;
|
||||
|
||||
final override MethodCall getACall() { result.getMethodName() = this }
|
||||
bindingset[this]
|
||||
SimpleSummarizedCallable() { mc.getMethodName() = this }
|
||||
|
||||
final override MethodCall getACall() { result = mc }
|
||||
}
|
||||
|
||||
private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable {
|
||||
|
||||
@@ -241,7 +241,11 @@ private module Cached {
|
||||
or
|
||||
FlowSummaryImplSpecific::ParsePositions::isParsedParameterPosition(_, pos)
|
||||
} or
|
||||
TKeywordArgumentPosition(string name) { name = any(KeywordParameter kp).getName() }
|
||||
TKeywordArgumentPosition(string name) {
|
||||
name = any(KeywordParameter kp).getName()
|
||||
or
|
||||
exists(any(Call c).getKeywordArgument(name))
|
||||
}
|
||||
|
||||
cached
|
||||
newtype TParameterPosition =
|
||||
@@ -463,18 +467,7 @@ predicate mayBenefitFromCallContext(DataFlowCall call, DataFlowCallable c) { non
|
||||
*/
|
||||
DataFlowCallable viableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
|
||||
*/
|
||||
predicate exprNodeReturnedFrom(DataFlow::ExprNode e, Callable c) {
|
||||
exists(ReturningNode r |
|
||||
nodeGetEnclosingCallable(r).asCallable() = c and
|
||||
(
|
||||
r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or
|
||||
r.(ExprReturnNode) = e
|
||||
)
|
||||
)
|
||||
}
|
||||
predicate exprNodeReturnedFrom = exprNodeReturnedFromCached/2;
|
||||
|
||||
/** A parameter position. */
|
||||
class ParameterPosition extends TParameterPosition {
|
||||
|
||||
@@ -1290,7 +1290,7 @@ class DataFlowCallOption extends TDataFlowCallOption {
|
||||
}
|
||||
}
|
||||
|
||||
/** Content tagged with the type of a containing object. */
|
||||
/** A `Content` tagged with the type of a containing object. */
|
||||
class TypedContent extends MkTypedContent {
|
||||
private Content c;
|
||||
private DataFlowType t;
|
||||
|
||||
@@ -285,11 +285,17 @@ private module Cached {
|
||||
// and we can remove this case.
|
||||
n.asExpr().getExpr() instanceof Self
|
||||
or
|
||||
// Nodes that can't be reached from another parameter or expression.
|
||||
not localFlowStepTypeTracker+(any(Node e |
|
||||
e instanceof ExprNode
|
||||
or
|
||||
e instanceof ParameterNode
|
||||
), n)
|
||||
or
|
||||
// Ensure all parameter SSA nodes are local sources -- this is needed by type tracking.
|
||||
// Note that when the parameter has a default value, it will be reachable from an
|
||||
// expression (the default value) and therefore won't be caught by the rule above.
|
||||
n = LocalFlow::getParameterDefNode(_)
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -297,6 +303,20 @@ private module Cached {
|
||||
TKnownArrayElementContent(int i) { i in [0 .. 10] } or
|
||||
TUnknownArrayElementContent() or
|
||||
TAnyArrayElementContent()
|
||||
|
||||
/**
|
||||
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
|
||||
*/
|
||||
cached
|
||||
predicate exprNodeReturnedFromCached(ExprNode e, Callable c) {
|
||||
exists(ReturningNode r |
|
||||
nodeGetEnclosingCallable(r).asCallable() = c and
|
||||
(
|
||||
r.(ExplicitReturnNode).getReturningNode().getReturnedValueNode() = e.asExpr() or
|
||||
r.(ExprReturnNode) = e
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TArrayElementContent = TKnownArrayElementContent or TUnknownArrayElementContent;
|
||||
@@ -306,9 +326,13 @@ import Cached
|
||||
/** Holds if `n` should be hidden from path explanations. */
|
||||
predicate nodeIsHidden(Node n) {
|
||||
exists(Ssa::Definition def | def = n.(SsaDefinitionNode).getDefinition() |
|
||||
def instanceof Ssa::PhiNode
|
||||
def instanceof Ssa::PhiNode or
|
||||
def instanceof Ssa::CapturedEntryDefinition or
|
||||
def instanceof Ssa::CapturedCallDefinition
|
||||
)
|
||||
or
|
||||
n = LocalFlow::getParameterDefNode(_)
|
||||
or
|
||||
isDesugarNode(n.(ExprNode).getExprNode().getExpr())
|
||||
or
|
||||
n instanceof SummaryNode
|
||||
|
||||
@@ -18,11 +18,11 @@ class Node extends TNode {
|
||||
Parameter asParameter() { result = this.(ParameterNode).getParameter() }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
// TODO: cache
|
||||
cached
|
||||
final string toString() { result = this.(NodeImpl).toStringImpl() }
|
||||
|
||||
/** Gets the location of this node. */
|
||||
// TODO: cache
|
||||
cached
|
||||
final Location getLocation() { result = this.(NodeImpl).getLocationImpl() }
|
||||
|
||||
/**
|
||||
@@ -121,7 +121,8 @@ class LocalSourceNode extends Node {
|
||||
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
|
||||
}
|
||||
|
||||
predicate hasLocalSource(Node sink, Node source) {
|
||||
cached
|
||||
private predicate hasLocalSource(Node sink, Node source) {
|
||||
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
|
||||
// recursive case, so instead we check it explicitly here.
|
||||
source = sink and
|
||||
@@ -151,12 +152,14 @@ predicate localFlowStep = localFlowStepImpl/2;
|
||||
* Holds if data flows from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprFlow(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) {
|
||||
localFlow(exprNode(e1), exprNode(e2))
|
||||
}
|
||||
|
||||
@@ -90,7 +90,9 @@ module Public {
|
||||
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
|
||||
|
||||
/** Gets the bottom element of this stack. */
|
||||
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
|
||||
SummaryComponent bottom() {
|
||||
this = TSingletonSummaryComponentStack(result) or result = this.tail().bottom()
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this stack. */
|
||||
string toString() {
|
||||
|
||||
@@ -20,15 +20,6 @@ private predicate hasCapturedVariableRead(BasicBlock bb, LocalVariable v) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if an entry definition is needed for captured variable `v` at index
|
||||
* `i` in entry block `bb`.
|
||||
*/
|
||||
predicate capturedEntryWrite(EntryBasicBlock bb, int i, LocalVariable v) {
|
||||
hasCapturedVariableRead(bb.getASuccessor*(), v) and
|
||||
i = -1
|
||||
}
|
||||
|
||||
/** Holds if `bb` contains a caputured write to variable `v`. */
|
||||
pragma[noinline]
|
||||
private predicate writesCapturedVariable(BasicBlock bb, LocalVariable v) {
|
||||
@@ -132,6 +123,16 @@ private predicate hasVariableReadWithCapturedWrite(BasicBlock bb, LocalVariable
|
||||
|
||||
cached
|
||||
private module Cached {
|
||||
/**
|
||||
* Holds if an entry definition is needed for captured variable `v` at index
|
||||
* `i` in entry block `bb`.
|
||||
*/
|
||||
cached
|
||||
predicate capturedEntryWrite(EntryBasicBlock bb, int i, LocalVariable v) {
|
||||
hasCapturedVariableRead(bb.getASuccessor*(), v) and
|
||||
i = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the call at index `i` in basic block `bb` may reach a callable
|
||||
* that writes captured variable `v`.
|
||||
|
||||
@@ -8,12 +8,14 @@ private import FlowSummaryImpl as FlowSummaryImpl
|
||||
* Holds if taint propagates from `source` to `sink` in zero or more local
|
||||
* (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) }
|
||||
|
||||
/**
|
||||
* Holds if taint can flow from `e1` to `e2` in zero or more
|
||||
* local (intra-procedural) steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
predicate localExprTaint(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) {
|
||||
localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2))
|
||||
}
|
||||
|
||||
@@ -4,21 +4,9 @@ private import codeql.ruby.controlflow.CfgNodes
|
||||
private import codeql.ruby.DataFlow
|
||||
private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
private import codeql.ruby.ast.internal.Module
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import ActionView
|
||||
|
||||
private class ActionControllerBaseAccess extends ConstantReadAccess {
|
||||
ActionControllerBaseAccess() {
|
||||
this.getName() = "Base" and
|
||||
this.getScopeExpr().(ConstantAccess).getName() = "ActionController"
|
||||
}
|
||||
}
|
||||
|
||||
// ApplicationController extends ActionController::Base, but we
|
||||
// treat it separately in case the ApplicationController definition
|
||||
// is not in the database
|
||||
private class ApplicationControllerAccess extends ConstantReadAccess {
|
||||
ApplicationControllerAccess() { this.getName() = "ApplicationController" }
|
||||
}
|
||||
private import codeql.ruby.frameworks.ActionDispatch
|
||||
|
||||
/**
|
||||
* A `ClassDeclaration` for a class that extends `ActionController::Base`.
|
||||
@@ -35,16 +23,13 @@ private class ApplicationControllerAccess extends ConstantReadAccess {
|
||||
*/
|
||||
class ActionControllerControllerClass extends ClassDeclaration {
|
||||
ActionControllerControllerClass() {
|
||||
// class FooController < ActionController::Base
|
||||
this.getSuperclassExpr() instanceof ActionControllerBaseAccess
|
||||
or
|
||||
// class FooController < ApplicationController
|
||||
this.getSuperclassExpr() instanceof ApplicationControllerAccess
|
||||
or
|
||||
// class BarController < FooController
|
||||
exists(ActionControllerControllerClass other |
|
||||
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
|
||||
)
|
||||
this.getSuperclassExpr() =
|
||||
[
|
||||
API::getTopLevelMember("ActionController").getMember("Base"),
|
||||
// In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
|
||||
// treat it separately in case the `ApplicationController` definition is not in the database.
|
||||
API::getTopLevelMember("ApplicationController")
|
||||
].getASubclass().getAUse().asExpr().getExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,6 +70,26 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler:
|
||||
// not end at an explicit render or redirect
|
||||
/** Gets the controller class containing this method. */
|
||||
ActionControllerControllerClass getControllerClass() { result = controllerClass }
|
||||
|
||||
/**
|
||||
* Gets a route to this handler, if one exists.
|
||||
* May return multiple results.
|
||||
*/
|
||||
ActionDispatch::Route getARoute() {
|
||||
exists(string name |
|
||||
isRoute(result, name, controllerClass) and
|
||||
isActionControllerMethod(this, name, controllerClass)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isRoute(
|
||||
ActionDispatch::Route route, string name, ActionControllerControllerClass controllerClass
|
||||
) {
|
||||
route.getController() + "_controller" =
|
||||
ActionDispatch::underscore(namespaceDeclaration(controllerClass)) and
|
||||
name = route.getAction()
|
||||
}
|
||||
|
||||
// A method call with a `self` receiver from within a controller class
|
||||
|
||||
951
ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll
Normal file
951
ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll
Normal file
@@ -0,0 +1,951 @@
|
||||
/**
|
||||
* Models routing configuration specified using the `ActionDispatch` library, which is part of Rails.
|
||||
*/
|
||||
|
||||
private import codeql.ruby.AST
|
||||
private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.DataFlow
|
||||
|
||||
/**
|
||||
* Models routing configuration specified using the `ActionDispatch` library, which is part of Rails.
|
||||
*/
|
||||
module ActionDispatch {
|
||||
/**
|
||||
* A block that defines some routes.
|
||||
* Route blocks can contribute to the path or controller namespace of their child routes.
|
||||
* For example, in the block below
|
||||
* ```rb
|
||||
* scope path: "/admin" do
|
||||
* get "/dashboard", to: "admin_dashboard#show"
|
||||
* end
|
||||
* ```
|
||||
* the route defined by the call to `get` has the full path `/admin/dashboard`.
|
||||
* We track these contributions via `getPathComponent` and `getControllerComponent`.
|
||||
*/
|
||||
abstract private class RouteBlock extends TRouteBlock {
|
||||
/**
|
||||
* Gets the name of a primary CodeQL class to which this route block belongs.
|
||||
*/
|
||||
string getAPrimaryQlClass() { result = "RouteBlock" }
|
||||
|
||||
/**
|
||||
* Gets a string representation of this route block.
|
||||
*/
|
||||
string toString() { none() }
|
||||
|
||||
/**
|
||||
* Gets a `Stmt` within this route block.
|
||||
*/
|
||||
abstract Stmt getAStmt();
|
||||
|
||||
/**
|
||||
* Gets the parent of this route block, if one exists.
|
||||
*/
|
||||
abstract RouteBlock getParent();
|
||||
|
||||
/**
|
||||
* Gets the `n`th parent of this route block.
|
||||
* The zeroth parent is this block, the first parent is the direct parent of this block, etc.
|
||||
*/
|
||||
RouteBlock getParent(int n) {
|
||||
if n = 0 then result = this else result = this.getParent().getParent(n - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the component of the path defined by this block, if it exists.
|
||||
*/
|
||||
abstract string getPathComponent();
|
||||
|
||||
/**
|
||||
* Gets the component of the controller namespace defined by this block, if it exists.
|
||||
*/
|
||||
abstract string getControllerComponent();
|
||||
|
||||
/**
|
||||
* Gets the location of this route block.
|
||||
*/
|
||||
abstract Location getLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* A route block that is not the top-level block.
|
||||
* This block will always have a parent.
|
||||
*/
|
||||
abstract private class NestedRouteBlock extends RouteBlock {
|
||||
RouteBlock parent;
|
||||
|
||||
override RouteBlock getParent() { result = parent }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NestedRouteBlock" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A top-level routes block.
|
||||
* ```rb
|
||||
* Rails.application.routes.draw do
|
||||
* ...
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
private class TopLevelRouteBlock extends RouteBlock, TTopLevelRouteBlock {
|
||||
MethodCall call;
|
||||
// Routing blocks create scopes which define the namespace for controllers and paths,
|
||||
// though they can be overridden in various ways.
|
||||
// The namespaces can differ, so we track them separately.
|
||||
Block block;
|
||||
|
||||
TopLevelRouteBlock() { this = TTopLevelRouteBlock(_, call, block) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "TopLevelRouteBlock" }
|
||||
|
||||
Block getBlock() { result = block }
|
||||
|
||||
override Stmt getAStmt() { result = block.getAStmt() }
|
||||
|
||||
override RouteBlock getParent() { none() }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
|
||||
override string getPathComponent() { none() }
|
||||
|
||||
override string getControllerComponent() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route block defined by a call to `constraints`.
|
||||
* ```rb
|
||||
* constraints(foo: /some_regex/) do
|
||||
* get "/posts/:foo", to "posts#something"
|
||||
* end
|
||||
* ```
|
||||
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints
|
||||
*/
|
||||
private class ConstraintsRouteBlock extends NestedRouteBlock, TConstraintsRouteBlock {
|
||||
private Block block;
|
||||
private MethodCall call;
|
||||
|
||||
ConstraintsRouteBlock() { this = TConstraintsRouteBlock(parent, call, block) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstraintsRouteBlock" }
|
||||
|
||||
override Stmt getAStmt() { result = block.getAStmt() }
|
||||
|
||||
override string getPathComponent() { result = "" }
|
||||
|
||||
override string getControllerComponent() { result = "" }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route block defined by a call to `scope`.
|
||||
* ```rb
|
||||
* scope(path: "/some_path", module: "some_module") do
|
||||
* get "/posts/:foo", to "posts#something"
|
||||
* end
|
||||
* ```
|
||||
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-scope
|
||||
*/
|
||||
private class ScopeRouteBlock extends NestedRouteBlock, TScopeRouteBlock {
|
||||
private MethodCall call;
|
||||
private Block block;
|
||||
|
||||
ScopeRouteBlock() { this = TScopeRouteBlock(parent, call, block) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ScopeRouteBlock" }
|
||||
|
||||
override Stmt getAStmt() { result = block.getAStmt() }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
|
||||
override string getPathComponent() {
|
||||
call.getKeywordArgument("path").getConstantValue().isStringOrSymbol(result)
|
||||
or
|
||||
not exists(call.getKeywordArgument("path")) and
|
||||
call.getArgument(0).getConstantValue().isStringOrSymbol(result)
|
||||
}
|
||||
|
||||
override string getControllerComponent() {
|
||||
call.getKeywordArgument(["controller", "module"]).getConstantValue().isStringOrSymbol(result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A route block defined by a call to `resources`.
|
||||
* ```rb
|
||||
* resources :articles do
|
||||
* get "/comments", to "comments#index"
|
||||
* end
|
||||
* ```
|
||||
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources
|
||||
*/
|
||||
private class ResourcesRouteBlock extends NestedRouteBlock, TResourcesRouteBlock {
|
||||
private MethodCall call;
|
||||
private Block block;
|
||||
|
||||
ResourcesRouteBlock() { this = TResourcesRouteBlock(parent, call, block) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ResourcesRouteBlock" }
|
||||
|
||||
override Stmt getAStmt() { result = block.getAStmt() }
|
||||
|
||||
/**
|
||||
* Gets the `resources` call that gives rise to this route block.
|
||||
*/
|
||||
MethodCall getDefiningMethodCall() { result = call }
|
||||
|
||||
override string getPathComponent() {
|
||||
exists(string resource | call.getArgument(0).getConstantValue().isStringOrSymbol(resource) |
|
||||
result = resource + "/:" + singularize(resource) + "_id"
|
||||
)
|
||||
}
|
||||
|
||||
override string getControllerComponent() { result = "" }
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route block that is guarded by a conditional statement.
|
||||
* For example:
|
||||
* ```rb
|
||||
* if Rails.env.test?
|
||||
* get "/foo/bar", to: "foo#bar"
|
||||
* end
|
||||
* ```
|
||||
* We ignore the condition and analyze both branches to obtain as
|
||||
* much routing information as possible.
|
||||
*/
|
||||
private class ConditionalRouteBlock extends NestedRouteBlock, TConditionalRouteBlock {
|
||||
private ConditionalExpr e;
|
||||
|
||||
ConditionalRouteBlock() { this = TConditionalRouteBlock(parent, e) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConditionalRouteBlock" }
|
||||
|
||||
override Stmt getAStmt() { result = e.getBranch(_).(StmtSequence).getAStmt() }
|
||||
|
||||
override string getPathComponent() { none() }
|
||||
|
||||
override string getControllerComponent() { none() }
|
||||
|
||||
override string toString() { result = e.toString() }
|
||||
|
||||
override Location getLocation() { result = e.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route block defined by a call to `namespace`.
|
||||
* ```rb
|
||||
* namespace :admin do
|
||||
* resources :posts
|
||||
* end
|
||||
* ```
|
||||
* https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-namespace
|
||||
*/
|
||||
private class NamespaceRouteBlock extends NestedRouteBlock, TNamespaceRouteBlock {
|
||||
private MethodCall call;
|
||||
private Block block;
|
||||
|
||||
NamespaceRouteBlock() { this = TNamespaceRouteBlock(parent, call, block) }
|
||||
|
||||
override Stmt getAStmt() { result = block.getAStmt() }
|
||||
|
||||
override string getPathComponent() { result = this.getNamespace() }
|
||||
|
||||
override string getControllerComponent() { result = this.getNamespace() }
|
||||
|
||||
private string getNamespace() {
|
||||
call.getArgument(0).getConstantValue().isStringOrSymbol(result)
|
||||
}
|
||||
|
||||
override string toString() { result = call.toString() }
|
||||
|
||||
override Location getLocation() { result = call.getLocation() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route configuration. This defines a combination of HTTP method and URL
|
||||
* path which should be routed to a particular controller-action pair.
|
||||
* This can arise from an explicit call to a routing method, for example:
|
||||
* ```rb
|
||||
* get "/photos", to: "photos#index"
|
||||
* ```
|
||||
* or via a convenience method like `resources`, which defines mutiple routes at once:
|
||||
* ```rb
|
||||
* resources :photos
|
||||
* ```
|
||||
*/
|
||||
class Route extends TRoute instanceof RouteImpl {
|
||||
/**
|
||||
* Gets the name of a primary CodeQL class to which this route belongs.
|
||||
*/
|
||||
string getAPrimaryQlClass() { result = "Route" }
|
||||
|
||||
/** Gets a string representation of this route. */
|
||||
string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets the location of the method call that defines this route.
|
||||
*/
|
||||
Location getLocation() { result = super.getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the full controller targeted by this route.
|
||||
*/
|
||||
string getController() { result = super.getController() }
|
||||
|
||||
/**
|
||||
* Gets the action targeted by this route.
|
||||
*/
|
||||
string getAction() { result = super.getAction() }
|
||||
|
||||
/**
|
||||
* Gets the HTTP method of this route.
|
||||
* The result is one of [get, post, put, patch, delete].
|
||||
*/
|
||||
string getHttpMethod() { result = super.getHttpMethod() }
|
||||
|
||||
/**
|
||||
* Gets the full path of the route.
|
||||
*/
|
||||
string getPath() { result = super.getPath() }
|
||||
|
||||
/**
|
||||
* Get a URL capture. This is a wildcard URL segment whose value is placed in `params`.
|
||||
* For example, in
|
||||
* ```ruby
|
||||
* get "/foo/:bar/baz", to: "users#index"
|
||||
* ```
|
||||
* the capture is `:bar`.
|
||||
*/
|
||||
string getACapture() { result = super.getACapture() }
|
||||
}
|
||||
|
||||
/**
|
||||
* The implementation of `Route`.
|
||||
* This class is abstract and is thus kept private so we don't expose it to
|
||||
* users.
|
||||
* Extend this class to add new instances of routes.
|
||||
*/
|
||||
abstract private class RouteImpl extends TRoute {
|
||||
/**
|
||||
* Gets the name of a primary CodeQL class to which this route belongs.
|
||||
*/
|
||||
string getAPrimaryQlClass() { result = "RouteImpl" }
|
||||
|
||||
MethodCall method;
|
||||
|
||||
/** Gets a string representation of this route. */
|
||||
string toString() { result = method.toString() }
|
||||
|
||||
/**
|
||||
* Gets the location of the method call that defines this route.
|
||||
*/
|
||||
Location getLocation() { result = method.getLocation() }
|
||||
|
||||
/**
|
||||
* Gets the method call that defines this route.
|
||||
*/
|
||||
MethodCall getDefiningMethodCall() { result = method }
|
||||
|
||||
/**
|
||||
* Get the last component of the path. For example, in
|
||||
* ```rb
|
||||
* get "/photos", to: "photos#index"
|
||||
* ```
|
||||
* this is `/photos`.
|
||||
* If the string has any interpolations, this predicate will have no result.
|
||||
*/
|
||||
abstract string getLastPathComponent();
|
||||
|
||||
/**
|
||||
* Gets the HTTP method of this route.
|
||||
* The result is one of [get, post, put, patch, delete].
|
||||
*/
|
||||
abstract string getHttpMethod();
|
||||
|
||||
/**
|
||||
* Gets the last controller component.
|
||||
* This is the controller specified in the route itself.
|
||||
*/
|
||||
abstract string getLastControllerComponent();
|
||||
|
||||
/**
|
||||
* Gets a component of the controller.
|
||||
* This behaves identically to `getPathComponent`, but for controller information.
|
||||
*/
|
||||
string getControllerComponent(int n) {
|
||||
if n = 0
|
||||
then result = this.getLastControllerComponent()
|
||||
else result = this.getParentBlock().getParent(n - 1).getControllerComponent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full controller targeted by this route.
|
||||
*/
|
||||
string getController() {
|
||||
result =
|
||||
concat(int n |
|
||||
this.getControllerComponent(n) != ""
|
||||
|
|
||||
this.getControllerComponent(n), "/" order by n desc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the action targeted by this route.
|
||||
*/
|
||||
abstract string getAction();
|
||||
|
||||
/**
|
||||
* Gets the parent `RouteBlock` of this route.
|
||||
*/
|
||||
abstract RouteBlock getParentBlock();
|
||||
|
||||
/**
|
||||
* Gets a component of the path. Components are numbered from 0 up, where 0
|
||||
* is the last component, 1 is the second-last, etc.
|
||||
* For example, in the following route:
|
||||
*
|
||||
* ```rb
|
||||
* namespace path: "foo" do
|
||||
* namespace path: "bar" do
|
||||
* get "baz", to: "foo#bar
|
||||
* end
|
||||
* end
|
||||
* ```
|
||||
*
|
||||
* the components are:
|
||||
*
|
||||
* | n | component
|
||||
* |---|----------
|
||||
* | 0 | baz
|
||||
* | 1 | bar
|
||||
* | 2 | foo
|
||||
*/
|
||||
string getPathComponent(int n) {
|
||||
if n = 0
|
||||
then result = this.getLastPathComponent()
|
||||
else result = this.getParentBlock().getParent(n - 1).getPathComponent()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full path of the route.
|
||||
*/
|
||||
string getPath() {
|
||||
result =
|
||||
concat(int n |
|
||||
this.getPathComponent(n) != ""
|
||||
|
|
||||
// Strip leading and trailing slashes from each path component before combining
|
||||
stripSlashes(this.getPathComponent(n)), "/" order by n desc
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a URL capture. This is a wildcard URL segment whose value is placed in `params`.
|
||||
* For example, in
|
||||
* ```ruby
|
||||
* get "/foo/:bar/baz", to: "users#index"
|
||||
* ```
|
||||
* the capture is `:bar`.
|
||||
* We don't currently make use of this, but it may be useful in future to more accurately
|
||||
* model the contents of the `params` hash.
|
||||
*/
|
||||
string getACapture() { result = this.getPathComponent(_).regexpFind(":[^:/]+", _, _) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route generated by an explicit call to `get`, `post`, etc.
|
||||
*
|
||||
* ```ruby
|
||||
* get "/photos", to: "photos#index"
|
||||
* put "/photos/:id", to: "photos#update"
|
||||
* ```
|
||||
*/
|
||||
private class ExplicitRoute extends RouteImpl, TExplicitRoute {
|
||||
RouteBlock parentBlock;
|
||||
|
||||
ExplicitRoute() { this = TExplicitRoute(parentBlock, method) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ExplicitRoute" }
|
||||
|
||||
override RouteBlock getParentBlock() { result = parentBlock }
|
||||
|
||||
override string getLastPathComponent() {
|
||||
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
|
||||
}
|
||||
|
||||
override string getLastControllerComponent() {
|
||||
method.getKeywordArgument("controller").getConstantValue().isStringOrSymbol(result)
|
||||
or
|
||||
not exists(method.getKeywordArgument("controller")) and
|
||||
(
|
||||
result = extractController(this.getActionString())
|
||||
or
|
||||
// If controller is not specified, and we're in a `resources` route block, use the controller of that route.
|
||||
// For example, in
|
||||
//
|
||||
// resources :posts do
|
||||
// get "timestamp", to: :timestamp
|
||||
// end
|
||||
//
|
||||
// The route is GET /posts/:post_id/timestamp => posts/timestamp
|
||||
not exists(extractController(this.getActionString())) and
|
||||
exists(ResourcesRoute r |
|
||||
r.getDefiningMethodCall() = parentBlock.(ResourcesRouteBlock).getDefiningMethodCall()
|
||||
|
|
||||
result = r.getLastControllerComponent()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private string getActionString() {
|
||||
method.getKeywordArgument("to").getConstantValue().isStringOrSymbol(result)
|
||||
or
|
||||
method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and
|
||||
result = "<redirect>#<redirect>"
|
||||
}
|
||||
|
||||
override string getAction() {
|
||||
// get "/photos", action: "index"
|
||||
method.getKeywordArgument("action").getConstantValue().isStringOrSymbol(result)
|
||||
or
|
||||
not exists(method.getKeywordArgument("action")) and
|
||||
(
|
||||
// get "/photos", to: "photos#index"
|
||||
// get "/photos", to: redirect("some_url")
|
||||
result = extractAction(this.getActionString())
|
||||
or
|
||||
// resources :photos, only: [] do
|
||||
// get "/", to: "index"
|
||||
// end
|
||||
not exists(extractAction(this.getActionString())) and result = this.getActionString()
|
||||
or
|
||||
// get :some_action
|
||||
not exists(this.getActionString()) and
|
||||
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
|
||||
)
|
||||
}
|
||||
|
||||
override string getHttpMethod() { result = method.getMethodName().toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route generated by a call to `resources`.
|
||||
*
|
||||
* ```ruby
|
||||
* resources :photos
|
||||
* ```
|
||||
* This creates eight routes, equivalent to the following code:
|
||||
* ```ruby
|
||||
* get "/photos", to: "photos#index"
|
||||
* get "/photos/new", to: "photos#new"
|
||||
* post "/photos", to: "photos#create"
|
||||
* get "/photos/:id", to: "photos#show"
|
||||
* get "/photos/:id/edit", to: "photos#edit"
|
||||
* patch "/photos/:id", to: "photos#update"
|
||||
* put "/photos/:id", to: "photos#update"
|
||||
* delete "/photos/:id", to: "photos#delete"
|
||||
* ```
|
||||
*
|
||||
* `resources` can take a block. Any routes defined inside the block will inherit a path component of
|
||||
* `/<resource>/:<resource>_id`. For example:
|
||||
*
|
||||
* ```ruby
|
||||
* resources :photos do
|
||||
* get "/foo", to: "photos#foo"
|
||||
* end
|
||||
* ```
|
||||
* This creates the eight default routes, plus one more, which is nested under "/photos/:photo_id", equivalent to:
|
||||
* ```ruby
|
||||
* get "/photos/:photo_id/foo", to: "photos#foo"
|
||||
* ```
|
||||
*/
|
||||
private class ResourcesRoute extends RouteImpl, TResourcesRoute {
|
||||
RouteBlock parent;
|
||||
string resource;
|
||||
string action;
|
||||
string httpMethod;
|
||||
string pathComponent;
|
||||
|
||||
ResourcesRoute() {
|
||||
this = TResourcesRoute(parent, method, action) and
|
||||
method.getArgument(0).getConstantValue().isStringOrSymbol(resource) and
|
||||
isDefaultResourceRoute(resource, httpMethod, pathComponent, action)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ResourcesRoute" }
|
||||
|
||||
override RouteBlock getParentBlock() { result = parent }
|
||||
|
||||
override string getLastPathComponent() { result = pathComponent }
|
||||
|
||||
override string getLastControllerComponent() {
|
||||
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
|
||||
}
|
||||
|
||||
override string getAction() { result = action }
|
||||
|
||||
override string getHttpMethod() { result = httpMethod }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route generated by a call to `resource`.
|
||||
* This is like a `resources` route, but creates routes for a singular resource.
|
||||
* This means there's no index route, no id parameter, and the resource name is expected to be singular.
|
||||
* It will still be routed to a pluralised controller name.
|
||||
* ```ruby
|
||||
* resource :account
|
||||
* ```
|
||||
*/
|
||||
private class SingularResourceRoute extends RouteImpl, TResourceRoute {
|
||||
RouteBlock parent;
|
||||
string resource;
|
||||
string action;
|
||||
string httpMethod;
|
||||
string pathComponent;
|
||||
|
||||
SingularResourceRoute() {
|
||||
this = TResourceRoute(parent, method, action) and
|
||||
method.getArgument(0).getConstantValue().isStringOrSymbol(resource) and
|
||||
isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action)
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "SingularResourceRoute" }
|
||||
|
||||
override RouteBlock getParentBlock() { result = parent }
|
||||
|
||||
override string getLastPathComponent() { result = pathComponent }
|
||||
|
||||
override string getLastControllerComponent() {
|
||||
method.getArgument(0).getConstantValue().isStringOrSymbol(result)
|
||||
}
|
||||
|
||||
override string getAction() { result = action }
|
||||
|
||||
override string getHttpMethod() { result = httpMethod }
|
||||
}
|
||||
|
||||
/**
|
||||
* A route generated by a call to `match`.
|
||||
* This is a lower level primitive that powers `get`, `post` etc.
|
||||
* The first argument can be a path or a (path, controller-action) pair.
|
||||
* The controller, action and HTTP method can be specified with the
|
||||
* `controller:`, `action:` and `via:` keyword arguments, respectively.
|
||||
* ```ruby
|
||||
* match 'photos/:id' => 'photos#show', via: :get
|
||||
* match 'photos/:id', to: 'photos#show', via: :get
|
||||
* match 'photos/:id', to 'photos#show', via: [:get, :post]
|
||||
* match 'photos/:id', controller: 'photos', action: 'show', via: :get
|
||||
* ```
|
||||
*/
|
||||
private class MatchRoute extends RouteImpl, TMatchRoute {
|
||||
private RouteBlock parent;
|
||||
|
||||
MatchRoute() { this = TMatchRoute(parent, method) }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "MatchRoute" }
|
||||
|
||||
override RouteBlock getParentBlock() { result = parent }
|
||||
|
||||
override string getLastPathComponent() {
|
||||
[method.getArgument(0), method.getArgument(0).(Pair).getKey()]
|
||||
.getConstantValue()
|
||||
.isStringOrSymbol(result)
|
||||
}
|
||||
|
||||
override string getLastControllerComponent() {
|
||||
result =
|
||||
extractController(method.getKeywordArgument("to").getConstantValue().getStringOrSymbol()) or
|
||||
method.getKeywordArgument("controller").getConstantValue().isStringOrSymbol(result) or
|
||||
result =
|
||||
extractController(method
|
||||
.getArgument(0)
|
||||
.(Pair)
|
||||
.getValue()
|
||||
.getConstantValue()
|
||||
.getStringOrSymbol())
|
||||
}
|
||||
|
||||
override string getHttpMethod() {
|
||||
exists(string via |
|
||||
method.getKeywordArgument("via").getConstantValue().isStringOrSymbol(via)
|
||||
|
|
||||
via = "all" and result = anyHttpMethod()
|
||||
or
|
||||
via != "all" and result = via
|
||||
)
|
||||
or
|
||||
result =
|
||||
method
|
||||
.getKeywordArgument("via")
|
||||
.(ArrayLiteral)
|
||||
.getElement(_)
|
||||
.getConstantValue()
|
||||
.getStringOrSymbol()
|
||||
}
|
||||
|
||||
override string getAction() {
|
||||
result = extractAction(method.getKeywordArgument("to").getConstantValue().getStringOrSymbol()) or
|
||||
method.getKeywordArgument("action").getConstantValue().isStringOrSymbol(result) or
|
||||
result =
|
||||
extractAction(method.getArgument(0).(Pair).getValue().getConstantValue().getStringOrSymbol())
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/**
|
||||
* This module contains the IPA types backing `RouteBlock` and `Route`, cached for performance.
|
||||
*/
|
||||
cached
|
||||
private module Cached {
|
||||
cached
|
||||
newtype TRouteBlock =
|
||||
TTopLevelRouteBlock(MethodCall routes, MethodCall draw, Block b) {
|
||||
routes.getMethodName() = "routes" and
|
||||
draw.getMethodName() = "draw" and
|
||||
draw.getReceiver() = routes and
|
||||
draw.getBlock() = b
|
||||
} or
|
||||
// constraints(foo: /some_regex/) do
|
||||
// get "/posts/:foo", to "posts#something"
|
||||
// end
|
||||
TConstraintsRouteBlock(RouteBlock parent, MethodCall constraints, Block b) {
|
||||
parent.getAStmt() = constraints and
|
||||
constraints.getMethodName() = "constraints" and
|
||||
constraints.getBlock() = b
|
||||
} or
|
||||
// scope(path: "/some_path", module: "some_module") do
|
||||
// get "/posts/:foo", to "posts#something"
|
||||
// end
|
||||
TScopeRouteBlock(RouteBlock parent, MethodCall scope, Block b) {
|
||||
parent.getAStmt() = scope and scope.getMethodName() = "scope" and scope.getBlock() = b
|
||||
} or
|
||||
// resources :articles do
|
||||
// get "/comments", to "comments#index"
|
||||
// end
|
||||
TResourcesRouteBlock(RouteBlock parent, MethodCall resources, Block b) {
|
||||
parent.getAStmt() = resources and
|
||||
resources.getMethodName() = "resources" and
|
||||
resources.getBlock() = b
|
||||
} or
|
||||
// A conditional statement guarding some routes.
|
||||
// We ignore the condition and analyze both branches to obtain as
|
||||
// much routing information as possible.
|
||||
TConditionalRouteBlock(RouteBlock parent, ConditionalExpr e) { parent.getAStmt() = e } or
|
||||
// namespace :admin do
|
||||
// resources :posts
|
||||
// end
|
||||
TNamespaceRouteBlock(RouteBlock parent, MethodCall namespace, Block b) {
|
||||
parent.getAStmt() = namespace and
|
||||
namespace.getMethodName() = "namespace" and
|
||||
namespace.getBlock() = b
|
||||
}
|
||||
|
||||
/**
|
||||
* A route configuration. See `Route` for more info
|
||||
*/
|
||||
cached
|
||||
newtype TRoute =
|
||||
/**
|
||||
* See `ExplicitRoute`
|
||||
*/
|
||||
TExplicitRoute(RouteBlock b, MethodCall m) {
|
||||
b.getAStmt() = m and m.getMethodName() = anyHttpMethod()
|
||||
} or
|
||||
/**
|
||||
* See `ResourcesRoute`
|
||||
*/
|
||||
TResourcesRoute(RouteBlock b, MethodCall m, string action) {
|
||||
b.getAStmt() = m and
|
||||
m.getMethodName() = "resources" and
|
||||
action in ["show", "index", "new", "edit", "create", "update", "destroy"] and
|
||||
applyActionFilters(m, action)
|
||||
} or
|
||||
/**
|
||||
* See `SingularResourceRoute`
|
||||
*/
|
||||
TResourceRoute(RouteBlock b, MethodCall m, string action) {
|
||||
b.getAStmt() = m and
|
||||
m.getMethodName() = "resource" and
|
||||
action in ["show", "new", "edit", "create", "update", "destroy"] and
|
||||
applyActionFilters(m, action)
|
||||
} or
|
||||
/**
|
||||
* See `MatchRoute`
|
||||
*/
|
||||
TMatchRoute(RouteBlock b, MethodCall m) { b.getAStmt() = m and m.getMethodName() = "match" }
|
||||
}
|
||||
|
||||
/**
|
||||
* Several routing methods support the keyword arguments `only:` and `except:`.
|
||||
* - `only:` restricts the set of actions to just those in the argument.
|
||||
* - `except:` removes the given actions from the set.
|
||||
*/
|
||||
bindingset[action]
|
||||
private predicate applyActionFilters(MethodCall m, string action) {
|
||||
// Respect the `only` keyword argument, which restricts the set of actions.
|
||||
(
|
||||
not exists(m.getKeywordArgument("only"))
|
||||
or
|
||||
exists(Expr only | only = m.getKeywordArgument("only") |
|
||||
[only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringOrSymbol(action)
|
||||
)
|
||||
) and
|
||||
// Respect the `except` keyword argument, which removes actions from the default set.
|
||||
(
|
||||
not exists(m.getKeywordArgument("except"))
|
||||
or
|
||||
exists(Expr except | except = m.getKeywordArgument("except") |
|
||||
[except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringOrSymbol() !=
|
||||
action
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the (resource, method, path, action) combination would be generated by a call to `resources :<resource>`.
|
||||
*/
|
||||
bindingset[resource]
|
||||
private predicate isDefaultResourceRoute(
|
||||
string resource, string method, string path, string action
|
||||
) {
|
||||
action = "create" and
|
||||
(method = "post" and path = "/" + resource)
|
||||
or
|
||||
action = "index" and
|
||||
(method = "get" and path = "/" + resource)
|
||||
or
|
||||
action = "new" and
|
||||
(method = "get" and path = "/" + resource + "/new")
|
||||
or
|
||||
action = "edit" and
|
||||
(method = "get" and path = "/" + resource + ":id/edit")
|
||||
or
|
||||
action = "show" and
|
||||
(method = "get" and path = "/" + resource + "/:id")
|
||||
or
|
||||
action = "update" and
|
||||
(method in ["put", "patch"] and path = "/" + resource + "/:id")
|
||||
or
|
||||
action = "destroy" and
|
||||
(method = "delete" and path = "/" + resource + "/:id")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the (resource, method, path, action) combination would be generated by a call to `resource :<resource>`.
|
||||
*/
|
||||
bindingset[resource]
|
||||
private predicate isDefaultSingularResourceRoute(
|
||||
string resource, string method, string path, string action
|
||||
) {
|
||||
action = "create" and
|
||||
(method = "post" and path = "/" + resource)
|
||||
or
|
||||
action = "new" and
|
||||
(method = "get" and path = "/" + resource + "/new")
|
||||
or
|
||||
action = "edit" and
|
||||
(method = "get" and path = "/" + resource + "/edit")
|
||||
or
|
||||
action = "show" and
|
||||
(method = "get" and path = "/" + resource)
|
||||
or
|
||||
action = "update" and
|
||||
(method in ["put", "patch"] and path = "/" + resource)
|
||||
or
|
||||
action = "destroy" and
|
||||
(method = "delete" and path = "/" + resource)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the controller from a Rails routing string
|
||||
* ```
|
||||
* extractController("posts#show") = "posts"
|
||||
* ```
|
||||
*/
|
||||
bindingset[input]
|
||||
private string extractController(string input) { result = input.regexpCapture("([^#]+)#.+", 1) }
|
||||
|
||||
/**
|
||||
* Extract the action from a Rails routing string
|
||||
* ```
|
||||
* extractAction("posts#show") = "show"
|
||||
*/
|
||||
bindingset[input]
|
||||
private string extractAction(string input) { result = input.regexpCapture("[^#]+#(.+)", 1) }
|
||||
|
||||
/**
|
||||
* Returns the lowercase name of every HTTP method we support.
|
||||
*/
|
||||
private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] }
|
||||
|
||||
/**
|
||||
* The inverse of `pluralize`
|
||||
* photos => photo
|
||||
* stories => story
|
||||
* not_plural => not_plural
|
||||
*/
|
||||
bindingset[input]
|
||||
private string singularize(string input) {
|
||||
exists(string prefix | prefix = input.regexpCapture("(.*)ies", 1) | result = prefix + "y")
|
||||
or
|
||||
not input.matches("%ies") and
|
||||
exists(string prefix | prefix = input.regexpCapture("(.*)s", 1) | result = prefix)
|
||||
or
|
||||
not input.regexpMatch(".*(ies|s)") and result = input
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a camel-case string to underscore case. Converts `::` to `/`.
|
||||
* This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle.
|
||||
* Note: All-uppercase words like `CONSTANT` are not handled correctly.
|
||||
*/
|
||||
bindingset[base]
|
||||
string underscore(string base) {
|
||||
base = "" and result = ""
|
||||
or
|
||||
result =
|
||||
base.charAt(0).toLowerCase() +
|
||||
// Walk along the string, keeping track of the previous character
|
||||
// in order to determine if we've crossed a boundary.
|
||||
// Boundaries are:
|
||||
// - lower case to upper case: B in FooBar
|
||||
// - entering a namespace: B in Foo::Bar
|
||||
concat(int i, string prev, string char |
|
||||
prev = base.charAt(i) and
|
||||
char = base.charAt(i + 1)
|
||||
|
|
||||
any(string s |
|
||||
char.regexpMatch("[A-Za-z0-9]") and
|
||||
if prev = ":"
|
||||
then s = "/" + char.toLowerCase()
|
||||
else
|
||||
if prev.isLowercase() and char.isUppercase()
|
||||
then s = "_" + char.toLowerCase()
|
||||
else s = char.toLowerCase()
|
||||
)
|
||||
order by
|
||||
i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip leading and trailing forward slashes from the string.
|
||||
*/
|
||||
bindingset[input]
|
||||
private string stripSlashes(string input) {
|
||||
result = input.regexpReplaceAll("^/+(.+)$", "$1").regexpReplaceAll("^(.*[^/])/+$", "$1")
|
||||
}
|
||||
}
|
||||
@@ -7,20 +7,6 @@ private import codeql.ruby.ast.internal.Module
|
||||
private import codeql.ruby.ApiGraphs
|
||||
private import codeql.ruby.frameworks.StandardLibrary
|
||||
|
||||
private class ActiveRecordBaseAccess extends ConstantReadAccess {
|
||||
ActiveRecordBaseAccess() {
|
||||
this.getName() = "Base" and
|
||||
this.getScopeExpr().(ConstantAccess).getName() = "ActiveRecord"
|
||||
}
|
||||
}
|
||||
|
||||
// ApplicationRecord extends ActiveRecord::Base, but we
|
||||
// treat it separately in case the ApplicationRecord definition
|
||||
// is not in the database
|
||||
private class ApplicationRecordAccess extends ConstantReadAccess {
|
||||
ApplicationRecordAccess() { this.getName() = "ApplicationRecord" }
|
||||
}
|
||||
|
||||
/// See https://api.rubyonrails.org/classes/ActiveRecord/Persistence.html
|
||||
private string activeRecordPersistenceInstanceMethodName() {
|
||||
result =
|
||||
@@ -41,26 +27,28 @@ private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName)
|
||||
}
|
||||
|
||||
/**
|
||||
* A `ClassDeclaration` for a class that extends `ActiveRecord::Base`. For example,
|
||||
* A `ClassDeclaration` for a class that inherits from `ActiveRecord::Base`. For example,
|
||||
*
|
||||
* ```rb
|
||||
* class UserGroup < ActiveRecord::Base
|
||||
* has_many :users
|
||||
* end
|
||||
*
|
||||
* class SpecialUserGroup < UserGroup
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class ActiveRecordModelClass extends ClassDeclaration {
|
||||
ActiveRecordModelClass() {
|
||||
// class Foo < ActiveRecord::Base
|
||||
this.getSuperclassExpr() instanceof ActiveRecordBaseAccess
|
||||
or
|
||||
// class Foo < ApplicationRecord
|
||||
this.getSuperclassExpr() instanceof ApplicationRecordAccess
|
||||
or
|
||||
// class Bar < Foo
|
||||
exists(ActiveRecordModelClass other |
|
||||
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
|
||||
)
|
||||
this.getSuperclassExpr() =
|
||||
[
|
||||
API::getTopLevelMember("ActiveRecord").getMember("Base"),
|
||||
// In Rails applications `ApplicationRecord` typically extends `ActiveRecord::Base`, but we
|
||||
// treat it separately in case the `ApplicationRecord` definition is not in the database.
|
||||
API::getTopLevelMember("ApplicationRecord")
|
||||
].getASubclass().getAUse().asExpr().getExpr()
|
||||
}
|
||||
|
||||
// Gets the class declaration for this class and all of its super classes
|
||||
|
||||
37
ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll
Normal file
37
ruby/ql/lib/codeql/ruby/frameworks/ActiveSupport.qll
Normal file
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Modeling for `ActiveSupport`, which is a utility gem that ships with Rails.
|
||||
* https://rubygems.org/gems/activesupport
|
||||
*/
|
||||
|
||||
import codeql.ruby.Concepts
|
||||
import codeql.ruby.DataFlow
|
||||
import codeql.ruby.frameworks.StandardLibrary
|
||||
|
||||
/**
|
||||
* Modeling for `ActiveSupport`.
|
||||
*/
|
||||
module ActiveSupport {
|
||||
/**
|
||||
* Extensions to core classes
|
||||
*/
|
||||
module CoreExtensions {
|
||||
/**
|
||||
* Extensions to the `String` class
|
||||
*/
|
||||
module String {
|
||||
/**
|
||||
* A call to `String#constantize`, which tries to find a declared constant with the given name.
|
||||
* Passing user input to this method may result in instantiation of arbitrary Ruby classes.
|
||||
*/
|
||||
class Constantize extends CodeExecution::Range, DataFlow::CallNode {
|
||||
// We treat this an `UnknownMethodCall` in order to match every call to `constantize` that isn't overridden.
|
||||
// We can't (yet) rely on API Graphs or dataflow to tell us that the receiver is a String.
|
||||
Constantize() {
|
||||
this.asExpr().getExpr().(UnknownMethodCall).getMethodName() = "constantize"
|
||||
}
|
||||
|
||||
override DataFlow::Node getCode() { result = this.getReceiver() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2510
ruby/ql/lib/codeql/ruby/frameworks/Array.qll
Normal file
2510
ruby/ql/lib/codeql/ruby/frameworks/Array.qll
Normal file
File diff suppressed because it is too large
Load Diff
@@ -212,6 +212,15 @@ module File {
|
||||
FileInstance() { this = fileInstance() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `File.open`, considered as a `FileSystemAccess`.
|
||||
*/
|
||||
class FileOpen extends DataFlow::CallNode, FileSystemAccess::Range {
|
||||
FileOpen() { this = API::getTopLevelMember("File").getAMethodCall("open") }
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A read using the `File` module, e.g. the `f.read` call in
|
||||
*
|
||||
|
||||
@@ -10,44 +10,7 @@ private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
private import codeql.ruby.ast.internal.Module
|
||||
private import codeql.ruby.ApiGraphs
|
||||
|
||||
private class GraphqlRelayClassicMutationAccess extends ConstantReadAccess {
|
||||
//GraphQL::Schema::RelayClassicMutation
|
||||
GraphqlRelayClassicMutationAccess() {
|
||||
this =
|
||||
API::getTopLevelMember("GraphQL")
|
||||
.getMember("Schema")
|
||||
.getMember("RelayClassicMutation")
|
||||
.getAUse()
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
private class GraphqlSchemaResolverAccess extends ConstantReadAccess {
|
||||
//GraphQL::Schema::Resolver
|
||||
GraphqlSchemaResolverAccess() {
|
||||
this =
|
||||
API::getTopLevelMember("GraphQL")
|
||||
.getMember("Schema")
|
||||
.getMember("Resolver")
|
||||
.getAUse()
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
private class GraphqlSchemaObjectAccess extends ConstantReadAccess {
|
||||
//GraphQL::Schema::Object
|
||||
GraphqlSchemaObjectAccess() {
|
||||
this =
|
||||
API::getTopLevelMember("GraphQL")
|
||||
.getMember("Schema")
|
||||
.getMember("Object")
|
||||
.getAUse()
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
}
|
||||
}
|
||||
private API::Node graphQlSchema() { result = API::getTopLevelMember("GraphQL").getMember("Schema") }
|
||||
|
||||
/**
|
||||
* A `ClassDeclaration` for a class that extends `GraphQL::Schema::RelayClassicMutation`.
|
||||
@@ -77,13 +40,8 @@ private class GraphqlSchemaObjectAccess extends ConstantReadAccess {
|
||||
*/
|
||||
private class GraphqlRelayClassicMutationClass extends ClassDeclaration {
|
||||
GraphqlRelayClassicMutationClass() {
|
||||
// class BaseMutation < GraphQL::Schema::RelayClassicMutation
|
||||
this.getSuperclassExpr() instanceof GraphqlRelayClassicMutationAccess
|
||||
or
|
||||
// class MyMutation < BaseMutation
|
||||
exists(GraphqlRelayClassicMutationClass other |
|
||||
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
|
||||
)
|
||||
this.getSuperclassExpr() =
|
||||
graphQlSchema().getMember("RelayClassicMutation").getASubclass*().getAUse().asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,13 +70,8 @@ private class GraphqlRelayClassicMutationClass extends ClassDeclaration {
|
||||
*/
|
||||
private class GraphqlSchemaResolverClass extends ClassDeclaration {
|
||||
GraphqlSchemaResolverClass() {
|
||||
// class BaseResolver < GraphQL::Schema::Resolver
|
||||
this.getSuperclassExpr() instanceof GraphqlSchemaResolverAccess
|
||||
or
|
||||
// class MyResolver < BaseResolver
|
||||
exists(GraphqlSchemaResolverClass other |
|
||||
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
|
||||
)
|
||||
this.getSuperclassExpr() =
|
||||
graphQlSchema().getMember("Resolver").getASubclass().getAUse().asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,13 +91,8 @@ private class GraphqlSchemaResolverClass extends ClassDeclaration {
|
||||
*/
|
||||
class GraphqlSchemaObjectClass extends ClassDeclaration {
|
||||
GraphqlSchemaObjectClass() {
|
||||
// class BaseObject < GraphQL::Schema::Object
|
||||
this.getSuperclassExpr() instanceof GraphqlSchemaObjectAccess
|
||||
or
|
||||
// class MyObject < BaseObject
|
||||
exists(GraphqlSchemaObjectClass other |
|
||||
other.getModule() = resolveConstantReadAccess(this.getSuperclassExpr())
|
||||
)
|
||||
this.getSuperclassExpr() =
|
||||
graphQlSchema().getMember("Object").getASubclass().getAUse().asExpr().getExpr()
|
||||
}
|
||||
|
||||
/** Gets a `GraphqlFieldDefinitionMethodCall` called in this class. */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,10 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
|
||||
|
||||
OpenUriRequest() {
|
||||
requestNode =
|
||||
[API::getTopLevelMember("URI"), API::getTopLevelMember("URI").getReturn("parse")]
|
||||
.getReturn("open") and
|
||||
[
|
||||
[API::getTopLevelMember("URI"), API::getTopLevelMember("URI").getReturn("parse")]
|
||||
.getReturn("open"), API::getTopLevelMember("OpenURI").getReturn("open_uri")
|
||||
] and
|
||||
requestUse = requestNode.getAnImmediateUse() and
|
||||
this = requestUse.asExpr().getExpr()
|
||||
}
|
||||
|
||||
@@ -46,6 +46,6 @@ module ServerSideRequestForgery {
|
||||
HttpRequestAsSink() { exists(HTTP::Client::Request req | req.getURL() = this) }
|
||||
}
|
||||
|
||||
/** String interpolation with a fixed prefix, considered as a flow sanitizer. */
|
||||
/** A string interpolation with a fixed prefix, considered as a flow sanitizer. */
|
||||
class StringInterpolationAsSanitizer extends PrefixedStringInterpolation, Sanitizer { }
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ private import codeql.ruby.Concepts
|
||||
private import codeql.ruby.dataflow.RemoteFlowSources
|
||||
private import codeql.ruby.dataflow.BarrierGuards
|
||||
private import codeql.ruby.dataflow.Sanitizers
|
||||
private import codeql.ruby.frameworks.ActionController
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
@@ -54,15 +55,21 @@ module UrlRedirect {
|
||||
*/
|
||||
class RedirectLocationAsSink extends Sink {
|
||||
RedirectLocationAsSink() {
|
||||
exists(HTTP::Server::HttpRedirectResponse e |
|
||||
exists(HTTP::Server::HttpRedirectResponse e, MethodBase method |
|
||||
this = e.getRedirectLocation() and
|
||||
// As a rough heuristic, assume that methods with these names are handlers for POST/PUT/PATCH/DELETE requests,
|
||||
// which are not as vulnerable to URL redirection because browsers will not initiate them from clicking a link.
|
||||
not this.asExpr()
|
||||
.getExpr()
|
||||
.getEnclosingMethod()
|
||||
.getName()
|
||||
.regexpMatch(".*(create|update|destroy).*")
|
||||
// We only want handlers for GET requests.
|
||||
// Handlers for other HTTP methods are not as vulnerable to URL
|
||||
// redirection as browsers will not initiate them from clicking a link.
|
||||
method = this.asExpr().getExpr().getEnclosingMethod() and
|
||||
(
|
||||
// If there's a Rails GET route to this handler, we can be certain that it is a candidate.
|
||||
method.(ActionControllerActionMethod).getARoute().getHttpMethod() = "get"
|
||||
or
|
||||
// Otherwise, we have to rely on a heuristic to filter out invulnerable handlers.
|
||||
// We exclude any handlers with names containing create/update/destroy, as these are not likely to handle GET requests.
|
||||
not exists(method.(ActionControllerActionMethod).getARoute()) and
|
||||
not method.getName().regexpMatch(".*(create|update|destroy).*")
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1052,13 +1052,13 @@ private module SuffixConstruction {
|
||||
*/
|
||||
pragma[noinline]
|
||||
private string relevant(RegExpRoot root) {
|
||||
exists(ascii(result))
|
||||
exists(ascii(result)) and exists(root)
|
||||
or
|
||||
exists(InputSymbol s | belongsTo(s, root) | result = intersect(s, _))
|
||||
or
|
||||
// The characters from `hasSimpleRejectEdge`. Only `\n` is really needed (as `\n` is not in the `ascii` relation).
|
||||
// The three chars must be kept in sync with `hasSimpleRejectEdge`.
|
||||
result = ["|", "\n", "Z"]
|
||||
result = ["|", "\n", "Z"] and exists(root)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
private import TypeTrackerSpecific
|
||||
|
||||
/**
|
||||
* Any string that may appear as the name of a piece of content. This will usually include things like:
|
||||
* A string that may appear as the name of a piece of content. This will usually include things like:
|
||||
* - Attribute names (in Python)
|
||||
* - Property names (in JavaScript)
|
||||
*
|
||||
@@ -18,7 +18,7 @@ class ContentName extends string {
|
||||
ContentName() { this = getPossibleContentName() }
|
||||
}
|
||||
|
||||
/** Either a content name, or the empty string (representing no content). */
|
||||
/** A content name, or the empty string (representing no content). */
|
||||
class OptionalContentName extends string {
|
||||
OptionalContentName() { this instanceof ContentName or this = "" }
|
||||
}
|
||||
@@ -200,7 +200,7 @@ module StepSummary {
|
||||
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentName content)
|
||||
|
||||
/**
|
||||
* Summary of the steps needed to track a value to a given dataflow node.
|
||||
* A summary of the steps needed to track a value to a given dataflow node.
|
||||
*
|
||||
* This can be used to track objects that implement a certain API in order to
|
||||
* recognize calls to that API. Note that type-tracking does not by itself provide a
|
||||
@@ -347,7 +347,7 @@ module TypeTracker {
|
||||
private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, OptionalContentName content)
|
||||
|
||||
/**
|
||||
* Summary of the steps needed to back-track a use of a value to a given dataflow node.
|
||||
* A summary of the steps needed to back-track a use of a value to a given dataflow node.
|
||||
*
|
||||
* This can for example be used to track callbacks that are passed to a certain API,
|
||||
* so we can model specific parameters of that callback as having a certain type.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-all
|
||||
version: 0.0.8-dev
|
||||
version: 0.0.10-dev
|
||||
groups: ruby
|
||||
extractor: ruby
|
||||
dbscheme: ruby.dbscheme
|
||||
|
||||
@@ -56,13 +56,15 @@ case @diagnostic.severity of
|
||||
|
||||
@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable
|
||||
|
||||
@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_operator | @ruby_token_simple_symbol
|
||||
@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_constant | @ruby_token_identifier | @ruby_token_operator | @ruby_token_simple_symbol | @ruby_underscore_nonlocal_variable
|
||||
|
||||
@ruby_underscore_nonlocal_variable = @ruby_token_class_variable | @ruby_token_global_variable | @ruby_token_instance_variable
|
||||
|
||||
@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant
|
||||
|
||||
@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic
|
||||
|
||||
@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern
|
||||
@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern
|
||||
|
||||
@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric
|
||||
|
||||
@@ -74,13 +76,12 @@ case @diagnostic.severity of
|
||||
|
||||
@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier
|
||||
|
||||
@ruby_underscore_variable = @ruby_token_class_variable | @ruby_token_constant | @ruby_token_global_variable | @ruby_token_identifier | @ruby_token_instance_variable | @ruby_token_self | @ruby_token_super
|
||||
@ruby_underscore_variable = @ruby_token_constant | @ruby_token_identifier | @ruby_token_self | @ruby_token_super | @ruby_underscore_nonlocal_variable
|
||||
|
||||
ruby_alias_def(
|
||||
unique int id: @ruby_alias,
|
||||
int alias: @ruby_underscore_method_name ref,
|
||||
int name: @ruby_underscore_method_name ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_underscore_method_name ref
|
||||
);
|
||||
|
||||
#keyset[ruby_alternative_pattern, index]
|
||||
@@ -91,8 +92,7 @@ ruby_alternative_pattern_alternatives(
|
||||
);
|
||||
|
||||
ruby_alternative_pattern_def(
|
||||
unique int id: @ruby_alternative_pattern,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_alternative_pattern
|
||||
);
|
||||
|
||||
@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
|
||||
@@ -105,8 +105,7 @@ ruby_argument_list_child(
|
||||
);
|
||||
|
||||
ruby_argument_list_def(
|
||||
unique int id: @ruby_argument_list,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_argument_list
|
||||
);
|
||||
|
||||
@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
|
||||
@@ -119,8 +118,7 @@ ruby_array_child(
|
||||
);
|
||||
|
||||
ruby_array_def(
|
||||
unique int id: @ruby_array,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_array
|
||||
);
|
||||
|
||||
ruby_array_pattern_class(
|
||||
@@ -138,15 +136,13 @@ ruby_array_pattern_child(
|
||||
);
|
||||
|
||||
ruby_array_pattern_def(
|
||||
unique int id: @ruby_array_pattern,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_array_pattern
|
||||
);
|
||||
|
||||
ruby_as_pattern_def(
|
||||
unique int id: @ruby_as_pattern,
|
||||
int name: @ruby_token_identifier ref,
|
||||
int value: @ruby_underscore_pattern_expr ref,
|
||||
int loc: @location ref
|
||||
int value: @ruby_underscore_pattern_expr ref
|
||||
);
|
||||
|
||||
@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs
|
||||
@@ -156,8 +152,7 @@ ruby_as_pattern_def(
|
||||
ruby_assignment_def(
|
||||
unique int id: @ruby_assignment,
|
||||
int left: @ruby_assignment_left_type ref,
|
||||
int right: @ruby_assignment_right_type ref,
|
||||
int loc: @location ref
|
||||
int right: @ruby_assignment_right_type ref
|
||||
);
|
||||
|
||||
@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
|
||||
@@ -170,8 +165,7 @@ ruby_bare_string_child(
|
||||
);
|
||||
|
||||
ruby_bare_string_def(
|
||||
unique int id: @ruby_bare_string,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_bare_string
|
||||
);
|
||||
|
||||
@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
|
||||
@@ -184,8 +178,7 @@ ruby_bare_symbol_child(
|
||||
);
|
||||
|
||||
ruby_bare_symbol_def(
|
||||
unique int id: @ruby_bare_symbol,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_bare_symbol
|
||||
);
|
||||
|
||||
@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -198,8 +191,7 @@ ruby_begin_child(
|
||||
);
|
||||
|
||||
ruby_begin_def(
|
||||
unique int id: @ruby_begin,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_begin
|
||||
);
|
||||
|
||||
@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -212,8 +204,7 @@ ruby_begin_block_child(
|
||||
);
|
||||
|
||||
ruby_begin_block_def(
|
||||
unique int id: @ruby_begin_block,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_begin_block
|
||||
);
|
||||
|
||||
case @ruby_binary.operator of
|
||||
@@ -249,8 +240,7 @@ ruby_binary_def(
|
||||
unique int id: @ruby_binary,
|
||||
int left: @ruby_underscore_expression ref,
|
||||
int operator: int ref,
|
||||
int right: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int right: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_block_parameters(
|
||||
@@ -268,20 +258,25 @@ ruby_block_child(
|
||||
);
|
||||
|
||||
ruby_block_def(
|
||||
unique int id: @ruby_block,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_block
|
||||
);
|
||||
|
||||
ruby_block_argument_child(
|
||||
unique int ruby_block_argument: @ruby_block_argument ref,
|
||||
unique int child: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
ruby_block_argument_def(
|
||||
unique int id: @ruby_block_argument,
|
||||
int child: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_block_argument
|
||||
);
|
||||
|
||||
ruby_block_parameter_name(
|
||||
unique int ruby_block_parameter: @ruby_block_parameter ref,
|
||||
unique int name: @ruby_token_identifier ref
|
||||
);
|
||||
|
||||
ruby_block_parameter_def(
|
||||
unique int id: @ruby_block_parameter,
|
||||
int name: @ruby_token_identifier ref,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_block_parameter
|
||||
);
|
||||
|
||||
@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
|
||||
@@ -294,8 +289,7 @@ ruby_block_parameters_child(
|
||||
);
|
||||
|
||||
ruby_block_parameters_def(
|
||||
unique int id: @ruby_block_parameters,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_block_parameters
|
||||
);
|
||||
|
||||
ruby_break_child(
|
||||
@@ -304,8 +298,7 @@ ruby_break_child(
|
||||
);
|
||||
|
||||
ruby_break_def(
|
||||
unique int id: @ruby_break,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_break
|
||||
);
|
||||
|
||||
ruby_call_arguments(
|
||||
@@ -331,8 +324,7 @@ ruby_call_receiver(
|
||||
|
||||
ruby_call_def(
|
||||
unique int id: @ruby_call,
|
||||
int method: @ruby_call_method_type ref,
|
||||
int loc: @location ref
|
||||
int method: @ruby_call_method_type ref
|
||||
);
|
||||
|
||||
ruby_case_value(
|
||||
@@ -350,8 +342,7 @@ ruby_case_child(
|
||||
);
|
||||
|
||||
ruby_case_def(
|
||||
unique int id: @ruby_case__,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_case__
|
||||
);
|
||||
|
||||
#keyset[ruby_case_match, index]
|
||||
@@ -368,8 +359,7 @@ ruby_case_match_else(
|
||||
|
||||
ruby_case_match_def(
|
||||
unique int id: @ruby_case_match,
|
||||
int value: @ruby_underscore_statement ref,
|
||||
int loc: @location ref
|
||||
int value: @ruby_underscore_statement ref
|
||||
);
|
||||
|
||||
#keyset[ruby_chained_string, index]
|
||||
@@ -380,8 +370,7 @@ ruby_chained_string_child(
|
||||
);
|
||||
|
||||
ruby_chained_string_def(
|
||||
unique int id: @ruby_chained_string,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_chained_string
|
||||
);
|
||||
|
||||
@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant
|
||||
@@ -402,16 +391,14 @@ ruby_class_child(
|
||||
|
||||
ruby_class_def(
|
||||
unique int id: @ruby_class,
|
||||
int name: @ruby_class_name_type ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_class_name_type ref
|
||||
);
|
||||
|
||||
ruby_conditional_def(
|
||||
unique int id: @ruby_conditional,
|
||||
int alternative: @ruby_underscore_arg ref,
|
||||
int condition: @ruby_underscore_arg ref,
|
||||
int consequence: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int consequence: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
|
||||
@@ -424,8 +411,7 @@ ruby_delimited_symbol_child(
|
||||
);
|
||||
|
||||
ruby_delimited_symbol_def(
|
||||
unique int id: @ruby_delimited_symbol,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_delimited_symbol
|
||||
);
|
||||
|
||||
@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs
|
||||
@@ -438,8 +424,7 @@ ruby_destructured_left_assignment_child(
|
||||
);
|
||||
|
||||
ruby_destructured_left_assignment_def(
|
||||
unique int id: @ruby_destructured_left_assignment,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_destructured_left_assignment
|
||||
);
|
||||
|
||||
@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
|
||||
@@ -452,8 +437,7 @@ ruby_destructured_parameter_child(
|
||||
);
|
||||
|
||||
ruby_destructured_parameter_def(
|
||||
unique int id: @ruby_destructured_parameter,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_destructured_parameter
|
||||
);
|
||||
|
||||
@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -466,8 +450,7 @@ ruby_do_child(
|
||||
);
|
||||
|
||||
ruby_do_def(
|
||||
unique int id: @ruby_do,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_do
|
||||
);
|
||||
|
||||
ruby_do_block_parameters(
|
||||
@@ -485,8 +468,7 @@ ruby_do_block_child(
|
||||
);
|
||||
|
||||
ruby_do_block_def(
|
||||
unique int id: @ruby_do_block,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_do_block
|
||||
);
|
||||
|
||||
@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression
|
||||
@@ -500,8 +482,7 @@ ruby_element_reference_child(
|
||||
|
||||
ruby_element_reference_def(
|
||||
unique int id: @ruby_element_reference,
|
||||
int object: @ruby_underscore_primary ref,
|
||||
int loc: @location ref
|
||||
int object: @ruby_underscore_primary ref
|
||||
);
|
||||
|
||||
@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -514,8 +495,7 @@ ruby_else_child(
|
||||
);
|
||||
|
||||
ruby_else_def(
|
||||
unique int id: @ruby_else,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_else
|
||||
);
|
||||
|
||||
@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif
|
||||
@@ -532,8 +512,7 @@ ruby_elsif_consequence(
|
||||
|
||||
ruby_elsif_def(
|
||||
unique int id: @ruby_elsif,
|
||||
int condition: @ruby_underscore_statement ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_statement ref
|
||||
);
|
||||
|
||||
@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -546,8 +525,7 @@ ruby_end_block_child(
|
||||
);
|
||||
|
||||
ruby_end_block_def(
|
||||
unique int id: @ruby_end_block,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_end_block
|
||||
);
|
||||
|
||||
@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -560,14 +538,12 @@ ruby_ensure_child(
|
||||
);
|
||||
|
||||
ruby_ensure_def(
|
||||
unique int id: @ruby_ensure,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_ensure
|
||||
);
|
||||
|
||||
ruby_exception_variable_def(
|
||||
unique int id: @ruby_exception_variable,
|
||||
int child: @ruby_underscore_lhs ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_underscore_lhs ref
|
||||
);
|
||||
|
||||
@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg
|
||||
@@ -580,8 +556,12 @@ ruby_exceptions_child(
|
||||
);
|
||||
|
||||
ruby_exceptions_def(
|
||||
unique int id: @ruby_exceptions,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_exceptions
|
||||
);
|
||||
|
||||
ruby_expression_reference_pattern_def(
|
||||
unique int id: @ruby_expression_reference_pattern,
|
||||
int value: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_find_pattern_class(
|
||||
@@ -599,8 +579,7 @@ ruby_find_pattern_child(
|
||||
);
|
||||
|
||||
ruby_find_pattern_def(
|
||||
unique int id: @ruby_find_pattern,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_find_pattern
|
||||
);
|
||||
|
||||
@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs
|
||||
@@ -609,8 +588,7 @@ ruby_for_def(
|
||||
unique int id: @ruby_for,
|
||||
int body: @ruby_do ref,
|
||||
int pattern: @ruby_for_pattern_type ref,
|
||||
int value: @ruby_in ref,
|
||||
int loc: @location ref
|
||||
int value: @ruby_in ref
|
||||
);
|
||||
|
||||
@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair
|
||||
@@ -623,8 +601,7 @@ ruby_hash_child(
|
||||
);
|
||||
|
||||
ruby_hash_def(
|
||||
unique int id: @ruby_hash,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_hash
|
||||
);
|
||||
|
||||
ruby_hash_pattern_class(
|
||||
@@ -642,14 +619,12 @@ ruby_hash_pattern_child(
|
||||
);
|
||||
|
||||
ruby_hash_pattern_def(
|
||||
unique int id: @ruby_hash_pattern,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_hash_pattern
|
||||
);
|
||||
|
||||
ruby_hash_splat_argument_def(
|
||||
unique int id: @ruby_hash_splat_argument,
|
||||
int child: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
ruby_hash_splat_parameter_name(
|
||||
@@ -658,8 +633,7 @@ ruby_hash_splat_parameter_name(
|
||||
);
|
||||
|
||||
ruby_hash_splat_parameter_def(
|
||||
unique int id: @ruby_hash_splat_parameter,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_hash_splat_parameter
|
||||
);
|
||||
|
||||
@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end
|
||||
@@ -672,8 +646,7 @@ ruby_heredoc_body_child(
|
||||
);
|
||||
|
||||
ruby_heredoc_body_def(
|
||||
unique int id: @ruby_heredoc_body,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_heredoc_body
|
||||
);
|
||||
|
||||
@ruby_if_alternative_type = @ruby_else | @ruby_elsif
|
||||
@@ -690,27 +663,23 @@ ruby_if_consequence(
|
||||
|
||||
ruby_if_def(
|
||||
unique int id: @ruby_if,
|
||||
int condition: @ruby_underscore_statement ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_statement ref
|
||||
);
|
||||
|
||||
ruby_if_guard_def(
|
||||
unique int id: @ruby_if_guard,
|
||||
int condition: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_if_modifier_def(
|
||||
unique int id: @ruby_if_modifier,
|
||||
int body: @ruby_underscore_statement ref,
|
||||
int condition: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_in_def(
|
||||
unique int id: @ruby_in,
|
||||
int child: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
ruby_in_clause_body(
|
||||
@@ -727,8 +696,7 @@ ruby_in_clause_guard(
|
||||
|
||||
ruby_in_clause_def(
|
||||
unique int id: @ruby_in_clause,
|
||||
int pattern: @ruby_underscore_pattern_top_expr_body ref,
|
||||
int loc: @location ref
|
||||
int pattern: @ruby_underscore_pattern_top_expr_body ref
|
||||
);
|
||||
|
||||
@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -741,8 +709,7 @@ ruby_interpolation_child(
|
||||
);
|
||||
|
||||
ruby_interpolation_def(
|
||||
unique int id: @ruby_interpolation,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_interpolation
|
||||
);
|
||||
|
||||
ruby_keyword_parameter_value(
|
||||
@@ -752,8 +719,7 @@ ruby_keyword_parameter_value(
|
||||
|
||||
ruby_keyword_parameter_def(
|
||||
unique int id: @ruby_keyword_parameter,
|
||||
int name: @ruby_token_identifier ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_token_identifier ref
|
||||
);
|
||||
|
||||
@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol
|
||||
@@ -765,8 +731,7 @@ ruby_keyword_pattern_value(
|
||||
|
||||
ruby_keyword_pattern_def(
|
||||
unique int id: @ruby_keyword_pattern,
|
||||
int key__: @ruby_keyword_pattern_key_type ref,
|
||||
int loc: @location ref
|
||||
int key__: @ruby_keyword_pattern_key_type ref
|
||||
);
|
||||
|
||||
@ruby_lambda_body_type = @ruby_block | @ruby_do_block
|
||||
@@ -778,8 +743,7 @@ ruby_lambda_parameters(
|
||||
|
||||
ruby_lambda_def(
|
||||
unique int id: @ruby_lambda,
|
||||
int body: @ruby_lambda_body_type ref,
|
||||
int loc: @location ref
|
||||
int body: @ruby_lambda_body_type ref
|
||||
);
|
||||
|
||||
@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
|
||||
@@ -792,8 +756,7 @@ ruby_lambda_parameters_child(
|
||||
);
|
||||
|
||||
ruby_lambda_parameters_def(
|
||||
unique int id: @ruby_lambda_parameters,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_lambda_parameters
|
||||
);
|
||||
|
||||
@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs
|
||||
@@ -806,8 +769,7 @@ ruby_left_assignment_list_child(
|
||||
);
|
||||
|
||||
ruby_left_assignment_list_def(
|
||||
unique int id: @ruby_left_assignment_list,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_left_assignment_list
|
||||
);
|
||||
|
||||
ruby_method_parameters(
|
||||
@@ -826,8 +788,7 @@ ruby_method_child(
|
||||
|
||||
ruby_method_def(
|
||||
unique int id: @ruby_method,
|
||||
int name: @ruby_underscore_method_name ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_underscore_method_name ref
|
||||
);
|
||||
|
||||
@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier
|
||||
@@ -840,8 +801,7 @@ ruby_method_parameters_child(
|
||||
);
|
||||
|
||||
ruby_method_parameters_def(
|
||||
unique int id: @ruby_method_parameters,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_method_parameters
|
||||
);
|
||||
|
||||
@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant
|
||||
@@ -857,8 +817,7 @@ ruby_module_child(
|
||||
|
||||
ruby_module_def(
|
||||
unique int id: @ruby_module,
|
||||
int name: @ruby_module_name_type ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_module_name_type ref
|
||||
);
|
||||
|
||||
ruby_next_child(
|
||||
@@ -867,8 +826,7 @@ ruby_next_child(
|
||||
);
|
||||
|
||||
ruby_next_def(
|
||||
unique int id: @ruby_next,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_next
|
||||
);
|
||||
|
||||
case @ruby_operator_assignment.operator of
|
||||
@@ -892,30 +850,30 @@ ruby_operator_assignment_def(
|
||||
unique int id: @ruby_operator_assignment,
|
||||
int left: @ruby_underscore_lhs ref,
|
||||
int operator: int ref,
|
||||
int right: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int right: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_optional_parameter_def(
|
||||
unique int id: @ruby_optional_parameter,
|
||||
int name: @ruby_token_identifier ref,
|
||||
int value: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int value: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg
|
||||
|
||||
ruby_pair_value(
|
||||
unique int ruby_pair: @ruby_pair ref,
|
||||
unique int value: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
ruby_pair_def(
|
||||
unique int id: @ruby_pair,
|
||||
int key__: @ruby_pair_key_type ref,
|
||||
int value: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int key__: @ruby_pair_key_type ref
|
||||
);
|
||||
|
||||
ruby_parenthesized_pattern_def(
|
||||
unique int id: @ruby_parenthesized_pattern,
|
||||
int child: @ruby_underscore_pattern_expr ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_underscore_pattern_expr ref
|
||||
);
|
||||
|
||||
@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -928,16 +886,14 @@ ruby_parenthesized_statements_child(
|
||||
);
|
||||
|
||||
ruby_parenthesized_statements_def(
|
||||
unique int id: @ruby_parenthesized_statements,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_parenthesized_statements
|
||||
);
|
||||
|
||||
@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg
|
||||
|
||||
ruby_pattern_def(
|
||||
unique int id: @ruby_pattern,
|
||||
int child: @ruby_pattern_child_type ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_pattern_child_type ref
|
||||
);
|
||||
|
||||
@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement
|
||||
@@ -950,8 +906,7 @@ ruby_program_child(
|
||||
);
|
||||
|
||||
ruby_program_def(
|
||||
unique int id: @ruby_program,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_program
|
||||
);
|
||||
|
||||
@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive
|
||||
@@ -976,16 +931,14 @@ case @ruby_range.operator of
|
||||
|
||||
ruby_range_def(
|
||||
unique int id: @ruby_range,
|
||||
int operator: int ref,
|
||||
int loc: @location ref
|
||||
int operator: int ref
|
||||
);
|
||||
|
||||
@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer
|
||||
|
||||
ruby_rational_def(
|
||||
unique int id: @ruby_rational,
|
||||
int child: @ruby_rational_child_type ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_rational_child_type ref
|
||||
);
|
||||
|
||||
ruby_redo_child(
|
||||
@@ -994,8 +947,7 @@ ruby_redo_child(
|
||||
);
|
||||
|
||||
ruby_redo_def(
|
||||
unique int id: @ruby_redo,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_redo
|
||||
);
|
||||
|
||||
@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
|
||||
@@ -1008,8 +960,7 @@ ruby_regex_child(
|
||||
);
|
||||
|
||||
ruby_regex_def(
|
||||
unique int id: @ruby_regex,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_regex
|
||||
);
|
||||
|
||||
ruby_rescue_body(
|
||||
@@ -1028,8 +979,7 @@ ruby_rescue_variable(
|
||||
);
|
||||
|
||||
ruby_rescue_def(
|
||||
unique int id: @ruby_rescue,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_rescue
|
||||
);
|
||||
|
||||
@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement
|
||||
@@ -1037,8 +987,7 @@ ruby_rescue_def(
|
||||
ruby_rescue_modifier_def(
|
||||
unique int id: @ruby_rescue_modifier,
|
||||
int body: @ruby_rescue_modifier_body_type ref,
|
||||
int handler: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int handler: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_rest_assignment_child(
|
||||
@@ -1047,8 +996,7 @@ ruby_rest_assignment_child(
|
||||
);
|
||||
|
||||
ruby_rest_assignment_def(
|
||||
unique int id: @ruby_rest_assignment,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_rest_assignment
|
||||
);
|
||||
|
||||
ruby_retry_child(
|
||||
@@ -1057,8 +1005,7 @@ ruby_retry_child(
|
||||
);
|
||||
|
||||
ruby_retry_def(
|
||||
unique int id: @ruby_retry,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_retry
|
||||
);
|
||||
|
||||
ruby_return_child(
|
||||
@@ -1067,8 +1014,7 @@ ruby_return_child(
|
||||
);
|
||||
|
||||
ruby_return_def(
|
||||
unique int id: @ruby_return,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_return
|
||||
);
|
||||
|
||||
@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg
|
||||
@@ -1081,8 +1027,7 @@ ruby_right_assignment_list_child(
|
||||
);
|
||||
|
||||
ruby_right_assignment_list_def(
|
||||
unique int id: @ruby_right_assignment_list,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_right_assignment_list
|
||||
);
|
||||
|
||||
@ruby_scope_resolution_name_type = @ruby_token_constant | @ruby_token_identifier
|
||||
@@ -1096,14 +1041,12 @@ ruby_scope_resolution_scope(
|
||||
|
||||
ruby_scope_resolution_def(
|
||||
unique int id: @ruby_scope_resolution,
|
||||
int name: @ruby_scope_resolution_name_type ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_scope_resolution_name_type ref
|
||||
);
|
||||
|
||||
ruby_setter_def(
|
||||
unique int id: @ruby_setter,
|
||||
int name: @ruby_token_identifier ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_token_identifier ref
|
||||
);
|
||||
|
||||
@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -1117,8 +1060,7 @@ ruby_singleton_class_child(
|
||||
|
||||
ruby_singleton_class_def(
|
||||
unique int id: @ruby_singleton_class,
|
||||
int value: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int value: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable
|
||||
@@ -1140,14 +1082,12 @@ ruby_singleton_method_child(
|
||||
ruby_singleton_method_def(
|
||||
unique int id: @ruby_singleton_method,
|
||||
int name: @ruby_underscore_method_name ref,
|
||||
int object: @ruby_singleton_method_object_type ref,
|
||||
int loc: @location ref
|
||||
int object: @ruby_singleton_method_object_type ref
|
||||
);
|
||||
|
||||
ruby_splat_argument_def(
|
||||
unique int id: @ruby_splat_argument,
|
||||
int child: @ruby_underscore_arg ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_underscore_arg ref
|
||||
);
|
||||
|
||||
ruby_splat_parameter_name(
|
||||
@@ -1156,8 +1096,7 @@ ruby_splat_parameter_name(
|
||||
);
|
||||
|
||||
ruby_splat_parameter_def(
|
||||
unique int id: @ruby_splat_parameter,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_splat_parameter
|
||||
);
|
||||
|
||||
@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
|
||||
@@ -1170,8 +1109,7 @@ ruby_string_child(
|
||||
);
|
||||
|
||||
ruby_string_def(
|
||||
unique int id: @ruby_string__,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_string__
|
||||
);
|
||||
|
||||
#keyset[ruby_string_array, index]
|
||||
@@ -1182,8 +1120,7 @@ ruby_string_array_child(
|
||||
);
|
||||
|
||||
ruby_string_array_def(
|
||||
unique int id: @ruby_string_array,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_string_array
|
||||
);
|
||||
|
||||
@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content
|
||||
@@ -1196,14 +1133,12 @@ ruby_subshell_child(
|
||||
);
|
||||
|
||||
ruby_subshell_def(
|
||||
unique int id: @ruby_subshell,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_subshell
|
||||
);
|
||||
|
||||
ruby_superclass_def(
|
||||
unique int id: @ruby_superclass,
|
||||
int child: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int child: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
#keyset[ruby_symbol_array, index]
|
||||
@@ -1214,8 +1149,7 @@ ruby_symbol_array_child(
|
||||
);
|
||||
|
||||
ruby_symbol_array_def(
|
||||
unique int id: @ruby_symbol_array,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_symbol_array
|
||||
);
|
||||
|
||||
@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement
|
||||
@@ -1228,8 +1162,7 @@ ruby_then_child(
|
||||
);
|
||||
|
||||
ruby_then_def(
|
||||
unique int id: @ruby_then,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_then
|
||||
);
|
||||
|
||||
@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric
|
||||
@@ -1247,8 +1180,7 @@ case @ruby_unary.operator of
|
||||
ruby_unary_def(
|
||||
unique int id: @ruby_unary,
|
||||
int operand: @ruby_unary_operand_type ref,
|
||||
int operator: int ref,
|
||||
int loc: @location ref
|
||||
int operator: int ref
|
||||
);
|
||||
|
||||
#keyset[ruby_undef, index]
|
||||
@@ -1259,8 +1191,7 @@ ruby_undef_child(
|
||||
);
|
||||
|
||||
ruby_undef_def(
|
||||
unique int id: @ruby_undef,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_undef
|
||||
);
|
||||
|
||||
@ruby_unless_alternative_type = @ruby_else | @ruby_elsif
|
||||
@@ -1277,41 +1208,37 @@ ruby_unless_consequence(
|
||||
|
||||
ruby_unless_def(
|
||||
unique int id: @ruby_unless,
|
||||
int condition: @ruby_underscore_statement ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_statement ref
|
||||
);
|
||||
|
||||
ruby_unless_guard_def(
|
||||
unique int id: @ruby_unless_guard,
|
||||
int condition: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_unless_modifier_def(
|
||||
unique int id: @ruby_unless_modifier,
|
||||
int body: @ruby_underscore_statement ref,
|
||||
int condition: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_until_def(
|
||||
unique int id: @ruby_until,
|
||||
int body: @ruby_do ref,
|
||||
int condition: @ruby_underscore_statement ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_statement ref
|
||||
);
|
||||
|
||||
ruby_until_modifier_def(
|
||||
unique int id: @ruby_until_modifier,
|
||||
int body: @ruby_underscore_statement ref,
|
||||
int condition: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
@ruby_variable_reference_pattern_name_type = @ruby_token_identifier | @ruby_underscore_nonlocal_variable
|
||||
|
||||
ruby_variable_reference_pattern_def(
|
||||
unique int id: @ruby_variable_reference_pattern,
|
||||
int name: @ruby_token_identifier ref,
|
||||
int loc: @location ref
|
||||
int name: @ruby_variable_reference_pattern_name_type ref
|
||||
);
|
||||
|
||||
ruby_when_body(
|
||||
@@ -1327,22 +1254,19 @@ ruby_when_pattern(
|
||||
);
|
||||
|
||||
ruby_when_def(
|
||||
unique int id: @ruby_when,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_when
|
||||
);
|
||||
|
||||
ruby_while_def(
|
||||
unique int id: @ruby_while,
|
||||
int body: @ruby_do ref,
|
||||
int condition: @ruby_underscore_statement ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_statement ref
|
||||
);
|
||||
|
||||
ruby_while_modifier_def(
|
||||
unique int id: @ruby_while_modifier,
|
||||
int body: @ruby_underscore_statement ref,
|
||||
int condition: @ruby_underscore_expression ref,
|
||||
int loc: @location ref
|
||||
int condition: @ruby_underscore_expression ref
|
||||
);
|
||||
|
||||
ruby_yield_child(
|
||||
@@ -1351,15 +1275,13 @@ ruby_yield_child(
|
||||
);
|
||||
|
||||
ruby_yield_def(
|
||||
unique int id: @ruby_yield,
|
||||
int loc: @location ref
|
||||
unique int id: @ruby_yield
|
||||
);
|
||||
|
||||
ruby_tokeninfo(
|
||||
unique int id: @ruby_token,
|
||||
int kind: int ref,
|
||||
string value: string ref,
|
||||
int loc: @location ref
|
||||
string value: string ref
|
||||
);
|
||||
|
||||
case @ruby_token.kind of
|
||||
@@ -1398,39 +1320,36 @@ case @ruby_token.kind of
|
||||
;
|
||||
|
||||
|
||||
@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield
|
||||
@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield
|
||||
|
||||
@ruby_ast_node_parent = @file | @ruby_ast_node
|
||||
|
||||
#keyset[parent, parent_index]
|
||||
ruby_ast_node_parent(
|
||||
int child: @ruby_ast_node ref,
|
||||
ruby_ast_node_info(
|
||||
unique int node: @ruby_ast_node ref,
|
||||
int parent: @ruby_ast_node_parent ref,
|
||||
int parent_index: int ref
|
||||
int parent_index: int ref,
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
erb_comment_directive_def(
|
||||
unique int id: @erb_comment_directive,
|
||||
int child: @erb_token_comment ref,
|
||||
int loc: @location ref
|
||||
int child: @erb_token_comment ref
|
||||
);
|
||||
|
||||
erb_directive_def(
|
||||
unique int id: @erb_directive,
|
||||
int child: @erb_token_code ref,
|
||||
int loc: @location ref
|
||||
int child: @erb_token_code ref
|
||||
);
|
||||
|
||||
erb_graphql_directive_def(
|
||||
unique int id: @erb_graphql_directive,
|
||||
int child: @erb_token_code ref,
|
||||
int loc: @location ref
|
||||
int child: @erb_token_code ref
|
||||
);
|
||||
|
||||
erb_output_directive_def(
|
||||
unique int id: @erb_output_directive,
|
||||
int child: @erb_token_code ref,
|
||||
int loc: @location ref
|
||||
int child: @erb_token_code ref
|
||||
);
|
||||
|
||||
@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content
|
||||
@@ -1443,15 +1362,13 @@ erb_template_child(
|
||||
);
|
||||
|
||||
erb_template_def(
|
||||
unique int id: @erb_template,
|
||||
int loc: @location ref
|
||||
unique int id: @erb_template
|
||||
);
|
||||
|
||||
erb_tokeninfo(
|
||||
unique int id: @erb_token,
|
||||
int kind: int ref,
|
||||
string value: string ref,
|
||||
int loc: @location ref
|
||||
string value: string ref
|
||||
);
|
||||
|
||||
case @erb_token.kind of
|
||||
@@ -1467,9 +1384,10 @@ case @erb_token.kind of
|
||||
@erb_ast_node_parent = @erb_ast_node | @file
|
||||
|
||||
#keyset[parent, parent_index]
|
||||
erb_ast_node_parent(
|
||||
int child: @erb_ast_node ref,
|
||||
erb_ast_node_info(
|
||||
unique int node: @erb_ast_node ref,
|
||||
int parent: @erb_ast_node_parent ref,
|
||||
int parent_index: int ref
|
||||
int parent_index: int ref,
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,122 +6,22 @@
|
||||
*/
|
||||
class Person extends string {
|
||||
Person() {
|
||||
this = "Ronil" or
|
||||
this = "Dina" or
|
||||
this = "Ravi" or
|
||||
this = "Bruce" or
|
||||
this = "Jo" or
|
||||
this = "Aida" or
|
||||
this = "Esme" or
|
||||
this = "Charlie" or
|
||||
this = "Fred" or
|
||||
this = "Meera" or
|
||||
this = "Maya" or
|
||||
this = "Chad" or
|
||||
this = "Tiana" or
|
||||
this = "Laura" or
|
||||
this = "George" or
|
||||
this = "Will" or
|
||||
this = "Mary" or
|
||||
this = "Almira" or
|
||||
this = "Susannah" or
|
||||
this = "Rhoda" or
|
||||
this = "Cynthia" or
|
||||
this = "Eunice" or
|
||||
this = "Olive" or
|
||||
this = "Virginia" or
|
||||
this = "Angeline" or
|
||||
this = "Helen" or
|
||||
this = "Cornelia" or
|
||||
this = "Harriet" or
|
||||
this = "Mahala" or
|
||||
this = "Abby" or
|
||||
this = "Margaret" or
|
||||
this = "Deb" or
|
||||
this = "Minerva" or
|
||||
this = "Severus" or
|
||||
this = "Lavina" or
|
||||
this = "Adeline" or
|
||||
this = "Cath" or
|
||||
this = "Elisa" or
|
||||
this = "Lucretia" or
|
||||
this = "Anne" or
|
||||
this = "Eleanor" or
|
||||
this = "Joanna" or
|
||||
this = "Adam" or
|
||||
this = "Agnes" or
|
||||
this = "Rosanna" or
|
||||
this = "Clara" or
|
||||
this = "Melissa" or
|
||||
this = "Amy" or
|
||||
this = "Isabel" or
|
||||
this = "Jemima" or
|
||||
this = "Cordelia" or
|
||||
this = "Melinda" or
|
||||
this = "Delila" or
|
||||
this = "Jeremiah" or
|
||||
this = "Elijah" or
|
||||
this = "Hester" or
|
||||
this = "Walter" or
|
||||
this = "Oliver" or
|
||||
this = "Hugh" or
|
||||
this = "Aaron" or
|
||||
this = "Reuben" or
|
||||
this = "Eli" or
|
||||
this = "Amos" or
|
||||
this = "Augustus" or
|
||||
this = "Theodore" or
|
||||
this = "Ira" or
|
||||
this = "Timothy" or
|
||||
this = "Cyrus" or
|
||||
this = "Horace" or
|
||||
this = "Simon" or
|
||||
this = "Asa" or
|
||||
this = "Frank" or
|
||||
this = "Nelson" or
|
||||
this = "Leonard" or
|
||||
this = "Harrison" or
|
||||
this = "Anthony" or
|
||||
this = "Louis" or
|
||||
this = "Milton" or
|
||||
this = "Noah" or
|
||||
this = "Cornelius" or
|
||||
this = "Abdul" or
|
||||
this = "Warren" or
|
||||
this = "Harvey" or
|
||||
this = "Dennis" or
|
||||
this = "Wesley" or
|
||||
this = "Sylvester" or
|
||||
this = "Gilbert" or
|
||||
this = "Sullivan" or
|
||||
this = "Edmund" or
|
||||
this = "Wilson" or
|
||||
this = "Perry" or
|
||||
this = "Matthew" or
|
||||
this = "Simba" or
|
||||
this = "Nala" or
|
||||
this = "Rafiki" or
|
||||
this = "Shenzi" or
|
||||
this = "Ernest" or
|
||||
this = "Gertrude" or
|
||||
this = "Oscar" or
|
||||
this = "Lilian" or
|
||||
this = "Raymond" or
|
||||
this = "Elgar" or
|
||||
this = "Elmer" or
|
||||
this = "Herbert" or
|
||||
this = "Maude" or
|
||||
this = "Mae" or
|
||||
this = "Otto" or
|
||||
this = "Edwin" or
|
||||
this = "Ophelia" or
|
||||
this = "Parsley" or
|
||||
this = "Sage" or
|
||||
this = "Rosemary" or
|
||||
this = "Thyme" or
|
||||
this = "Garfunkel" or
|
||||
this = "King Basil" or
|
||||
this = "Stephen"
|
||||
this =
|
||||
[
|
||||
"Ronil", "Dina", "Ravi", "Bruce", "Jo", "Aida", "Esme", "Charlie", "Fred", "Meera", "Maya",
|
||||
"Chad", "Tiana", "Laura", "George", "Will", "Mary", "Almira", "Susannah", "Rhoda",
|
||||
"Cynthia", "Eunice", "Olive", "Virginia", "Angeline", "Helen", "Cornelia", "Harriet",
|
||||
"Mahala", "Abby", "Margaret", "Deb", "Minerva", "Severus", "Lavina", "Adeline", "Cath",
|
||||
"Elisa", "Lucretia", "Anne", "Eleanor", "Joanna", "Adam", "Agnes", "Rosanna", "Clara",
|
||||
"Melissa", "Amy", "Isabel", "Jemima", "Cordelia", "Melinda", "Delila", "Jeremiah", "Elijah",
|
||||
"Hester", "Walter", "Oliver", "Hugh", "Aaron", "Reuben", "Eli", "Amos", "Augustus",
|
||||
"Theodore", "Ira", "Timothy", "Cyrus", "Horace", "Simon", "Asa", "Frank", "Nelson",
|
||||
"Leonard", "Harrison", "Anthony", "Louis", "Milton", "Noah", "Cornelius", "Abdul", "Warren",
|
||||
"Harvey", "Dennis", "Wesley", "Sylvester", "Gilbert", "Sullivan", "Edmund", "Wilson",
|
||||
"Perry", "Matthew", "Simba", "Nala", "Rafiki", "Shenzi", "Ernest", "Gertrude", "Oscar",
|
||||
"Lilian", "Raymond", "Elgar", "Elmer", "Herbert", "Maude", "Mae", "Otto", "Edwin",
|
||||
"Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel", "King Basil", "Stephen"
|
||||
]
|
||||
}
|
||||
|
||||
/** Gets the hair color of the person. If the person is bald, there is no result. */
|
||||
@@ -936,25 +836,12 @@ class Person extends string {
|
||||
|
||||
/** Holds if the person is deceased. */
|
||||
predicate isDeceased() {
|
||||
this = "Ernest" or
|
||||
this = "Gertrude" or
|
||||
this = "Oscar" or
|
||||
this = "Lilian" or
|
||||
this = "Edwin" or
|
||||
this = "Raymond" or
|
||||
this = "Elgar" or
|
||||
this = "Elmer" or
|
||||
this = "Herbert" or
|
||||
this = "Maude" or
|
||||
this = "Mae" or
|
||||
this = "Otto" or
|
||||
this = "Ophelia" or
|
||||
this = "Parsley" or
|
||||
this = "Sage" or
|
||||
this = "Rosemary" or
|
||||
this = "Thyme" or
|
||||
this = "Garfunkel" or
|
||||
this = "King Basil"
|
||||
this =
|
||||
[
|
||||
"Ernest", "Gertrude", "Oscar", "Lilian", "Edwin", "Raymond", "Elgar", "Elmer", "Herbert",
|
||||
"Maude", "Mae", "Otto", "Ophelia", "Parsley", "Sage", "Rosemary", "Thyme", "Garfunkel",
|
||||
"King Basil"
|
||||
]
|
||||
}
|
||||
|
||||
/** Gets a parent of the person (alive or deceased). */
|
||||
@@ -1195,12 +1082,7 @@ class Person extends string {
|
||||
}
|
||||
|
||||
/** Holds if the person is allowed in the region. Initially, all villagers are allowed in every region. */
|
||||
predicate isAllowedIn(string region) {
|
||||
region = "north" or
|
||||
region = "south" or
|
||||
region = "east" or
|
||||
region = "west"
|
||||
}
|
||||
predicate isAllowedIn(string region) { region = ["north", "south", "east", "west"] }
|
||||
}
|
||||
|
||||
/** Returns a parent of the person. */
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
class Location extends @location {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class ErbAstNodeParent extends @erb_ast_node_parent {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class ErbAstNode extends @erb_ast_node {
|
||||
string toString() { none() }
|
||||
|
||||
ErbAstNodeParent getParent(int index) { erb_ast_node_parent(this, result, index) }
|
||||
|
||||
Location getLocation() {
|
||||
erb_tokeninfo(this, _, _, result) or
|
||||
erb_comment_directive_def(this, _, result) or
|
||||
erb_directive_def(this, _, result) or
|
||||
erb_graphql_directive_def(this, _, result) or
|
||||
erb_output_directive_def(this, _, result) or
|
||||
erb_template_def(this, result)
|
||||
}
|
||||
}
|
||||
|
||||
from ErbAstNode node, ErbAstNodeParent parent, int index, Location loc
|
||||
where parent = node.getParent(index) and loc = node.getLocation()
|
||||
select node, parent, index, loc
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,120 @@
|
||||
class Location extends @location {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class RubyAstNodeParent extends @ruby_ast_node_parent {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
class RubyAstNode extends @ruby_ast_node {
|
||||
string toString() { none() }
|
||||
|
||||
RubyAstNodeParent getParent(int index) { ruby_ast_node_parent(this, result, index) }
|
||||
|
||||
Location getLocation() {
|
||||
ruby_tokeninfo(this, _, _, result) or
|
||||
ruby_alias_def(this, _, _, result) or
|
||||
ruby_alternative_pattern_def(this, result) or
|
||||
ruby_argument_list_def(this, result) or
|
||||
ruby_array_def(this, result) or
|
||||
ruby_array_pattern_def(this, result) or
|
||||
ruby_as_pattern_def(this, _, _, result) or
|
||||
ruby_assignment_def(this, _, _, result) or
|
||||
ruby_bare_string_def(this, result) or
|
||||
ruby_bare_symbol_def(this, result) or
|
||||
ruby_begin_def(this, result) or
|
||||
ruby_begin_block_def(this, result) or
|
||||
ruby_binary_def(this, _, _, _, result) or
|
||||
ruby_block_def(this, result) or
|
||||
ruby_block_argument_def(this, result) or
|
||||
ruby_block_parameter_def(this, result) or
|
||||
ruby_block_parameters_def(this, result) or
|
||||
ruby_break_def(this, result) or
|
||||
ruby_call_def(this, _, result) or
|
||||
ruby_case_def(this, result) or
|
||||
ruby_case_match_def(this, _, result) or
|
||||
ruby_chained_string_def(this, result) or
|
||||
ruby_class_def(this, _, result) or
|
||||
ruby_conditional_def(this, _, _, _, result) or
|
||||
ruby_delimited_symbol_def(this, result) or
|
||||
ruby_destructured_left_assignment_def(this, result) or
|
||||
ruby_destructured_parameter_def(this, result) or
|
||||
ruby_do_def(this, result) or
|
||||
ruby_do_block_def(this, result) or
|
||||
ruby_element_reference_def(this, _, result) or
|
||||
ruby_else_def(this, result) or
|
||||
ruby_elsif_def(this, _, result) or
|
||||
ruby_end_block_def(this, result) or
|
||||
ruby_ensure_def(this, result) or
|
||||
ruby_exception_variable_def(this, _, result) or
|
||||
ruby_exceptions_def(this, result) or
|
||||
ruby_expression_reference_pattern_def(this, _, result) or
|
||||
ruby_find_pattern_def(this, result) or
|
||||
ruby_for_def(this, _, _, _, result) or
|
||||
ruby_hash_def(this, result) or
|
||||
ruby_hash_pattern_def(this, result) or
|
||||
ruby_hash_splat_argument_def(this, _, result) or
|
||||
ruby_hash_splat_parameter_def(this, result) or
|
||||
ruby_heredoc_body_def(this, result) or
|
||||
ruby_if_def(this, _, result) or
|
||||
ruby_if_guard_def(this, _, result) or
|
||||
ruby_if_modifier_def(this, _, _, result) or
|
||||
ruby_in_def(this, _, result) or
|
||||
ruby_in_clause_def(this, _, result) or
|
||||
ruby_interpolation_def(this, result) or
|
||||
ruby_keyword_parameter_def(this, _, result) or
|
||||
ruby_keyword_pattern_def(this, _, result) or
|
||||
ruby_lambda_def(this, _, result) or
|
||||
ruby_lambda_parameters_def(this, result) or
|
||||
ruby_left_assignment_list_def(this, result) or
|
||||
ruby_method_def(this, _, result) or
|
||||
ruby_method_parameters_def(this, result) or
|
||||
ruby_module_def(this, _, result) or
|
||||
ruby_next_def(this, result) or
|
||||
ruby_operator_assignment_def(this, _, _, _, result) or
|
||||
ruby_optional_parameter_def(this, _, _, result) or
|
||||
ruby_pair_def(this, _, result) or
|
||||
ruby_parenthesized_pattern_def(this, _, result) or
|
||||
ruby_parenthesized_statements_def(this, result) or
|
||||
ruby_pattern_def(this, _, result) or
|
||||
ruby_program_def(this, result) or
|
||||
ruby_range_def(this, _, result) or
|
||||
ruby_rational_def(this, _, result) or
|
||||
ruby_redo_def(this, result) or
|
||||
ruby_regex_def(this, result) or
|
||||
ruby_rescue_def(this, result) or
|
||||
ruby_rescue_modifier_def(this, _, _, result) or
|
||||
ruby_rest_assignment_def(this, result) or
|
||||
ruby_retry_def(this, result) or
|
||||
ruby_return_def(this, result) or
|
||||
ruby_right_assignment_list_def(this, result) or
|
||||
ruby_scope_resolution_def(this, _, result) or
|
||||
ruby_setter_def(this, _, result) or
|
||||
ruby_singleton_class_def(this, _, result) or
|
||||
ruby_singleton_method_def(this, _, _, result) or
|
||||
ruby_splat_argument_def(this, _, result) or
|
||||
ruby_splat_parameter_def(this, result) or
|
||||
ruby_string_def(this, result) or
|
||||
ruby_string_array_def(this, result) or
|
||||
ruby_subshell_def(this, result) or
|
||||
ruby_superclass_def(this, _, result) or
|
||||
ruby_symbol_array_def(this, result) or
|
||||
ruby_then_def(this, result) or
|
||||
ruby_unary_def(this, _, _, result) or
|
||||
ruby_undef_def(this, result) or
|
||||
ruby_unless_def(this, _, result) or
|
||||
ruby_unless_guard_def(this, _, result) or
|
||||
ruby_unless_modifier_def(this, _, _, result) or
|
||||
ruby_until_def(this, _, _, result) or
|
||||
ruby_until_modifier_def(this, _, _, result) or
|
||||
ruby_variable_reference_pattern_def(this, _, result) or
|
||||
ruby_when_def(this, result) or
|
||||
ruby_while_def(this, _, _, result) or
|
||||
ruby_while_modifier_def(this, _, _, result) or
|
||||
ruby_yield_def(this, result)
|
||||
}
|
||||
}
|
||||
|
||||
from RubyAstNode node, RubyAstNodeParent parent, int index, Location loc
|
||||
where parent = node.getParent(index) and loc = node.getLocation()
|
||||
select node, parent, index, loc
|
||||
@@ -0,0 +1,116 @@
|
||||
description: Move location columns to a single table
|
||||
compatibility: full
|
||||
erb_ast_node_info.rel: run erb_ast_node_info.qlo
|
||||
ruby_ast_node_info.rel: run ruby_ast_node_info.qlo
|
||||
|
||||
erb_ast_node_parent.rel: delete
|
||||
ruby_ast_node_parent.rel: delete
|
||||
|
||||
erb_tokeninfo.rel: reorder erb_tokeninfo.rel (int id, int kind, string value, int loc) id kind value
|
||||
ruby_tokeninfo.rel: reorder ruby_tokeninfo.rel (int id, int kind, string value, int loc) id kind value
|
||||
|
||||
erb_comment_directive_def.rel: reorder erb_comment_directive_def.rel (int id, int child, int loc) id child
|
||||
erb_directive_def.rel: reorder erb_directive_def.rel (int id, int child, int loc) id child
|
||||
erb_graphql_directive_def.rel: reorder erb_graphql_directive_def.rel (int id, int child, int loc) id child
|
||||
erb_output_directive_def.rel: reorder erb_output_directive_def.rel (int id, int child, int loc) id child
|
||||
erb_template_def.rel: reorder erb_template_def.rel (int id, int loc) id
|
||||
|
||||
ruby_alias_def.rel: reorder ruby_alias_def.rel (int id, int alias, int name, int loc) id alias name
|
||||
ruby_alternative_pattern_def.rel: reorder ruby_alternative_pattern_def.rel (int id, int loc) id
|
||||
ruby_argument_list_def.rel: reorder ruby_argument_list_def.rel (int id, int loc) id
|
||||
ruby_array_def.rel: reorder ruby_array_def.rel (int id, int loc) id
|
||||
ruby_array_pattern_def.rel: reorder ruby_array_pattern_def.rel (int id, int loc) id
|
||||
ruby_as_pattern_def.rel: reorder ruby_as_pattern_def.rel (int id, int name, int value, int loc) id name value
|
||||
ruby_assignment_def.rel: reorder ruby_assignment_def.rel (int id, int left, int right, int loc) id left right
|
||||
ruby_bare_string_def.rel: reorder ruby_bare_string_def.rel (int id, int loc) id
|
||||
ruby_bare_symbol_def.rel: reorder ruby_bare_symbol_def.rel (int id, int loc) id
|
||||
ruby_begin_def.rel: reorder ruby_begin_def.rel (int id, int loc) id
|
||||
ruby_begin_block_def.rel: reorder ruby_begin_block_def.rel (int id, int loc) id
|
||||
ruby_binary_def.rel: reorder ruby_binary_def.rel (int id, int left, int operator, int right, int loc) id left operator right
|
||||
ruby_block_def.rel: reorder ruby_block_def.rel (int id, int loc) id
|
||||
ruby_block_argument_def.rel: reorder ruby_block_argument_def.rel (int id, int loc) id
|
||||
ruby_block_parameter_def.rel: reorder ruby_block_parameter_def.rel (int id, int loc) id
|
||||
ruby_block_parameters_def.rel: reorder ruby_block_parameters_def.rel (int id, int loc) id
|
||||
ruby_break_def.rel: reorder ruby_break_def.rel (int id, int loc) id
|
||||
ruby_call_def.rel: reorder ruby_call_def.rel (int id, int method, int loc) id method
|
||||
ruby_case_def.rel: reorder ruby_case_def.rel (int id, int loc) id
|
||||
ruby_case_match_def.rel: reorder ruby_case_match_def.rel (int id, int value, int loc) id value
|
||||
ruby_chained_string_def.rel: reorder ruby_chained_string_def.rel (int id, int loc) id
|
||||
ruby_class_def.rel: reorder ruby_class_def.rel (int id, int name, int loc) id name
|
||||
ruby_conditional_def.rel: reorder ruby_conditional_def.rel (int id, int alternative, int condition, int consequence, int loc) id alternative condition consequence
|
||||
ruby_delimited_symbol_def.rel: reorder ruby_delimited_symbol_def.rel (int id, int loc) id
|
||||
ruby_destructured_left_assignment_def.rel: reorder ruby_destructured_left_assignment_def.rel (int id, int loc) id
|
||||
ruby_destructured_parameter_def.rel: reorder ruby_destructured_parameter_def.rel (int id, int loc) id
|
||||
ruby_do_def.rel: reorder ruby_do_def.rel (int id, int loc) id
|
||||
ruby_do_block_def.rel: reorder ruby_do_block_def.rel (int id, int loc) id
|
||||
ruby_element_reference_def.rel: reorder ruby_element_reference_def.rel (int id, int object, int loc) id object
|
||||
ruby_else_def.rel: reorder ruby_else_def.rel (int id, int loc) id
|
||||
ruby_elsif_def.rel: reorder ruby_elsif_def.rel (int id, int condition, int loc) id condition
|
||||
ruby_end_block_def.rel: reorder ruby_end_block_def.rel (int id, int loc) id
|
||||
ruby_ensure_def.rel: reorder ruby_ensure_def.rel (int id, int loc) id
|
||||
ruby_exception_variable_def.rel: reorder ruby_exception_variable_def.rel (int id, int child, int loc) id child
|
||||
ruby_exceptions_def.rel: reorder ruby_exceptions_def.rel (int id, int loc) id
|
||||
ruby_expression_reference_pattern_def.rel: reorder ruby_expression_reference_pattern_def.rel (int id, int value, int loc) id value
|
||||
ruby_find_pattern_def.rel: reorder ruby_find_pattern_def.rel (int id, int loc) id
|
||||
ruby_for_def.rel: reorder ruby_for_def.rel (int id, int body, int pattern, int value, int loc) id body pattern value
|
||||
ruby_hash_def.rel: reorder ruby_hash_def.rel (int id, int loc) id
|
||||
ruby_hash_pattern_def.rel: reorder ruby_hash_pattern_def.rel (int id, int loc) id
|
||||
ruby_hash_splat_argument_def.rel: reorder ruby_hash_splat_argument_def.rel (int id, int child, int loc) id child
|
||||
ruby_hash_splat_parameter_def.rel: reorder ruby_hash_splat_parameter_def.rel (int id, int loc) id
|
||||
ruby_heredoc_body_def.rel: reorder ruby_heredoc_body_def.rel (int id, int loc) id
|
||||
ruby_if_def.rel: reorder ruby_if_def.rel (int id, int condition, int loc) id condition
|
||||
ruby_if_guard_def.rel: reorder ruby_if_guard_def.rel (int id, int condition, int loc) id condition
|
||||
ruby_if_modifier_def.rel: reorder ruby_if_modifier_def.rel (int id, int body, int condition, int loc) id body condition
|
||||
ruby_in_def.rel: reorder ruby_in_def.rel (int id, int child, int loc) id child
|
||||
ruby_in_clause_def.rel: reorder ruby_in_clause_def.rel (int id, int pattern, int loc) id pattern
|
||||
ruby_interpolation_def.rel: reorder ruby_interpolation_def.rel (int id, int loc) id
|
||||
ruby_keyword_parameter_def.rel: reorder ruby_keyword_parameter_def.rel (int id, int name, int loc) id name
|
||||
ruby_keyword_pattern_def.rel: reorder ruby_keyword_pattern_def.rel (int id, int key__, int loc) id key__
|
||||
ruby_lambda_def.rel: reorder ruby_lambda_def.rel (int id, int body, int loc) id body
|
||||
ruby_lambda_parameters_def.rel: reorder ruby_lambda_parameters_def.rel (int id, int loc) id
|
||||
ruby_left_assignment_list_def.rel: reorder ruby_left_assignment_list_def.rel (int id, int loc) id
|
||||
ruby_method_def.rel: reorder ruby_method_def.rel (int id, int name, int loc) id name
|
||||
ruby_method_parameters_def.rel: reorder ruby_method_parameters_def.rel (int id, int loc) id
|
||||
ruby_module_def.rel: reorder ruby_module_def.rel (int id, int name, int loc) id name
|
||||
ruby_next_def.rel: reorder ruby_next_def.rel (int id, int loc) id
|
||||
ruby_operator_assignment_def.rel: reorder ruby_operator_assignment_def.rel (int id, int left, int operator, int right, int loc) id left operator right
|
||||
ruby_optional_parameter_def.rel: reorder ruby_optional_parameter_def.rel (int id, int name, int value, int loc) id name value
|
||||
ruby_pair_def.rel: reorder ruby_pair_def.rel (int id, int key__, int loc) id key__
|
||||
ruby_parenthesized_pattern_def.rel: reorder ruby_parenthesized_pattern_def.rel (int id, int child, int loc) id child
|
||||
ruby_parenthesized_statements_def.rel: reorder ruby_parenthesized_statements_def.rel (int id, int loc) id
|
||||
ruby_pattern_def.rel: reorder ruby_pattern_def.rel (int id, int child, int loc) id child
|
||||
ruby_program_def.rel: reorder ruby_program_def.rel (int id, int loc) id
|
||||
ruby_range_def.rel: reorder ruby_range_def.rel (int id, int operator, int loc) id operator
|
||||
ruby_rational_def.rel: reorder ruby_rational_def.rel (int id, int child, int loc) id child
|
||||
ruby_redo_def.rel: reorder ruby_redo_def.rel (int id, int loc) id
|
||||
ruby_regex_def.rel: reorder ruby_regex_def.rel (int id, int loc) id
|
||||
ruby_rescue_def.rel: reorder ruby_rescue_def.rel (int id, int loc) id
|
||||
ruby_rescue_modifier_def.rel: reorder ruby_rescue_modifier_def.rel (int id, int body, int handler, int loc) id body handler
|
||||
ruby_rest_assignment_def.rel: reorder ruby_rest_assignment_def.rel (int id, int loc) id
|
||||
ruby_retry_def.rel: reorder ruby_retry_def.rel (int id, int loc) id
|
||||
ruby_return_def.rel: reorder ruby_return_def.rel (int id, int loc) id
|
||||
ruby_right_assignment_list_def.rel: reorder ruby_right_assignment_list_def.rel (int id, int loc) id
|
||||
ruby_scope_resolution_def.rel: reorder ruby_scope_resolution_def.rel (int id, int name, int loc) id name
|
||||
ruby_setter_def.rel: reorder ruby_setter_def.rel (int id, int name, int loc) id name
|
||||
ruby_singleton_class_def.rel: reorder ruby_singleton_class_def.rel (int id, int value, int loc) id value
|
||||
ruby_singleton_method_def.rel: reorder ruby_singleton_method_def.rel (int id, int name, int object, int loc) id name object
|
||||
ruby_splat_argument_def.rel: reorder ruby_splat_argument_def.rel (int id, int child, int loc) id child
|
||||
ruby_splat_parameter_def.rel: reorder ruby_splat_parameter_def.rel (int id, int loc) id
|
||||
ruby_string_def.rel: reorder ruby_string_def.rel (int id, int loc) id
|
||||
ruby_string_array_def.rel: reorder ruby_string_array_def.rel (int id, int loc) id
|
||||
ruby_subshell_def.rel: reorder ruby_subshell_def.rel (int id, int loc) id
|
||||
ruby_superclass_def.rel: reorder ruby_superclass_def.rel (int id, int child, int loc) id child
|
||||
ruby_symbol_array_def.rel: reorder ruby_symbol_array_def.rel (int id, int loc) id
|
||||
ruby_then_def.rel: reorder ruby_then_def.rel (int id, int loc) id
|
||||
ruby_unary_def.rel: reorder ruby_unary_def.rel (int id, int operand, int operator, int loc) id operand operator
|
||||
ruby_undef_def.rel: reorder ruby_undef_def.rel (int id, int loc) id
|
||||
ruby_unless_def.rel: reorder ruby_unless_def.rel (int id, int condition, int loc) id condition
|
||||
ruby_unless_guard_def.rel: reorder ruby_unless_guard_def.rel (int id, int condition, int loc) id condition
|
||||
ruby_unless_modifier_def.rel: reorder ruby_unless_modifier_def.rel (int id, int body, int condition, int loc) id body condition
|
||||
ruby_until_def.rel: reorder ruby_until_def.rel (int id, int body, int condition, int loc) id body condition
|
||||
ruby_until_modifier_def.rel: reorder ruby_until_modifier_def.rel (int id, int body, int condition, int loc) id body condition
|
||||
ruby_variable_reference_pattern_def.rel: reorder ruby_variable_reference_pattern_def.rel (int id, int name, int loc) id name
|
||||
ruby_when_def.rel: reorder ruby_when_def.rel (int id, int loc) id
|
||||
ruby_while_def.rel: reorder ruby_while_def.rel (int id, int body, int condition, int loc) id body condition
|
||||
ruby_while_modifier_def.rel: reorder ruby_while_modifier_def.rel (int id, int body, int condition, int loc) id body condition
|
||||
ruby_yield_def.rel: reorder ruby_yield_def.rel (int id, int loc) id
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
class AstNode extends @ruby_ast_node {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AstNode ruby_block_argument, AstNode child
|
||||
where ruby_block_argument_def(ruby_block_argument, child, _)
|
||||
select ruby_block_argument, child
|
||||
@@ -0,0 +1,7 @@
|
||||
class AstNode extends @ruby_ast_node {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AstNode ruby_block_parameter, AstNode name
|
||||
where ruby_block_parameter_def(ruby_block_parameter, name, _)
|
||||
select ruby_block_parameter, name
|
||||
@@ -0,0 +1,7 @@
|
||||
class AstNode extends @ruby_ast_node {
|
||||
string toString() { none() }
|
||||
}
|
||||
|
||||
from AstNode ruby_pair, AstNode value
|
||||
where ruby_pair_def(ruby_pair, _, value, _)
|
||||
select ruby_pair, value
|
||||
@@ -0,0 +1,8 @@
|
||||
description: Update grammar
|
||||
compatibility: full
|
||||
ruby_block_argument_child.rel: run ruby_block_argument_child.qlo
|
||||
ruby_block_parameter_name.rel: run ruby_block_parameter_name.qlo
|
||||
ruby_pair_value.rel: run ruby_pair_value.qlo
|
||||
ruby_block_argument_def.rel: reorder ruby_block_argument_def.rel ( int id, int child, int loc ) id loc
|
||||
ruby_block_parameter_def.rel: reorder ruby_block_parameter_def.rel ( int id, int name, int loc ) id loc
|
||||
ruby_pair_def.rel: reorder ruby_pair_def.rel (int id, int key__, int value, int loc) id key__ loc
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Add `unique` annotation
|
||||
compatibility: full
|
||||
@@ -1,3 +1,15 @@
|
||||
## 0.0.9
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `rb/weak-cookie-configuration`. The query finds cases where cookie configuration options are set to values that may make an application more vulnerable to certain attacks.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The query `rb/csrf-protection-disabled` has been extended to find calls to the Rails method `protect_from_forgery` that may weaken CSRF protection.
|
||||
|
||||
## 0.0.7
|
||||
|
||||
## 0.0.6
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
lgtm,codescanning
|
||||
* Added a new query, `rb/weak-cookie-configuration`. The query finds cases where cookie configuration options are set to values that may make an application more vulnerable to certain attacks.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
lgtm,codescanning
|
||||
* The query `rb/csrf-protection-disabled` has been extended to find calls to the Rails method `protect_from_forgery` that may weaken CSRF protection.
|
||||
9
ruby/ql/src/change-notes/released/0.0.8.md
Normal file
9
ruby/ql/src/change-notes/released/0.0.8.md
Normal file
@@ -0,0 +1,9 @@
|
||||
## 0.0.8
|
||||
|
||||
### New Queries
|
||||
|
||||
* Added a new query, `rb/weak-cookie-configuration`. The query finds cases where cookie configuration options are set to values that may make an application more vulnerable to certain attacks.
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* The query `rb/csrf-protection-disabled` has been extended to find calls to the Rails method `protect_from_forgery` that may weaken CSRF protection.
|
||||
1
ruby/ql/src/change-notes/released/0.0.9.md
Normal file
1
ruby/ql/src/change-notes/released/0.0.9.md
Normal file
@@ -0,0 +1 @@
|
||||
## 0.0.9
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 0.0.7
|
||||
lastReleaseVersion: 0.0.9
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-queries
|
||||
version: 0.0.8-dev
|
||||
version: 0.0.10-dev
|
||||
groups:
|
||||
- ruby
|
||||
- queries
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
private import InlineExpectationsTestPrivate
|
||||
|
||||
/**
|
||||
* Base class for tests with inline expectations. The test extends this class to provide the actual
|
||||
* The base class for tests with inline expectations. The test extends this class to provide the actual
|
||||
* results of the query, which are then compared with the expected results in comments to produce a
|
||||
* list of failure messages that point out where the actual results differ from the expected
|
||||
* results.
|
||||
@@ -123,6 +123,15 @@ abstract class InlineExpectationsTest extends string {
|
||||
*/
|
||||
abstract predicate hasActualResult(Location location, string element, string tag, string value);
|
||||
|
||||
/**
|
||||
* Like `hasActualResult`, but returns results that do not require a matching annotation.
|
||||
* A failure will still arise if there is an annotation that does not match any results, but not vice versa.
|
||||
* Override this predicate to specify optional results.
|
||||
*/
|
||||
predicate hasOptionalResult(Location location, string element, string tag, string value) {
|
||||
none()
|
||||
}
|
||||
|
||||
final predicate hasFailureMessage(FailureLocatable element, string message) {
|
||||
exists(ActualResult actualResult |
|
||||
actualResult.getTest() = this and
|
||||
@@ -134,7 +143,8 @@ abstract class InlineExpectationsTest extends string {
|
||||
)
|
||||
or
|
||||
not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and
|
||||
message = "Unexpected result: " + actualResult.getExpectationText()
|
||||
message = "Unexpected result: " + actualResult.getExpectationText() and
|
||||
not actualResult.isOptional()
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -243,9 +253,13 @@ private string expectationPattern() {
|
||||
|
||||
private newtype TFailureLocatable =
|
||||
TActualResult(
|
||||
InlineExpectationsTest test, Location location, string element, string tag, string value
|
||||
InlineExpectationsTest test, Location location, string element, string tag, string value,
|
||||
boolean optional
|
||||
) {
|
||||
test.hasActualResult(location, element, tag, value)
|
||||
test.hasActualResult(location, element, tag, value) and
|
||||
optional = false
|
||||
or
|
||||
test.hasOptionalResult(location, element, tag, value) and optional = true
|
||||
} or
|
||||
TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) {
|
||||
exists(TColumn column, string tags |
|
||||
@@ -277,8 +291,9 @@ class ActualResult extends FailureLocatable, TActualResult {
|
||||
string element;
|
||||
string tag;
|
||||
string value;
|
||||
boolean optional;
|
||||
|
||||
ActualResult() { this = TActualResult(test, location, element, tag, value) }
|
||||
ActualResult() { this = TActualResult(test, location, element, tag, value, optional) }
|
||||
|
||||
override string toString() { result = element }
|
||||
|
||||
@@ -289,6 +304,8 @@ class ActualResult extends FailureLocatable, TActualResult {
|
||||
override string getTag() { result = tag }
|
||||
|
||||
override string getValue() { result = value }
|
||||
|
||||
predicate isOptional() { optional = true }
|
||||
}
|
||||
|
||||
abstract private class Expectation extends FailureLocatable {
|
||||
|
||||
@@ -457,6 +457,9 @@ calls/calls.rb:
|
||||
# 267| getArgument: [BlockArgument] &...
|
||||
# 267| getValue: [MethodCall] call to bar
|
||||
# 267| getReceiver: [ConstantReadAccess] X
|
||||
# 268| getStmt: [MethodCall] call to foo
|
||||
# 268| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 268| getArgument: [BlockArgument] &...
|
||||
# 270| getStmt: [MethodCall] call to foo
|
||||
# 270| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 270| getArgument: [SplatExpr] * ...
|
||||
@@ -673,6 +676,29 @@ calls/calls.rb:
|
||||
# 341| getArgument: [LocalVariableAccess] x
|
||||
# 341| getArgument: [LocalVariableAccess] y
|
||||
# 341| getArgument: [LocalVariableAccess] z
|
||||
# 344| getStmt: [MethodCall] call to foo
|
||||
# 344| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 344| getArgument: [Pair] Pair
|
||||
# 344| getKey: [SymbolLiteral] :x
|
||||
# 344| getValue: [IntegerLiteral] 42
|
||||
# 345| getStmt: [MethodCall] call to foo
|
||||
# 345| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 345| getArgument: [Pair] Pair
|
||||
# 345| getKey: [SymbolLiteral] :x
|
||||
# 345| getValue: [LocalVariableAccess] x
|
||||
# 345| getArgument: [Pair] Pair
|
||||
# 345| getKey: [SymbolLiteral] :novar
|
||||
# 345| getValue: [MethodCall] call to novar
|
||||
# 346| getStmt: [MethodCall] call to foo
|
||||
# 346| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 346| getArgument: [Pair] Pair
|
||||
# 346| getKey: [SymbolLiteral] :X
|
||||
# 346| getValue: [IntegerLiteral] 42
|
||||
# 347| getStmt: [MethodCall] call to foo
|
||||
# 347| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 347| getArgument: [Pair] Pair
|
||||
# 347| getKey: [SymbolLiteral] :X
|
||||
# 347| getValue: [ConstantReadAccess] X
|
||||
control/cases.rb:
|
||||
# 1| [Toplevel] cases.rb
|
||||
# 2| getStmt: [AssignExpr] ... = ...
|
||||
@@ -925,8 +951,8 @@ control/cases.rb:
|
||||
# 87| getBranch: [InClause] in ... then ...
|
||||
# 87| getPattern: [IntegerLiteral] 5
|
||||
# 88| getBranch: [InClause] in ... then ...
|
||||
# 88| getPattern: [VariableReferencePattern] ^...
|
||||
# 88| getVariableAccess: [LocalVariableAccess] foo
|
||||
# 88| getPattern: [ReferencePattern] ^...
|
||||
# 88| getExpr: [LocalVariableAccess] foo
|
||||
# 89| getBranch: [InClause] in ... then ...
|
||||
# 89| getPattern: [StringLiteral] "string"
|
||||
# 89| getComponent: [StringTextComponent] string
|
||||
@@ -1023,8 +1049,8 @@ control/cases.rb:
|
||||
# 111| getBranch: [InClause] in ... then ...
|
||||
# 111| getPattern: [AlternativePattern] ... | ...
|
||||
# 111| getAlternative: [IntegerLiteral] 5
|
||||
# 111| getAlternative: [VariableReferencePattern] ^...
|
||||
# 111| getVariableAccess: [LocalVariableAccess] foo
|
||||
# 111| getAlternative: [ReferencePattern] ^...
|
||||
# 111| getExpr: [LocalVariableAccess] foo
|
||||
# 111| getAlternative: [StringLiteral] "string"
|
||||
# 111| getComponent: [StringTextComponent] string
|
||||
# 111| getAlternative: [LocalVariableAccess] var
|
||||
@@ -1115,6 +1141,35 @@ control/cases.rb:
|
||||
# 144| getClass: [ConstantReadAccess] Bar
|
||||
# 144| getKey: [SymbolLiteral] :a
|
||||
# 144| getValue: [IntegerLiteral] 1
|
||||
# 147| getStmt: [CaseExpr] case ...
|
||||
# 147| getValue: [MethodCall] call to expr
|
||||
# 147| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 148| getBranch: [InClause] in ... then ...
|
||||
# 148| getPattern: [ReferencePattern] ^...
|
||||
# 148| getExpr: [LocalVariableAccess] foo
|
||||
# 149| getBranch: [InClause] in ... then ...
|
||||
# 149| getPattern: [ReferencePattern] ^...
|
||||
# 149| getExpr: [GlobalVariableAccess] $foo
|
||||
# 150| getBranch: [InClause] in ... then ...
|
||||
# 150| getPattern: [ReferencePattern] ^...
|
||||
# 150| getExpr: [InstanceVariableAccess] @foo
|
||||
# 151| getBranch: [InClause] in ... then ...
|
||||
# 151| getPattern: [ReferencePattern] ^...
|
||||
# 151| getExpr: [ClassVariableAccess] @@foo
|
||||
# 154| getStmt: [CaseExpr] case ...
|
||||
# 154| getValue: [MethodCall] call to expr
|
||||
# 154| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 155| getBranch: [InClause] in ... then ...
|
||||
# 155| getPattern: [ReferencePattern] ^...
|
||||
# 155| getExpr: [LocalVariableAccess] foo
|
||||
# 156| getBranch: [InClause] in ... then ...
|
||||
# 156| getPattern: [ReferencePattern] ^...
|
||||
# 156| getExpr: [InstanceVariableAccess] @foo
|
||||
# 157| getBranch: [InClause] in ... then ...
|
||||
# 157| getPattern: [ReferencePattern] ^...
|
||||
# 157| getExpr: [AddExpr] ... + ...
|
||||
# 157| getAnOperand/getLeftOperand/getReceiver: [IntegerLiteral] 1
|
||||
# 157| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1
|
||||
modules/classes.rb:
|
||||
# 2| [Toplevel] classes.rb
|
||||
# 3| getStmt: [ClassDeclaration] Foo
|
||||
@@ -1945,6 +2000,23 @@ literals/literals.rb:
|
||||
# 180| getComponent: [StringTextComponent]
|
||||
# 180| cat file.txt
|
||||
# 180|
|
||||
# 184| getStmt: [AssignExpr] ... = ...
|
||||
# 184| getAnOperand/getLeftOperand: [LocalVariableAccess] x
|
||||
# 184| getAnOperand/getRightOperand: [IntegerLiteral] 42
|
||||
# 185| getStmt: [HashLiteral] {...}
|
||||
# 185| getElement: [Pair] Pair
|
||||
# 185| getKey: [SymbolLiteral] :x
|
||||
# 185| getValue: [LocalVariableAccess] x
|
||||
# 185| getElement: [Pair] Pair
|
||||
# 185| getKey: [SymbolLiteral] :y
|
||||
# 185| getValue: [IntegerLiteral] 5
|
||||
# 186| getStmt: [HashLiteral] {...}
|
||||
# 186| getElement: [Pair] Pair
|
||||
# 186| getKey: [SymbolLiteral] :y
|
||||
# 186| getValue: [MethodCall] call to y
|
||||
# 186| getElement: [Pair] Pair
|
||||
# 186| getKey: [SymbolLiteral] :Z
|
||||
# 186| getValue: [ConstantReadAccess] Z
|
||||
control/loops.rb:
|
||||
# 1| [Toplevel] loops.rb
|
||||
# 2| getStmt: [AssignExpr] ... = ...
|
||||
@@ -2679,6 +2751,19 @@ params/params.rb:
|
||||
# 77| getParameter: [SimpleParameter] val
|
||||
# 77| getDefiningAccess: [LocalVariableAccess] val
|
||||
# 77| getParameter: [HashSplatNilParameter] **nil
|
||||
# 81| getStmt: [Method] anonymous_block_parameter
|
||||
# 81| getParameter: [SimpleParameter] array
|
||||
# 81| getDefiningAccess: [LocalVariableAccess] array
|
||||
# 81| getParameter: [BlockParameter] &
|
||||
# 81| getDefiningAccess: [LocalVariableAccess] __synth__0
|
||||
# 82| getStmt: [MethodCall] call to proc
|
||||
# 82| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 82| getArgument: [BlockArgument] &...
|
||||
# 82| getValue: [LocalVariableAccess] __synth__0
|
||||
# 83| getStmt: [MethodCall] call to each
|
||||
# 83| getReceiver: [LocalVariableAccess] array
|
||||
# 83| getArgument: [BlockArgument] &...
|
||||
# 83| getValue: [LocalVariableAccess] __synth__0
|
||||
erb/template.html.erb:
|
||||
# 19| [Toplevel] template.html.erb
|
||||
# 19| getStmt: [StringLiteral] "hello world"
|
||||
|
||||
@@ -477,6 +477,24 @@ literals/literals.rb:
|
||||
# 118| getArgument: [HashSplatExpr] ** ...
|
||||
# 118| getAnOperand/getOperand/getReceiver: [MethodCall] call to baz
|
||||
# 118| getReceiver: [Self, SelfVariableAccess] self
|
||||
# 185| [HashLiteral] {...}
|
||||
# 185| getDesugared: [MethodCall] call to []
|
||||
# 185| getReceiver: [ConstantReadAccess] Hash
|
||||
# 185| getArgument: [Pair] Pair
|
||||
# 185| getKey: [SymbolLiteral] :x
|
||||
# 185| getValue: [LocalVariableAccess] x
|
||||
# 185| getArgument: [Pair] Pair
|
||||
# 185| getKey: [SymbolLiteral] :y
|
||||
# 185| getValue: [IntegerLiteral] 5
|
||||
# 186| [HashLiteral] {...}
|
||||
# 186| getDesugared: [MethodCall] call to []
|
||||
# 186| getReceiver: [ConstantReadAccess] Hash
|
||||
# 186| getArgument: [Pair] Pair
|
||||
# 186| getKey: [SymbolLiteral] :y
|
||||
# 186| getValue: [MethodCall] call to y
|
||||
# 186| getArgument: [Pair] Pair
|
||||
# 186| getKey: [SymbolLiteral] :Z
|
||||
# 186| getValue: [ConstantReadAccess] Z
|
||||
control/loops.rb:
|
||||
# 9| [ForExpr] for ... in ...
|
||||
# 9| getDesugared: [MethodCall] call to each
|
||||
|
||||
@@ -65,6 +65,13 @@
|
||||
| calls/calls.rb:340:27:340:27 | 4 | 4 |
|
||||
| calls/calls.rb:340:29:340:29 | 5 | 5 |
|
||||
| calls/calls.rb:340:31:340:31 | 6 | 6 |
|
||||
| calls/calls.rb:344:5:344:5 | :x | :x |
|
||||
| calls/calls.rb:344:8:344:9 | 42 | 42 |
|
||||
| calls/calls.rb:345:5:345:5 | :x | :x |
|
||||
| calls/calls.rb:345:9:345:13 | :novar | :novar |
|
||||
| calls/calls.rb:346:5:346:5 | :X | :X |
|
||||
| calls/calls.rb:346:8:346:9 | 42 | 42 |
|
||||
| calls/calls.rb:347:5:347:5 | :X | :X |
|
||||
| constants/constants.rb:3:19:3:27 | "const_a" | const_a |
|
||||
| constants/constants.rb:6:15:6:23 | "const_b" | const_b |
|
||||
| constants/constants.rb:17:12:17:18 | "Hello" | Hello |
|
||||
@@ -236,6 +243,11 @@
|
||||
| control/cases.rb:142:11:142:11 | :y | :y |
|
||||
| control/cases.rb:144:11:144:11 | :a | :a |
|
||||
| control/cases.rb:144:14:144:14 | 1 | 1 |
|
||||
| control/cases.rb:148:7:148:9 | foo | 42 |
|
||||
| control/cases.rb:155:8:155:10 | foo | 42 |
|
||||
| control/cases.rb:157:8:157:8 | 1 | 1 |
|
||||
| control/cases.rb:157:8:157:12 | ... + ... | 2 |
|
||||
| control/cases.rb:157:12:157:12 | 1 | 1 |
|
||||
| control/conditionals.rb:2:5:2:5 | 0 | 0 |
|
||||
| control/conditionals.rb:3:5:3:5 | 0 | 0 |
|
||||
| control/conditionals.rb:4:5:4:5 | 0 | 0 |
|
||||
@@ -571,6 +583,13 @@
|
||||
| literals/literals.rb:162:11:162:16 | <<-BLA | \nsome text\\nand some more\n |
|
||||
| literals/literals.rb:167:9:167:19 | <<~SQUIGGLY | \n indented stuff\n |
|
||||
| literals/literals.rb:180:10:180:19 | <<`SCRIPT` | \n cat file.txt\n |
|
||||
| literals/literals.rb:184:5:184:6 | 42 | 42 |
|
||||
| literals/literals.rb:185:2:185:2 | :x | :x |
|
||||
| literals/literals.rb:185:2:185:2 | x | 42 |
|
||||
| literals/literals.rb:185:6:185:6 | :y | :y |
|
||||
| literals/literals.rb:185:8:185:8 | 5 | 5 |
|
||||
| literals/literals.rb:186:2:186:2 | :y | :y |
|
||||
| literals/literals.rb:186:7:186:7 | :Z | :Z |
|
||||
| misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js |
|
||||
| misc/misc.rb:1:7:1:11 | "bar" | bar |
|
||||
| misc/misc.rb:3:7:3:9 | foo | foo |
|
||||
|
||||
@@ -15,6 +15,16 @@ keywordArguments
|
||||
| calls.rb:249:15:249:30 | Pair | calls.rb:249:15:249:20 | call to foo | calls.rb:249:25:249:30 | call to bar |
|
||||
| calls.rb:278:5:278:13 | Pair | calls.rb:278:5:278:8 | :blah | calls.rb:278:11:278:13 | call to bar |
|
||||
| calls.rb:279:5:279:16 | Pair | calls.rb:279:5:279:8 | :blah | calls.rb:279:11:279:16 | call to bar |
|
||||
| calls.rb:344:5:344:9 | Pair | calls.rb:344:5:344:5 | :x | calls.rb:344:8:344:9 | 42 |
|
||||
| calls.rb:345:5:345:6 | Pair | calls.rb:345:5:345:5 | :x | calls.rb:345:5:345:5 | x |
|
||||
| calls.rb:345:9:345:14 | Pair | calls.rb:345:9:345:13 | :novar | calls.rb:345:9:345:13 | call to novar |
|
||||
| calls.rb:346:5:346:9 | Pair | calls.rb:346:5:346:5 | :X | calls.rb:346:8:346:9 | 42 |
|
||||
| calls.rb:347:5:347:6 | Pair | calls.rb:347:5:347:5 | :X | calls.rb:347:5:347:5 | X |
|
||||
keywordArgumentsByKeyword
|
||||
| calls.rb:278:1:278:14 | call to foo | blah | calls.rb:278:11:278:13 | call to bar |
|
||||
| calls.rb:279:1:279:17 | call to foo | blah | calls.rb:279:11:279:16 | call to bar |
|
||||
| calls.rb:344:1:344:10 | call to foo | x | calls.rb:344:8:344:9 | 42 |
|
||||
| calls.rb:345:1:345:15 | call to foo | novar | calls.rb:345:9:345:13 | call to novar |
|
||||
| calls.rb:345:1:345:15 | call to foo | x | calls.rb:345:5:345:5 | x |
|
||||
| calls.rb:346:1:346:10 | call to foo | X | calls.rb:346:8:346:9 | 42 |
|
||||
| calls.rb:347:1:347:7 | call to foo | X | calls.rb:347:5:347:5 | X |
|
||||
|
||||
@@ -3,6 +3,7 @@ callsWithNoReceiverArgumentsOrBlock
|
||||
| calls.rb:286:5:286:9 | call to super | super |
|
||||
| calls.rb:287:5:287:11 | call to super | super |
|
||||
| calls.rb:305:5:305:9 | call to super | super |
|
||||
| calls.rb:345:9:345:13 | call to novar | novar |
|
||||
callsWithArguments
|
||||
| calls.rb:14:1:14:11 | call to foo | foo | 0 | calls.rb:14:5:14:5 | 0 |
|
||||
| calls.rb:14:1:14:11 | call to foo | foo | 1 | calls.rb:14:8:14:8 | 1 |
|
||||
@@ -26,6 +27,7 @@ callsWithArguments
|
||||
| calls.rb:249:1:249:32 | call to [] | [] | 1 | calls.rb:249:15:249:30 | Pair |
|
||||
| calls.rb:266:1:266:9 | call to foo | foo | 0 | calls.rb:266:5:266:8 | &... |
|
||||
| calls.rb:267:1:267:12 | call to foo | foo | 0 | calls.rb:267:5:267:11 | &... |
|
||||
| calls.rb:268:1:268:6 | call to foo | foo | 0 | calls.rb:268:5:268:5 | &... |
|
||||
| calls.rb:270:1:270:9 | call to foo | foo | 0 | calls.rb:270:5:270:8 | * ... |
|
||||
| calls.rb:271:1:271:12 | call to foo | foo | 0 | calls.rb:271:5:271:11 | * ... |
|
||||
| calls.rb:274:1:274:10 | call to foo | foo | 0 | calls.rb:274:5:274:9 | ** ... |
|
||||
@@ -106,6 +108,11 @@ callsWithArguments
|
||||
| calls.rb:341:3:341:13 | call to foo | foo | 0 | calls.rb:341:7:341:7 | x |
|
||||
| calls.rb:341:3:341:13 | call to foo | foo | 1 | calls.rb:341:10:341:10 | y |
|
||||
| calls.rb:341:3:341:13 | call to foo | foo | 2 | calls.rb:341:13:341:13 | z |
|
||||
| calls.rb:344:1:344:10 | call to foo | foo | 0 | calls.rb:344:5:344:9 | Pair |
|
||||
| calls.rb:345:1:345:15 | call to foo | foo | 0 | calls.rb:345:5:345:6 | Pair |
|
||||
| calls.rb:345:1:345:15 | call to foo | foo | 1 | calls.rb:345:9:345:14 | Pair |
|
||||
| calls.rb:346:1:346:10 | call to foo | foo | 0 | calls.rb:346:5:346:9 | Pair |
|
||||
| calls.rb:347:1:347:7 | call to foo | foo | 0 | calls.rb:347:5:347:6 | Pair |
|
||||
callsWithReceiver
|
||||
| calls.rb:2:1:2:5 | call to foo | calls.rb:2:1:2:5 | self |
|
||||
| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:3 | Foo |
|
||||
@@ -267,6 +274,7 @@ callsWithReceiver
|
||||
| calls.rb:266:6:266:8 | call to bar | calls.rb:266:6:266:8 | self |
|
||||
| calls.rb:267:1:267:12 | call to foo | calls.rb:267:1:267:12 | self |
|
||||
| calls.rb:267:6:267:11 | call to bar | calls.rb:267:6:267:6 | X |
|
||||
| calls.rb:268:1:268:6 | call to foo | calls.rb:268:1:268:6 | self |
|
||||
| calls.rb:270:1:270:9 | call to foo | calls.rb:270:1:270:9 | self |
|
||||
| calls.rb:270:5:270:8 | * ... | calls.rb:270:6:270:8 | call to bar |
|
||||
| calls.rb:270:6:270:8 | call to bar | calls.rb:270:6:270:8 | self |
|
||||
@@ -356,6 +364,10 @@ callsWithReceiver
|
||||
| calls.rb:340:17:340:23 | call to [] | calls.rb:340:17:340:23 | Array |
|
||||
| calls.rb:340:26:340:32 | call to [] | calls.rb:340:26:340:32 | Array |
|
||||
| calls.rb:341:3:341:13 | call to foo | calls.rb:341:3:341:13 | self |
|
||||
| calls.rb:344:1:344:10 | call to foo | calls.rb:344:1:344:10 | self |
|
||||
| calls.rb:345:1:345:15 | call to foo | calls.rb:345:1:345:15 | self |
|
||||
| calls.rb:346:1:346:10 | call to foo | calls.rb:346:1:346:10 | self |
|
||||
| calls.rb:347:1:347:7 | call to foo | calls.rb:347:1:347:7 | self |
|
||||
callsWithBlock
|
||||
| calls.rb:17:1:17:17 | call to foo | calls.rb:17:5:17:17 | { ... } |
|
||||
| calls.rb:20:1:22:3 | call to foo | calls.rb:20:5:22:3 | do ... end |
|
||||
|
||||
@@ -265,7 +265,7 @@ X::foo rescue X::bar
|
||||
# block argument
|
||||
foo(&bar)
|
||||
foo(&X::bar)
|
||||
|
||||
foo(&)
|
||||
# splat argument
|
||||
foo(*bar)
|
||||
foo(*X::bar)
|
||||
@@ -340,3 +340,8 @@ end
|
||||
for x, y, z in [[1,2,3], [4,5,6]]
|
||||
foo x, y, z
|
||||
end
|
||||
|
||||
foo(x: 42)
|
||||
foo(x:, novar:)
|
||||
foo(X: 42)
|
||||
foo(X:)
|
||||
@@ -8,6 +8,8 @@ caseValues
|
||||
| cases.rb:116:1:124:3 | case ... | cases.rb:116:6:116:9 | call to expr |
|
||||
| cases.rb:128:1:133:3 | case ... | cases.rb:128:6:128:9 | call to expr |
|
||||
| cases.rb:137:1:145:3 | case ... | cases.rb:137:6:137:9 | call to expr |
|
||||
| cases.rb:147:1:152:3 | case ... | cases.rb:147:6:147:9 | call to expr |
|
||||
| cases.rb:154:1:158:3 | case ... | cases.rb:154:6:154:9 | call to expr |
|
||||
caseNoValues
|
||||
| cases.rb:18:1:22:3 | case ... |
|
||||
caseElseBranches
|
||||
@@ -22,6 +24,8 @@ caseNoElseBranches
|
||||
| cases.rb:116:1:124:3 | case ... |
|
||||
| cases.rb:128:1:133:3 | case ... |
|
||||
| cases.rb:137:1:145:3 | case ... |
|
||||
| cases.rb:147:1:152:3 | case ... |
|
||||
| cases.rb:154:1:158:3 | case ... |
|
||||
caseWhenBranches
|
||||
| cases.rb:8:1:15:3 | case ... | cases.rb:9:1:10:7 | when ... | 0 | cases.rb:9:6:9:6 | b | cases.rb:9:7:10:7 | then ... |
|
||||
| cases.rb:8:1:15:3 | case ... | cases.rb:11:1:12:7 | when ... | 0 | cases.rb:11:6:11:6 | c | cases.rb:11:10:12:7 | then ... |
|
||||
@@ -121,3 +125,10 @@ caseAllBranches
|
||||
| cases.rb:137:1:145:3 | case ... | 4 | cases.rb:142:3:142:14 | in ... then ... |
|
||||
| cases.rb:137:1:145:3 | case ... | 5 | cases.rb:143:3:143:15 | in ... then ... |
|
||||
| cases.rb:137:1:145:3 | case ... | 6 | cases.rb:144:3:144:23 | in ... then ... |
|
||||
| cases.rb:147:1:152:3 | case ... | 0 | cases.rb:148:3:148:10 | in ... then ... |
|
||||
| cases.rb:147:1:152:3 | case ... | 1 | cases.rb:149:3:149:11 | in ... then ... |
|
||||
| cases.rb:147:1:152:3 | case ... | 2 | cases.rb:150:3:150:11 | in ... then ... |
|
||||
| cases.rb:147:1:152:3 | case ... | 3 | cases.rb:151:3:151:12 | in ... then ... |
|
||||
| cases.rb:154:1:158:3 | case ... | 0 | cases.rb:155:3:155:12 | in ... then ... |
|
||||
| cases.rb:154:1:158:3 | case ... | 1 | cases.rb:156:3:156:13 | in ... then ... |
|
||||
| cases.rb:154:1:158:3 | case ... | 2 | cases.rb:157:3:157:14 | in ... then ... |
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
| cases.rb:116:1:124:3 | case ... | CaseExpr |
|
||||
| cases.rb:128:1:133:3 | case ... | CaseExpr |
|
||||
| cases.rb:137:1:145:3 | case ... | CaseExpr |
|
||||
| cases.rb:147:1:152:3 | case ... | CaseExpr |
|
||||
| cases.rb:154:1:158:3 | case ... | CaseExpr |
|
||||
| conditionals.rb:10:1:12:3 | if ... | IfExpr |
|
||||
| conditionals.rb:15:1:19:3 | if ... | IfExpr |
|
||||
| conditionals.rb:22:1:30:3 | if ... | IfExpr |
|
||||
|
||||
@@ -144,3 +144,15 @@ case expr
|
||||
in Bar( a: 1, **nil);
|
||||
end
|
||||
|
||||
case expr
|
||||
in ^foo;
|
||||
in ^$foo;
|
||||
in ^@foo;
|
||||
in ^@@foo;
|
||||
end
|
||||
|
||||
case expr
|
||||
in ^(foo);
|
||||
in ^(@foo);
|
||||
in ^(1 + 1);
|
||||
end
|
||||
@@ -217,6 +217,14 @@ allLiterals
|
||||
| literals.rb:171:9:171:15 | <<"DOC" | HereDoc | <none> |
|
||||
| literals.rb:176:9:176:15 | <<'DOC' | HereDoc | <none> |
|
||||
| literals.rb:180:10:180:19 | <<`SCRIPT` | HereDoc | \n cat file.txt\n |
|
||||
| literals.rb:184:5:184:6 | 42 | IntegerLiteral | 42 |
|
||||
| literals.rb:185:1:185:9 | {...} | HashLiteral | <none> |
|
||||
| literals.rb:185:2:185:2 | :x | SymbolLiteral | :x |
|
||||
| literals.rb:185:6:185:6 | :y | SymbolLiteral | :y |
|
||||
| literals.rb:185:8:185:8 | 5 | IntegerLiteral | 5 |
|
||||
| literals.rb:186:1:186:9 | {...} | HashLiteral | <none> |
|
||||
| literals.rb:186:2:186:2 | :y | SymbolLiteral | :y |
|
||||
| literals.rb:186:7:186:7 | :Z | SymbolLiteral | :Z |
|
||||
stringlikeLiterals
|
||||
| literals.rb:46:1:46:2 | "" | |
|
||||
| literals.rb:47:1:47:2 | "" | |
|
||||
@@ -327,6 +335,10 @@ stringlikeLiterals
|
||||
| literals.rb:171:9:171:15 | <<"DOC" | <none> |
|
||||
| literals.rb:176:9:176:15 | <<'DOC' | <none> |
|
||||
| literals.rb:180:10:180:19 | <<`SCRIPT` | \n cat file.txt\n |
|
||||
| literals.rb:185:2:185:2 | :x | :x |
|
||||
| literals.rb:185:6:185:6 | :y | :y |
|
||||
| literals.rb:186:2:186:2 | :y | :y |
|
||||
| literals.rb:186:7:186:7 | :Z | :Z |
|
||||
stringLiterals
|
||||
| literals.rb:46:1:46:2 | "" | |
|
||||
| literals.rb:47:1:47:2 | "" | |
|
||||
@@ -435,6 +447,10 @@ symbolLiterals
|
||||
| literals.rb:117:3:117:5 | :foo | :foo |
|
||||
| literals.rb:117:11:117:14 | :bar | :bar |
|
||||
| literals.rb:118:3:118:5 | :foo | :foo |
|
||||
| literals.rb:185:2:185:2 | :x | :x |
|
||||
| literals.rb:185:6:185:6 | :y | :y |
|
||||
| literals.rb:186:2:186:2 | :y | :y |
|
||||
| literals.rb:186:7:186:7 | :Z | :Z |
|
||||
subshellLiterals
|
||||
| literals.rb:130:1:130:7 | `ls -l` | ls -l |
|
||||
| literals.rb:131:1:131:9 | `ls -l` | ls -l |
|
||||
@@ -701,6 +717,8 @@ hashLiterals
|
||||
| literals.rb:116:1:116:2 | {...} | 0 |
|
||||
| literals.rb:117:1:117:33 | {...} | 3 |
|
||||
| literals.rb:118:1:118:17 | {...} | 2 |
|
||||
| literals.rb:185:1:185:9 | {...} | 2 |
|
||||
| literals.rb:186:1:186:9 | {...} | 2 |
|
||||
hashLiteralElements
|
||||
| literals.rb:88:1:88:14 | {...} | 0 | literals.rb:88:3:88:12 | Pair | Pair |
|
||||
| literals.rb:117:1:117:33 | {...} | 0 | literals.rb:117:3:117:8 | Pair | Pair |
|
||||
@@ -708,12 +726,20 @@ hashLiteralElements
|
||||
| literals.rb:117:1:117:33 | {...} | 2 | literals.rb:117:22:117:31 | Pair | Pair |
|
||||
| literals.rb:118:1:118:17 | {...} | 0 | literals.rb:118:3:118:8 | Pair | Pair |
|
||||
| literals.rb:118:1:118:17 | {...} | 1 | literals.rb:118:11:118:15 | ** ... | HashSplatExpr |
|
||||
| literals.rb:185:1:185:9 | {...} | 0 | literals.rb:185:2:185:3 | Pair | Pair |
|
||||
| literals.rb:185:1:185:9 | {...} | 1 | literals.rb:185:6:185:8 | Pair | Pair |
|
||||
| literals.rb:186:1:186:9 | {...} | 0 | literals.rb:186:2:186:3 | Pair | Pair |
|
||||
| literals.rb:186:1:186:9 | {...} | 1 | literals.rb:186:7:186:8 | Pair | Pair |
|
||||
hashLiteralKeyValuePairs
|
||||
| literals.rb:88:1:88:14 | {...} | literals.rb:88:3:88:12 | Pair | literals.rb:88:3:88:5 | :foo | literals.rb:88:8:88:12 | "bar" |
|
||||
| literals.rb:117:1:117:33 | {...} | literals.rb:117:3:117:8 | Pair | literals.rb:117:3:117:5 | :foo | literals.rb:117:8:117:8 | 1 |
|
||||
| literals.rb:117:1:117:33 | {...} | literals.rb:117:11:117:19 | Pair | literals.rb:117:11:117:14 | :bar | literals.rb:117:19:117:19 | 2 |
|
||||
| literals.rb:117:1:117:33 | {...} | literals.rb:117:22:117:31 | Pair | literals.rb:117:22:117:26 | "baz" | literals.rb:117:31:117:31 | 3 |
|
||||
| literals.rb:118:1:118:17 | {...} | literals.rb:118:3:118:8 | Pair | literals.rb:118:3:118:5 | :foo | literals.rb:118:8:118:8 | 7 |
|
||||
| literals.rb:185:1:185:9 | {...} | literals.rb:185:2:185:3 | Pair | literals.rb:185:2:185:2 | :x | literals.rb:185:2:185:2 | x |
|
||||
| literals.rb:185:1:185:9 | {...} | literals.rb:185:6:185:8 | Pair | literals.rb:185:6:185:6 | :y | literals.rb:185:8:185:8 | 5 |
|
||||
| literals.rb:186:1:186:9 | {...} | literals.rb:186:2:186:3 | Pair | literals.rb:186:2:186:2 | :y | literals.rb:186:2:186:2 | call to y |
|
||||
| literals.rb:186:1:186:9 | {...} | literals.rb:186:7:186:8 | Pair | literals.rb:186:7:186:7 | :Z | literals.rb:186:7:186:7 | Z |
|
||||
finiteRangeLiterals
|
||||
| literals.rb:121:2:121:6 | _ .. _ | literals.rb:121:2:121:2 | 1 | literals.rb:121:5:121:6 | 10 |
|
||||
| literals.rb:122:2:122:7 | _ ... _ | literals.rb:122:2:122:2 | 1 | literals.rb:122:6:122:7 | 10 |
|
||||
@@ -802,6 +828,8 @@ numericLiterals
|
||||
| literals.rb:140:12:140:12 | 1 | IntegerLiteral | 1 |
|
||||
| literals.rb:146:10:146:10 | 1 | IntegerLiteral | 1 |
|
||||
| literals.rb:146:14:146:14 | 1 | IntegerLiteral | 1 |
|
||||
| literals.rb:184:5:184:6 | 42 | IntegerLiteral | 42 |
|
||||
| literals.rb:185:8:185:8 | 5 | IntegerLiteral | 5 |
|
||||
integerLiterals
|
||||
| literals.rb:10:1:10:4 | 1234 | IntegerLiteral | 1234 |
|
||||
| literals.rb:11:1:11:5 | 5_678 | IntegerLiteral | 5678 |
|
||||
@@ -865,6 +893,8 @@ integerLiterals
|
||||
| literals.rb:140:12:140:12 | 1 | IntegerLiteral | 1 |
|
||||
| literals.rb:146:10:146:10 | 1 | IntegerLiteral | 1 |
|
||||
| literals.rb:146:14:146:14 | 1 | IntegerLiteral | 1 |
|
||||
| literals.rb:184:5:184:6 | 42 | IntegerLiteral | 42 |
|
||||
| literals.rb:185:8:185:8 | 5 | IntegerLiteral | 5 |
|
||||
floatLiterals
|
||||
| literals.rb:30:1:30:5 | 12.34 | FloatLiteral | 12.34 |
|
||||
| literals.rb:31:1:31:7 | 1234e-2 | FloatLiteral | 12.34 |
|
||||
|
||||
@@ -181,3 +181,6 @@ output = <<`SCRIPT`
|
||||
cat file.txt
|
||||
SCRIPT
|
||||
|
||||
x = 42
|
||||
{x:, y:5}
|
||||
{y: , Z:}
|
||||
@@ -35,6 +35,7 @@ idParams
|
||||
| params.rb:70:48:70:48 | c | c |
|
||||
| params.rb:73:27:73:32 | wibble | wibble |
|
||||
| params.rb:77:16:77:18 | val | val |
|
||||
| params.rb:81:31:81:35 | array | array |
|
||||
blockParams
|
||||
| params.rb:46:28:46:33 | &block | block |
|
||||
| params.rb:62:29:62:34 | &block | block |
|
||||
@@ -87,6 +88,8 @@ paramsInMethods
|
||||
| params.rb:62:1:64:3 | use_block_with_optional | 0 | params.rb:62:29:62:34 | &block | BlockParameter |
|
||||
| params.rb:73:1:74:3 | method_with_nil_splat | 0 | params.rb:73:27:73:32 | wibble | SimpleParameter |
|
||||
| params.rb:73:1:74:3 | method_with_nil_splat | 1 | params.rb:73:35:73:39 | **nil | HashSplatNilParameter |
|
||||
| params.rb:81:1:84:3 | anonymous_block_parameter | 0 | params.rb:81:31:81:35 | array | SimpleParameter |
|
||||
| params.rb:81:1:84:3 | anonymous_block_parameter | 1 | params.rb:81:38:81:38 | & | BlockParameter |
|
||||
paramsInBlocks
|
||||
| params.rb:9:11:11:3 | do ... end | 0 | params.rb:9:15:9:17 | key | SimpleParameter |
|
||||
| params.rb:9:11:11:3 | do ... end | 1 | params.rb:9:20:9:24 | value | SimpleParameter |
|
||||
@@ -157,3 +160,5 @@ params
|
||||
| params.rb:73:35:73:39 | **nil | 1 | HashSplatNilParameter |
|
||||
| params.rb:77:16:77:18 | val | 0 | SimpleParameter |
|
||||
| params.rb:77:21:77:25 | **nil | 1 | HashSplatNilParameter |
|
||||
| params.rb:81:31:81:35 | array | 0 | SimpleParameter |
|
||||
| params.rb:81:38:81:38 | & | 1 | BlockParameter |
|
||||
|
||||
@@ -77,3 +77,8 @@ end
|
||||
array.each do |val, **nil|
|
||||
end
|
||||
|
||||
# Anonymous block parameter
|
||||
def anonymous_block_parameter(array, &)
|
||||
proc(&)
|
||||
array.each(&)
|
||||
end
|
||||
|
||||
@@ -48,9 +48,9 @@ break_ensure.rb:
|
||||
#-----| -> 0
|
||||
|
||||
# 3| ... > ...
|
||||
#-----| true -> break
|
||||
#-----| false -> if ...
|
||||
#-----| raise -> while ...
|
||||
#-----| false -> if ...
|
||||
#-----| true -> break
|
||||
|
||||
# 3| 0
|
||||
#-----| -> ... > ...
|
||||
@@ -160,8 +160,8 @@ break_ensure.rb:
|
||||
#-----| -> 0
|
||||
|
||||
# 16| ... > ...
|
||||
#-----| true -> break
|
||||
#-----| false -> if ...
|
||||
#-----| true -> break
|
||||
#-----| raise -> [ensure: raise] y
|
||||
|
||||
# 16| 0
|
||||
@@ -364,16 +364,16 @@ break_ensure.rb:
|
||||
#-----| -> 0
|
||||
|
||||
# 35| ... > ...
|
||||
#-----| true -> break
|
||||
#-----| false -> if ...
|
||||
#-----| true -> break
|
||||
|
||||
# 35| [ensure: raise] ... > ...
|
||||
#-----| true -> [ensure: raise] break
|
||||
#-----| false -> [ensure: raise] if ...
|
||||
#-----| true -> [ensure: raise] break
|
||||
|
||||
# 35| [ensure: return] ... > ...
|
||||
#-----| true -> [ensure: return] break
|
||||
#-----| false -> [ensure: return] if ...
|
||||
#-----| true -> [ensure: return] break
|
||||
|
||||
# 35| 0
|
||||
#-----| -> ... > ...
|
||||
@@ -443,8 +443,8 @@ break_ensure.rb:
|
||||
|
||||
# 47| ... > ...
|
||||
#-----| false -> if ...
|
||||
#-----| raise -> [ensure: raise] x
|
||||
#-----| true -> self
|
||||
#-----| raise -> [ensure: raise] x
|
||||
|
||||
# 47| 1
|
||||
#-----| -> ... > ...
|
||||
@@ -535,8 +535,8 @@ case.rb:
|
||||
#-----| -> 1
|
||||
|
||||
# 3| 1
|
||||
#-----| no-match -> when ...
|
||||
#-----| match -> self
|
||||
#-----| no-match -> when ...
|
||||
|
||||
# 3| then ...
|
||||
#-----| -> case ...
|
||||
@@ -622,8 +622,8 @@ case.rb:
|
||||
#-----| -> 1
|
||||
|
||||
# 11| 1
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> 3
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 11| then ...
|
||||
#-----| -> case ...
|
||||
@@ -635,8 +635,8 @@ case.rb:
|
||||
#-----| -> 2
|
||||
|
||||
# 12| 2
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> 4
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 12| then ...
|
||||
#-----| -> case ...
|
||||
@@ -654,8 +654,8 @@ case.rb:
|
||||
#-----| -> 5
|
||||
|
||||
# 14| ... == ...
|
||||
#-----| false -> in ... then ...
|
||||
#-----| true -> 6
|
||||
#-----| false -> in ... then ...
|
||||
|
||||
# 14| 5
|
||||
#-----| -> ... == ...
|
||||
@@ -721,8 +721,8 @@ case.rb:
|
||||
#-----| -> 1
|
||||
|
||||
# 22| 1
|
||||
#-----| match -> case ...
|
||||
#-----| raise -> exit case_match_no_match (abnormal)
|
||||
#-----| match -> case ...
|
||||
|
||||
# 26| case_match_raise
|
||||
#-----| -> case_match_array
|
||||
@@ -751,8 +751,8 @@ case.rb:
|
||||
#-----| -> -> { ... }
|
||||
|
||||
# 28| -> { ... }
|
||||
#-----| match -> case ...
|
||||
#-----| raise -> exit case_match_raise (abnormal)
|
||||
#-----| match -> case ...
|
||||
|
||||
# 28| enter -> { ... }
|
||||
#-----| -> x
|
||||
@@ -811,8 +811,8 @@ case.rb:
|
||||
#-----| -> [ ..., * ]
|
||||
|
||||
# 35| [ ..., * ]
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> x
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 35| x
|
||||
#-----| match -> case ...
|
||||
@@ -821,8 +821,8 @@ case.rb:
|
||||
#-----| -> [ ..., * ]
|
||||
|
||||
# 36| [ ..., * ]
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> x
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 36| x
|
||||
#-----| match -> case ...
|
||||
@@ -831,12 +831,12 @@ case.rb:
|
||||
#-----| -> Bar
|
||||
|
||||
# 37| Bar
|
||||
#-----| match -> [ ..., * ]
|
||||
#-----| raise -> exit case_match_array (abnormal)
|
||||
#-----| match -> [ ..., * ]
|
||||
|
||||
# 37| [ ..., * ]
|
||||
#-----| match -> a
|
||||
#-----| raise -> exit case_match_array (abnormal)
|
||||
#-----| match -> a
|
||||
|
||||
# 37| a
|
||||
#-----| match -> b
|
||||
@@ -880,19 +880,19 @@ case.rb:
|
||||
#-----| -> [ *,...,* ]
|
||||
|
||||
# 43| [ *,...,* ]
|
||||
#-----| match -> x
|
||||
#-----| raise -> exit case_match_find (abnormal)
|
||||
#-----| match -> x
|
||||
|
||||
# 43| x
|
||||
#-----| -> 1
|
||||
|
||||
# 43| 1
|
||||
#-----| match -> 2
|
||||
#-----| raise -> exit case_match_find (abnormal)
|
||||
#-----| match -> 2
|
||||
|
||||
# 43| 2
|
||||
#-----| match -> y
|
||||
#-----| raise -> exit case_match_find (abnormal)
|
||||
#-----| match -> y
|
||||
|
||||
# 43| y
|
||||
#-----| -> case ...
|
||||
@@ -931,12 +931,12 @@ case.rb:
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 49| { ..., ** }
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> 1
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 49| 1
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> a
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 49| a
|
||||
#-----| match -> rest
|
||||
@@ -952,8 +952,8 @@ case.rb:
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 50| { ..., ** }
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> 1
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 50| 1
|
||||
#-----| match -> case ...
|
||||
@@ -963,12 +963,12 @@ case.rb:
|
||||
#-----| -> Bar
|
||||
|
||||
# 51| Bar
|
||||
#-----| match -> { ..., ** }
|
||||
#-----| raise -> exit case_match_hash (abnormal)
|
||||
#-----| match -> { ..., ** }
|
||||
|
||||
# 51| { ..., ** }
|
||||
#-----| match -> case ...
|
||||
#-----| raise -> exit case_match_hash (abnormal)
|
||||
#-----| match -> case ...
|
||||
|
||||
# 55| case_match_variable
|
||||
#-----| -> case_match_underscore
|
||||
@@ -1186,8 +1186,8 @@ case.rb:
|
||||
#-----| -> ... => ...
|
||||
|
||||
# 82| 5
|
||||
#-----| no-match -> in ... then ...
|
||||
#-----| match -> x
|
||||
#-----| no-match -> in ... then ...
|
||||
|
||||
# 82| ... => ...
|
||||
#-----| -> 5
|
||||
@@ -1360,16 +1360,16 @@ case.rb:
|
||||
#-----| -> 0
|
||||
|
||||
# 91| ""
|
||||
#-----| no-match -> [ ..., * ]
|
||||
#-----| match -> case ...
|
||||
#-----| no-match -> [ ..., * ]
|
||||
|
||||
# 91| [ ..., * ]
|
||||
#-----| no-match -> { ..., ** }
|
||||
#-----| match -> case ...
|
||||
#-----| no-match -> { ..., ** }
|
||||
|
||||
# 91| { ..., ** }
|
||||
#-----| match -> case ...
|
||||
#-----| raise -> exit case_match_various (abnormal)
|
||||
#-----| match -> case ...
|
||||
|
||||
# 95| case_match_guard_no_else
|
||||
#-----| -> exit case.rb (normal)
|
||||
@@ -1404,8 +1404,8 @@ case.rb:
|
||||
#-----| -> 5
|
||||
|
||||
# 97| ... == ...
|
||||
#-----| true -> 6
|
||||
#-----| raise -> exit case_match_guard_no_else (abnormal)
|
||||
#-----| true -> 6
|
||||
|
||||
# 97| 5
|
||||
#-----| -> ... == ...
|
||||
@@ -1820,8 +1820,8 @@ cfg.rb:
|
||||
#-----| -> 1
|
||||
|
||||
# 42| 1
|
||||
#-----| no-match -> when ...
|
||||
#-----| match -> self
|
||||
#-----| no-match -> when ...
|
||||
|
||||
# 42| then ...
|
||||
#-----| -> case ...
|
||||
@@ -1893,8 +1893,8 @@ cfg.rb:
|
||||
#-----| -> 1
|
||||
|
||||
# 48| ... == ...
|
||||
#-----| false -> when ...
|
||||
#-----| true -> self
|
||||
#-----| false -> when ...
|
||||
|
||||
# 48| 1
|
||||
#-----| -> ... == ...
|
||||
@@ -2662,8 +2662,8 @@ cfg.rb:
|
||||
#-----| -> 10
|
||||
|
||||
# 113| ... > ...
|
||||
#-----| false -> ... if ...
|
||||
#-----| true -> self
|
||||
#-----| false -> ... if ...
|
||||
|
||||
# 113| 10
|
||||
#-----| -> ... > ...
|
||||
@@ -3246,8 +3246,8 @@ cfg.rb:
|
||||
#-----| -> 0
|
||||
|
||||
# 174| ... == ...
|
||||
#-----| true -> ... unless ...
|
||||
#-----| false -> self
|
||||
#-----| true -> ... unless ...
|
||||
|
||||
# 174| 0
|
||||
#-----| -> ... == ...
|
||||
@@ -4310,8 +4310,8 @@ ifs.rb:
|
||||
#-----| -> 2
|
||||
|
||||
# 2| ... > ...
|
||||
#-----| false -> x
|
||||
#-----| true -> self
|
||||
#-----| false -> x
|
||||
|
||||
# 2| 2
|
||||
#-----| -> ... > ...
|
||||
@@ -4772,8 +4772,8 @@ ifs.rb:
|
||||
#-----| -> self
|
||||
|
||||
# 47| b
|
||||
#-----| false -> else ...
|
||||
#-----| true -> self
|
||||
#-----| false -> else ...
|
||||
|
||||
# 47| then ...
|
||||
#-----| -> if ...
|
||||
@@ -5125,8 +5125,8 @@ loops.rb:
|
||||
#-----| -> y
|
||||
|
||||
# 31| ... < ...
|
||||
#-----| true -> do ...
|
||||
#-----| false -> while ...
|
||||
#-----| true -> do ...
|
||||
|
||||
# 31| y
|
||||
#-----| -> ... < ...
|
||||
@@ -5246,8 +5246,8 @@ raise.rb:
|
||||
#-----| -> self
|
||||
|
||||
# 19| ExceptionA
|
||||
#-----| match -> self
|
||||
#-----| raise -> exit m2 (abnormal)
|
||||
#-----| match -> self
|
||||
|
||||
# 19| then ...
|
||||
#-----| -> rescue ...
|
||||
@@ -5488,8 +5488,8 @@ raise.rb:
|
||||
#-----| match -> e
|
||||
|
||||
# 62| ExceptionB
|
||||
#-----| match -> e
|
||||
#-----| raise -> exit m6 (abnormal)
|
||||
#-----| match -> e
|
||||
|
||||
# 62| e
|
||||
#-----| -> self
|
||||
@@ -5545,8 +5545,8 @@ raise.rb:
|
||||
#-----| -> 2
|
||||
|
||||
# 69| ... > ...
|
||||
#-----| false -> x
|
||||
#-----| true -> self
|
||||
#-----| false -> x
|
||||
#-----| raise -> [ensure: raise] self
|
||||
|
||||
# 69| 2
|
||||
@@ -5588,8 +5588,8 @@ raise.rb:
|
||||
#-----| -> "x < 0"
|
||||
|
||||
# 74| call to puts
|
||||
#-----| -> self
|
||||
#-----| raise -> [ensure: raise] self
|
||||
#-----| -> self
|
||||
|
||||
# 74| self
|
||||
#-----| -> 0 <= x <= 2
|
||||
@@ -5681,8 +5681,8 @@ raise.rb:
|
||||
#-----| -> 2
|
||||
|
||||
# 82| ... > ...
|
||||
#-----| false -> x
|
||||
#-----| true -> self
|
||||
#-----| false -> x
|
||||
#-----| raise -> [ensure: raise] self
|
||||
|
||||
# 82| 2
|
||||
@@ -5724,8 +5724,8 @@ raise.rb:
|
||||
#-----| -> "x < 0"
|
||||
|
||||
# 87| call to puts
|
||||
#-----| -> self
|
||||
#-----| raise -> [ensure: raise] self
|
||||
#-----| -> self
|
||||
|
||||
# 87| self
|
||||
#-----| -> 0 <= x <= 2
|
||||
@@ -5836,8 +5836,8 @@ raise.rb:
|
||||
#-----| -> 2
|
||||
|
||||
# 97| ... > ...
|
||||
#-----| false -> x
|
||||
#-----| true -> self
|
||||
#-----| false -> x
|
||||
#-----| raise -> [ensure: raise] self
|
||||
|
||||
# 97| 2
|
||||
@@ -5879,8 +5879,8 @@ raise.rb:
|
||||
#-----| -> "x < 0"
|
||||
|
||||
# 102| call to puts
|
||||
#-----| -> self
|
||||
#-----| raise -> [ensure: raise] self
|
||||
#-----| -> self
|
||||
|
||||
# 102| self
|
||||
#-----| -> 0 <= x <= 2
|
||||
@@ -6093,8 +6093,8 @@ raise.rb:
|
||||
#-----| -> "inner ensure"
|
||||
|
||||
# 113| call to puts
|
||||
#-----| -> self
|
||||
#-----| raise -> [ensure: raise] self
|
||||
#-----| -> self
|
||||
|
||||
# 113| self
|
||||
#-----| -> End m9
|
||||
@@ -6465,8 +6465,8 @@ raise.rb:
|
||||
#-----| -> call to nil?
|
||||
|
||||
# 155| call to nil?
|
||||
#-----| false -> ... if ...
|
||||
#-----| true -> self
|
||||
#-----| false -> ... if ...
|
||||
|
||||
# 158| enter m15
|
||||
#-----| -> self
|
||||
@@ -6532,8 +6532,8 @@ raise.rb:
|
||||
#-----| -> call to raise
|
||||
|
||||
# 161| x
|
||||
#-----| true -> ... unless ...
|
||||
#-----| false -> self
|
||||
#-----| true -> ... unless ...
|
||||
|
||||
# 166| C
|
||||
#-----| -> self
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
classMethodCalls
|
||||
| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getMethod("m").getReturn() |
|
||||
| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getMethod("m").getReturn() |
|
||||
instanceMethodCalls
|
||||
| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() |
|
||||
13
ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.ql
Normal file
13
ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.ql
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Tests of the public API of API Graphs
|
||||
*/
|
||||
|
||||
import codeql.ruby.ApiGraphs
|
||||
|
||||
query predicate classMethodCalls(API::Node node) {
|
||||
node = API::getTopLevelMember("M1").getMember("C1").getReturn("m")
|
||||
}
|
||||
|
||||
query predicate instanceMethodCalls(API::Node node) {
|
||||
node = API::getTopLevelMember("M1").getMember("C1").getInstance().getReturn("m")
|
||||
}
|
||||
39
ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb
Normal file
39
ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb
Normal file
@@ -0,0 +1,39 @@
|
||||
Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn()
|
||||
a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn()
|
||||
b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn()
|
||||
end
|
||||
|
||||
Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something").getMethod("withNamedArg").getReturn()
|
||||
a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn()
|
||||
b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn()
|
||||
end
|
||||
|
||||
Something.withLambda ->(a, b) { #$ use=getMember("Something").getMethod("withLambda").getReturn()
|
||||
a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn()
|
||||
}
|
||||
|
||||
Something.namedCallback( #$ use=getMember("Something").getMethod("namedCallback").getReturn()
|
||||
onEvent: ->(a, b) {
|
||||
a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn()
|
||||
}
|
||||
)
|
||||
|
||||
Something.nestedCall1 do |a| #$ use=getMember("Something").getMethod("nestedCall1").getReturn()
|
||||
a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn()
|
||||
b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn()
|
||||
end
|
||||
end
|
||||
|
||||
def getCallback()
|
||||
->(x) {
|
||||
x.something #$ use=getMember("Something").getMethod("indirectCallback").getParameter(0).getParameter(0).getMethod("something").getReturn()
|
||||
}
|
||||
end
|
||||
Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn()
|
||||
|
||||
Something.withMixed do |a, *args, b| #$ use=getMember("Something").getMethod("withMixed").getReturn()
|
||||
a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn()
|
||||
# b.something # not currently handled correctly
|
||||
end
|
||||
@@ -1,20 +1,20 @@
|
||||
MyModule #$ use=getMember("MyModule")
|
||||
print MyModule.foo #$ use=getMember("MyModule").getReturn("foo")
|
||||
Kernel.print(e) #$ use=getMember("Kernel").getReturn("print")
|
||||
print MyModule.foo #$ use=getMember("MyModule").getMethod("foo").getReturn()
|
||||
Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() def=getMember("Kernel").getMethod("print").getParameter(0)
|
||||
Object::Kernel #$ use=getMember("Kernel")
|
||||
Object::Kernel.print(e) #$ use=getMember("Kernel").getReturn("print")
|
||||
Object::Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn()
|
||||
begin
|
||||
print MyModule.bar #$ use=getMember("MyModule").getReturn("bar")
|
||||
print MyModule.bar #$ use=getMember("MyModule").getMethod("bar").getReturn()
|
||||
raise AttributeError #$ use=getMember("AttributeError")
|
||||
rescue AttributeError => e #$ use=getMember("AttributeError")
|
||||
Kernel.print(e) #$ use=getMember("Kernel").getReturn("print")
|
||||
Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn()
|
||||
end
|
||||
Unknown.new.run #$ use=getMember("Unknown").instance.getReturn("run")
|
||||
Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMethod("run").getReturn()
|
||||
Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz")
|
||||
|
||||
Const = [1, 2, 3] #$ use=getMember("Array").getReturn("[]")
|
||||
Const.each do |c| #$ use=getMember("Const").getReturn("each")
|
||||
puts c
|
||||
Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn()
|
||||
Const.each do |c| #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock()
|
||||
puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0)
|
||||
end
|
||||
|
||||
foo = Foo #$ use=getMember("Foo")
|
||||
@@ -28,4 +28,37 @@ module Outer
|
||||
end
|
||||
end
|
||||
|
||||
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getReturn("foo")
|
||||
Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getMethod("foo").getReturn()
|
||||
|
||||
module M1
|
||||
class C1
|
||||
def self.m
|
||||
end
|
||||
|
||||
def m
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class C2 < M1::C1 #$ use=getMember("M1").getMember("C1")
|
||||
end
|
||||
|
||||
module M2
|
||||
class C3 < M1::C1 #$ use=getMember("M1").getMember("C1")
|
||||
end
|
||||
|
||||
class C4 < C2 #$ use=getMember("C2")
|
||||
end
|
||||
end
|
||||
|
||||
C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass()
|
||||
M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass()
|
||||
M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass()
|
||||
|
||||
M1::C1.m #$ use=getMember("M1").getMember("C1").getMethod("m").getReturn()
|
||||
M2::C3.m #$ use=getMember("M2").getMember("C3").getMethod("m").getReturn() use=getMember("M1").getMember("C1").getASubclass().getMethod("m").getReturn()
|
||||
|
||||
M1::C1.new.m #$ use=getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn()
|
||||
M2::C3.new.m #$ use=getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn()
|
||||
|
||||
Foo.foo(a,b:c) #$ use=getMember("Foo").getMethod("foo").getReturn() def=getMember("Foo").getMethod("foo").getParameter(0) def=getMember("Foo").getMethod("foo").getKeywordParameter("b")
|
||||
|
||||
@@ -6,20 +6,26 @@ import codeql.ruby.ApiGraphs
|
||||
class ApiUseTest extends InlineExpectationsTest {
|
||||
ApiUseTest() { this = "ApiUseTest" }
|
||||
|
||||
override string getARelevantTag() { result = "use" }
|
||||
override string getARelevantTag() { result = ["use", "def"] }
|
||||
|
||||
private predicate relevantNode(API::Node a, DataFlow::Node n, Location l) {
|
||||
n = a.getAUse() and
|
||||
l = n.getLocation()
|
||||
private predicate relevantNode(API::Node a, DataFlow::Node n, Location l, string tag) {
|
||||
l = n.getLocation() and
|
||||
(
|
||||
tag = "use" and
|
||||
n = a.getAUse()
|
||||
or
|
||||
tag = "def" and
|
||||
n = a.getARhs()
|
||||
)
|
||||
}
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location) |
|
||||
tag = "use" and
|
||||
tag = "use" and // def tags are always optional
|
||||
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) |
|
||||
// Only report the longest path on this line:
|
||||
value =
|
||||
max(API::Node a2, Location l2, DataFlow::Node n2 |
|
||||
relevantNode(a2, n2, l2) and
|
||||
relevantNode(a2, n2, l2, tag) and
|
||||
l2.getFile() = location.getFile() and
|
||||
l2.getStartLine() = location.getStartLine()
|
||||
|
|
||||
@@ -30,6 +36,36 @@ class ApiUseTest extends InlineExpectationsTest {
|
||||
element = n.toString()
|
||||
)
|
||||
}
|
||||
|
||||
// We also permit optional annotations for any other path on the line.
|
||||
// This is used to test subclass paths, which typically have a shorter canonical path.
|
||||
override predicate hasOptionalResult(Location location, string element, string tag, string value) {
|
||||
exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) |
|
||||
element = n.toString() and
|
||||
value = getAPath(a, _)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) }
|
||||
|
||||
/**
|
||||
* Gets a path of the given `length` from the root to the given node.
|
||||
* This is a copy of `API::getAPath()` without the restriction on path length,
|
||||
* which would otherwise rule out paths involving `getASubclass()`.
|
||||
*/
|
||||
string getAPath(API::Node node, int length) {
|
||||
node instanceof API::Root and
|
||||
length = 0 and
|
||||
result = ""
|
||||
or
|
||||
exists(API::Node pred, API::Label::ApiLabel lbl, string predpath |
|
||||
pred.getASuccessor(lbl) = node and
|
||||
predpath = getAPath(pred, length - 1) and
|
||||
exists(string dot | if length = 1 then dot = "" else dot = "." |
|
||||
result = predpath + dot + lbl and
|
||||
// avoid producing strings longer than 1MB
|
||||
result.length() < 1000 * 1000
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,10 +6,6 @@ import ruby
|
||||
import TestUtilities.InlineFlowTest
|
||||
import PathGraph
|
||||
|
||||
class HasFlowTest extends InlineFlowTest {
|
||||
override DataFlow::Configuration getTaintFlowConfig() { none() }
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink, source, sink, "$@", source, source.toString()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,15 +2,26 @@ actionControllerControllerClasses
|
||||
| ActiveRecordInjection.rb:27:1:58:3 | FooController |
|
||||
| ActiveRecordInjection.rb:60:1:90:3 | BarController |
|
||||
| ActiveRecordInjection.rb:92:1:96:3 | BazController |
|
||||
| app/controllers/comments_controller.rb:1:1:7:3 | CommentsController |
|
||||
| app/controllers/foo/bars_controller.rb:3:1:31:3 | BarsController |
|
||||
| app/controllers/photos_controller.rb:1:1:4:3 | PhotosController |
|
||||
| app/controllers/posts_controller.rb:1:1:10:3 | PostsController |
|
||||
| app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController |
|
||||
actionControllerActionMethods
|
||||
| ActiveRecordInjection.rb:32:3:57:5 | some_request_handler |
|
||||
| ActiveRecordInjection.rb:61:3:69:5 | some_other_request_handler |
|
||||
| ActiveRecordInjection.rb:71:3:89:5 | safe_paths |
|
||||
| ActiveRecordInjection.rb:93:3:95:5 | yet_another_handler |
|
||||
| app/controllers/comments_controller.rb:2:3:3:5 | index |
|
||||
| app/controllers/comments_controller.rb:5:3:6:5 | show |
|
||||
| app/controllers/foo/bars_controller.rb:5:3:7:5 | index |
|
||||
| app/controllers/foo/bars_controller.rb:9:3:18:5 | show_debug |
|
||||
| app/controllers/foo/bars_controller.rb:20:3:24:5 | show |
|
||||
| app/controllers/photos_controller.rb:2:3:3:5 | show |
|
||||
| app/controllers/posts_controller.rb:2:3:3:5 | index |
|
||||
| app/controllers/posts_controller.rb:5:3:6:5 | show |
|
||||
| app/controllers/posts_controller.rb:8:3:9:5 | upvote |
|
||||
| app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
|
||||
paramsCalls
|
||||
| ActiveRecordInjection.rb:35:30:35:35 | call to params |
|
||||
| ActiveRecordInjection.rb:39:29:39:34 | call to params |
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
actionDispatchRoutes
|
||||
| app/config/routes.rb:2:3:8:5 | call to resources | get | posts | posts | index |
|
||||
| app/config/routes.rb:2:3:8:5 | call to resources | get | posts/:id | posts | show |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | delete | posts/:post_id/comments/:id | comments | destroy |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments | comments | index |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments/:id | comments | show |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments/new | comments | new |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | get | posts/:post_id/comments:id/edit | comments | edit |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | patch | posts/:post_id/comments/:id | comments | update |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | post | posts/:post_id/comments | comments | create |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | put | posts/:post_id/comments/:id | comments | update |
|
||||
| app/config/routes.rb:4:7:4:41 | call to resources | post | posts/:post_id/comments/:comment_id/replies | replies | create |
|
||||
| app/config/routes.rb:5:7:5:28 | call to post | post | posts/:post_id/comments/:comment_id/flag | comments | flag |
|
||||
| app/config/routes.rb:7:5:7:37 | call to post | post | posts/:post_id/upvote | posts | upvote |
|
||||
| app/config/routes.rb:11:5:11:54 | call to post | post | destroy_all_posts | posts | destroy_alll |
|
||||
| app/config/routes.rb:15:5:15:46 | call to get | get | numbers/:number | numbers | show |
|
||||
| app/config/routes.rb:19:5:19:44 | call to get | get | admin/jobs | background_jobs | index |
|
||||
| app/config/routes.rb:23:5:23:64 | call to get | get | admin/secrets | secrets | view_secrets |
|
||||
| app/config/routes.rb:24:5:24:42 | call to delete | delete | admin/:user_id | users | destroy |
|
||||
| app/config/routes.rb:27:3:27:48 | call to match | get | photos/:id | photos | show |
|
||||
| app/config/routes.rb:28:3:28:50 | call to match | get | photos/:id | photos | show |
|
||||
| app/config/routes.rb:29:3:29:69 | call to match | get | photos/:id | photos | show |
|
||||
| app/config/routes.rb:30:3:30:50 | call to match | delete | photos/:id | photos | show |
|
||||
| app/config/routes.rb:30:3:30:50 | call to match | get | photos/:id | photos | show |
|
||||
| app/config/routes.rb:30:3:30:50 | call to match | patch | photos/:id | photos | show |
|
||||
| app/config/routes.rb:30:3:30:50 | call to match | post | photos/:id | photos | show |
|
||||
| app/config/routes.rb:30:3:30:50 | call to match | put | photos/:id | photos | show |
|
||||
| app/config/routes.rb:33:5:33:43 | call to post | post | upgrade | users | start_upgrade |
|
||||
| app/config/routes.rb:37:5:37:31 | call to get | get | current_billing_cycle | billing/enterprise | current_billing_cycle |
|
||||
| app/config/routes.rb:40:3:40:40 | call to resource | get | global_config | global_config | show |
|
||||
| app/config/routes.rb:43:5:45:7 | call to resources | get | foo/bar | foo/bar | index |
|
||||
| app/config/routes.rb:43:5:45:7 | call to resources | get | foo/bar/:id | foo/bar | show |
|
||||
| app/config/routes.rb:44:7:44:39 | call to get | get | foo/bar/:bar_id/show_debug | foo/bar | show_debug |
|
||||
| app/config/routes.rb:49:5:49:95 | call to delete | delete | users/:user/notifications | users/notifications | destroy |
|
||||
| app/config/routes.rb:50:5:50:94 | call to post | post | users/:user/notifications/:notification_id/mark_as_read | users/notifications | mark_as_read |
|
||||
actionDispatchControllerMethods
|
||||
| app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:2:3:3:5 | index |
|
||||
| app/config/routes.rb:2:3:8:5 | call to resources | app/controllers/posts_controller.rb:5:3:6:5 | show |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:2:3:3:5 | index |
|
||||
| app/config/routes.rb:3:5:6:7 | call to resources | app/controllers/comments_controller.rb:5:3:6:5 | show |
|
||||
| app/config/routes.rb:7:5:7:37 | call to post | app/controllers/posts_controller.rb:8:3:9:5 | upvote |
|
||||
| app/config/routes.rb:27:3:27:48 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
|
||||
| app/config/routes.rb:28:3:28:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
|
||||
| app/config/routes.rb:29:3:29:69 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
|
||||
| app/config/routes.rb:30:3:30:50 | call to match | app/controllers/photos_controller.rb:2:3:3:5 | show |
|
||||
| app/config/routes.rb:50:5:50:94 | call to post | app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read |
|
||||
underscore
|
||||
| Foo | foo |
|
||||
| Foo::Bar | foo/bar |
|
||||
| Foo::Bar::Baz | foo/bar/baz |
|
||||
| Foo::Bar::BazQuux | foo/bar/baz_quux |
|
||||
| FooBar | foo_bar |
|
||||
| FooBar::Baz | foo_bar/baz |
|
||||
| HTTPServerRequest | httpserver_request |
|
||||
| LotsOfCapitalLetters | lots_of_capital_letters |
|
||||
| invalid | invalid |
|
||||
26
ruby/ql/test/library-tests/frameworks/ActionDispatch.ql
Normal file
26
ruby/ql/test/library-tests/frameworks/ActionDispatch.ql
Normal file
@@ -0,0 +1,26 @@
|
||||
private import ruby
|
||||
private import codeql.ruby.frameworks.ActionDispatch
|
||||
private import codeql.ruby.frameworks.ActionController
|
||||
|
||||
query predicate actionDispatchRoutes(
|
||||
ActionDispatch::Route r, string method, string path, string controller, string action
|
||||
) {
|
||||
r.getHttpMethod() = method and
|
||||
r.getPath() = path and
|
||||
r.getController() = controller and
|
||||
r.getAction() = action
|
||||
}
|
||||
|
||||
query predicate actionDispatchControllerMethods(
|
||||
ActionDispatch::Route r, ActionControllerActionMethod m
|
||||
) {
|
||||
m.getARoute() = r
|
||||
}
|
||||
|
||||
query predicate underscore(string input, string output) {
|
||||
output = ActionDispatch::underscore(input) and
|
||||
input in [
|
||||
"Foo", "FooBar", "Foo::Bar", "FooBar::Baz", "Foo::Bar::Baz", "Foo::Bar::BazQuux", "invalid",
|
||||
"HTTPServerRequest", "LotsOfCapitalLetters"
|
||||
]
|
||||
}
|
||||
@@ -93,4 +93,4 @@ class BazController < BarController
|
||||
def yet_another_handler
|
||||
Admin.delete_by(params[:admin_condition])
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,2 @@
|
||||
| active_support.rb:1:1:1:22 | call to constantize | active_support.rb:1:1:1:10 | "Foo::Bar" |
|
||||
| active_support.rb:3:1:3:13 | call to constantize | active_support.rb:3:1:3:1 | call to a |
|
||||
5
ruby/ql/test/library-tests/frameworks/ActiveSupport.ql
Normal file
5
ruby/ql/test/library-tests/frameworks/ActiveSupport.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import codeql.ruby.frameworks.ActiveSupport
|
||||
|
||||
query DataFlow::Node constantizeCalls(ActiveSupport::CoreExtensions::String::Constantize c) {
|
||||
result = c.getCode()
|
||||
}
|
||||
3
ruby/ql/test/library-tests/frameworks/active_support.rb
Normal file
3
ruby/ql/test/library-tests/frameworks/active_support.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
"Foo::Bar".constantize
|
||||
|
||||
a.constantize
|
||||
52
ruby/ql/test/library-tests/frameworks/app/config/routes.rb
Normal file
52
ruby/ql/test/library-tests/frameworks/app/config/routes.rb
Normal file
@@ -0,0 +1,52 @@
|
||||
Rails.application.routes.draw do
|
||||
resources :posts, only: [:show, :index] do
|
||||
resources :comments do
|
||||
resources :replies, only: [:create]
|
||||
post "flag", to: :flag
|
||||
end
|
||||
post "upvote", to: "posts#upvote"
|
||||
end
|
||||
|
||||
if Rails.env.test?
|
||||
post "destroy_all_posts", to: "posts#destroy_alll"
|
||||
end
|
||||
|
||||
constraints(number: /[0-9]+/) do
|
||||
get "/numbers/:number", to: "numbers#show"
|
||||
end
|
||||
|
||||
scope path: "/admin" do
|
||||
get "/jobs", to: "background_jobs#index"
|
||||
end
|
||||
|
||||
scope "/admin" do
|
||||
get "secrets", controller: "secrets", action: "view_secrets"
|
||||
delete ":user_id", to: "users#destroy"
|
||||
end
|
||||
|
||||
match "photos/:id" => "photos#show", via: :get
|
||||
match "photos/:id", to: "photos#show", via: :get
|
||||
match "photos/:id", controller: "photos", action: "show", via: :get
|
||||
match "photos/:id", to: "photos#show", via: :all
|
||||
|
||||
scope controller: "users" do
|
||||
post "upgrade", action: "start_upgrade"
|
||||
end
|
||||
|
||||
scope module: "enterprise", controller: "billing" do
|
||||
get "current_billing_cycle"
|
||||
end
|
||||
|
||||
resource :global_config, only: [:show]
|
||||
|
||||
namespace :foo do
|
||||
resources :bar, only: [:index, :show] do
|
||||
get "show_debug", to: :show_debug
|
||||
end
|
||||
end
|
||||
|
||||
scope "/users/:user" do
|
||||
delete "/notifications", to: "users/notifications#destroy", as: :user_destroy_notifications
|
||||
post "notifications/:notification_id/mark_as_read", to: "users/notifications#mark_as_read"
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user