diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index b1c6262a244..3af26170184 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -1,6 +1,7 @@ import codeql.Locations import ast.Call import ast.Control +import ast.Constant import ast.Expr import ast.Method import ast.Module diff --git a/ql/src/codeql_ruby/ast/Call.qll b/ql/src/codeql_ruby/ast/Call.qll index 2f75be0b1f0..38d569e2021 100644 --- a/ql/src/codeql_ruby/ast/Call.qll +++ b/ql/src/codeql_ruby/ast/Call.qll @@ -11,41 +11,30 @@ class Call extends Expr { /** * Gets the receiver of this call, if any. For example: + * * ```rb * foo.bar - * baz() + * Baz::qux + * corge() * ``` - * The result for the call to `bar` is the `Expr` for `foo`, while the call - * to `baz` has no result. + * + * The result for the call to `bar` is the `Expr` for `foo`; the result for + * the call to `qux` is the `Expr` for `Baz`; for the call to `corge` there + * is no result. */ final Expr getReceiver() { result = range.getReceiver() } /** * Gets the name of the method being called. For example, in: + * * ```rb * foo.bar x, y * ``` - * the result is `"bar"`. * - * N.B. in the following example, where the method name is a scope - * resolution, the result is the name being resolved, i.e. `"bar"`. Use - * `getMethodScopeResolution` to get the complete `ScopeResolution`. - * ```rb - * Foo::bar x, y - * ``` + * the result is `"bar"`. */ final string getMethodName() { result = range.getMethodName() } - /** - * Gets the scope resolution of this call, if any. In the following example, - * the result is the `ScopeResolution` for `Foo::bar`, while - * `getMethodName()` returns `"bar"`. - * ```rb - * Foo::bar() - * ``` - */ - final ScopeResolution getMethodScopeResolution() { result = range.getMethodScopeResolution() } - /** * Gets the `n`th argument of this method call. In the following example, the * result for n=0 is the `IntegerLiteral` 0, while for n=1 the result is a diff --git a/ql/src/codeql_ruby/ast/Constant.qll b/ql/src/codeql_ruby/ast/Constant.qll new file mode 100644 index 00000000000..3c11c0f8e91 --- /dev/null +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -0,0 +1,96 @@ +private import codeql_ruby.AST +private import internal.Constant + +/** An access to a constant. */ +class ConstantAccess extends Expr { + override ConstantAccess::Range range; + + /** Gets the name of the constant being accessed. */ + string getName() { result = range.getName() } + + /** + * Gets the expression used in the access's scope resolution operation, if + * any. In the following example, the result is the `Call` expression for + * `foo()`. + * + * ```rb + * foo()::MESSAGE + * ``` + * + * However, there is no result for the following example, since there is no + * scope resolution operation. + * + * ```rb + * MESSAGE + * ``` + */ + Expr getScopeExpr() { result = range.getScopeExpr() } + + /** + * Holds if the access uses the scope resolution operator to refer to the + * global scope, as in this example: + * + * ```rb + * ::MESSAGE + * ``` + */ + predicate hasGlobalScope() { range.hasGlobalScope() } +} + +/** + * A use (read) of a constant. + * + * For example, the right-hand side of the assignment in: + * + * ```rb + * x = Foo + * ``` + * + * Or the superclass `Bar` in this example: + * + * ```rb + * class Foo < Bar + * end + * ``` + */ +class ConstantReadAccess extends ConstantAccess { + final override ConstantReadAccess::Range range; + + final override string getAPrimaryQlClass() { result = "ConstantReadAccess" } +} + +/** + * A definition of a constant. + * + * Examples: + * + * ```rb + * Foo = 1 # defines constant Foo as an integer + * M::Foo = 1 # defines constant Foo as an integer in module M + * + * class Bar; end # defines constant Bar as a class + * class M::Bar; end # defines constant Bar as a class in module M + * + * module Baz; end # defines constant Baz as a module + * module M::Baz; end # defines constant Baz as a module in module M + * ``` + */ +class ConstantWriteAccess extends ConstantAccess { + override ConstantWriteAccess::Range range; + + override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } +} + +/** + * A definition of a constant via assignment. For example, the left-hand + * operand in the following example: + * + * ```rb + * MAX_SIZE = 100 + * ``` + */ +class ConstantAssignment extends ConstantWriteAccess { + override ConstantAssignment::Range range; + + override string getAPrimaryQlClass() { result = "ConstantAssignment" } +} diff --git a/ql/src/codeql_ruby/ast/Expr.qll b/ql/src/codeql_ruby/ast/Expr.qll index 06ac419e6a2..3440d9bd6b9 100644 --- a/ql/src/codeql_ruby/ast/Expr.qll +++ b/ql/src/codeql_ruby/ast/Expr.qll @@ -176,39 +176,6 @@ class ParenthesizedExpr extends ExprSequence, @parenthesized_statements { final override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } } -/** - * A scope resolution, typically used to access constants defined in a class or - * module. - * ```rb - * Foo::Bar - * ``` - */ -class ScopeResolution extends Expr, @scope_resolution { - final override ScopeResolution::Range range; - - final override string getAPrimaryQlClass() { result = "ScopeResolution" } - - /** - * Gets the expression representing the scope, if any. In the following - * example, the scope is the `Expr` for `Foo`: - * ```rb - * Foo::Bar - * ``` - * However, in the following example, accessing the `Bar` constant in the - * `Object` class, there is no result: - * ```rb - * ::Bar - * ``` - */ - final Expr getScope() { result = range.getScope() } - - /** - * Gets the name being resolved. For example, in `Foo::Bar`, the result is - * `"Bar"`. - */ - final string getName() { result = range.getName() } -} - /** * A pair expression. For example, in a hash: * ```rb diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index 1c5e404c431..8e03ee69c5b 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -1,4 +1,5 @@ private import codeql_ruby.AST +private import codeql_ruby.ast.Constant private import internal.Module /** @@ -36,7 +37,7 @@ class ModuleBase extends BodyStatement { * end * ``` */ -class Class extends ModuleBase { +class Class extends ModuleBase, ConstantWriteAccess { final override Class::Range range; final override string getAPrimaryQlClass() { result = "Class" } @@ -49,41 +50,59 @@ class Class extends ModuleBase { * end * ``` * - * N.B. in the following example, where the class name is a scope resolution, - * the result is the name being resolved, i.e. `"Bar"`. Use - * `getScopeResolutionName` to get the complete `ScopeResolution`. + * N.B. in the following example, where the class name uses the scope + * resolution operator, the result is the name being resolved, i.e. `"Bar"`. + * Use `getScopeExpr` to get the `Foo` for `Foo`. * ```rb * class Foo::Bar * end * ``` */ - final string getName() { result = range.getName() } + final override string getName() { result = range.getName() } /** - * Gets the scope resolution used to define the class name, if any. In the - * following example, the result is the `ScopeResolution` for `Foo::Bar`, - * while `getName()` returns `"Bar"`. + * Gets the scope expression used in the class name's scope resolution + * operation, if any. + * + * In the following example, the result is the `Expr` for `Foo`. + * * ```rb * class Foo::Bar * end * ``` * - * In the following example, the name is not a scope resolution, so there is - * no result. + * However, there is no result for the following example, since there is no + * scope resolution operation. + * * ```rb * class Baz * end * ``` */ - final ScopeResolution getNameScopeResolution() { result = range.getNameScopeResolution() } + final override Expr getScopeExpr() { result = range.getScopeExpr() } + + /** + * Holds if the class name uses the scope resolution operator to access the + * global scope, as in this example: + * + * ```rb + * class ::Foo + * end + * ``` + */ + final override predicate hasGlobalScope() { range.hasGlobalScope() } /** * Gets the `Expr` used as the superclass in the class definition, if any. * - * TODO: add example for `class A < Foo` once we have `ConstantAccess` + * In the following example, the result is a `ConstantReadAccess`. + * ```rb + * class Foo < Bar + * end + * ``` * - * For example, where the superclass is a call expression, the result is a - * `Call`. + * In the following example, where the superclass is a call expression, the + * result is a `Call`. * ```rb * class C < foo() * end @@ -145,7 +164,7 @@ class SingletonClass extends ModuleBase, @singleton_class { * end * ``` */ -class Module extends ModuleBase, @module { +class Module extends ModuleBase, ConstantWriteAccess, @module { final override Module::Range range; final override string getAPrimaryQlClass() { result = "Module" } @@ -158,31 +177,45 @@ class Module extends ModuleBase, @module { * end * ``` * - * N.B. in the following example, where the module name is a scope - * resolution, the result is the name being resolved, i.e. `"Bar"`. Use - * `getScopeResolutionName` to get the complete `ScopeResolution`. + * N.B. in the following example, where the module name uses the scope + * resolution operator, the result is the name being resolved, i.e. `"Bar"`. + * Use `getScopeExpr` to get the `Expr` for `Foo`. * ```rb * module Foo::Bar * end * ``` */ - final string getName() { result = range.getName() } + final override string getName() { result = range.getName() } /** - * Gets the scope resolution used to define the module name, if any. In the - * following example, the result is the `ScopeResolution` for `Foo::Bar`, - * while `getName()` returns `"bar"`. + * Gets the scope expression used in the module name's scope resolution + * operation, if any. + * + * In the following example, the result is the `Expr` for `Foo`. + * * ```rb * module Foo::Bar * end * ``` * - * In the following example, the name is not a scope resolution, so this - * predicate has no result: + * However, there is no result for the following example, since there is no + * scope resolution operation. + * * ```rb * module Baz * end * ``` */ - final ScopeResolution getNameScopeResolution() { result = range.getNameScopeResolution() } + final override Expr getScopeExpr() { result = range.getScopeExpr() } + + /** + * Holds if the module name uses the scope resolution operator to access the + * global scope, as in this example: + * + * ```rb + * class ::Foo + * end + * ``` + */ + final override predicate hasGlobalScope() { range.hasGlobalScope() } } diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index 6480f3720f8..5061329da25 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -35,8 +35,6 @@ module AstNode { or this instanceof Generated::Rescue or - this instanceof Generated::Constant - or this instanceof Generated::RestAssignment or this = any(Generated::RestAssignment ra).getChild() diff --git a/ql/src/codeql_ruby/ast/internal/Call.qll b/ql/src/codeql_ruby/ast/internal/Call.qll index 88b68831021..48f64a6cfc6 100644 --- a/ql/src/codeql_ruby/ast/internal/Call.qll +++ b/ql/src/codeql_ruby/ast/internal/Call.qll @@ -9,8 +9,6 @@ module Call { abstract string getMethodName(); - abstract ScopeResolution getMethodScopeResolution(); - abstract Expr getArgument(int n); abstract Block getBlock(); @@ -27,7 +25,24 @@ module Call { final override string getMethodName() { result = generated.getValue() } - final override ScopeResolution getMethodScopeResolution() { none() } + final override Expr getArgument(int n) { none() } + + final override Block getBlock() { none() } + } + + private class ScopeResolutionIdentifierCallRange extends Call::Range, @scope_resolution { + final override Generated::ScopeResolution generated; + Generated::Identifier identifier; + + ScopeResolutionIdentifierCallRange() { + identifier = generated.getName() and + vcall(this) and + not access(identifier, _) + } + + final override Expr getReceiver() { result = generated.getScope() } + + final override string getMethodName() { result = identifier.getValue() } final override Expr getArgument(int n) { none() } @@ -37,15 +52,18 @@ module Call { private class RegularCallRange extends Call::Range, @call { final override Generated::Call generated; - final override Expr getReceiver() { result = generated.getReceiver() } + final override Expr getReceiver() { + if exists(generated.getReceiver()) + then result = generated.getReceiver() + else result = generated.getMethod().(Generated::ScopeResolution).getScope() + } final override string getMethodName() { result = generated.getMethod().(Generated::Token).getValue() or - result = this.getMethodScopeResolution().getName() + result = + generated.getMethod().(Generated::ScopeResolution).getName().(Generated::Token).getValue() } - final override ScopeResolution getMethodScopeResolution() { result = generated.getMethod() } - final override Expr getArgument(int n) { result = generated.getArguments().getChild(n) } final override Block getBlock() { result = generated.getBlock() } @@ -60,8 +78,6 @@ module YieldCall { final override string getMethodName() { result = "yield" } - final override ScopeResolution getMethodScopeResolution() { none() } - final override Expr getArgument(int n) { result = generated.getChild().getChild(n) } final override Block getBlock() { none() } @@ -82,8 +98,6 @@ module SuperCall { final override string getMethodName() { result = generated.getValue() } - final override ScopeResolution getMethodScopeResolution() { none() } - final override Expr getArgument(int n) { none() } final override Block getBlock() { none() } @@ -100,8 +114,6 @@ module SuperCall { result = generated.getMethod().(Generated::Super).getValue() } - final override ScopeResolution getMethodScopeResolution() { none() } - final override Expr getArgument(int n) { result = generated.getArguments().getChild(n) } final override Block getBlock() { result = generated.getBlock() } diff --git a/ql/src/codeql_ruby/ast/internal/Constant.qll b/ql/src/codeql_ruby/ast/internal/Constant.qll new file mode 100644 index 00000000000..3bcd815f640 --- /dev/null +++ b/ql/src/codeql_ruby/ast/internal/Constant.qll @@ -0,0 +1,89 @@ +private import codeql_ruby.ast.internal.Expr +private import codeql_ruby.ast.internal.Pattern +private import codeql_ruby.ast.internal.TreeSitter +private import codeql_ruby.ast.internal.Variable + +module ConstantAccess { + abstract class Range extends Expr::Range { + override string toString() { result = this.getName() } + + abstract string getName(); + + abstract Expr::Range getScopeExpr(); + + abstract predicate hasGlobalScope(); + } +} + +module ConstantReadAccess { + abstract class Range extends ConstantAccess::Range { } + + private class TokenConstantReadAccessRange extends ConstantReadAccess::Range, @token_constant { + final override Generated::Constant generated; + + // A tree-sitter `constant` token is a read of that constant in any context + // where an identifier would be a vcall. + TokenConstantReadAccessRange() { vcall(this) } + + final override string getName() { result = generated.getValue() } + + final override Expr::Range getScopeExpr() { none() } + + final override predicate hasGlobalScope() { none() } + } + + private class ScopeResolutionReadAccessRange extends ConstantReadAccess::Range, @scope_resolution { + final override Generated::ScopeResolution generated; + Generated::Constant constant; + + // A tree-sitter `scope_resolution` node with a `constant` name field is a + // read of that constant in any context where an identifier would be a + // vcall. + ScopeResolutionReadAccessRange() { + constant = generated.getName() and + vcall(this) + } + + final override string getName() { result = constant.getValue() } + + final override Expr::Range getScopeExpr() { result = generated.getScope() } + + final override predicate hasGlobalScope() { not exists(generated.getScope()) } + } +} + +module ConstantWriteAccess { + abstract class Range extends ConstantAccess::Range { } +} + +module ConstantAssignment { + abstract class Range extends ConstantWriteAccess::Range { } + + private class TokenConstantAssignmentRange extends ConstantAssignment::Range, @token_constant { + final override Generated::Constant generated; + + TokenConstantAssignmentRange() { explicitAssignmentNode(this, _) } + + final override string getName() { result = generated.getValue() } + + final override Expr::Range getScopeExpr() { none() } + + final override predicate hasGlobalScope() { none() } + } + + private class ScopeResolutionAssignmentRange extends ConstantAssignment::Range, @scope_resolution { + final override Generated::ScopeResolution generated; + Generated::Constant constant; + + ScopeResolutionAssignmentRange() { + constant = generated.getName() and + explicitAssignmentNode(this, _) + } + + final override string getName() { result = constant.getValue() } + + final override Expr::Range getScopeExpr() { result = generated.getScope() } + + final override predicate hasGlobalScope() { not exists(generated.getScope()) } + } +} diff --git a/ql/src/codeql_ruby/ast/internal/Expr.qll b/ql/src/codeql_ruby/ast/internal/Expr.qll index e35fe5af156..575af203d61 100644 --- a/ql/src/codeql_ruby/ast/internal/Expr.qll +++ b/ql/src/codeql_ruby/ast/internal/Expr.qll @@ -1,5 +1,6 @@ private import codeql_ruby.AST private import codeql_ruby.ast.internal.AST +private import codeql_ruby.ast.internal.Pattern private import codeql_ruby.ast.internal.TreeSitter private import codeql_ruby.ast.internal.Variable @@ -255,18 +256,6 @@ module Ensure { } } -module ScopeResolution { - class Range extends Expr::Range, @scope_resolution { - final override Generated::ScopeResolution generated; - - final Expr getScope() { result = generated.getScope() } - - final string getName() { result = generated.getName().(Generated::Token).getValue() } - - final override string toString() { result = "...::" + this.getName() } - } -} - module Pair { class Range extends Expr::Range, @pair { final override Generated::Pair generated; diff --git a/ql/src/codeql_ruby/ast/internal/Module.qll b/ql/src/codeql_ruby/ast/internal/Module.qll index b17963207ef..b0b2ae67022 100644 --- a/ql/src/codeql_ruby/ast/internal/Module.qll +++ b/ql/src/codeql_ruby/ast/internal/Module.qll @@ -1,4 +1,5 @@ private import codeql_ruby.AST +private import codeql_ruby.ast.internal.Constant private import codeql_ruby.ast.internal.Expr private import codeql_ruby.ast.internal.TreeSitter @@ -7,17 +8,27 @@ module ModuleBase { } module Class { - class Range extends ModuleBase::Range, @class { + class Range extends ModuleBase::Range, ConstantWriteAccess::Range, @class { final override Generated::Class generated; final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - final string getName() { + final override string getName() { result = generated.getName().(Generated::Token).getValue() or - result = this.getNameScopeResolution().getName() + result = + generated.getName().(Generated::ScopeResolution).getName().(Generated::Token).getValue() } - final ScopeResolution getNameScopeResolution() { result = generated.getName() } + final override Expr::Range getScopeExpr() { + result = generated.getName().(Generated::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Generated::ScopeResolution sr | + sr = generated.getName() and + not exists(sr.getScope()) + ) + } final Expr getSuperclassExpr() { result = generated.getSuperclass().getChild() } @@ -38,17 +49,27 @@ module SingletonClass { } module Module { - class Range extends ModuleBase::Range, @module { + class Range extends ModuleBase::Range, ConstantWriteAccess::Range, @module { final override Generated::Module generated; final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - final string getName() { + final override string getName() { result = generated.getName().(Generated::Token).getValue() or - result = this.getNameScopeResolution().getName() + result = + generated.getName().(Generated::ScopeResolution).getName().(Generated::Token).getValue() } - final ScopeResolution getNameScopeResolution() { result = generated.getName() } + final override Expr::Range getScopeExpr() { + result = generated.getName().(Generated::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Generated::ScopeResolution sr | + sr = generated.getName() and + not exists(sr.getScope()) + ) + } final override string toString() { result = this.getName() } } diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index 1deed56915e..da2066b7024 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -165,8 +165,9 @@ private module Cached { not scope.inherits(name, _) } - // Token types that can be vcalls - private class VcallToken = @token_identifier or @token_super; + // Db types that can be vcalls + private class VcallToken = + @scope_resolution or @token_constant or @token_identifier or @token_super; /** * Holds if `i` is an `identifier` node occurring in the context where it diff --git a/ql/test/library-tests/ast/calls/arguments.expected b/ql/test/library-tests/ast/calls/arguments.expected index 39c53ae071d..3872ebda8ff 100644 --- a/ql/test/library-tests/ast/calls/arguments.expected +++ b/ql/test/library-tests/ast/calls/arguments.expected @@ -1,10 +1,15 @@ blockArguments -| calls.rb:201:5:201:8 | &... | calls.rb:201:6:201:8 | call to bar | +| calls.rb:266:5:266:8 | &... | calls.rb:266:6:266:8 | call to bar | +| calls.rb:267:5:267:11 | &... | calls.rb:267:6:267:11 | call to bar | splatArguments -| calls.rb:204:5:204:8 | *... | calls.rb:204:6:204:8 | call to bar | +| calls.rb:270:5:270:8 | *... | calls.rb:270:6:270:8 | call to bar | +| calls.rb:271:5:271:11 | *... | calls.rb:271:6:271:11 | call to bar | hashSplatArguments -| calls.rb:207:5:207:9 | **... | calls.rb:207:7:207:9 | call to bar | +| 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:210:5:210:13 | Pair | calls.rb:210:5:210:8 | :blah | calls.rb:210:11:210:13 | 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 -| calls.rb:210:1:210:14 | call to foo | blah | calls.rb:210:11:210:13 | call to bar | +| 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 | diff --git a/ql/test/library-tests/ast/calls/calls.expected b/ql/test/library-tests/ast/calls/calls.expected index 72591f575a8..893e3fb4e99 100644 --- a/ql/test/library-tests/ast/calls/calls.expected +++ b/ql/test/library-tests/ast/calls/calls.expected @@ -1,144 +1,214 @@ callsWithNoReceiverArgumentsOrBlock | calls.rb:2:1:2:5 | call to foo | foo | -| calls.rb:5:1:5:10 | call to bar | bar | -| calls.rb:28:3:28:7 | call to yield | yield | -| calls.rb:42:1:42:3 | call to foo | foo | -| calls.rb:45:2:45:4 | call to foo | foo | -| calls.rb:48:11:48:13 | call to foo | foo | -| calls.rb:51:2:51:4 | call to foo | foo | -| calls.rb:54:8:54:10 | call to foo | foo | -| calls.rb:57:9:57:11 | call to bar | bar | -| calls.rb:60:8:60:10 | call to foo | foo | -| calls.rb:60:13:60:15 | call to bar | bar | -| calls.rb:64:3:64:5 | call to foo | foo | -| calls.rb:68:9:68:11 | call to foo | foo | -| calls.rb:71:7:71:9 | call to foo | foo | -| calls.rb:74:1:74:3 | call to foo | foo | -| calls.rb:74:7:74:9 | call to bar | bar | -| calls.rb:77:2:77:4 | call to foo | foo | -| calls.rb:80:9:80:11 | call to bar | bar | -| calls.rb:83:10:83:12 | call to bar | bar | -| calls.rb:86:1:86:3 | call to foo | foo | -| calls.rb:90:6:90:8 | call to foo | foo | -| calls.rb:91:6:91:8 | call to bar | bar | -| calls.rb:92:3:92:5 | call to baz | baz | -| calls.rb:97:3:97:5 | call to foo | foo | -| calls.rb:101:17:101:19 | call to foo | foo | -| calls.rb:105:10:105:12 | call to foo | foo | -| calls.rb:106:3:106:5 | call to bar | bar | -| calls.rb:111:3:111:5 | call to foo | foo | -| calls.rb:115:5:115:7 | call to foo | foo | -| calls.rb:116:3:116:5 | call to bar | bar | -| calls.rb:120:40:120:42 | call to foo | foo | -| calls.rb:124:40:124:42 | call to foo | foo | -| calls.rb:129:3:129:5 | call to foo | foo | -| calls.rb:133:1:133:3 | call to foo | foo | -| calls.rb:133:7:133:9 | call to bar | bar | -| calls.rb:133:13:133:15 | call to baz | baz | -| calls.rb:136:4:136:6 | call to foo | foo | -| calls.rb:137:3:137:8 | call to wibble | wibble | -| calls.rb:138:7:138:9 | call to bar | bar | -| calls.rb:139:3:139:8 | call to wobble | wobble | -| calls.rb:141:3:141:8 | call to wabble | wabble | -| calls.rb:145:1:145:3 | call to bar | bar | -| calls.rb:145:8:145:10 | call to foo | foo | -| calls.rb:148:8:148:10 | call to foo | foo | -| calls.rb:149:3:149:5 | call to bar | bar | -| calls.rb:153:1:153:3 | call to bar | bar | -| calls.rb:153:12:153:14 | call to foo | foo | -| calls.rb:156:7:156:9 | call to foo | foo | -| calls.rb:157:3:157:5 | call to bar | bar | -| calls.rb:161:1:161:3 | call to bar | bar | -| calls.rb:161:11:161:13 | call to foo | foo | -| calls.rb:164:7:164:9 | call to foo | foo | -| calls.rb:165:3:165:5 | call to bar | bar | -| calls.rb:169:1:169:3 | call to bar | bar | -| calls.rb:169:11:169:13 | call to foo | foo | -| calls.rb:172:10:172:12 | call to bar | bar | -| calls.rb:173:3:173:5 | call to baz | baz | -| calls.rb:177:1:177:3 | call to foo | foo | -| calls.rb:177:5:177:7 | call to bar | bar | -| calls.rb:180:8:180:10 | call to bar | bar | -| calls.rb:183:1:183:3 | call to foo | foo | -| calls.rb:186:1:186:3 | call to foo | foo | -| calls.rb:186:6:186:8 | call to bar | bar | -| calls.rb:189:3:189:5 | call to foo | foo | -| calls.rb:189:10:189:12 | call to bar | bar | -| calls.rb:193:8:193:10 | call to foo | foo | -| calls.rb:194:8:194:10 | call to bar | bar | -| calls.rb:198:1:198:3 | call to foo | foo | -| calls.rb:198:12:198:14 | call to bar | bar | -| calls.rb:201:6:201:8 | call to bar | bar | -| calls.rb:204:6:204:8 | call to bar | bar | -| calls.rb:207:7:207:9 | call to bar | bar | -| calls.rb:210:11:210:13 | call to bar | bar | -| calls.rb:217:5:217:9 | call to super | super | -| calls.rb:218:5:218:11 | call to super | super | -| calls.rb:234:5:234:7 | call to foo | foo | -| calls.rb:235:5:235:14 | call to super | super | -| calls.rb:236:5:236:9 | call to super | super | -callsWithScopeResolutionName -| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:8 | ...::bar | +| calls.rb:8:1:8:7 | call to bar | bar | +| calls.rb:31:3:31:7 | call to yield | yield | +| calls.rb:46:1:46:3 | call to foo | foo | +| calls.rb:50:2:50:4 | call to foo | foo | +| calls.rb:54:11:54:13 | call to foo | foo | +| calls.rb:58:2:58:4 | call to foo | foo | +| calls.rb:62:8:62:10 | call to foo | foo | +| calls.rb:66:9:66:11 | call to bar | bar | +| calls.rb:70:8:70:10 | call to foo | foo | +| calls.rb:74:3:74:5 | call to foo | foo | +| calls.rb:79:9:79:11 | call to foo | foo | +| calls.rb:82:7:82:9 | call to foo | foo | +| calls.rb:85:1:85:3 | call to foo | foo | +| calls.rb:88:2:88:4 | call to foo | foo | +| calls.rb:92:9:92:11 | call to bar | bar | +| calls.rb:96:3:96:5 | call to bar | bar | +| calls.rb:101:1:101:3 | call to foo | foo | +| calls.rb:102:1:102:3 | call to bar | bar | +| calls.rb:106:6:106:8 | call to foo | foo | +| calls.rb:107:6:107:8 | call to bar | bar | +| calls.rb:108:3:108:5 | call to baz | baz | +| calls.rb:117:3:117:5 | call to foo | foo | +| calls.rb:122:17:122:19 | call to foo | foo | +| calls.rb:128:10:128:12 | call to foo | foo | +| calls.rb:129:3:129:5 | call to bar | bar | +| calls.rb:137:3:137:5 | call to foo | foo | +| calls.rb:142:5:142:7 | call to foo | foo | +| calls.rb:143:3:143:5 | call to bar | bar | +| calls.rb:148:40:148:42 | call to foo | foo | +| calls.rb:154:40:154:42 | call to foo | foo | +| calls.rb:161:3:161:5 | call to foo | foo | +| calls.rb:166:1:166:3 | call to foo | foo | +| calls.rb:166:7:166:9 | call to bar | bar | +| calls.rb:166:13:166:15 | call to baz | baz | +| calls.rb:170:4:170:6 | call to foo | foo | +| calls.rb:171:3:171:8 | call to wibble | wibble | +| calls.rb:172:7:172:9 | call to bar | bar | +| calls.rb:173:3:173:8 | call to wobble | wobble | +| calls.rb:175:3:175:8 | call to wabble | wabble | +| calls.rb:186:1:186:3 | call to bar | bar | +| calls.rb:186:8:186:10 | call to foo | foo | +| calls.rb:190:8:190:10 | call to foo | foo | +| calls.rb:191:3:191:5 | call to bar | bar | +| calls.rb:198:1:198:3 | call to bar | bar | +| calls.rb:198:12:198:14 | call to foo | foo | +| calls.rb:202:7:202:9 | call to foo | foo | +| calls.rb:203:3:203:5 | call to bar | bar | +| calls.rb:210:1:210:3 | call to bar | bar | +| calls.rb:210:11:210:13 | call to foo | foo | +| calls.rb:214:7:214:9 | call to foo | foo | +| calls.rb:215:3:215:5 | call to bar | bar | +| calls.rb:222:1:222:3 | call to bar | bar | +| calls.rb:222:11:222:13 | call to foo | foo | +| calls.rb:226:10:226:12 | call to bar | bar | +| calls.rb:227:3:227:5 | call to baz | baz | +| calls.rb:234:1:234:3 | call to foo | foo | +| calls.rb:234:5:234:7 | call to bar | bar | +| calls.rb:238:8:238:10 | call to bar | bar | +| calls.rb:241:1:241:3 | call to foo | foo | +| calls.rb:245:1:245:3 | call to foo | foo | +| calls.rb:245:6:245:8 | call to bar | bar | +| calls.rb:249:3:249:5 | call to foo | foo | +| calls.rb:249:10:249:12 | call to bar | bar | +| calls.rb:253:8:253:10 | call to foo | foo | +| calls.rb:254:8:254:10 | call to bar | bar | +| calls.rb:262:1:262:3 | call to foo | foo | +| calls.rb:262:12:262:14 | call to bar | bar | +| calls.rb:266:6:266:8 | call to bar | bar | +| calls.rb:270:6:270:8 | call to bar | bar | +| calls.rb:274:7:274:9 | call to bar | bar | +| calls.rb:278:11:278:13 | call to bar | bar | +| calls.rb:286:5:286:9 | call to super | super | +| calls.rb:287:5:287:11 | call to super | super | +| calls.rb:303:5:303:7 | call to foo | foo | +| calls.rb:304:5:304:14 | call to super | super | +| calls.rb:305:5:305:9 | call to super | super | callsWithArguments -| calls.rb:11:1:11:11 | call to foo | foo | 0 | calls.rb:11:5:11:5 | 0 | -| calls.rb:11:1:11:11 | call to foo | foo | 1 | calls.rb:11:8:11:8 | 1 | -| calls.rb:11:1:11:11 | call to foo | foo | 2 | calls.rb:11:11:11:11 | 2 | -| calls.rb:22:1:24:3 | call to bar | bar | 0 | calls.rb:22:9:22:13 | foo | -| calls.rb:33:3:33:16 | call to yield | yield | 0 | calls.rb:33:9:33:11 | 100 | -| calls.rb:33:3:33:16 | call to yield | yield | 1 | calls.rb:33:14:33:16 | 200 | -| calls.rb:48:1:48:14 | call to some_func | some_func | 0 | calls.rb:48:11:48:13 | call to foo | -| calls.rb:201:1:201:9 | call to foo | foo | 0 | calls.rb:201:5:201:8 | &... | -| calls.rb:204:1:204:9 | call to foo | foo | 0 | calls.rb:204:5:204:8 | *... | -| calls.rb:207:1:207:10 | call to foo | foo | 0 | calls.rb:207:5:207:9 | **... | -| calls.rb:210:1:210:14 | call to foo | foo | 0 | calls.rb:210:5:210:13 | Pair | -| calls.rb:219:5:219:16 | call to super | super | 0 | calls.rb:219:11:219:16 | blah | -| calls.rb:220:5:220:17 | call to super | super | 0 | calls.rb:220:11:220:11 | 1 | -| calls.rb:220:5:220:17 | call to super | super | 1 | calls.rb:220:14:220:14 | 2 | -| calls.rb:220:5:220:17 | call to super | super | 2 | calls.rb:220:17:220:17 | 3 | -| calls.rb:223:5:223:30 | call to super | super | 0 | calls.rb:223:11:223:11 | 4 | -| calls.rb:223:5:223:30 | call to super | super | 1 | calls.rb:223:14:223:14 | 5 | -| calls.rb:224:5:224:33 | call to super | super | 0 | calls.rb:224:11:224:11 | 6 | -| calls.rb:224:5:224:33 | call to super | super | 1 | calls.rb:224:14:224:14 | 7 | +| 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 | +| calls.rb:14:1:14:11 | call to foo | foo | 2 | calls.rb:14:11:14:11 | 2 | +| calls.rb:25:1:27:3 | call to bar | bar | 0 | calls.rb:25:9:25:13 | foo | +| calls.rb:36:3:36:16 | call to yield | yield | 0 | calls.rb:36:9:36:11 | 100 | +| calls.rb:36:3:36:16 | call to yield | yield | 1 | calls.rb:36:14:36:16 | 200 | +| calls.rb:54:1:54:14 | call to some_func | some_func | 0 | calls.rb:54:11:54:13 | call to foo | +| calls.rb:55:1:55:17 | call to some_func | some_func | 0 | calls.rb:55:11:55:16 | call to foo | +| 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 | *... | +| 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 | **... | +| calls.rb:275:1:275:13 | call to foo | foo | 0 | calls.rb:275:5:275:12 | **... | +| calls.rb:278:1:278:14 | call to foo | foo | 0 | calls.rb:278:5:278:13 | Pair | +| calls.rb:279:1:279:17 | call to foo | foo | 0 | calls.rb:279:5:279:16 | Pair | +| calls.rb:288:5:288:16 | call to super | super | 0 | calls.rb:288:11:288:16 | blah | +| calls.rb:289:5:289:17 | call to super | super | 0 | calls.rb:289:11:289:11 | 1 | +| calls.rb:289:5:289:17 | call to super | super | 1 | calls.rb:289:14:289:14 | 2 | +| calls.rb:289:5:289:17 | call to super | super | 2 | calls.rb:289:17:289:17 | 3 | +| calls.rb:292:5:292:30 | call to super | super | 0 | calls.rb:292:11:292:11 | 4 | +| calls.rb:292:5:292:30 | call to super | super | 1 | calls.rb:292:14:292:14 | 5 | +| calls.rb:293:5:293:33 | call to super | super | 0 | calls.rb:293:11:293:11 | 6 | +| calls.rb:293:5:293:33 | call to super | super | 1 | calls.rb:293:14:293:14 | 7 | callsWithReceiver -| calls.rb:8:1:8:7 | call to bar | calls.rb:8:1:8:3 | 123 | -| calls.rb:22:1:24:3 | call to bar | calls.rb:22:1:22:3 | 123 | -| calls.rb:86:1:86:9 | call to bar | calls.rb:86:1:86:3 | call to foo | -| calls.rb:234:5:234:13 | call to super | calls.rb:234:5:234:7 | call to foo | -| calls.rb:236:5:236:15 | call to super | calls.rb:236:5:236:9 | call to super | +| calls.rb:5:1:5:10 | call to bar | calls.rb:5:1:5:3 | Foo | +| calls.rb:11:1:11:7 | call to bar | calls.rb:11:1:11:3 | 123 | +| calls.rb:25:1:27:3 | call to bar | calls.rb:25:1:25:3 | 123 | +| calls.rb:47:1:47:6 | call to foo | calls.rb:47:1:47:1 | X | +| calls.rb:51:2:51:7 | call to foo | calls.rb:51:2:51:2 | X | +| calls.rb:55:11:55:16 | call to foo | calls.rb:55:11:55:11 | X | +| calls.rb:59:2:59:7 | call to foo | calls.rb:59:2:59:2 | X | +| calls.rb:63:8:63:13 | call to foo | calls.rb:63:8:63:8 | X | +| calls.rb:67:9:67:14 | call to bar | calls.rb:67:9:67:9 | X | +| calls.rb:70:13:70:18 | call to bar | calls.rb:70:13:70:13 | X | +| calls.rb:75:3:75:8 | call to foo | calls.rb:75:3:75:3 | X | +| calls.rb:79:14:79:19 | call to bar | calls.rb:79:14:79:14 | X | +| calls.rb:82:12:82:17 | call to bar | calls.rb:82:12:82:12 | X | +| calls.rb:85:7:85:12 | call to bar | calls.rb:85:7:85:7 | X | +| calls.rb:89:2:89:7 | call to bar | calls.rb:89:2:89:2 | X | +| calls.rb:92:14:92:19 | call to baz | calls.rb:92:14:92:14 | X | +| calls.rb:97:3:97:8 | call to baz | calls.rb:97:3:97:3 | X | +| calls.rb:101:1:101:9 | call to bar | calls.rb:101:1:101:3 | call to foo | +| calls.rb:102:1:102:9 | call to baz | calls.rb:102:1:102:3 | call to bar | +| calls.rb:110:6:110:11 | call to foo | calls.rb:110:6:110:6 | X | +| calls.rb:111:6:111:11 | call to bar | calls.rb:111:6:111:6 | X | +| calls.rb:112:3:112:8 | call to baz | calls.rb:112:3:112:3 | X | +| calls.rb:118:3:118:8 | call to bar | calls.rb:118:3:118:3 | X | +| calls.rb:124:18:124:23 | call to foo | calls.rb:124:18:124:18 | X | +| calls.rb:131:10:131:15 | call to foo | calls.rb:131:10:131:10 | X | +| calls.rb:132:3:132:8 | call to bar | calls.rb:132:3:132:3 | X | +| calls.rb:138:3:138:8 | call to bar | calls.rb:138:3:138:3 | X | +| calls.rb:144:3:144:8 | call to baz | calls.rb:144:3:144:3 | X | +| calls.rb:150:41:150:46 | call to foo | calls.rb:150:41:150:41 | X | +| calls.rb:156:41:156:46 | call to foo | calls.rb:156:41:156:41 | X | +| calls.rb:162:3:162:8 | call to bar | calls.rb:162:3:162:3 | X | +| calls.rb:167:1:167:6 | call to foo | calls.rb:167:1:167:1 | X | +| calls.rb:167:10:167:15 | call to bar | calls.rb:167:10:167:10 | X | +| calls.rb:167:19:167:24 | call to baz | calls.rb:167:19:167:19 | X | +| calls.rb:177:4:177:9 | call to foo | calls.rb:177:4:177:4 | X | +| calls.rb:178:3:178:11 | call to wibble | calls.rb:178:3:178:3 | X | +| calls.rb:179:7:179:12 | call to bar | calls.rb:179:7:179:7 | X | +| calls.rb:180:3:180:11 | call to wobble | calls.rb:180:3:180:3 | X | +| calls.rb:182:3:182:11 | call to wabble | calls.rb:182:3:182:3 | X | +| calls.rb:187:1:187:6 | call to bar | calls.rb:187:1:187:1 | X | +| calls.rb:187:11:187:16 | call to foo | calls.rb:187:11:187:11 | X | +| calls.rb:193:8:193:13 | call to foo | calls.rb:193:8:193:8 | X | +| calls.rb:194:3:194:8 | call to bar | calls.rb:194:3:194:3 | X | +| calls.rb:199:1:199:6 | call to bar | calls.rb:199:1:199:1 | X | +| calls.rb:199:15:199:20 | call to foo | calls.rb:199:15:199:15 | X | +| calls.rb:205:7:205:12 | call to foo | calls.rb:205:7:205:7 | X | +| calls.rb:206:3:206:8 | call to bar | calls.rb:206:3:206:3 | X | +| calls.rb:211:1:211:6 | call to bar | calls.rb:211:1:211:1 | X | +| calls.rb:211:14:211:19 | call to foo | calls.rb:211:14:211:14 | X | +| calls.rb:217:7:217:12 | call to foo | calls.rb:217:7:217:7 | X | +| calls.rb:218:3:218:8 | call to bar | calls.rb:218:3:218:3 | X | +| calls.rb:223:1:223:6 | call to bar | calls.rb:223:1:223:1 | X | +| calls.rb:223:14:223:19 | call to foo | calls.rb:223:14:223:14 | X | +| calls.rb:229:10:229:15 | call to bar | calls.rb:229:10:229:10 | X | +| calls.rb:230:3:230:8 | call to baz | calls.rb:230:3:230:3 | X | +| calls.rb:235:1:235:6 | call to foo | calls.rb:235:1:235:1 | X | +| calls.rb:235:8:235:13 | call to bar | calls.rb:235:8:235:8 | X | +| calls.rb:238:15:238:20 | call to baz | calls.rb:238:15:238:15 | X | +| calls.rb:242:1:242:6 | call to foo | calls.rb:242:1:242:1 | X | +| 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:15:249:20 | call to foo | calls.rb:249:15:249:15 | X | +| calls.rb:249:25:249:30 | call to bar | calls.rb:249:25:249:25 | X | +| calls.rb:257:8:257:13 | call to foo | calls.rb:257:8:257:8 | X | +| calls.rb:258:8:258:13 | call to bar | calls.rb:258:8:258:8 | X | +| calls.rb:263:1:263:6 | call to foo | calls.rb:263:1:263:1 | X | +| calls.rb:263:15:263:20 | call to bar | calls.rb:263:15:263:15 | X | +| calls.rb:267:6:267:11 | call to bar | calls.rb:267:6:267:6 | X | +| calls.rb:271:6:271:11 | call to bar | calls.rb:271:6:271:6 | X | +| calls.rb:275:7:275:12 | call to bar | calls.rb:275:7:275:7 | X | +| calls.rb:279:11:279:16 | call to bar | calls.rb:279:11:279:11 | X | +| calls.rb:303:5:303:13 | call to super | calls.rb:303:5:303:7 | call to foo | +| calls.rb:305:5:305:15 | call to super | calls.rb:305:5:305:9 | call to super | callsWithBlock -| calls.rb:14:1:14:17 | call to foo | calls.rb:14:5:14:17 | { ... } | -| calls.rb:17:1:19:3 | call to foo | calls.rb:17:5:19:3 | do ... end | -| calls.rb:22:1:24:3 | call to bar | calls.rb:22:16:24:3 | do ... end | -| calls.rb:80:1:80:13 | call to foo | calls.rb:80:7:80:13 | { ... } | -| calls.rb:83:1:83:16 | call to foo | calls.rb:83:7:83:16 | do ... end | -| calls.rb:221:5:221:23 | call to super | calls.rb:221:11:221:23 | { ... } | -| calls.rb:222:5:222:26 | call to super | calls.rb:222:11:222:26 | do ... end | -| calls.rb:223:5:223:30 | call to super | calls.rb:223:16:223:30 | { ... } | -| calls.rb:224:5:224:33 | call to super | calls.rb:224:16:224:33 | do ... end | +| 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 | +| calls.rb:25:1:27:3 | call to bar | calls.rb:25:16:27:3 | do ... end | +| calls.rb:92:1:92:21 | call to foo | calls.rb:92:7:92:21 | { ... } | +| calls.rb:95:1:98:3 | call to foo | calls.rb:95:7:98:3 | do ... end | +| calls.rb:290:5:290:23 | call to super | calls.rb:290:11:290:23 | { ... } | +| calls.rb:291:5:291:26 | call to super | calls.rb:291:11:291:26 | do ... end | +| calls.rb:292:5:292:30 | call to super | calls.rb:292:16:292:30 | { ... } | +| calls.rb:293:5:293:33 | call to super | calls.rb:293:16:293:33 | do ... end | yieldCalls -| calls.rb:28:3:28:7 | call to yield | -| calls.rb:33:3:33:16 | call to yield | +| calls.rb:31:3:31:7 | call to yield | +| calls.rb:36:3:36:16 | call to yield | superCalls -| calls.rb:217:5:217:9 | call to super | -| calls.rb:218:5:218:11 | call to super | -| calls.rb:219:5:219:16 | call to super | -| calls.rb:220:5:220:17 | call to super | -| calls.rb:221:5:221:23 | call to super | -| calls.rb:222:5:222:26 | call to super | -| calls.rb:223:5:223:30 | call to super | -| calls.rb:224:5:224:33 | call to super | -| calls.rb:236:5:236:9 | call to super | +| calls.rb:286:5:286:9 | call to super | +| calls.rb:287:5:287:11 | call to super | +| calls.rb:288:5:288:16 | call to super | +| calls.rb:289:5:289:17 | call to super | +| calls.rb:290:5:290:23 | call to super | +| calls.rb:291:5:291:26 | call to super | +| calls.rb:292:5:292:30 | call to super | +| calls.rb:293:5:293:33 | call to super | +| calls.rb:305:5:305:9 | call to super | superCallsWithArguments -| calls.rb:219:5:219:16 | call to super | 0 | calls.rb:219:11:219:16 | blah | -| calls.rb:220:5:220:17 | call to super | 0 | calls.rb:220:11:220:11 | 1 | -| calls.rb:220:5:220:17 | call to super | 1 | calls.rb:220:14:220:14 | 2 | -| calls.rb:220:5:220:17 | call to super | 2 | calls.rb:220:17:220:17 | 3 | -| calls.rb:223:5:223:30 | call to super | 0 | calls.rb:223:11:223:11 | 4 | -| calls.rb:223:5:223:30 | call to super | 1 | calls.rb:223:14:223:14 | 5 | -| calls.rb:224:5:224:33 | call to super | 0 | calls.rb:224:11:224:11 | 6 | -| calls.rb:224:5:224:33 | call to super | 1 | calls.rb:224:14:224:14 | 7 | +| calls.rb:288:5:288:16 | call to super | 0 | calls.rb:288:11:288:16 | blah | +| calls.rb:289:5:289:17 | call to super | 0 | calls.rb:289:11:289:11 | 1 | +| calls.rb:289:5:289:17 | call to super | 1 | calls.rb:289:14:289:14 | 2 | +| calls.rb:289:5:289:17 | call to super | 2 | calls.rb:289:17:289:17 | 3 | +| calls.rb:292:5:292:30 | call to super | 0 | calls.rb:292:11:292:11 | 4 | +| calls.rb:292:5:292:30 | call to super | 1 | calls.rb:292:14:292:14 | 5 | +| calls.rb:293:5:293:33 | call to super | 0 | calls.rb:293:11:293:11 | 6 | +| calls.rb:293:5:293:33 | call to super | 1 | calls.rb:293:14:293:14 | 7 | superCallsWithBlock -| calls.rb:221:5:221:23 | call to super | calls.rb:221:11:221:23 | { ... } | -| calls.rb:222:5:222:26 | call to super | calls.rb:222:11:222:26 | do ... end | -| calls.rb:223:5:223:30 | call to super | calls.rb:223:16:223:30 | { ... } | -| calls.rb:224:5:224:33 | call to super | calls.rb:224:16:224:33 | do ... end | +| calls.rb:290:5:290:23 | call to super | calls.rb:290:11:290:23 | { ... } | +| calls.rb:291:5:291:26 | call to super | calls.rb:291:11:291:26 | do ... end | +| calls.rb:292:5:292:30 | call to super | calls.rb:292:16:292:30 | { ... } | +| calls.rb:293:5:293:33 | call to super | calls.rb:293:16:293:33 | do ... end | diff --git a/ql/test/library-tests/ast/calls/calls.ql b/ql/test/library-tests/ast/calls/calls.ql index bc6c244356e..93861bef7a1 100644 --- a/ql/test/library-tests/ast/calls/calls.ql +++ b/ql/test/library-tests/ast/calls/calls.ql @@ -8,10 +8,6 @@ query predicate callsWithNoReceiverArgumentsOrBlock(Call c, string name) { not exists(c.getBlock()) } -query predicate callsWithScopeResolutionName(Call c, ScopeResolution sr) { - sr = c.getMethodScopeResolution() -} - query predicate callsWithArguments(Call c, string name, int n, Expr argN) { name = c.getMethodName() and argN = c.getArgument(n) diff --git a/ql/test/library-tests/ast/calls/calls.rb b/ql/test/library-tests/ast/calls/calls.rb index c60cf682001..c38896ccf47 100644 --- a/ql/test/library-tests/ast/calls/calls.rb +++ b/ql/test/library-tests/ast/calls/calls.rb @@ -4,6 +4,9 @@ foo() # call whose name is a scope resolution Foo::bar() +# call whose name is a global scope resolution +::bar() + # call with a receiver, no arguments or block 123.bar @@ -35,55 +38,68 @@ end # ------------------------------------------------------------------------------ # Calls without parentheses or arguments are parsed by tree-sitter simply as -# `identifier` nodes, so here we test that our AST library correctly represents -# them as calls in all the following contexts. +# `identifier` nodes (or `scope_resolution` nodes whose `name` field is an +# `identifier), so here we test that our AST library correctly represents them +# as calls in all the following contexts. # root level (child of program) foo +X::foo # in a parenthesized statement (foo) +(X::foo) # in an argument list some_func(foo) +some_func(X::foo) # in an array [foo] +[X::foo] # RHS of an assignment var1 = foo +var1 = X::foo # RHS an operator assignment var1 += bar +var1 += X::bar # RHS assignment list -var1 = foo, bar +var1 = foo, X::bar # in a begin-end block begin foo + X::foo end # in a BEGIN block -BEGIN { foo } +BEGIN { foo; X::bar } # in an END block -END { foo } +END { foo; X::bar } # both operands of a binary operation -foo + bar +foo + X::bar # unary operand !foo +~X::bar # in a curly brace block -foo() { bar } +foo() { bar; X::baz } # in a do-end block -foo() do bar end +foo() do + bar + X::baz +end # the receiver in a call can itself be a call foo.bar() +bar.baz() # the value for a case expr # and the when pattern and body @@ -91,46 +107,64 @@ case foo when bar baz end +case X::foo +when X::bar + X::baz +end # in a class definition class MyClass foo + X::bar end # in a superclass class MyClass < foo end +class MyClass2 < X::foo +end # in a singleton class value or body class << foo bar end +class << X::foo + X::bar +end # in a method body def some_method foo + X::bar end # in a singleton method object or body def foo.some_method bar + X::baz end # in the default value for a keyword parameter def method_with_keyword_param(keyword: foo) end +def method_with_keyword_param2(keyword: X::foo) +end # in the default value for an optional parameter def method_with_optional_param(param = foo) end +def method_with_optional_param2(param = X::foo) +end # in a module module SomeModule foo + X::bar end # ternary if: condition, consequence, and alternative can all be calls foo ? bar : baz +X::foo ? X::bar : X::baz # if/elsif/else conditions and bodies if foo @@ -140,74 +174,109 @@ elsif bar else wabble end +if X::foo + X::wibble +elsif X::bar + X::wobble +else + X::wabble +end # if-modifier condition/body bar if foo +X::bar if X::foo # unless condition/body unless foo bar end +unless X::foo + X::bar +end # unless-modifier condition/body bar unless foo +X::bar unless X::foo # while loop condition/body while foo do bar end +while X::foo do + X::bar +end # while-modifier loop condition/body bar while foo +X::bar while X::foo # until loop condition/body until foo do bar end +until X::foo do + X::bar +end # until-modifier loop condition/body bar until foo +X::bar until X::foo # the collection being iterated over in a for loop, and the body for x in bar baz end +for x in X::bar + X::baz +end # in an array indexing operation, both the object and the index can be calls foo[bar] +X::foo[X::bar] # interpolation -"foo-#{bar}" +"foo-#{bar}-#{X::baz}" # the scope in a scope resolution foo::Bar +X::foo::Bar # in a range foo..bar +X::foo..X::bar # the key/value in a hash pair -{ foo => bar } +{ foo => bar, X::foo => X::bar } # rescue exceptions and ensure begin rescue foo ensure bar end +begin +rescue X::foo +ensure X::bar +end # rescue-modifier body and handler foo rescue bar +X::foo rescue X::bar # block argument foo(&bar) +foo(&X::bar) # splat argument foo(*bar) +foo(*X::bar) # hash-splat argument foo(**bar) +foo(**X::bar) # the value in a keyword argument foo(blah: bar) +foo(blah: X::bar) # ------------------------------------------------------------------------------ # calls to `super` diff --git a/ql/test/library-tests/ast/constants/constants.expected b/ql/test/library-tests/ast/constants/constants.expected new file mode 100644 index 00000000000..c29abf34181 --- /dev/null +++ b/ql/test/library-tests/ast/constants/constants.expected @@ -0,0 +1,20 @@ +| constants.rb:1:1:12:3 | ModuleA | write | ModuleA | Module | +| constants.rb:2:5:3:7 | ClassA | write | ClassA | Class | +| constants.rb:5:5:11:7 | ModuleB | write | ModuleB | Module | +| constants.rb:6:9:7:11 | ClassB | write | ClassB | Class | +| constants.rb:6:24:6:27 | Base | read | Base | ConstantReadAccess | +| constants.rb:9:9:10:11 | ClassC | write | ClassC | Class | +| constants.rb:9:24:9:24 | X | read | X | ConstantReadAccess | +| constants.rb:9:24:9:27 | Y | read | Y | ConstantReadAccess | +| constants.rb:9:24:9:30 | Z | read | Z | ConstantReadAccess | +| constants.rb:14:1:14:8 | GREETING | write | GREETING | ConstantAssignment | +| constants.rb:17:5:17:9 | Names | write | Names | ConstantAssignment | +| constants.rb:19:5:19:9 | Names | read | Names | ConstantReadAccess | +| constants.rb:20:18:20:25 | GREETING | read | GREETING | ConstantReadAccess | +| constants.rb:28:1:29:3 | ClassD | write | ClassD | Class | +| constants.rb:28:7:28:13 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:31:1:32:3 | ModuleC | write | ModuleC | Module | +| constants.rb:31:8:31:14 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:34:1:34:7 | ModuleA | read | ModuleA | ConstantReadAccess | +| constants.rb:34:1:34:16 | ModuleB | read | ModuleB | ConstantReadAccess | +| constants.rb:34:1:34:26 | MAX_SIZE | write | MAX_SIZE | ConstantAssignment | diff --git a/ql/test/library-tests/ast/constants/constants.ql b/ql/test/library-tests/ast/constants/constants.ql new file mode 100644 index 00000000000..1bb3011f782 --- /dev/null +++ b/ql/test/library-tests/ast/constants/constants.ql @@ -0,0 +1,8 @@ +import ruby + +from ConstantAccess a, string kind +where + a instanceof ConstantReadAccess and kind = "read" + or + a instanceof ConstantWriteAccess and kind = "write" +select a, kind, a.getName(), a.getAPrimaryQlClass() diff --git a/ql/test/library-tests/ast/constants/constants.rb b/ql/test/library-tests/ast/constants/constants.rb new file mode 100644 index 00000000000..201b75b9588 --- /dev/null +++ b/ql/test/library-tests/ast/constants/constants.rb @@ -0,0 +1,34 @@ +module ModuleA + class ClassA + end + + module ModuleB + class ClassB < Base + end + + class ClassC < X::Y::Z + end + end +end + +GREETING = 'Hello' + +def foo + Names = ['Vera', 'Chuck', 'Dave'] + + Names.each do |name| + puts "#{ GREETING } #{ name }" + end + + # A call to Kernel::Array; despite beginning with an upper-case character, + # we don't consider this to be a constant access. + Array('foo') +end + +class ModuleA::ClassD +end + +module ModuleA::ModuleC +end + +ModuleA::ModuleB::MAX_SIZE = 1024 \ No newline at end of file diff --git a/ql/test/library-tests/ast/modules/classes.expected b/ql/test/library-tests/ast/modules/classes.expected index 11091f6135e..49afd7ee415 100644 --- a/ql/test/library-tests/ast/modules/classes.expected +++ b/ql/test/library-tests/ast/modules/classes.expected @@ -5,12 +5,15 @@ classes | classes.rb:16:1:17:3 | MyClass | Class | MyClass | | classes.rb:20:1:37:3 | Wibble | Class | Wibble | | classes.rb:32:3:33:5 | ClassInWibble | Class | ClassInWibble | +| classes.rb:55:1:56:3 | MyClassInGlobalScope | Class | MyClassInGlobalScope | | modules.rb:6:5:7:7 | ClassInFooBar | Class | ClassInFooBar | | modules.rb:19:3:20:5 | ClassInFoo | Class | ClassInFoo | | modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | Class | ClassInAnotherDefinitionOfFoo | | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | Class | ClassInAnotherDefinitionOfFooBar | -classesWithScopeResolutionNames -| classes.rb:16:1:17:3 | MyClass | classes.rb:16:7:16:23 | ...::MyClass | +classesWithNameScopeExprs +| classes.rb:16:1:17:3 | MyClass | classes.rb:16:7:16:14 | MyModule | +classesWithGlobalNameScopeExprs +| classes.rb:55:1:56:3 | MyClassInGlobalScope | exprsInClasses | classes.rb:20:1:37:3 | Wibble | 0 | classes.rb:21:3:23:5 | method_a | Method | | classes.rb:20:1:37:3 | Wibble | 1 | classes.rb:25:3:27:5 | method_b | Method | @@ -26,4 +29,5 @@ classesInClasses modulesInClasses | classes.rb:20:1:37:3 | Wibble | classes.rb:35:3:36:5 | ModuleInWibble | ModuleInWibble | classesWithASuperclass +| classes.rb:7:1:8:3 | Bar | classes.rb:7:13:7:21 | BaseClass | | classes.rb:11:1:12:3 | Baz | classes.rb:11:13:11:32 | call to superclass_for | diff --git a/ql/test/library-tests/ast/modules/classes.ql b/ql/test/library-tests/ast/modules/classes.ql index f88d785581d..7293e193768 100644 --- a/ql/test/library-tests/ast/modules/classes.ql +++ b/ql/test/library-tests/ast/modules/classes.ql @@ -4,9 +4,9 @@ query predicate classes(Class c, string pClass, string name) { pClass = c.getAPrimaryQlClass() and name = c.getName() } -query predicate classesWithScopeResolutionNames(Class c, ScopeResolution name) { - name = c.getNameScopeResolution() -} +query predicate classesWithNameScopeExprs(Class c, Expr se) { se = c.getScopeExpr() } + +query predicate classesWithGlobalNameScopeExprs(Class c) { c.hasGlobalScope() } query predicate exprsInClasses(Class c, int i, Expr e, string eClass) { e = c.getExpr(i) and eClass = e.getAPrimaryQlClass() diff --git a/ql/test/library-tests/ast/modules/classes.rb b/ql/test/library-tests/ast/modules/classes.rb index a6bc21f799c..ce05212ada1 100644 --- a/ql/test/library-tests/ast/modules/classes.rb +++ b/ql/test/library-tests/ast/modules/classes.rb @@ -49,4 +49,8 @@ class << x another_method_call $global_var2 = 456 +end + +# a class where the name is a scope resolution using the global scope +class ::MyClassInGlobalScope end \ No newline at end of file diff --git a/ql/test/library-tests/ast/modules/module_base.expected b/ql/test/library-tests/ast/modules/module_base.expected index 1755384327e..11a2769c39c 100644 --- a/ql/test/library-tests/ast/modules/module_base.expected +++ b/ql/test/library-tests/ast/modules/module_base.expected @@ -8,6 +8,7 @@ moduleBases | classes.rb:32:3:33:5 | ClassInWibble | Class | | classes.rb:35:3:36:5 | ModuleInWibble | Module | | classes.rb:41:1:52:3 | class << ... | Class | +| classes.rb:55:1:56:3 | MyClassInGlobalScope | Class | | modules.rb:1:1:2:3 | Empty | Module | | modules.rb:4:1:24:3 | Foo | Module | | modules.rb:5:3:14:5 | Bar | Module | @@ -18,6 +19,7 @@ moduleBases | modules.rb:37:1:46:3 | Bar | Module | | modules.rb:48:1:57:3 | Bar | Module | | modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | Class | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | Module | moduleBaseClasses | classes.rb:20:1:37:3 | Wibble | classes.rb:32:3:33:5 | ClassInWibble | | modules.rb:4:1:24:3 | Foo | modules.rb:19:3:20:5 | ClassInFoo | diff --git a/ql/test/library-tests/ast/modules/modules.expected b/ql/test/library-tests/ast/modules/modules.expected index 9d650a05cbb..14569cf827c 100644 --- a/ql/test/library-tests/ast/modules/modules.expected +++ b/ql/test/library-tests/ast/modules/modules.expected @@ -7,8 +7,11 @@ modules | modules.rb:26:1:35:3 | Foo | Module | Foo | | modules.rb:37:1:46:3 | Bar | Module | Bar | | modules.rb:48:1:57:3 | Bar | Module | Bar | -modulesWithScopeResolutionNames -| modules.rb:48:1:57:3 | Bar | modules.rb:48:8:48:15 | ...::Bar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | Module | MyModuleInGlobalScope | +modulesWithScopeExprs +| modules.rb:48:1:57:3 | Bar | modules.rb:48:8:48:10 | Foo | +modulesWithGlobalNameScopeExprs +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | exprsInModules | modules.rb:4:1:24:3 | Foo | 0 | modules.rb:5:3:14:5 | Bar | Module | | modules.rb:4:1:24:3 | Foo | 1 | modules.rb:16:3:17:5 | method_in_foo | Method | diff --git a/ql/test/library-tests/ast/modules/modules.ql b/ql/test/library-tests/ast/modules/modules.ql index 5fb00a77868..8f7d7a952c5 100644 --- a/ql/test/library-tests/ast/modules/modules.ql +++ b/ql/test/library-tests/ast/modules/modules.ql @@ -4,9 +4,9 @@ query predicate modules(Module m, string pClass, string name) { pClass = m.getAPrimaryQlClass() and name = m.getName() } -query predicate modulesWithScopeResolutionNames(Module m, ScopeResolution name) { - name = m.getNameScopeResolution() -} +query predicate modulesWithScopeExprs(Module m, Expr se) { se = m.getScopeExpr() } + +query predicate modulesWithGlobalNameScopeExprs(Module m) { m.hasGlobalScope() } query predicate exprsInModules(Module m, int i, Expr e, string eClass) { e = m.getExpr(i) and eClass = e.getAPrimaryQlClass() diff --git a/ql/test/library-tests/ast/modules/modules.rb b/ql/test/library-tests/ast/modules/modules.rb index f34ddcec27a..dfba11f8d6e 100644 --- a/ql/test/library-tests/ast/modules/modules.rb +++ b/ql/test/library-tests/ast/modules/modules.rb @@ -54,4 +54,8 @@ module Foo::Bar puts 'module Foo::Bar again' $global_var = 4 +end + +# a module where the name is a scope resolution using the global scope +module ::MyModuleInGlobalScope end \ No newline at end of file diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index 2e113103246..ee31803fec4 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -894,9 +894,9 @@ cfg.rb: #-----| -> character # 58| Silly -#-----| -> Silly +#-----| -> (no string representation) -# 58| Silly +# 58| (no string representation) #-----| -> Object # 58| Superclass @@ -1375,9 +1375,9 @@ cfg.rb: #-----| -> ... > ... # 115| C -#-----| -> C +#-----| -> (no string representation) -# 115| C +# 115| (no string representation) #-----| -> 42 # 116| ... = ... @@ -1426,9 +1426,9 @@ cfg.rb: #-----| -> Array # 122| M -#-----| -> M +#-----| -> (no string representation) -# 122| M +# 122| (no string representation) #-----| -> nil # 123| ... = ... @@ -1588,20 +1588,20 @@ cfg.rb: # 135| 3 #-----| -> init -# 137| ...::Constant +# 137| Constant #-----| -> M # 137| M +#-----| -> (no string representation) + +# 137| (no string representation) #-----| -> Constant -# 137| Constant -#-----| -> ...::Constant - -# 138| ...::Constant +# 138| Constant #-----| -> class << ... # 138| call to itself -#-----| -> Constant +#-----| -> (no string representation) # 138| M #-----| -> itself @@ -1609,8 +1609,8 @@ cfg.rb: # 138| itself #-----| -> call to itself -# 138| Constant -#-----| -> ...::Constant +# 138| (no string representation) +#-----| -> Constant # 140| class << ... #-----| -> Silly @@ -2721,9 +2721,9 @@ loops.rb: raise.rb: # 1| ExceptionA -#-----| -> ExceptionA +#-----| -> (no string representation) -# 1| ExceptionA +# 1| (no string representation) #-----| -> Exception # 1| Superclass @@ -2733,9 +2733,9 @@ raise.rb: #-----| -> Superclass # 4| ExceptionB -#-----| -> ExceptionB +#-----| -> (no string representation) -# 4| ExceptionB +# 4| (no string representation) #-----| -> Exception # 4| Superclass