Ruby: Desugar hash literals

```rb
{ a: 1, **splat, b: 2 }
```

becomes

```rb
::Hash.[](a: 1, **splat, b: 2)
```
This commit is contained in:
Tom Hvitved
2022-01-25 16:10:24 +01:00
parent 39436828de
commit dd27ed8392
17 changed files with 257 additions and 99 deletions

View File

@@ -794,14 +794,15 @@ private module ArrayLiteralDesugar {
exists(ArrayLiteral al |
parent = al and
i = -1 and
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements() + 1))
child = SynthChild(MethodCallKind("[]", false, al.getNumberOfElements()))
or
exists(AstNode mc | mc = TMethodCallSynth(al, -1, _, _, _) |
parent = mc and
exists(AstNode mc |
mc = TMethodCallSynth(al, -1, _, _, _) and
parent = mc
|
i = 0 and
child = SynthChild(ConstantReadAccessKind("::Array"))
or
parent = mc and
child = childRef(al.getElement(i - 1))
)
)
@@ -825,13 +826,58 @@ private module ArrayLiteralDesugar {
final override predicate methodCall(string name, boolean setter, int arity) {
name = "[]" and
setter = false and
arity = any(ArrayLiteral al).getNumberOfElements() + 1
arity = any(ArrayLiteral al).getNumberOfElements()
}
final override predicate constantReadAccess(string name) { name = "::Array" }
}
}
private module HashLiteralDesugar {
pragma[nomagic]
private predicate hashLiteralSynthesis(AstNode parent, int i, Child child) {
exists(HashLiteral hl |
parent = hl and
i = -1 and
child = SynthChild(MethodCallKind("[]", false, hl.getNumberOfElements()))
or
exists(AstNode mc |
mc = TMethodCallSynth(hl, -1, _, _, _) and
parent = mc
|
i = 0 and
child = SynthChild(ConstantReadAccessKind("::Hash"))
or
child = childRef(hl.getElement(i - 1))
)
)
}
/**
* ```rb
* { a: 1, **splat, b: 2 }
* ```
* desugars to
*
* ```rb
* ::Hash.[](a: 1, **splat, b: 2)
* ```
*/
private class HashLiteralSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
hashLiteralSynthesis(parent, i, child)
}
final override predicate methodCall(string name, boolean setter, int arity) {
name = "[]" and
setter = false and
arity = any(HashLiteral hl).getNumberOfElements()
}
final override predicate constantReadAccess(string name) { name = "::Hash" }
}
}
/**
* ```rb
* for x in xs

View File

@@ -484,6 +484,9 @@ module ExprNodes {
/** Gets the `n`th argument of this call. */
final ExprCfgNode getArgument(int n) { e.hasCfgChild(e.getArgument(n), this, result) }
/** Gets an argument of this call. */
final ExprCfgNode getAnArgument() { result = this.getArgument(_) }
/** Gets the the keyword argument whose key is `keyword` of this call. */
final ExprCfgNode getKeywordArgument(string keyword) {
exists(PairCfgNode n |
@@ -940,4 +943,39 @@ module ExprNodes {
final override ElementReference getExpr() { result = super.getExpr() }
}
/**
* A control-flow node that wraps an array literal. Array literals are desugared
* into calls to `Array.[]`, so this includes both desugared calls as well as
* explicit calls.
*/
class ArrayLiteralCfgNode extends MethodCallCfgNode {
ArrayLiteralCfgNode() {
exists(ConstantReadAccess array |
array = this.getReceiver().getExpr() and
e.(MethodCall).getMethodName() = "[]" and
array.getName() = "Array" and
array.hasGlobalScope()
)
}
}
/**
* A control-flow node that wraps a hash literal. Hash literals are desugared
* into calls to `Hash.[]`, so this includes both desugared calls as well as
* explicit calls.
*/
class HashLiteralCfgNode extends MethodCallCfgNode {
HashLiteralCfgNode() {
exists(ConstantReadAccess array |
array = this.getReceiver().getExpr() and
e.(MethodCall).getMethodName() = "[]" and
array.getName() = "Hash" and
array.hasGlobalScope()
)
}
/** Gets a pair of this hash literal. */
PairCfgNode getAKeyValuePair() { result = this.getAnArgument() }
}
}

View File

@@ -986,10 +986,6 @@ module Trees {
private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { }
private class HashLiteralTree extends StandardPostOrderTree, HashLiteral {
final override ControlFlowTree getChildElement(int i) { result = this.getElement(i) }
}
private class HashSplatNilParameterTree extends LeafTree, HashSplatNilParameter { }
private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { }

View File

@@ -278,16 +278,9 @@ private DataFlow::LocalSourceNode trackInstance(Module tp, TypeTracker t) {
or
result.asExpr().getExpr() instanceof StringlikeLiteral and tp = TResolved("String")
or
exists(ConstantReadAccess array, MethodCall mc |
result.asExpr().getExpr() = mc and
mc.getMethodName() = "[]" and
mc.getReceiver() = array and
array.getName() = "Array" and
array.hasGlobalScope() and
tp = TResolved("Array")
)
result.asExpr() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode and tp = TResolved("Array")
or
result.asExpr().getExpr() instanceof HashLiteral and tp = TResolved("Hash")
result.asExpr() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode and tp = TResolved("Hash")
or
result.asExpr().getExpr() instanceof MethodBase and tp = TResolved("Symbol")
or

View File

@@ -52,14 +52,15 @@ private class LibXmlRubyXmlParserCall extends XmlParserCall::Range, DataFlow::Ca
override DataFlow::Node getInput() { result = this.getArgument(0) }
override predicate externalEntitiesEnabled() {
exists(Pair pair |
pair = this.getArgument(1).asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(CfgNodes::ExprNodes::PairCfgNode pair |
pair =
this.getArgument(1).asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
pair.getKey().getConstantValue().isStringOrSymbol("options") and
pair.getValue() =
[
trackEnableFeature(TNOENT()), trackEnableFeature(TDTDLOAD()),
trackDisableFeature(TNONET())
].asExpr().getExpr()
].asExpr()
)
}
}

View File

@@ -1,4 +1,5 @@
private import ruby
private import codeql.ruby.CFG
private import codeql.ruby.Concepts
private import codeql.ruby.ApiGraphs
@@ -91,16 +92,16 @@ class ExconHttpRequest extends HTTP::Client::Request::Range {
predicate argSetsVerifyPeer(DataFlow::Node arg, boolean value, DataFlow::Node kvNode) {
// Either passed as an individual key:value argument, e.g.:
// Excon.get(..., ssl_verify_peer: false)
isSslVerifyPeerPair(arg.asExpr().getExpr(), value) and
isSslVerifyPeerPair(arg.asExpr(), value) and
kvNode = arg
or
// Or as a single hash argument, e.g.:
// Excon.get(..., { ssl_verify_peer: false, ... })
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
isSslVerifyPeerPair(p, value) and
optionsNode.flowsTo(arg) and
kvNode.asExpr().getExpr() = p
kvNode.asExpr() = p
)
}
@@ -133,10 +134,10 @@ private predicate hasBooleanValue(DataFlow::Node node, boolean value) {
}
/** Holds if `p` is the pair `ssl_verify_peer: <value>`. */
private predicate isSslVerifyPeerPair(Pair p, boolean value) {
private predicate isSslVerifyPeerPair(CfgNodes::ExprNodes::PairCfgNode p, boolean value) {
exists(DataFlow::Node key, DataFlow::Node valueNode |
key.asExpr().getExpr() = p.getKey() and valueNode.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
valueNode.asExpr() = p.getValue() and
isSslVerifyPeerLiteral(key) and
hasBooleanValue(valueNode, value)
)

View File

@@ -1,4 +1,5 @@
private import ruby
private import codeql.ruby.CFG
private import codeql.ruby.Concepts
private import codeql.ruby.ApiGraphs
@@ -56,16 +57,16 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
|
// Either passed as an individual key:value argument, e.g.:
// Faraday.new(..., ssl: {...})
isSslOptionsPairDisablingValidation(arg.asExpr().getExpr()) and
isSslOptionsPairDisablingValidation(arg.asExpr()) and
disablingNode = arg
or
// Or as a single hash argument, e.g.:
// Faraday.new(..., { ssl: {...} })
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
isSslOptionsPairDisablingValidation(p) and
optionsNode.flowsTo(arg) and
disablingNode.asExpr().getExpr() = p
disablingNode.asExpr() = p
)
)
}
@@ -78,10 +79,10 @@ class FaradayHttpRequest extends HTTP::Client::Request::Range {
* containing either `verify: false` or
* `verify_mode: OpenSSL::SSL::VERIFY_NONE`.
*/
private predicate isSslOptionsPairDisablingValidation(Pair p) {
private predicate isSslOptionsPairDisablingValidation(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isSymbolLiteral(key, "ssl") and
(isHashWithVerifyFalse(value) or isHashWithVerifyModeNone(value))
)
@@ -101,7 +102,7 @@ private predicate isSymbolLiteral(DataFlow::Node node, string valueText) {
*/
private predicate isHashWithVerifyFalse(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode hash |
isVerifyFalsePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and
isVerifyFalsePair(hash.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()) and
hash.flowsTo(node)
)
}
@@ -112,7 +113,7 @@ private predicate isHashWithVerifyFalse(DataFlow::Node node) {
*/
private predicate isHashWithVerifyModeNone(DataFlow::Node node) {
exists(DataFlow::LocalSourceNode hash |
isVerifyModeNonePair(hash.asExpr().getExpr().(HashLiteral).getAKeyValuePair()) and
isVerifyModeNonePair(hash.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()) and
hash.flowsTo(node)
)
}
@@ -121,10 +122,10 @@ private predicate isHashWithVerifyModeNone(DataFlow::Node node) {
* Holds if the pair `p` has the key `:verify_mode` and the value
* `OpenSSL::SSL::VERIFY_NONE`.
*/
private predicate isVerifyModeNonePair(Pair p) {
private predicate isVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isSymbolLiteral(key, "verify_mode") and
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
)
@@ -133,10 +134,10 @@ private predicate isVerifyModeNonePair(Pair p) {
/**
* Holds if the pair `p` has the key `:verify` and the value `false`.
*/
private predicate isVerifyFalsePair(Pair p) {
private predicate isVerifyFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isSymbolLiteral(key, "verify") and
isFalse(value)
)

View File

@@ -1,4 +1,5 @@
private import ruby
private import codeql.ruby.CFG
private import codeql.ruby.Concepts
private import codeql.ruby.ApiGraphs
@@ -48,20 +49,21 @@ class HttpartyRequest extends HTTP::Client::Request::Range {
// argument, and we're looking for `{ verify: false }` or
// `{ verify_peer: false }`.
exists(DataFlow::Node arg, int i |
i > 0 and arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i)
i > 0 and
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getArgument(i)
|
// Either passed as an individual key:value argument, e.g.:
// HTTParty.get(..., verify: false)
isVerifyFalsePair(arg.asExpr().getExpr()) and
isVerifyFalsePair(arg.asExpr()) and
disablingNode = arg
or
// Or as a single hash argument, e.g.:
// HTTParty.get(..., { verify: false, ... })
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
isVerifyFalsePair(p) and
optionsNode.flowsTo(arg) and
disablingNode.asExpr().getExpr() = p
disablingNode.asExpr() = p
)
)
}
@@ -88,10 +90,10 @@ private predicate isFalse(DataFlow::Node node) {
/**
* Holds if `p` is the pair `verify: false` or `verify_peer: false`.
*/
private predicate isVerifyFalsePair(Pair p) {
private predicate isVerifyFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isVerifyLiteral(key) and
isFalse(value)
)

View File

@@ -1,4 +1,5 @@
private import ruby
private import codeql.ruby.CFG
private import codeql.ruby.Concepts
private import codeql.ruby.ApiGraphs
private import codeql.ruby.frameworks.StandardLibrary
@@ -32,8 +33,7 @@ class OpenUriRequest extends HTTP::Client::Request::Range {
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
exists(DataFlow::Node arg |
arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(_)
|
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getAnArgument() and
argumentDisablesValidation(arg, disablingNode)
)
}
@@ -68,8 +68,7 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
override predicate disablesCertificateValidation(DataFlow::Node disablingNode) {
exists(DataFlow::Node arg, int i |
i > 0 and
arg.asExpr().getExpr() = requestUse.asExpr().getExpr().(MethodCall).getArgument(i)
|
arg.asExpr() = requestUse.asExpr().(CfgNodes::ExprNodes::MethodCallCfgNode).getArgument(i) and
argumentDisablesValidation(arg, disablingNode)
)
}
@@ -85,24 +84,24 @@ class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range {
private predicate argumentDisablesValidation(DataFlow::Node arg, DataFlow::Node disablingNode) {
// Either passed as an individual key:value argument, e.g.:
// URI.open(..., ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE)
isSslVerifyModeNonePair(arg.asExpr().getExpr()) and
isSslVerifyModeNonePair(arg.asExpr()) and
disablingNode = arg
or
// Or as a single hash argument, e.g.:
// URI.open(..., { ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE, ... })
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
isSslVerifyModeNonePair(p) and
optionsNode.flowsTo(arg) and
disablingNode.asExpr().getExpr() = p
disablingNode.asExpr() = p
)
}
/** Holds if `p` is the pair `ssl_verify_mode: OpenSSL::SSL::VERIFY_NONE`. */
private predicate isSslVerifyModeNonePair(Pair p) {
private predicate isSslVerifyModeNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isSslVerifyModeLiteral(key) and
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
)

View File

@@ -1,4 +1,5 @@
private import ruby
private import codeql.ruby.CFG
private import codeql.ruby.Concepts
private import codeql.ruby.ApiGraphs
@@ -50,16 +51,16 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
|
// Either passed as an individual key:value argument, e.g.:
// RestClient::Resource.new(..., verify_ssl: OpenSSL::SSL::VERIFY_NONE)
isVerifySslNonePair(arg.asExpr().getExpr()) and
isVerifySslNonePair(arg.asExpr()) and
disablingNode = arg
or
// Or as a single hash argument, e.g.:
// RestClient::Resource.new(..., { verify_ssl: OpenSSL::SSL::VERIFY_NONE })
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
isVerifySslNonePair(p) and
optionsNode.flowsTo(arg) and
disablingNode.asExpr().getExpr() = p
disablingNode.asExpr() = p
)
)
}
@@ -68,10 +69,10 @@ class RestClientHttpRequest extends HTTP::Client::Request::Range {
}
/** Holds if `p` is the pair `verify_ssl: OpenSSL::SSL::VERIFY_NONE`. */
private predicate isVerifySslNonePair(Pair p) {
private predicate isVerifySslNonePair(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isSslVerifyModeLiteral(key) and
value = API::getTopLevelMember("OpenSSL").getMember("SSL").getMember("VERIFY_NONE").getAUse()
)

View File

@@ -1,4 +1,5 @@
private import ruby
private import codeql.ruby.CFG
private import codeql.ruby.Concepts
private import codeql.ruby.ApiGraphs
@@ -31,16 +32,16 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
|
// Either passed as an individual key:value argument, e.g.:
// Typhoeus.get(..., ssl_verifypeer: false)
isSslVerifyPeerFalsePair(arg.asExpr().getExpr()) and
isSslVerifyPeerFalsePair(arg.asExpr()) and
disablingNode = arg
or
// Or as a single hash argument, e.g.:
// Typhoeus.get(..., { ssl_verifypeer: false, ... })
exists(DataFlow::LocalSourceNode optionsNode, Pair p |
p = optionsNode.asExpr().getExpr().(HashLiteral).getAKeyValuePair() and
exists(DataFlow::LocalSourceNode optionsNode, CfgNodes::ExprNodes::PairCfgNode p |
p = optionsNode.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair() and
isSslVerifyPeerFalsePair(p) and
optionsNode.flowsTo(arg) and
disablingNode.asExpr().getExpr() = p
disablingNode.asExpr() = p
)
)
}
@@ -49,11 +50,10 @@ class TyphoeusHttpRequest extends HTTP::Client::Request::Range {
}
/** Holds if `p` is the pair `ssl_verifypeer: false`. */
private predicate isSslVerifyPeerFalsePair(Pair p) {
private predicate isSslVerifyPeerFalsePair(CfgNodes::ExprNodes::PairCfgNode p) {
exists(DataFlow::Node key, DataFlow::Node value |
key.asExpr().getExpr() = p.getKey() and
value.asExpr().getExpr() = p.getValue()
|
key.asExpr() = p.getKey() and
value.asExpr() = p.getValue() and
isSslVerifyPeerLiteral(key) and
isFalse(value)
)

View File

@@ -73,12 +73,12 @@ module UnsafeDeserialization {
result = "object" and isSafe = false
}
private predicate isOjModePair(Pair p, string modeValue) {
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
p.getKey().getConstantValue().isStringOrSymbol("mode") and
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
symbolLiteral.flowsTo(value) and
value.asExpr().getExpr() = p.getValue()
value.asExpr() = p.getValue()
)
}
@@ -91,7 +91,8 @@ module UnsafeDeserialization {
OjOptionsHashWithModeKey() {
exists(DataFlow::LocalSourceNode options |
options.flowsTo(this) and
isOjModePair(options.asExpr().getExpr().(HashLiteral).getAKeyValuePair(), modeValue)
isOjModePair(options.asExpr().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair(),
modeValue)
)
}
@@ -143,7 +144,7 @@ module UnsafeDeserialization {
exists(DataFlow::Node arg, int i | i >= 1 and arg = this.getArgument(i) |
arg.(OjOptionsHashWithModeKey).hasKnownMode(isSafe)
or
isOjModePair(arg.asExpr().getExpr(), getAKnownOjModeName(isSafe))
isOjModePair(arg.asExpr(), getAKnownOjModeName(isSafe))
)
}
}

View File

@@ -47,6 +47,19 @@ calls/calls.rb:
# 230| getReceiver: [ConstantReadAccess] X
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 249| [HashLiteral] {...}
# 249| getDesugared: [MethodCall] call to []
# 249| getReceiver: [ConstantReadAccess] Hash
# 249| getArgument: [Pair] Pair
# 249| getKey: [MethodCall] call to foo
# 249| getReceiver: [Self, SelfVariableAccess] self
# 249| getValue: [MethodCall] call to bar
# 249| getReceiver: [Self, SelfVariableAccess] self
# 249| getArgument: [Pair] Pair
# 249| getKey: [MethodCall] call to foo
# 249| getReceiver: [ConstantReadAccess] X
# 249| getValue: [MethodCall] call to bar
# 249| getReceiver: [ConstantReadAccess] X
# 314| [AssignExpr] ... = ...
# 314| getDesugared: [StmtSequence] ...
# 314| getStmt: [SetterMethodCall] call to foo=
@@ -292,6 +305,13 @@ constants/constants.rb:
# 20| getArgument: [StringLiteral] "Dave"
# 20| getComponent: [StringTextComponent] Dave
literals/literals.rb:
# 88| [HashLiteral] {...}
# 88| getDesugared: [MethodCall] call to []
# 88| getReceiver: [ConstantReadAccess] Hash
# 88| getArgument: [Pair] Pair
# 88| getKey: [SymbolLiteral] :foo
# 88| getValue: [StringLiteral] "bar"
# 88| getComponent: [StringTextComponent] bar
# 96| [ArrayLiteral] [...]
# 96| getDesugared: [MethodCall] call to []
# 96| getReceiver: [ConstantReadAccess] Array
@@ -432,6 +452,31 @@ literals/literals.rb:
# 113| getComponent: [StringTextComponent] #{BAR}
# 113| getArgument: [SymbolLiteral] :"baz"
# 113| getComponent: [StringTextComponent] baz
# 116| [HashLiteral] {...}
# 116| getDesugared: [MethodCall] call to []
# 116| getReceiver: [ConstantReadAccess] Hash
# 117| [HashLiteral] {...}
# 117| getDesugared: [MethodCall] call to []
# 117| getReceiver: [ConstantReadAccess] Hash
# 117| getArgument: [Pair] Pair
# 117| getKey: [SymbolLiteral] :foo
# 117| getValue: [IntegerLiteral] 1
# 117| getArgument: [Pair] Pair
# 117| getKey: [SymbolLiteral] :bar
# 117| getValue: [IntegerLiteral] 2
# 117| getArgument: [Pair] Pair
# 117| getKey: [StringLiteral] "baz"
# 117| getComponent: [StringTextComponent] baz
# 117| getValue: [IntegerLiteral] 3
# 118| [HashLiteral] {...}
# 118| getDesugared: [MethodCall] call to []
# 118| getReceiver: [ConstantReadAccess] Hash
# 118| getArgument: [Pair] Pair
# 118| getKey: [SymbolLiteral] :foo
# 118| getValue: [IntegerLiteral] 7
# 118| getArgument: [HashSplatExpr] ** ...
# 118| getAnOperand/getOperand/getReceiver: [MethodCall] call to baz
# 118| getReceiver: [Self, SelfVariableAccess] self
control/loops.rb:
# 9| [ForExpr] for ... in ...
# 9| getDesugared: [MethodCall] call to each
@@ -511,12 +556,14 @@ control/loops.rb:
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 22| getReceiver: [HashLiteral] {...}
# 22| getElement: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getValue: [IntegerLiteral] 0
# 22| getElement: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getValue: [IntegerLiteral] 1
# 22| getDesugared: [MethodCall] call to []
# 22| getReceiver: [ConstantReadAccess] Hash
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getValue: [IntegerLiteral] 0
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getValue: [IntegerLiteral] 1
# 28| [ForExpr] for ... in ...
# 28| getDesugared: [MethodCall] call to each
# 28| getBlock: [BraceBlock] { ... }
@@ -553,12 +600,14 @@ control/loops.rb:
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 31| getStmt: [BreakStmt] break
# 28| getReceiver: [HashLiteral] {...}
# 28| getElement: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getValue: [IntegerLiteral] 0
# 28| getElement: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getValue: [IntegerLiteral] 1
# 28| getDesugared: [MethodCall] call to []
# 28| getReceiver: [ConstantReadAccess] Hash
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getValue: [IntegerLiteral] 0
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getValue: [IntegerLiteral] 1
# 36| [AssignAddExpr] ... += ...
# 36| getDesugared: [AssignExpr] ... = ...
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
@@ -624,6 +673,15 @@ operations/operations.rb:
# 29| getDesugared: [MethodCall] call to []
# 29| getReceiver: [ConstantReadAccess] Array
# 29| getArgument: [IntegerLiteral] 2
# 29| [HashLiteral] {...}
# 29| getDesugared: [MethodCall] call to []
# 29| getReceiver: [ConstantReadAccess] Hash
# 29| getArgument: [Pair] Pair
# 29| getKey: [SymbolLiteral] :b
# 29| getValue: [IntegerLiteral] 4
# 29| getArgument: [Pair] Pair
# 29| getKey: [SymbolLiteral] :c
# 29| getValue: [IntegerLiteral] 5
# 69| [AssignAddExpr] ... += ...
# 69| getDesugared: [AssignExpr] ... = ...
# 69| getAnOperand/getLeftOperand: [LocalVariableAccess] x
@@ -721,6 +779,9 @@ operations/operations.rb:
# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var
# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
params/params.rb:
# 8| [HashLiteral] {...}
# 8| getDesugared: [MethodCall] call to []
# 8| getReceiver: [ConstantReadAccess] Hash
# 21| [ArrayLiteral] [...]
# 21| getDesugared: [MethodCall] call to []
# 21| getReceiver: [ConstantReadAccess] Array

View File

@@ -11,6 +11,8 @@ hashSplatExpr
| calls.rb:274:5:274:9 | ** ... | calls.rb:274:7:274:9 | call to bar |
| calls.rb:275:5:275:12 | ** ... | calls.rb:275:7:275:12 | call to bar |
keywordArguments
| calls.rb:249:3:249:12 | Pair | calls.rb:249:3:249:5 | call to foo | calls.rb:249:10:249:12 | call to bar |
| 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 |
keywordArgumentsByKeyword

View File

@@ -22,6 +22,8 @@ callsWithArguments
| calls.rb:85:1:85:12 | ... + ... | + | 0 | calls.rb:85:7:85:12 | call to bar |
| calls.rb:234:1:234:8 | ...[...] | [] | 0 | calls.rb:234:5:234:7 | call to bar |
| calls.rb:235:1:235:14 | ...[...] | [] | 0 | calls.rb:235:8:235:13 | call to bar |
| calls.rb:249:1:249:32 | call to [] | [] | 0 | calls.rb:249:3:249:12 | Pair |
| 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:270:1:270:9 | call to foo | foo | 0 | calls.rb:270:5:270:8 | * ... |
@@ -248,6 +250,7 @@ callsWithReceiver
| calls.rb:245:6:245:8 | call to bar | calls.rb:245:6:245:8 | self |
| calls.rb:246:1:246:6 | call to foo | calls.rb:246:1:246:1 | X |
| calls.rb:246:9:246:14 | call to bar | calls.rb:246:9:246:9 | X |
| calls.rb:249:1:249:32 | call to [] | calls.rb:249:1:249:32 | Hash |
| calls.rb:249:3:249:5 | call to foo | calls.rb:249:3:249:5 | self |
| calls.rb:249:10:249:12 | call to bar | calls.rb:249:10:249:12 | self |
| calls.rb:249:15:249:20 | call to foo | calls.rb:249:15:249:15 | X |

View File

@@ -2434,11 +2434,14 @@ cfg.rb:
#-----| -> map2
# 97| map1
#-----| -> a
#-----| -> Hash
# 97| {...}
# 97| call to []
#-----| -> ... = ...
# 97| Hash
#-----| -> a
# 97| Pair
#-----| -> c
@@ -2473,7 +2476,7 @@ cfg.rb:
#-----| -> f
# 97| Pair
#-----| -> {...}
#-----| -> call to []
# 97| "f"
#-----| -> Pair
@@ -2485,11 +2488,14 @@ cfg.rb:
#-----| -> parameters
# 98| map2
#-----| -> map1
#-----| -> Hash
# 98| {...}
# 98| call to []
#-----| -> ... = ...
# 98| Hash
#-----| -> map1
# 98| ** ...
#-----| -> x
@@ -2512,7 +2518,7 @@ cfg.rb:
#-----| -> "y"
# 98| ** ...
#-----| -> {...}
#-----| -> call to []
# 98| map1
#-----| -> ** ...

View File

@@ -148,6 +148,12 @@ positionalArguments
| cfg.rb:90:10:90:26 | call to [] | cfg.rb:90:21:90:25 | 3.4e5 |
| cfg.rb:91:6:91:10 | ... > ... | cfg.rb:91:10:91:10 | 3 |
| cfg.rb:92:3:92:8 | call to puts | cfg.rb:92:8:92:8 | x |
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:10:97:19 | Pair |
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:22:97:29 | Pair |
| cfg.rb:97:8:97:39 | call to [] | cfg.rb:97:32:97:37 | Pair |
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:10:98:15 | ** ... |
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:18:98:27 | Pair |
| cfg.rb:98:8:98:36 | call to [] | cfg.rb:98:30:98:35 | ** ... |
| cfg.rb:102:3:102:12 | call to puts | cfg.rb:102:8:102:12 | value |
| cfg.rb:103:10:103:20 | ...[...] | cfg.rb:103:17:103:19 | key |
| cfg.rb:108:1:108:12 | call to puts | cfg.rb:108:6:108:12 | ( ... ) |
@@ -344,3 +350,4 @@ positionalArguments
keywordArguments
| cfg.html.erb:6:9:6:58 | call to stylesheet_link_tag | media | cfg.html.erb:6:54:6:58 | "all" |
| cfg.html.erb:12:11:12:33 | call to link_to | id | cfg.html.erb:12:31:12:33 | "a" |
| cfg.rb:97:8:97:39 | call to [] | e | cfg.rb:97:35:97:37 | "f" |