diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index 28a05877c5f..11f201ac6dd 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -9,6 +9,7 @@ import ast.Module import ast.Parameter import ast.Operation import ast.Pattern +import ast.Scope import ast.Statement import ast.Variable private import ast.internal.AST @@ -17,11 +18,7 @@ private import ast.internal.AST * A node in the abstract syntax tree. This class is the base class for all Ruby * program elements. */ -class AstNode extends @ast_node { - AstNode::Range range; - - AstNode() { range = this } - +class AstNode extends TAstNode { /** * Gets the name of a primary CodeQL class to which this node belongs. * @@ -32,14 +29,22 @@ class AstNode extends @ast_node { string getAPrimaryQlClass() { result = "???" } /** Gets a textual representation of this node. */ - final string toString() { result = range.toString() } + cached + string toString() { none() } /** Gets the location of this node. */ - final Location getLocation() { result = range.getLocation() } + Location getLocation() { result = toGenerated(this).getLocation() } /** Gets a child node of this `AstNode`. */ - final AstNode getAChild() { range.child(_, result) } + final AstNode getAChild() { result = this.getAChild(_) } /** Gets the parent of this `AstNode`, if this node is not a root node. */ final AstNode getParent() { result.getAChild() = this } + + /** + * Gets a child of this node, which can also be retrieved using a predicate + * named `pred`. + */ + cached + AstNode getAChild(string pred) { none() } } diff --git a/ql/src/codeql_ruby/ast/Call.qll b/ql/src/codeql_ruby/ast/Call.qll index c7a73c1fa7e..b38b2b37263 100644 --- a/ql/src/codeql_ruby/ast/Call.qll +++ b/ql/src/codeql_ruby/ast/Call.qll @@ -1,12 +1,11 @@ private import codeql_ruby.AST -private import internal.Call +private import internal.AST +private import internal.TreeSitter /** * A call. */ -class Call extends Expr { - override Call::Range range; - +class Call extends Expr, TCall { override string getAPrimaryQlClass() { result = "Call" } /** @@ -21,7 +20,7 @@ class Call extends Expr { * yield 0, bar: 1 * ``` */ - final Expr getArgument(int n) { result = range.getArgument(n) } + Expr getArgument(int n) { none() } /** * Gets an argument of this method call. @@ -48,14 +47,27 @@ class Call extends Expr { * Gets the number of arguments of this method call. */ final int getNumberOfArguments() { result = count(this.getAnArgument()) } + + override AstNode getAChild(string pred) { pred = "getArgument" and result = this.getArgument(_) } +} + +bindingset[s] +private string getMethodName(MethodCall mc, string s) { + ( + not mc instanceof LhsExpr + or + mc.getParent() instanceof AssignOperation + ) and + result = s + or + mc instanceof LhsExpr and + result = s + "=" } /** * A method call. */ -class MethodCall extends Call { - override MethodCall::Range range; - +class MethodCall extends Call, TMethodCall { override string getAPrimaryQlClass() { result = "MethodCall" } /** @@ -71,7 +83,7 @@ class MethodCall extends Call { * the call to `qux` is the `Expr` for `Baz`; for the call to `corge` there * is no result. */ - final Expr getReceiver() { result = range.getReceiver() } + Expr getReceiver() { none() } /** * Gets the name of the method being called. For example, in: @@ -82,7 +94,7 @@ class MethodCall extends Call { * * the result is `"bar"`. */ - final string getMethodName() { result = range.getMethodName() } + string getMethodName() { none() } /** * Gets the block of this method call, if any. @@ -90,7 +102,67 @@ class MethodCall extends Call { * foo.each { |x| puts x } * ``` */ - final Block getBlock() { result = range.getBlock() } + Block getBlock() { none() } + + override string toString() { result = "call to " + concat(this.getMethodName(), "/") } + + final override AstNode getAChild(string pred) { + result = Call.super.getAChild(pred) + or + pred = "getReceiver" and result = this.getReceiver() + or + pred = "getBlock" and result = this.getBlock() + } +} + +private class IdentifierMethodCall extends MethodCall, TIdentifierMethodCall { + private Generated::Identifier g; + + IdentifierMethodCall() { this = TIdentifierMethodCall(g) } + + final override string getMethodName() { result = getMethodName(this, g.getValue()) } +} + +private class ScopeResolutionMethodCall extends MethodCall, TScopeResolutionMethodCall { + private Generated::ScopeResolution g; + private Generated::Identifier i; + + ScopeResolutionMethodCall() { this = TScopeResolutionMethodCall(g, i) } + + final override Expr getReceiver() { toGenerated(result) = g.getScope() } + + final override string getMethodName() { result = getMethodName(this, i.getValue()) } +} + +private class RegularMethodCall extends MethodCall, TRegularMethodCall { + private Generated::Call g; + + RegularMethodCall() { this = TRegularMethodCall(g) } + + final override Expr getReceiver() { + toGenerated(result) = g.getReceiver() + or + not exists(g.getReceiver()) and + toGenerated(result) = g.getMethod().(Generated::ScopeResolution).getScope() + } + + final override string getMethodName() { + exists(string res | result = getMethodName(this, res) | + res = "call" and g.getMethod() instanceof Generated::ArgumentList + or + res = g.getMethod().(Generated::Token).getValue() + or + res = g.getMethod().(Generated::ScopeResolution).getName().(Generated::Token).getValue() + ) + } + + final override Expr getArgument(int n) { + toGenerated(result) = g.getArguments().getChild(n) + or + toGenerated(result) = g.getMethod().(Generated::ArgumentList).getChild(n) + } + + final override Block getBlock() { toGenerated(result) = g.getBlock() } } /** @@ -101,8 +173,6 @@ class MethodCall extends Call { * ``` */ class SetterMethodCall extends MethodCall, LhsExpr { - final override SetterMethodCall::Range range; - final override string getAPrimaryQlClass() { result = "SetterMethodCall" } } @@ -112,10 +182,20 @@ class SetterMethodCall extends MethodCall, LhsExpr { * a[0] * ``` */ -class ElementReference extends MethodCall, @element_reference { - final override ElementReference::Range range; +class ElementReference extends MethodCall, TElementReference { + private Generated::ElementReference g; + + ElementReference() { this = TElementReference(g) } final override string getAPrimaryQlClass() { result = "ElementReference" } + + final override Expr getReceiver() { toGenerated(result) = g.getObject() } + + final override string getMethodName() { result = getMethodName(this, "[]") } + + final override Expr getArgument(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "...[...]" } } /** @@ -124,10 +204,16 @@ class ElementReference extends MethodCall, @element_reference { * yield x, y * ``` */ -class YieldCall extends Call, @yield { - final override YieldCall::Range range; +class YieldCall extends Call, TYieldCall { + private Generated::Yield g; + + YieldCall() { this = TYieldCall(g) } final override string getAPrimaryQlClass() { result = "YieldCall" } + + final override Expr getArgument(int n) { toGenerated(result) = g.getChild().getChild(n) } + + final override string toString() { result = "yield ..." } } /** @@ -140,20 +226,42 @@ class YieldCall extends Call, @yield { * end * ``` */ -class SuperCall extends MethodCall { - final override SuperCall::Range range; - +class SuperCall extends MethodCall, TSuperCall { final override string getAPrimaryQlClass() { result = "SuperCall" } } +private class TokenSuperCall extends SuperCall, TTokenSuperCall { + private Generated::Super g; + + TokenSuperCall() { this = TTokenSuperCall(g) } + + final override string getMethodName() { result = getMethodName(this, g.getValue()) } +} + +private class RegularSuperCall extends SuperCall, TRegularSuperCall { + private Generated::Call g; + + RegularSuperCall() { this = TRegularSuperCall(g) } + + final override string getMethodName() { + result = getMethodName(this, g.getMethod().(Generated::Super).getValue()) + } + + final override Expr getArgument(int n) { toGenerated(result) = g.getArguments().getChild(n) } + + final override Block getBlock() { toGenerated(result) = g.getBlock() } +} + /** * A block argument in a method call. * ```rb * foo(&block) * ``` */ -class BlockArgument extends Expr, @block_argument { - final override BlockArgument::Range range; +class BlockArgument extends Expr, TBlockArgument { + private Generated::BlockArgument g; + + BlockArgument() { this = TBlockArgument(g) } final override string getAPrimaryQlClass() { result = "BlockArgument" } @@ -164,7 +272,11 @@ class BlockArgument extends Expr, @block_argument { * foo(&bar) * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "&..." } + + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } /** @@ -173,8 +285,10 @@ class BlockArgument extends Expr, @block_argument { * foo(*args) * ``` */ -class SplatArgument extends Expr, @splat_argument { - final override SplatArgument::Range range; +class SplatArgument extends Expr, TSplatArgument { + private Generated::SplatArgument g; + + SplatArgument() { this = TSplatArgument(g) } final override string getAPrimaryQlClass() { result = "SplatArgument" } @@ -185,7 +299,11 @@ class SplatArgument extends Expr, @splat_argument { * foo(*bar) * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "*..." } + + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } /** @@ -194,8 +312,10 @@ class SplatArgument extends Expr, @splat_argument { * foo(**options) * ``` */ -class HashSplatArgument extends Expr, @hash_splat_argument { - final override HashSplatArgument::Range range; +class HashSplatArgument extends Expr, THashSplatArgument { + private Generated::HashSplatArgument g; + + HashSplatArgument() { this = THashSplatArgument(g) } final override string getAPrimaryQlClass() { result = "HashSplatArgument" } @@ -206,5 +326,9 @@ class HashSplatArgument extends Expr, @hash_splat_argument { * foo(**bar) * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getChild() } + + final override string toString() { result = "**..." } + + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } diff --git a/ql/src/codeql_ruby/ast/Constant.qll b/ql/src/codeql_ruby/ast/Constant.qll index 3c11c0f8e91..d8a5b9030a1 100644 --- a/ql/src/codeql_ruby/ast/Constant.qll +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -1,12 +1,12 @@ private import codeql_ruby.AST -private import internal.Constant +private import internal.AST +private import internal.Variable +private import internal.TreeSitter /** An access to a constant. */ -class ConstantAccess extends Expr { - override ConstantAccess::Range range; - +class ConstantAccess extends Expr, TConstantAccess { /** Gets the name of the constant being accessed. */ - string getName() { result = range.getName() } + string getName() { none() } /** * Gets the expression used in the access's scope resolution operation, if @@ -24,7 +24,7 @@ class ConstantAccess extends Expr { * MESSAGE * ``` */ - Expr getScopeExpr() { result = range.getScopeExpr() } + Expr getScopeExpr() { none() } /** * Holds if the access uses the scope resolution operator to refer to the @@ -34,7 +34,32 @@ class ConstantAccess extends Expr { * ::MESSAGE * ``` */ - predicate hasGlobalScope() { range.hasGlobalScope() } + predicate hasGlobalScope() { none() } + + override string toString() { result = this.getName() } + + override AstNode getAChild(string pred) { pred = "getScopeExpr" and result = this.getScopeExpr() } +} + +private class TokenConstantAccess extends ConstantAccess, TTokenConstantAccess { + private Generated::Constant g; + + TokenConstantAccess() { this = TTokenConstantAccess(g) } + + final override string getName() { result = g.getValue() } +} + +private class ScopeResolutionConstantAccess extends ConstantAccess, TScopeResolutionConstantAccess { + private Generated::ScopeResolution g; + private Generated::Constant constant; + + ScopeResolutionConstantAccess() { this = TScopeResolutionConstantAccess(g, constant) } + + final override string getName() { result = constant.getValue() } + + final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } + + final override predicate hasGlobalScope() { not exists(g.getScope()) } } /** @@ -54,7 +79,12 @@ class ConstantAccess extends Expr { * ``` */ class ConstantReadAccess extends ConstantAccess { - final override ConstantReadAccess::Range range; + ConstantReadAccess() { + not this instanceof ConstantWriteAccess + or + // `X` in `X ||= 10` is considered both a read and a write + this = any(AssignOperation a).getLeftOperand() + } final override string getAPrimaryQlClass() { result = "ConstantReadAccess" } } @@ -76,7 +106,9 @@ class ConstantReadAccess extends ConstantAccess { * ``` */ class ConstantWriteAccess extends ConstantAccess { - override ConstantWriteAccess::Range range; + ConstantWriteAccess() { + explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace + } override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } } @@ -89,8 +121,6 @@ class ConstantWriteAccess extends ConstantAccess { * MAX_SIZE = 100 * ``` */ -class ConstantAssignment extends ConstantWriteAccess { - override ConstantAssignment::Range range; - +class ConstantAssignment extends ConstantWriteAccess, LhsExpr { override string getAPrimaryQlClass() { result = "ConstantAssignment" } } diff --git a/ql/src/codeql_ruby/ast/Control.qll b/ql/src/codeql_ruby/ast/Control.qll index b8cf8144b2e..de63484c38d 100644 --- a/ql/src/codeql_ruby/ast/Control.qll +++ b/ql/src/codeql_ruby/ast/Control.qll @@ -1,5 +1,6 @@ private import codeql_ruby.AST -private import internal.Control +private import internal.AST +private import internal.TreeSitter /** * A control expression that can be any of the following: @@ -9,17 +10,13 @@ private import internal.Control * - `while`/`until` (including expression-modifier variants) * - `for` */ -class ControlExpr extends Expr { - override ControlExpr::Range range; -} +class ControlExpr extends Expr, TControlExpr { } /** * A conditional expression: `if`/`unless` (including expression-modifier * variants), and ternary-if (`?:`) expressions. */ -class ConditionalExpr extends ControlExpr { - override ConditionalExpr::Range range; - +class ConditionalExpr extends ControlExpr, TConditionalExpr { /** * Gets the condition expression. For example, the result is `foo` in the * following: @@ -29,13 +26,19 @@ class ConditionalExpr extends ControlExpr { * end * ``` */ - final Expr getCondition() { result = range.getCondition() } + Expr getCondition() { none() } /** * Gets the branch of this conditional expression that is taken when the * condition evaluates to `cond`, if any. */ - Stmt getBranch(boolean cond) { result = range.getBranch(cond) } + Stmt getBranch(boolean cond) { none() } + + override AstNode getAChild(string pred) { + pred = "getCondition" and result = this.getCondition() + or + pred = "getBranch" and result = this.getBranch(_) + } } /** @@ -48,16 +51,14 @@ class ConditionalExpr extends ControlExpr { * end * ``` */ -class IfExpr extends ConditionalExpr { - override IfExpr::Range range; - +class IfExpr extends ConditionalExpr, TIfExpr { final override string getAPrimaryQlClass() { result = "IfExpr" } /** Holds if this is an `elsif` expression. */ - final predicate isElsif() { this instanceof @elsif } + predicate isElsif() { none() } /** Gets the 'then' branch of this `if`/`elsif` expression. */ - final Stmt getThen() { result = range.getThen() } + Stmt getThen() { none() } /** * Gets the `elsif`/`else` branch of this `if`/`elsif` expression, if any. In @@ -90,7 +91,51 @@ class IfExpr extends ConditionalExpr { * end * ``` */ - final Stmt getElse() { result = range.getElse() } + Stmt getElse() { none() } + + final override Stmt getBranch(boolean cond) { + cond = true and result = this.getThen() + or + cond = false and result = this.getElse() + } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getThen" and result = this.getThen() + or + pred = "getElse" and result = this.getElse() + } +} + +private class If extends IfExpr, TIf { + private Generated::If g; + + If() { this = TIf(g) } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getThen() { toGenerated(result) = g.getConsequence() } + + final override Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override string toString() { result = "if ..." } +} + +private class Elsif extends IfExpr, TElsif { + private Generated::Elsif g; + + Elsif() { this = TElsif(g) } + + final override predicate isElsif() { any() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getThen() { toGenerated(result) = g.getConsequence() } + + final override Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override string toString() { result = "elsif ..." } } /** @@ -101,11 +146,15 @@ class IfExpr extends ConditionalExpr { * end * ``` */ -class UnlessExpr extends ConditionalExpr, @unless { - final override UnlessExpr::Range range; +class UnlessExpr extends ConditionalExpr, TUnlessExpr { + private Generated::Unless g; + + UnlessExpr() { this = TUnlessExpr(g) } final override string getAPrimaryQlClass() { result = "UnlessExpr" } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + /** * Gets the 'then' branch of this `unless` expression. In the following * example, the result is the `StmtSequence` containing `foo`. @@ -117,7 +166,7 @@ class UnlessExpr extends ConditionalExpr, @unless { * end * ``` */ - final Stmt getThen() { result = range.getThen() } + final Stmt getThen() { toGenerated(result) = g.getConsequence() } /** * Gets the 'else' branch of this `unless` expression. In the following @@ -130,7 +179,23 @@ class UnlessExpr extends ConditionalExpr, @unless { * end * ``` */ - final Stmt getElse() { result = range.getElse() } + final Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override Expr getBranch(boolean cond) { + cond = false and result = getThen() + or + cond = true and result = getElse() + } + + final override string toString() { result = "unless ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getThen" and result = this.getThen() + or + pred = "getElse" and result = this.getElse() + } } /** @@ -139,11 +204,17 @@ class UnlessExpr extends ConditionalExpr, @unless { * foo if bar * ``` */ -class IfModifierExpr extends ConditionalExpr, @if_modifier { - final override IfModifierExpr::Range range; +class IfModifierExpr extends ConditionalExpr, TIfModifierExpr { + private Generated::IfModifier g; + + IfModifierExpr() { this = TIfModifierExpr(g) } final override string getAPrimaryQlClass() { result = "IfModifierExpr" } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getBranch(boolean cond) { cond = true and result = this.getBody() } + /** * Gets the statement that is conditionally evaluated. In the following * example, the result is the `Expr` for `foo`. @@ -151,7 +222,15 @@ class IfModifierExpr extends ConditionalExpr, @if_modifier { * foo if bar * ``` */ - final Stmt getBody() { result = range.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } + + final override string toString() { result = "... if ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + } } /** @@ -160,11 +239,17 @@ class IfModifierExpr extends ConditionalExpr, @if_modifier { * y /= x unless x == 0 * ``` */ -class UnlessModifierExpr extends ConditionalExpr, @unless_modifier { - final override UnlessModifierExpr::Range range; +class UnlessModifierExpr extends ConditionalExpr, TUnlessModifierExpr { + private Generated::UnlessModifier g; + + UnlessModifierExpr() { this = TUnlessModifierExpr(g) } final override string getAPrimaryQlClass() { result = "UnlessModifierExpr" } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + final override Stmt getBranch(boolean cond) { cond = false and result = this.getBody() } + /** * Gets the statement that is conditionally evaluated. In the following * example, the result is the `Expr` for `foo`. @@ -172,7 +257,15 @@ class UnlessModifierExpr extends ConditionalExpr, @unless_modifier { * foo unless bar * ``` */ - final Stmt getBody() { result = range.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } + + final override string toString() { result = "... unless ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getBody" and result = this.getBody() + } } /** @@ -181,20 +274,42 @@ class UnlessModifierExpr extends ConditionalExpr, @unless_modifier { * (a > b) ? a : b * ``` */ -class TernaryIfExpr extends ConditionalExpr, @conditional { - final override TernaryIfExpr::Range range; +class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr { + private Generated::Conditional g; + + TernaryIfExpr() { this = TTernaryIfExpr(g) } final override string getAPrimaryQlClass() { result = "TernaryIfExpr" } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + /** Gets the 'then' branch of this ternary if expression. */ - final Stmt getThen() { result = range.getThen() } + final Stmt getThen() { toGenerated(result) = g.getConsequence() } /** Gets the 'else' branch of this ternary if expression. */ - final Stmt getElse() { result = range.getElse() } + final Stmt getElse() { toGenerated(result) = g.getAlternative() } + + final override Stmt getBranch(boolean cond) { + cond = true and result = getThen() + or + cond = false and result = getElse() + } + + final override string toString() { result = "... ? ... : ..." } + + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) + or + pred = "getThen" and result = this.getThen() + or + pred = "getElse" and result = this.getElse() + } } -class CaseExpr extends ControlExpr, @case__ { - final override CaseExpr::Range range; +class CaseExpr extends ControlExpr, TCaseExpr { + private Generated::Case g; + + CaseExpr() { this = TCaseExpr(g) } final override string getAPrimaryQlClass() { result = "CaseExpr" } @@ -217,13 +332,13 @@ class CaseExpr extends ControlExpr, @case__ { * end * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getValue() } /** * Gets the `n`th branch of this case expression, either a `WhenExpr` or a * `StmtSequence`. */ - final Expr getBranch(int n) { result = range.getBranch(n) } + final Expr getBranch(int n) { toGenerated(result) = g.getChild(n) } /** * Gets a branch of this case expression, either a `WhenExpr` or an @@ -241,6 +356,14 @@ class CaseExpr extends ControlExpr, @case__ { * Gets the number of branches of this case expression. */ final int getNumberOfBranches() { result = count(this.getBranch(_)) } + + final override string toString() { result = "case ..." } + + override AstNode getAChild(string pred) { + pred = "getValue" and result = this.getValue() + or + pred = "getBranch" and result = this.getBranch(_) + } } /** @@ -251,13 +374,15 @@ class CaseExpr extends ControlExpr, @case__ { * end * ``` */ -class WhenExpr extends Expr, @when { - final override WhenExpr::Range range; +class WhenExpr extends Expr, TWhenExpr { + private Generated::When g; + + WhenExpr() { this = TWhenExpr(g) } final override string getAPrimaryQlClass() { result = "WhenExpr" } /** Gets the body of this case-when expression. */ - final Stmt getBody() { result = range.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } /** * Gets the `n`th pattern (or condition) in this case-when expression. In the @@ -270,7 +395,7 @@ class WhenExpr extends Expr, @when { * end * ``` */ - final Expr getPattern(int n) { result = range.getPattern(n) } + final Expr getPattern(int n) { toGenerated(result) = g.getPattern(n).getChild() } /** * Gets a pattern (or condition) in this case-when expression. @@ -281,28 +406,43 @@ class WhenExpr extends Expr, @when { * Gets the number of patterns in this case-when expression. */ final int getNumberOfPatterns() { result = count(this.getPattern(_)) } + + final override string toString() { result = "when ..." } + + override AstNode getAChild(string pred) { + pred = "getBody" and result = this.getBody() + or + pred = "getPattern" and result = this.getPattern(_) + } } /** * A loop. That is, a `for` loop, a `while` or `until` loop, or their * expression-modifier variants. */ -class Loop extends ControlExpr { - override Loop::Range range; - +class Loop extends ControlExpr, TLoop { /** Gets the body of this loop. */ - Stmt getBody() { result = range.getBody() } + Stmt getBody() { none() } + + override AstNode getAChild(string pred) { pred = "getBody" and result = this.getBody() } } /** * A loop using a condition expression. That is, a `while` or `until` loop, or * their expression-modifier variants. */ -class ConditionalLoop extends Loop { - override ConditionalLoop::Range range; - +class ConditionalLoop extends Loop, TConditionalLoop { /** Gets the condition expression of this loop. */ - final Expr getCondition() { result = range.getCondition() } + Expr getCondition() { none() } + + override AstNode getAChild(string pred) { + result = Loop.super.getAChild(pred) + or + pred = "getCondition" and result = this.getCondition() + } + + /** Holds if the loop body is entered when the condition is `condValue`. */ + predicate entersLoopWhenConditionIs(boolean condValue) { none() } } /** @@ -314,13 +454,25 @@ class ConditionalLoop extends Loop { * end * ``` */ -class WhileExpr extends ConditionalLoop, @while { - final override WhileExpr::Range range; +class WhileExpr extends ConditionalLoop, TWhileExpr { + private Generated::While g; + + WhileExpr() { this = TWhileExpr(g) } final override string getAPrimaryQlClass() { result = "WhileExpr" } /** Gets the body of this `while` loop. */ - final override Stmt getBody() { result = range.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `while` loops, this holds when `condValue` is true. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = true } + + final override string toString() { result = "while ..." } } /** @@ -332,13 +484,25 @@ class WhileExpr extends ConditionalLoop, @while { * end * ``` */ -class UntilExpr extends ConditionalLoop, @until { - final override UntilExpr::Range range; +class UntilExpr extends ConditionalLoop, TUntilExpr { + private Generated::Until g; + + UntilExpr() { this = TUntilExpr(g) } final override string getAPrimaryQlClass() { result = "UntilExpr" } /** Gets the body of this `until` loop. */ - final override Stmt getBody() { result = range.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `until` loops, this holds when `condValue` is false. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = false } + + final override string toString() { result = "until ..." } } /** @@ -347,10 +511,24 @@ class UntilExpr extends ConditionalLoop, @until { * foo while bar * ``` */ -class WhileModifierExpr extends ConditionalLoop, @while_modifier { - final override WhileModifierExpr::Range range; +class WhileModifierExpr extends ConditionalLoop, TWhileModifierExpr { + private Generated::WhileModifier g; + + WhileModifierExpr() { this = TWhileModifierExpr(g) } + + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `while`-modifier loops, this holds when `condValue` is true. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = true } final override string getAPrimaryQlClass() { result = "WhileModifierExpr" } + + final override string toString() { result = "... while ..." } } /** @@ -359,10 +537,24 @@ class WhileModifierExpr extends ConditionalLoop, @while_modifier { * foo until bar * ``` */ -class UntilModifierExpr extends ConditionalLoop, @until_modifier { - final override UntilModifierExpr::Range range; +class UntilModifierExpr extends ConditionalLoop, TUntilModifierExpr { + private Generated::UntilModifier g; + + UntilModifierExpr() { this = TUntilModifierExpr(g) } + + final override Stmt getBody() { toGenerated(result) = g.getBody() } + + final override Expr getCondition() { toGenerated(result) = g.getCondition() } + + /** + * Holds if the loop body is entered when the condition is `condValue`. For + * `until`-modifier loops, this holds when `condValue` is false. + */ + final override predicate entersLoopWhenConditionIs(boolean condValue) { condValue = false } final override string getAPrimaryQlClass() { result = "UntilModifierExpr" } + + final override string toString() { result = "... until ..." } } /** @@ -373,16 +565,18 @@ class UntilModifierExpr extends ConditionalLoop, @until_modifier { * end * ``` */ -class ForExpr extends Loop, @for { - final override ForExpr::Range range; +class ForExpr extends Loop, TForExpr { + private Generated::For g; + + ForExpr() { this = TForExpr(g) } final override string getAPrimaryQlClass() { result = "ForExpr" } /** Gets the body of this `for` loop. */ - final override Stmt getBody() { result = range.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } /** Gets the pattern representing the iteration argument. */ - final Pattern getPattern() { result = range.getPattern() } + final Pattern getPattern() { toGenerated(result) = g.getPattern() } /** * Gets the value being iterated over. In the following example, the result @@ -393,5 +587,15 @@ class ForExpr extends Loop, @for { * end * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getValue().getChild() } + + final override string toString() { result = "for ... in ..." } + + override AstNode getAChild(string pred) { + result = Loop.super.getAChild(pred) + or + pred = "getPattern" and result = this.getPattern() + or + pred = "getValue" and result = this.getValue() + } } diff --git a/ql/src/codeql_ruby/ast/Expr.qll b/ql/src/codeql_ruby/ast/Expr.qll index dedcf04de4e..6f27b0462bc 100644 --- a/ql/src/codeql_ruby/ast/Expr.qll +++ b/ql/src/codeql_ruby/ast/Expr.qll @@ -1,16 +1,13 @@ private import codeql_ruby.AST -private import internal.Expr +private import internal.AST +private import internal.TreeSitter /** * An expression. * * This is the root QL class for all expressions. */ -class Expr extends Stmt { - override Expr::Range range; - - Expr() { this = range } -} +class Expr extends Stmt, TExpr { } /** * A reference to the current object. For example: @@ -18,10 +15,10 @@ class Expr extends Stmt { * - `self.method_name` * - `def self.method_name ... end` */ -class Self extends Expr, @token_self { - override Self::Range range; - +class Self extends Expr, TSelf { final override string getAPrimaryQlClass() { result = "Self" } + + final override string toString() { result = "self" } } /** @@ -35,32 +32,91 @@ class Self extends Expr, @token_self { * return 1, 2, *items, k: 5, **map * ``` */ -class ArgumentList extends Expr { - override ArgumentList::Range range; +class ArgumentList extends Expr, TArgumentList { + private Generated::AstNode g; - override string getAPrimaryQlClass() { result = "ArgumentList" } + ArgumentList() { this = TArgumentList(g) } + + /** Gets the `i`th element in this argument list. */ + Expr getElement(int i) { + toGenerated(result) in [ + g.(Generated::ArgumentList).getChild(i), g.(Generated::RightAssignmentList).getChild(i) + ] + } + + final override string getAPrimaryQlClass() { result = "ArgumentList" } + + final override string toString() { result = "..., ..." } + + final override AstNode getAChild(string pred) { + pred = "getElement" and result = this.getElement(_) + } } /** A sequence of expressions. */ -class StmtSequence extends Expr { - override StmtSequence::Range range; - +class StmtSequence extends Expr, TStmtSequence { override string getAPrimaryQlClass() { result = "StmtSequence" } + override string toString() { + exists(int c | c = this.getNumberOfStatements() | + c = 0 and result = ";" + or + c = 1 and result = this.getStmt(0).toString() + or + c > 1 and result = "...; ..." + ) + } + /** Gets the `n`th statement in this sequence. */ - final Stmt getStmt(int n) { result = range.getStmt(n) } + Stmt getStmt(int n) { none() } /** Gets a statement in this sequence. */ final Stmt getAStmt() { result = this.getStmt(_) } - /** Gets the last expression in this sequence, if any. */ - final Expr getLastExpr() { result = this.getStmt(this.getNumberOfStatements() - 1) } + /** Gets the last statement in this sequence, if any. */ + final Stmt getLastStmt() { result = this.getStmt(this.getNumberOfStatements() - 1) } /** Gets the number of statements in this sequence. */ final int getNumberOfStatements() { result = count(this.getAStmt()) } /** Holds if this sequence has no statements. */ final predicate isEmpty() { this.getNumberOfStatements() = 0 } + + override AstNode getAChild(string pred) { pred = "getStmt" and result = this.getStmt(_) } +} + +private class Then extends StmtSequence, TThen { + private Generated::Then g; + + Then() { this = TThen(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } +} + +private class Else extends StmtSequence, TElse { + private Generated::Else g; + + Else() { this = TElse(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } +} + +private class Do extends StmtSequence, TDo { + private Generated::Do g; + + Do() { this = TDo(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } +} + +private class Ensure extends StmtSequence, TEnsure { + private Generated::Ensure g; + + Ensure() { this = TEnsure(g) } + + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string toString() { result = "ensure ..." } } /** @@ -68,22 +124,70 @@ class StmtSequence extends Expr { * or do-block. That is, any body that may also include rescue/ensure/else * statements. */ -class BodyStatement extends StmtSequence { - override BodyStatement::Range range; +class BodyStmt extends StmtSequence, TBodyStmt { + // Not defined by dispatch, as it should not be exposed + private Generated::AstNode getChild(int i) { + result = any(Generated::Method g | this = TMethod(g)).getChild(i) + or + result = any(Generated::SingletonMethod g | this = TSingletonMethod(g)).getChild(i) + or + exists(Generated::Lambda g | this = TLambda(g) | + result = g.getBody().(Generated::DoBlock).getChild(i) or + result = g.getBody().(Generated::Block).getChild(i) + ) + or + result = any(Generated::DoBlock g | this = TDoBlock(g)).getChild(i) + or + result = any(Generated::Program g | this = TToplevel(g)).getChild(i) and + not result instanceof Generated::BeginBlock + or + result = any(Generated::Class g | this = TClass(g)).getChild(i) + or + result = any(Generated::SingletonClass g | this = TSingletonClass(g)).getChild(i) + or + result = any(Generated::Module g | this = TModule(g)).getChild(i) + or + result = any(Generated::Begin g | this = TBeginExpr(g)).getChild(i) + } + + final override Stmt getStmt(int n) { + result = + rank[n + 1](AstNode node, int i | + toGenerated(node) = this.getChild(i) and + not node instanceof Else and + not node instanceof RescueClause and + not node instanceof Ensure + | + node order by i + ) + } /** Gets the `n`th rescue clause in this block. */ - final RescueClause getRescue(int n) { result = range.getRescue(n) } + final RescueClause getRescue(int n) { + result = + rank[n + 1](RescueClause node, int i | toGenerated(node) = getChild(i) | node order by i) + } /** Gets a rescue clause in this block. */ final RescueClause getARescue() { result = this.getRescue(_) } /** Gets the `else` clause in this block, if any. */ - final StmtSequence getElse() { result = range.getElse() } + final StmtSequence getElse() { result = unique(Else s | toGenerated(s) = getChild(_)) } /** Gets the `ensure` clause in this block, if any. */ - final StmtSequence getEnsure() { result = range.getEnsure() } + final StmtSequence getEnsure() { result = unique(Ensure s | toGenerated(s) = getChild(_)) } final predicate hasEnsure() { exists(this.getEnsure()) } + + override AstNode getAChild(string pred) { + result = StmtSequence.super.getAChild(pred) + or + pred = "getRescue" and result = this.getRescue(_) + or + pred = "getElse" and result = this.getElse() + or + pred = "getEnsure" and result = this.getEnsure() + } } /** @@ -101,10 +205,22 @@ class BodyStatement extends StmtSequence { * () * ``` */ -class ParenthesizedExpr extends StmtSequence, @parenthesized_statements { - final override ParenthesizedExpr::Range range; +class ParenthesizedExpr extends StmtSequence, TParenthesizedExpr { + private Generated::ParenthesizedStatements g; + + ParenthesizedExpr() { this = TParenthesizedExpr(g) } final override string getAPrimaryQlClass() { result = "ParenthesizedExpr" } + + final override string toString() { + exists(int c | c = this.getNumberOfStatements() | + c = 0 and result = "()" + or + c > 0 and result = "(" + StmtSequence.super.toString() + ")" + ) + } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -117,8 +233,10 @@ class ParenthesizedExpr extends StmtSequence, @parenthesized_statements { * baz(qux: 1) * ``` */ -class Pair extends Expr, @pair { - final override Pair::Range range; +class Pair extends Expr, TPair { + private Generated::Pair g; + + Pair() { this = TPair(g) } final override string getAPrimaryQlClass() { result = "Pair" } @@ -133,7 +251,7 @@ class Pair extends Expr, @pair { * { 'foo' => 123 } * ``` */ - final Expr getKey() { result = range.getKey() } + final Expr getKey() { toGenerated(result) = g.getKey() } /** * Gets the value expression of this pair. For example, the `InteralLiteral` @@ -142,7 +260,15 @@ class Pair extends Expr, @pair { * { 'foo' => 123 } * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getValue() } + + final override string toString() { result = "Pair" } + + override AstNode getAChild(string pred) { + pred = "getKey" and result = this.getKey() + or + pred = "getValue" and result = this.getValue() + } } /** @@ -154,8 +280,10 @@ class Pair extends Expr, @pair { * puts msg * end */ -class RescueClause extends Expr, @rescue { - final override RescueClause::Range range; +class RescueClause extends Expr, TRescueClause { + private Generated::Rescue g; + + RescueClause() { this = TRescueClause(g) } final override string getAPrimaryQlClass() { result = "RescueClause" } @@ -169,7 +297,7 @@ class RescueClause extends Expr, @rescue { * end * ``` */ - final Expr getException(int n) { result = range.getException(n) } + final Expr getException(int n) { toGenerated(result) = g.getExceptions().getChild(n) } /** * Gets an exception to match, if any. For example `FirstError` or `SecondError` in: @@ -181,7 +309,7 @@ class RescueClause extends Expr, @rescue { * end * ``` */ - final Expr getAnException() { result = getException(_) } + final Expr getAnException() { result = this.getException(_) } /** * Gets the variable to which to assign the matched exception, if any. @@ -194,12 +322,22 @@ class RescueClause extends Expr, @rescue { * end * ``` */ - final LhsExpr getVariableExpr() { result = range.getVariableExpr() } + final LhsExpr getVariableExpr() { toGenerated(result) = g.getVariable().getChild() } /** * Gets the exception handler body. */ - final StmtSequence getBody() { result = range.getBody() } + final StmtSequence getBody() { toGenerated(result) = g.getBody() } + + final override string toString() { result = "rescue ..." } + + override AstNode getAChild(string pred) { + pred = "getException" and result = this.getException(_) + or + pred = "getVariableExpr" and result = this.getVariableExpr() + or + pred = "getBody" and result = this.getBody() + } } /** @@ -208,8 +346,10 @@ class RescueClause extends Expr, @rescue { * contents = read_file rescue "" * ``` */ -class RescueModifierExpr extends Expr, @rescue_modifier { - final override RescueModifierExpr::Range range; +class RescueModifierExpr extends Expr, TRescueModifierExpr { + private Generated::RescueModifier g; + + RescueModifierExpr() { this = TRescueModifierExpr(g) } final override string getAPrimaryQlClass() { result = "RescueModifierExpr" } @@ -219,7 +359,7 @@ class RescueModifierExpr extends Expr, @rescue_modifier { * body rescue handler * ``` */ - final Stmt getBody() { result = range.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } /** * Gets the exception handler of this `RescueModifierExpr`. @@ -227,7 +367,15 @@ class RescueModifierExpr extends Expr, @rescue_modifier { * body rescue handler * ``` */ - final Stmt getHandler() { result = range.getHandler() } + final Stmt getHandler() { toGenerated(result) = g.getHandler() } + + final override string toString() { result = "... rescue ..." } + + override AstNode getAChild(string pred) { + pred = "getBody" and result = this.getBody() + or + pred = "getHandler" and result = this.getHandler() + } } /** @@ -237,13 +385,15 @@ class RescueModifierExpr extends Expr, @rescue_modifier { * "foo" "bar" "baz" * ``` */ -class StringConcatenation extends Expr, @chained_string { - final override StringConcatenation::Range range; +class StringConcatenation extends Expr, TStringConcatenation { + private Generated::ChainedString g; + + StringConcatenation() { this = TStringConcatenation(g) } final override string getAPrimaryQlClass() { result = "StringConcatenation" } /** Gets the `n`th string literal in this concatenation. */ - final StringLiteral getString(int n) { result = range.getString(n) } + final StringLiteral getString(int n) { toGenerated(result) = g.getChild(n) } /** Gets a string literal in this concatenation. */ final StringLiteral getAString() { result = this.getString(_) } @@ -277,4 +427,8 @@ class StringConcatenation extends Expr, @chained_string { valueText order by i ) } + + final override string toString() { result = "\"...\" \"...\"" } + + override AstNode getAChild(string pred) { pred = "getString" and result = this.getString(_) } } diff --git a/ql/src/codeql_ruby/ast/Literal.qll b/ql/src/codeql_ruby/ast/Literal.qll index 00aa5359acb..2354084910a 100644 --- a/ql/src/codeql_ruby/ast/Literal.qll +++ b/ql/src/codeql_ruby/ast/Literal.qll @@ -1,21 +1,20 @@ private import codeql_ruby.AST -private import internal.Literal +private import internal.AST +private import internal.TreeSitter /** * A literal. * * This is the QL root class for all literals. */ -class Literal extends Expr { - override Literal::Range range; - +class Literal extends Expr, TLiteral { /** * Gets the source text for this literal, if this is a simple literal. * * For complex literals, such as arrays, hashes, and strings with * interpolations, this predicate has no result. */ - final string getValueText() { result = range.getValueText() } + string getValueText() { none() } } /** @@ -31,9 +30,7 @@ class Literal extends Expr { * 1i * ``` */ -class NumericLiteral extends Literal { - override NumericLiteral::Range range; -} +class NumericLiteral extends Literal, TNumericLiteral { } /** * An integer literal. @@ -43,8 +40,14 @@ class NumericLiteral extends Literal { * 0xff * ``` */ -class IntegerLiteral extends NumericLiteral, @token_integer { - final override IntegerLiteral::Range range; +class IntegerLiteral extends NumericLiteral, TIntegerLiteral { + private Generated::Integer g; + + IntegerLiteral() { this = TIntegerLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } final override string getAPrimaryQlClass() { result = "IntegerLiteral" } } @@ -57,8 +60,14 @@ class IntegerLiteral extends NumericLiteral, @token_integer { * 2.7e+5 * ``` */ -class FloatLiteral extends NumericLiteral, @token_float { - final override FloatLiteral::Range range; +class FloatLiteral extends NumericLiteral, TFloatLiteral { + private Generated::Float g; + + FloatLiteral() { this = TFloatLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } final override string getAPrimaryQlClass() { result = "FloatLiteral" } } @@ -70,8 +79,14 @@ class FloatLiteral extends NumericLiteral, @token_float { * 123r * ``` */ -class RationalLiteral extends NumericLiteral, @rational { - final override RationalLiteral::Range range; +class RationalLiteral extends NumericLiteral, TRationalLiteral { + private Generated::Rational g; + + RationalLiteral() { this = TRationalLiteral(g) } + + final override string getValueText() { result = g.getChild().(Generated::Token).getValue() + "r" } + + final override string toString() { result = this.getValueText() } final override string getAPrimaryQlClass() { result = "RationalLiteral" } } @@ -83,15 +98,27 @@ class RationalLiteral extends NumericLiteral, @rational { * 1i * ``` */ -class ComplexLiteral extends NumericLiteral, @token_complex { - final override ComplexLiteral::Range range; +class ComplexLiteral extends NumericLiteral, TComplexLiteral { + private Generated::Complex g; + + ComplexLiteral() { this = TComplexLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } final override string getAPrimaryQlClass() { result = "ComplexLiteral" } } /** A `nil` literal. */ -class NilLiteral extends Literal, @token_nil { - final override NilLiteral::Range range; +class NilLiteral extends Literal, TNilLiteral { + private Generated::Nil g; + + NilLiteral() { this = TNilLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = this.getValueText() } final override string getAPrimaryQlClass() { result = "NilLiteral" } } @@ -105,30 +132,48 @@ class NilLiteral extends Literal, @token_nil { * FALSE * ``` */ -class BooleanLiteral extends Literal, BooleanLiteral::DbUnion { - final override BooleanLiteral::Range range; - +class BooleanLiteral extends Literal, TBooleanLiteral { final override string getAPrimaryQlClass() { result = "BooleanLiteral" } + final override string toString() { result = this.getValueText() } + /** Holds if the Boolean literal is `true` or `TRUE`. */ - predicate isTrue() { range.isTrue() } + predicate isTrue() { none() } /** Holds if the Boolean literal is `false` or `FALSE`. */ - predicate isFalse() { range.isFalse() } + predicate isFalse() { none() } +} + +private class TrueLiteral extends BooleanLiteral, TTrueLiteral { + private Generated::True g; + + TrueLiteral() { this = TTrueLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override predicate isTrue() { any() } +} + +private class FalseLiteral extends BooleanLiteral, TFalseLiteral { + private Generated::False g; + + FalseLiteral() { this = TFalseLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override predicate isFalse() { any() } } /** * The base class for a component of a string: `StringTextComponent`, * `StringEscapeSequenceComponent`, or `StringInterpolationComponent`. */ -class StringComponent extends AstNode { - override StringComponent::Range range; - +class StringComponent extends AstNode, TStringComponent { /** * Gets the source text for this string component. Has no result if this is * a `StringInterpolationComponent`. */ - final string getValueText() { result = range.getValueText() } + string getValueText() { none() } } /** @@ -143,8 +188,14 @@ class StringComponent extends AstNode { * "foo#{ bar() } baz" * ``` */ -class StringTextComponent extends StringComponent, StringTextComponent::StringContentToken { - final override StringTextComponent::Range range; +class StringTextComponent extends StringComponent, TStringTextComponent { + private Generated::Token g; + + StringTextComponent() { this = TStringTextComponent(g) } + + final override string toString() { result = g.getValue() } + + final override string getValueText() { result = g.getValue() } final override string getAPrimaryQlClass() { result = "StringTextComponent" } } @@ -152,8 +203,14 @@ class StringTextComponent extends StringComponent, StringTextComponent::StringCo /** * An escape sequence component of a string or string-like literal. */ -class StringEscapeSequenceComponent extends StringComponent, @token_escape_sequence { - final override StringEscapeSequenceComponent::Range range; +class StringEscapeSequenceComponent extends StringComponent, TStringEscapeSequenceComponent { + private Generated::EscapeSequence g; + + StringEscapeSequenceComponent() { this = TStringEscapeSequenceComponent(g) } + + final override string toString() { result = g.getValue() } + + final override string getValueText() { result = g.getValue() } final override string getAPrimaryQlClass() { result = "StringEscapeSequenceComponent" } } @@ -161,8 +218,17 @@ class StringEscapeSequenceComponent extends StringComponent, @token_escape_seque /** * An interpolation expression component of a string or string-like literal. */ -class StringInterpolationComponent extends StringComponent, StmtSequence, @interpolation { - final override StringInterpolationComponent::Range range; +class StringInterpolationComponent extends StringComponent, StmtSequence, + TStringInterpolationComponent { + private Generated::Interpolation g; + + StringInterpolationComponent() { this = TStringInterpolationComponent(g) } + + final override string toString() { result = "#{...}" } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } + + final override string getValueText() { none() } final override string getAPrimaryQlClass() { result = "StringInterpolationComponent" } } @@ -170,9 +236,7 @@ class StringInterpolationComponent extends StringComponent, StmtSequence, @inter /** * A string, symbol, regex, or subshell literal. */ -class StringlikeLiteral extends Literal { - override StringlikeLiteral::Range range; - +class StringlikeLiteral extends Literal, TStringlikeLiteral { /** * Gets the `n`th component of this string or string-like literal. The result * will be one of `StringTextComponent`, `StringInterpolationComponent`, and @@ -186,7 +250,7 @@ class StringlikeLiteral extends Literal { * "foo_#{ Time.now }" * ``` */ - final StringComponent getComponent(int n) { result = range.getComponent(n) } + StringComponent getComponent(int n) { none() } /** * Gets the number of components in this string or string-like literal. @@ -206,6 +270,91 @@ class StringlikeLiteral extends Literal { * ``` */ final int getNumberOfComponents() { result = count(this.getComponent(_)) } + + private string getStartDelimiter() { + this instanceof TStringLiteral and + result = "\"" + or + this instanceof TRegexLiteral and + result = "/" + or + this instanceof TSimpleSymbolLiteral and + result = ":" + or + this instanceof TComplexSymbolLiteral and + result = ":\"" + or + this instanceof THashKeySymbolLiteral and + result = "" + or + this instanceof TSubshellLiteral and + result = "`" + or + this instanceof THereDoc and + result = "" + } + + private string getEndDelimiter() { + this instanceof TStringLiteral and + result = "\"" + or + this instanceof TRegexLiteral and + result = "/" + or + this instanceof TSimpleSymbolLiteral and + result = "" + or + this instanceof TComplexSymbolLiteral and + result = "\"" + or + this instanceof THashKeySymbolLiteral and + result = "" + or + this instanceof TSubshellLiteral and + result = "`" + or + this instanceof THereDoc and + result = "" + } + + override string getValueText() { + // 0 components should result in the empty string + // if there are any interpolations, there should be no result + // otherwise, concatenate all the components + forall(StringComponent c | c = this.getComponent(_) | + not c instanceof StringInterpolationComponent + ) and + result = + concat(StringComponent c, int i | c = this.getComponent(i) | c.getValueText() order by i) + } + + override string toString() { + exists(string full, string summary | + full = + concat(StringComponent c, int i, string s | + c = this.getComponent(i) and + ( + s = toGenerated(c).(Generated::Token).getValue() + or + not toGenerated(c) instanceof Generated::Token and + s = "#{...}" + ) + | + s order by i + ) and + ( + // summary should be 32 chars max (incl. ellipsis) + full.length() > 32 and summary = full.substring(0, 29) + "..." + or + full.length() <= 32 and summary = full + ) and + result = this.getStartDelimiter() + summary + this.getEndDelimiter() + ) + } + + final override AstNode getAChild(string pred) { + pred = "getComponent" and result = this.getComponent(_) + } } /** @@ -216,12 +365,26 @@ class StringlikeLiteral extends Literal { * "hello, #{name}" * ``` */ -class StringLiteral extends StringlikeLiteral { - final override StringLiteral::Range range; - +class StringLiteral extends StringlikeLiteral, TStringLiteral { final override string getAPrimaryQlClass() { result = "StringLiteral" } } +private class RegularStringLiteral extends StringLiteral, TRegularStringLiteral { + private Generated::String g; + + RegularStringLiteral() { this = TRegularStringLiteral(g) } + + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } +} + +private class BareStringLiteral extends StringLiteral, TBareStringLiteral { + private Generated::BareString g; + + BareStringLiteral() { this = TBareStringLiteral(g) } + + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } +} + /** * A regular expression literal. * @@ -229,11 +392,15 @@ class StringLiteral extends StringlikeLiteral { * /[a-z]+/ * ``` */ -class RegexLiteral extends StringlikeLiteral, @regex { - final override RegexLiteral::Range range; +class RegexLiteral extends StringlikeLiteral, TRegexLiteral { + private Generated::Regex g; + + RegexLiteral() { this = TRegexLiteral(g) } final override string getAPrimaryQlClass() { result = "RegexLiteral" } + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } + /** * Gets the regex flags as a string. * @@ -242,7 +409,16 @@ class RegexLiteral extends StringlikeLiteral, @regex { * /foo/i # => "i" * /foo/imxo # => "imxo" */ - final string getFlagString() { result = range.getFlagString() } + final string getFlagString() { + // For `/foo/i`, there should be an `/i` token in the database with `this` + // as its parents. Strip the delimiter, which can vary. + result = + max(Generated::Token t | + t.getParent() = g + | + t.getValue().suffix(1) order by t.getParentIndex() + ) + } /** * Holds if the regex was specified using the `i` flag to indicate case @@ -264,16 +440,49 @@ class RegexLiteral extends StringlikeLiteral, @regex { * :"foo bar #{baz}" * ``` */ -class SymbolLiteral extends StringlikeLiteral { - final override SymbolLiteral::Range range; - - SymbolLiteral() { - not any(UndefStmt u).getAMethodName() = this and - not any(AliasStmt a).getNewName() = this and - not any(AliasStmt a).getOldName() = this +class SymbolLiteral extends StringlikeLiteral, TSymbolLiteral { + final override string getAPrimaryQlClass() { + not this instanceof MethodName and result = "SymbolLiteral" } +} - final override string getAPrimaryQlClass() { result = "SymbolLiteral" } +private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral { + private Generated::SimpleSymbol g; + + SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) } + + // Tree-sitter gives us value text including the colon, which we skip. + final override string getValueText() { result = g.getValue().suffix(1) } + + final override string toString() { result = g.getValue() } +} + +private class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { } + +private class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral { + private Generated::DelimitedSymbol g; + + DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } +} + +private class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral { + private Generated::BareSymbol g; + + BareSymbolLiteral() { this = TBareSymbolLiteral(g) } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } +} + +private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral { + private Generated::HashKeySymbol g; + + HashKeySymbolLiteral() { this = THashKeySymbolLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = ":" + this.getValueText() } } /** @@ -284,10 +493,14 @@ class SymbolLiteral extends StringlikeLiteral { * %x(/bin/sh foo.sh) * ``` */ -class SubshellLiteral extends StringlikeLiteral, @subshell { - final override SubshellLiteral::Range range; +class SubshellLiteral extends StringlikeLiteral, TSubshellLiteral { + private Generated::Subshell g; + + SubshellLiteral() { this = TSubshellLiteral(g) } final override string getAPrimaryQlClass() { result = "SubshellLiteral" } + + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } } /** @@ -298,8 +511,14 @@ class SubshellLiteral extends StringlikeLiteral, @subshell { * ?\u{61} * ``` */ -class CharacterLiteral extends Literal, @token_character { - final override CharacterLiteral::Range range; +class CharacterLiteral extends Literal, TCharacterLiteral { + private Generated::Character g; + + CharacterLiteral() { this = TCharacterLiteral(g) } + + final override string getValueText() { result = g.getValue() } + + final override string toString() { result = g.getValue() } final override string getAPrimaryQlClass() { result = "CharacterLiteral" } } @@ -313,8 +532,10 @@ class CharacterLiteral extends Literal, @token_character { * SQL * ``` */ -class HereDoc extends StringlikeLiteral { - final override HereDoc::Range range; +class HereDoc extends StringlikeLiteral, THereDoc { + private Generated::HeredocBeginning g; + + HereDoc() { this = THereDoc(g) } final override string getAPrimaryQlClass() { result = "HereDoc" } @@ -336,7 +557,13 @@ class HereDoc extends StringlikeLiteral { * <<`IDENTIFIER` * ``` */ - final string getQuoteStyle() { result = range.getQuoteStyle() } + final string getQuoteStyle() { + exists(string s | + s = g.getValue() and + s.charAt(s.length() - 1) = result and + result = ["'", "`", "\""] + ) + } /** * Gets the indentation modifier (`-` or `~`) of the here document identifier, if any. @@ -346,7 +573,36 @@ class HereDoc extends StringlikeLiteral { * < (x) { x + 1 } * ``` */ -class Lambda extends Callable, BodyStatement, @lambda { - final override Lambda::Range range; +class Lambda extends Callable, BodyStmt, TLambda { + private Generated::Lambda g; + + Lambda() { this = TLambda(g) } final override string getAPrimaryQlClass() { result = "Lambda" } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = "-> { ... }" } + + final override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) + or + result = BodyStmt.super.getAChild(pred) + } } /** A block. */ -class Block extends Callable, StmtSequence { - override Block::Range range; +class Block extends Callable, StmtSequence, Scope, TBlock { + override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) + or + result = StmtSequence.super.getAChild(pred) + } } /** A block enclosed within `do` and `end`. */ -class DoBlock extends Block, BodyStatement, @do_block { - final override DoBlock::Range range; +class DoBlock extends Block, BodyStmt, TDoBlock { + private Generated::DoBlock g; + + DoBlock() { this = TDoBlock(g) } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = "do ... end" } + + final override AstNode getAChild(string pred) { + result = Block.super.getAChild(pred) + or + result = BodyStmt.super.getAChild(pred) + } final override string getAPrimaryQlClass() { result = "DoBlock" } } @@ -82,8 +155,18 @@ class DoBlock extends Block, BodyStatement, @do_block { * names.each { |name| puts name } * ``` */ -class BraceBlock extends Block, @block { - final override BraceBlock::Range range; +class BraceBlock extends Block, TBraceBlock { + private Generated::Block g; + + BraceBlock() { this = TBraceBlock(g) } + + final override Parameter getParameter(int n) { + toGenerated(result) = g.getParameters().getChild(n) + } + + final override Stmt getStmt(int i) { toGenerated(result) = g.getChild(i) } + + final override string toString() { result = "{ ... }" } final override string getAPrimaryQlClass() { result = "BraceBlock" } } diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index 64238e60213..0eff778dbb8 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -1,18 +1,17 @@ private import codeql_ruby.AST private import codeql_ruby.ast.Constant -private import internal.Module +private import internal.AST +private import internal.TreeSitter /** * The base class for classes, singleton classes, and modules. */ -class ModuleBase extends BodyStatement { - override ModuleBase::Range range; - +class ModuleBase extends BodyStmt, TModuleBase { /** Gets a method defined in this module/class. */ - Method getAMethod() { result = this.getAStmt() } + MethodBase getAMethod() { result = this.getAStmt() } /** Gets the method named `name` in this module/class, if any. */ - Method getMethod(string name) { result = this.getAMethod() and result.getName() = name } + MethodBase getMethod(string name) { result = this.getAMethod() and result.getName() = name } /** Gets a class defined in this module/class. */ Class getAClass() { result = this.getAStmt() } @@ -37,20 +36,33 @@ class ModuleBase extends BodyStatement { * main * ``` */ -class Toplevel extends ModuleBase, @program { - final override Toplevel::Range range; +class Toplevel extends ModuleBase, TToplevel { + private Generated::Program g; + + Toplevel() { this = TToplevel(g) } final override string getAPrimaryQlClass() { result = "Toplevel" } /** * Gets the `n`th `BEGIN` block. */ - final BeginBlock getBeginBlock(int n) { result = range.getBeginBlock(n) } + final BeginBlock getBeginBlock(int n) { + toGenerated(result) = + rank[n + 1](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i) + } /** * Gets a `BEGIN` block. */ final BeginBlock getABeginBlock() { result = getBeginBlock(_) } + + final override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) + or + pred = "getBeginBlock" and result = this.getBeginBlock(_) + } + + final override string toString() { result = g.getLocation().getFile().getBaseName() } } /** @@ -67,9 +79,7 @@ class Toplevel extends ModuleBase, @program { * end * ``` */ -class Namespace extends ModuleBase, ConstantWriteAccess { - override Namespace::Range range; - +class Namespace extends ModuleBase, ConstantWriteAccess, TNamespace { override string getAPrimaryQlClass() { result = "Namespace" } /** @@ -88,7 +98,7 @@ class Namespace extends ModuleBase, ConstantWriteAccess { * end * ``` */ - override string getName() { result = range.getName() } + override string getName() { none() } /** * Gets the scope expression used in the module/class name's scope resolution @@ -109,7 +119,7 @@ class Namespace extends ModuleBase, ConstantWriteAccess { * end * ``` */ - override Expr getScopeExpr() { result = range.getScopeExpr() } + override Expr getScopeExpr() { none() } /** * Holds if the module/class name uses the scope resolution operator to access the @@ -120,7 +130,14 @@ class Namespace extends ModuleBase, ConstantWriteAccess { * end * ``` */ - override predicate hasGlobalScope() { range.hasGlobalScope() } + override predicate hasGlobalScope() { none() } + + override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) or + result = ConstantWriteAccess.super.getAChild(pred) + } + + final override string toString() { result = ConstantWriteAccess.super.toString() } } /** @@ -133,8 +150,10 @@ class Namespace extends ModuleBase, ConstantWriteAccess { * end * ``` */ -class Class extends Namespace, @class { - final override Class::Range range; +class Class extends Namespace, TClass { + private Generated::Class g; + + Class() { this = TClass(g) } final override string getAPrimaryQlClass() { result = "Class" } @@ -154,7 +173,29 @@ class Class extends Namespace, @class { * end * ``` */ - final Expr getSuperclassExpr() { result = range.getSuperclassExpr() } + final Expr getSuperclassExpr() { toGenerated(result) = g.getSuperclass().getChild() } + + final override string getName() { + result = g.getName().(Generated::Token).getValue() or + result = g.getName().(Generated::ScopeResolution).getName().(Generated::Token).getValue() + } + + final override Expr getScopeExpr() { + toGenerated(result) = g.getName().(Generated::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Generated::ScopeResolution sr | + sr = g.getName() and + not exists(sr.getScope()) + ) + } + + final override AstNode getAChild(string pred) { + result = Namespace.super.getAChild(pred) + or + pred = "getSuperclassExpr" and result = this.getSuperclassExpr() + } } /** @@ -168,8 +209,10 @@ class Class extends Namespace, @class { * end * ``` */ -class SingletonClass extends ModuleBase, @singleton_class { - final override SingletonClass::Range range; +class SingletonClass extends ModuleBase, TSingletonClass { + private Generated::SingletonClass g; + + SingletonClass() { this = TSingletonClass(g) } final override string getAPrimaryQlClass() { result = "Class" } @@ -182,7 +225,15 @@ class SingletonClass extends ModuleBase, @singleton_class { * end * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toGenerated(result) = g.getValue() } + + final override string toString() { result = "class << ..." } + + final override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) + or + pred = "getValue" and result = this.getValue() + } } /** @@ -210,8 +261,26 @@ class SingletonClass extends ModuleBase, @singleton_class { * end * ``` */ -class Module extends Namespace, @module { - final override Module::Range range; +class Module extends Namespace, TModule { + private Generated::Module g; + + Module() { this = TModule(g) } final override string getAPrimaryQlClass() { result = "Module" } + + final override string getName() { + result = g.getName().(Generated::Token).getValue() or + result = g.getName().(Generated::ScopeResolution).getName().(Generated::Token).getValue() + } + + final override Expr getScopeExpr() { + toGenerated(result) = g.getName().(Generated::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Generated::ScopeResolution sr | + sr = g.getName() and + not exists(sr.getScope()) + ) + } } diff --git a/ql/src/codeql_ruby/ast/Operation.qll b/ql/src/codeql_ruby/ast/Operation.qll index 8f3da5ffb5c..b5acf8c8116 100644 --- a/ql/src/codeql_ruby/ast/Operation.qll +++ b/ql/src/codeql_ruby/ast/Operation.qll @@ -1,33 +1,46 @@ private import codeql_ruby.AST -private import internal.Operation +private import internal.AST +private import internal.TreeSitter /** * An operation. * * This is the QL root class for all operations. */ -class Operation extends Expr { - override Operation::Range range; - +class Operation extends Expr, TOperation { /** Gets the operator of this operation. */ - final string getOperator() { result = range.getOperator() } + string getOperator() { none() } /** Gets an operand of this operation. */ - final Expr getAnOperand() { result = range.getAnOperand() } + Expr getAnOperand() { none() } + + override AstNode getAChild(string pred) { pred = "getAnOperand" and result = this.getAnOperand() } } /** A unary operation. */ -class UnaryOperation extends Operation, @unary { - override UnaryOperation::Range range; +class UnaryOperation extends Operation, TUnaryOperation { + private Generated::Unary g; + + UnaryOperation() { g = toGenerated(this) } /** Gets the operand of this unary operation. */ - final Expr getOperand() { result = range.getOperand() } + final Expr getOperand() { toGenerated(result) = g.getOperand() } + + final override string getOperator() { result = g.getOperator() } + + final override Expr getAnOperand() { result = this.getOperand() } + + final override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) + or + pred = "getOperand" and result = this.getOperand() + } + + final override string toString() { result = this.getOperator() + " ..." } } /** A unary logical operation. */ -class UnaryLogicalOperation extends UnaryOperation { - override UnaryLogicalOperation::Range range; -} +class UnaryLogicalOperation extends UnaryOperation, TUnaryLogicalOperation { } /** * A logical NOT operation, using either `!` or `not`. @@ -36,16 +49,12 @@ class UnaryLogicalOperation extends UnaryOperation { * not params.empty? * ``` */ -class NotExpr extends UnaryLogicalOperation, NotExpr::DbUnion { - final override NotExpr::Range range; - +class NotExpr extends UnaryLogicalOperation, TNotExpr { final override string getAPrimaryQlClass() { result = "NotExpr" } } /** A unary arithmetic operation. */ -class UnaryArithmeticOperation extends UnaryOperation { - override UnaryArithmeticOperation::Range range; -} +class UnaryArithmeticOperation extends UnaryOperation, TUnaryArithmeticOperation { } /** * A unary plus expression. @@ -53,9 +62,7 @@ class UnaryArithmeticOperation extends UnaryOperation { * + a * ``` */ -class UnaryPlusExpr extends UnaryArithmeticOperation, @unary_plus { - final override UnaryPlusExpr::Range range; - +class UnaryPlusExpr extends UnaryArithmeticOperation, TUnaryPlusExpr { final override string getAPrimaryQlClass() { result = "UnaryPlusExpr" } } @@ -65,16 +72,12 @@ class UnaryPlusExpr extends UnaryArithmeticOperation, @unary_plus { * - a * ``` */ -class UnaryMinusExpr extends UnaryArithmeticOperation, @unary_minus { - final override UnaryMinusExpr::Range range; - +class UnaryMinusExpr extends UnaryArithmeticOperation, TUnaryMinusExpr { final override string getAPrimaryQlClass() { result = "UnaryMinusExpr" } } /** A unary bitwise operation. */ -class UnaryBitwiseOperation extends UnaryOperation { - override UnaryBitwiseOperation::Range range; -} +class UnaryBitwiseOperation extends UnaryOperation, TUnaryBitwiseOperation { } /** * A complement (bitwise NOT) expression. @@ -82,9 +85,7 @@ class UnaryBitwiseOperation extends UnaryOperation { * ~x * ``` */ -class ComplementExpr extends UnaryBitwiseOperation, @unary_tilde { - final override ComplementExpr::Range range; - +class ComplementExpr extends UnaryBitwiseOperation, TComplementExpr { final override string getAPrimaryQlClass() { result = "ComplementExpr" } } @@ -94,29 +95,43 @@ class ComplementExpr extends UnaryBitwiseOperation, @unary_tilde { * defined? some_method * ``` */ -class DefinedExpr extends UnaryOperation, @unary_definedquestion { - final override DefinedExpr::Range range; - +class DefinedExpr extends UnaryOperation, TDefinedExpr { final override string getAPrimaryQlClass() { result = "DefinedExpr" } } /** A binary operation. */ -class BinaryOperation extends Operation, @binary { - override BinaryOperation::Range range; +class BinaryOperation extends Operation, TBinaryOperation { + private Generated::Binary g; + + BinaryOperation() { g = toGenerated(this) } + + final override string getOperator() { result = g.getOperator() } + + final override Expr getAnOperand() { + result = this.getLeftOperand() or result = this.getRightOperand() + } + + final override string toString() { result = "... " + this.getOperator() + " ..." } + + override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) + or + pred = "getLeftOperand" and result = this.getLeftOperand() + or + pred = "getRightOperand" and result = this.getRightOperand() + } /** Gets the left operand of this binary operation. */ - final Stmt getLeftOperand() { result = range.getLeftOperand() } + final Stmt getLeftOperand() { toGenerated(result) = g.getLeft() } /** Gets the right operand of this binary operation. */ - final Stmt getRightOperand() { result = range.getRightOperand() } + final Stmt getRightOperand() { toGenerated(result) = g.getRight() } } /** * A binary arithmetic operation. */ -class BinaryArithmeticOperation extends BinaryOperation { - override BinaryArithmeticOperation::Range range; -} +class BinaryArithmeticOperation extends BinaryOperation, TBinaryArithmeticOperation { } /** * An add expression. @@ -124,9 +139,7 @@ class BinaryArithmeticOperation extends BinaryOperation { * x + 1 * ``` */ -class AddExpr extends BinaryArithmeticOperation, @binary_plus { - final override AddExpr::Range range; - +class AddExpr extends BinaryArithmeticOperation, TAddExpr { final override string getAPrimaryQlClass() { result = "AddExpr" } } @@ -136,9 +149,7 @@ class AddExpr extends BinaryArithmeticOperation, @binary_plus { * x - 3 * ``` */ -class SubExpr extends BinaryArithmeticOperation, @binary_minus { - final override SubExpr::Range range; - +class SubExpr extends BinaryArithmeticOperation, TSubExpr { final override string getAPrimaryQlClass() { result = "SubExpr" } } @@ -148,9 +159,7 @@ class SubExpr extends BinaryArithmeticOperation, @binary_minus { * x * 10 * ``` */ -class MulExpr extends BinaryArithmeticOperation, @binary_star { - final override MulExpr::Range range; - +class MulExpr extends BinaryArithmeticOperation, TMulExpr { final override string getAPrimaryQlClass() { result = "MulExpr" } } @@ -160,9 +169,7 @@ class MulExpr extends BinaryArithmeticOperation, @binary_star { * x / y * ``` */ -class DivExpr extends BinaryArithmeticOperation, @binary_slash { - final override DivExpr::Range range; - +class DivExpr extends BinaryArithmeticOperation, TDivExpr { final override string getAPrimaryQlClass() { result = "DivExpr" } } @@ -172,9 +179,7 @@ class DivExpr extends BinaryArithmeticOperation, @binary_slash { * x % 2 * ``` */ -class ModuloExpr extends BinaryArithmeticOperation, @binary_percent { - final override ModuloExpr::Range range; - +class ModuloExpr extends BinaryArithmeticOperation, TModuloExpr { final override string getAPrimaryQlClass() { result = "ModuloExpr" } } @@ -184,18 +189,14 @@ class ModuloExpr extends BinaryArithmeticOperation, @binary_percent { * x ** 2 * ``` */ -class ExponentExpr extends BinaryArithmeticOperation, @binary_starstar { - final override ExponentExpr::Range range; - +class ExponentExpr extends BinaryArithmeticOperation, TExponentExpr { final override string getAPrimaryQlClass() { result = "ExponentExpr" } } /** * A binary logical operation. */ -class BinaryLogicalOperation extends BinaryOperation { - override BinaryLogicalOperation::Range range; -} +class BinaryLogicalOperation extends BinaryOperation, TBinaryLogicalOperation { } /** * A logical AND operation, using either `and` or `&&`. @@ -204,9 +205,7 @@ class BinaryLogicalOperation extends BinaryOperation { * a && b * ``` */ -class LogicalAndExpr extends BinaryLogicalOperation, LogicalAndExpr::DbUnion { - final override LogicalAndExpr::Range range; - +class LogicalAndExpr extends BinaryLogicalOperation, TLogicalAndExpr { final override string getAPrimaryQlClass() { result = "LogicalAndExpr" } } @@ -217,18 +216,14 @@ class LogicalAndExpr extends BinaryLogicalOperation, LogicalAndExpr::DbUnion { * a || b * ``` */ -class LogicalOrExpr extends BinaryLogicalOperation, LogicalOrExpr::DbUnion { - final override LogicalOrExpr::Range range; - +class LogicalOrExpr extends BinaryLogicalOperation, TLogicalOrExpr { final override string getAPrimaryQlClass() { result = "LogicalOrExpr" } } /** * A binary bitwise operation. */ -class BinaryBitwiseOperation extends BinaryOperation { - override BinaryBitwiseOperation::Range range; -} +class BinaryBitwiseOperation extends BinaryOperation, TBinaryBitwiseOperation { } /** * A left-shift operation. @@ -236,9 +231,7 @@ class BinaryBitwiseOperation extends BinaryOperation { * x << n * ``` */ -class LShiftExpr extends BinaryBitwiseOperation, @binary_langlelangle { - final override LShiftExpr::Range range; - +class LShiftExpr extends BinaryBitwiseOperation, TLShiftExpr { final override string getAPrimaryQlClass() { result = "LShiftExpr" } } @@ -248,9 +241,7 @@ class LShiftExpr extends BinaryBitwiseOperation, @binary_langlelangle { * x >> n * ``` */ -class RShiftExpr extends BinaryBitwiseOperation, @binary_ranglerangle { - final override RShiftExpr::Range range; - +class RShiftExpr extends BinaryBitwiseOperation, TRShiftExpr { final override string getAPrimaryQlClass() { result = "RShiftExpr" } } @@ -260,9 +251,7 @@ class RShiftExpr extends BinaryBitwiseOperation, @binary_ranglerangle { * x & 0xff * ``` */ -class BitwiseAndExpr extends BinaryBitwiseOperation, @binary_ampersand { - final override BitwiseAndExpr::Range range; - +class BitwiseAndExpr extends BinaryBitwiseOperation, TBitwiseAndExpr { final override string getAPrimaryQlClass() { result = "BitwiseAndExpr" } } @@ -272,9 +261,7 @@ class BitwiseAndExpr extends BinaryBitwiseOperation, @binary_ampersand { * x | 0x01 * ``` */ -class BitwiseOrExpr extends BinaryBitwiseOperation, @binary_pipe { - final override BitwiseOrExpr::Range range; - +class BitwiseOrExpr extends BinaryBitwiseOperation, TBitwiseOrExpr { final override string getAPrimaryQlClass() { result = "BitwiseOrExpr" } } @@ -284,9 +271,7 @@ class BitwiseOrExpr extends BinaryBitwiseOperation, @binary_pipe { * x ^ y * ``` */ -class BitwiseXorExpr extends BinaryBitwiseOperation, @binary_caret { - final override BitwiseXorExpr::Range range; - +class BitwiseXorExpr extends BinaryBitwiseOperation, TBitwiseXorExpr { final override string getAPrimaryQlClass() { result = "BitwiseXorExpr" } } @@ -294,16 +279,12 @@ class BitwiseXorExpr extends BinaryBitwiseOperation, @binary_caret { * A comparison operation. That is, either an equality operation or a * relational operation. */ -class ComparisonOperation extends BinaryOperation { - override ComparisonOperation::Range range; -} +class ComparisonOperation extends BinaryOperation, TComparisonOperation { } /** * An equality operation. */ -class EqualityOperation extends ComparisonOperation { - override EqualityOperation::Range range; -} +class EqualityOperation extends ComparisonOperation, TEqualityOperation { } /** * An equals expression. @@ -311,9 +292,7 @@ class EqualityOperation extends ComparisonOperation { * x == y * ``` */ -class EqExpr extends EqualityOperation, @binary_equalequal { - final override EqExpr::Range range; - +class EqExpr extends EqualityOperation, TEqExpr { final override string getAPrimaryQlClass() { result = "EqExpr" } } @@ -323,9 +302,7 @@ class EqExpr extends EqualityOperation, @binary_equalequal { * x != y * ``` */ -class NEExpr extends EqualityOperation, @binary_bangequal { - final override NEExpr::Range range; - +class NEExpr extends EqualityOperation, TNEExpr { final override string getAPrimaryQlClass() { result = "NEExpr" } } @@ -335,21 +312,27 @@ class NEExpr extends EqualityOperation, @binary_bangequal { * String === "foo" * ``` */ -class CaseEqExpr extends EqualityOperation, @binary_equalequalequal { - final override CaseEqExpr::Range range; - +class CaseEqExpr extends EqualityOperation, TCaseEqExpr { final override string getAPrimaryQlClass() { result = "CaseEqExpr" } } /** * A relational operation, that is, one of `<=`, `<`, `>`, or `>=`. */ -class RelationalOperation extends ComparisonOperation { - override RelationalOperation::Range range; +class RelationalOperation extends ComparisonOperation, TRelationalOperation { + /** Gets the greater operand. */ + Expr getGreaterOperand() { none() } - final Expr getGreaterOperand() { result = range.getGreaterOperand() } + /** Gets the lesser operand. */ + Expr getLesserOperand() { none() } - final Expr getLesserOperand() { result = range.getLesserOperand() } + final override AstNode getAChild(string pred) { + result = ComparisonOperation.super.getAChild(pred) + or + pred = "getGreaterOperand" and result = this.getGreaterOperand() + or + pred = "getLesserOperand" and result = this.getLesserOperand() + } } /** @@ -358,10 +341,12 @@ class RelationalOperation extends ComparisonOperation { * x > 0 * ``` */ -class GTExpr extends RelationalOperation, @binary_rangle { - final override GTExpr::Range range; - +class GTExpr extends RelationalOperation, TGTExpr { final override string getAPrimaryQlClass() { result = "GTExpr" } + + final override Expr getGreaterOperand() { result = this.getLeftOperand() } + + final override Expr getLesserOperand() { result = this.getRightOperand() } } /** @@ -370,10 +355,12 @@ class GTExpr extends RelationalOperation, @binary_rangle { * x >= 0 * ``` */ -class GEExpr extends RelationalOperation, @binary_rangleequal { - final override GEExpr::Range range; - +class GEExpr extends RelationalOperation, TGEExpr { final override string getAPrimaryQlClass() { result = "GEExpr" } + + final override Expr getGreaterOperand() { result = this.getLeftOperand() } + + final override Expr getLesserOperand() { result = this.getRightOperand() } } /** @@ -382,10 +369,12 @@ class GEExpr extends RelationalOperation, @binary_rangleequal { * x < 10 * ``` */ -class LTExpr extends RelationalOperation, @binary_langle { - final override LTExpr::Range range; - +class LTExpr extends RelationalOperation, TLTExpr { final override string getAPrimaryQlClass() { result = "LTExpr" } + + final override Expr getGreaterOperand() { result = this.getRightOperand() } + + final override Expr getLesserOperand() { result = this.getLeftOperand() } } /** @@ -394,10 +383,12 @@ class LTExpr extends RelationalOperation, @binary_langle { * x <= 10 * ``` */ -class LEExpr extends RelationalOperation, @binary_langleequal { - final override LEExpr::Range range; - +class LEExpr extends RelationalOperation, TLEExpr { final override string getAPrimaryQlClass() { result = "LEExpr" } + + final override Expr getGreaterOperand() { result = this.getRightOperand() } + + final override Expr getLesserOperand() { result = this.getLeftOperand() } } /** @@ -406,9 +397,7 @@ class LEExpr extends RelationalOperation, @binary_langleequal { * a <=> b * ``` */ -class SpaceshipExpr extends BinaryOperation, @binary_langleequalrangle { - final override SpaceshipExpr::Range range; - +class SpaceshipExpr extends BinaryOperation, TSpaceshipExpr { final override string getAPrimaryQlClass() { result = "SpaceshipExpr" } } @@ -418,9 +407,7 @@ class SpaceshipExpr extends BinaryOperation, @binary_langleequalrangle { * input =~ /\d/ * ``` */ -class RegexMatchExpr extends BinaryOperation, @binary_equaltilde { - final override RegexMatchExpr::Range range; - +class RegexMatchExpr extends BinaryOperation, TRegexMatchExpr { final override string getAPrimaryQlClass() { result = "RegexMatchExpr" } } @@ -430,9 +417,7 @@ class RegexMatchExpr extends BinaryOperation, @binary_equaltilde { * input !~ /\d/ * ``` */ -class NoRegexMatchExpr extends BinaryOperation, @binary_bangtilde { - final override NoRegexMatchExpr::Range range; - +class NoRegexMatchExpr extends BinaryOperation, TNoRegexMatchExpr { final override string getAPrimaryQlClass() { result = "NoRegexMatchExpr" } } @@ -441,14 +426,26 @@ class NoRegexMatchExpr extends BinaryOperation, @binary_bangtilde { * * This is a QL base class for all assignments. */ -class Assignment extends Operation { - override Assignment::Range range; - +class Assignment extends Operation, TAssignment { /** Gets the left hand side of this assignment. */ - Pattern getLeftOperand() { result = range.getLeftOperand() } + Pattern getLeftOperand() { none() } /** Gets the right hand side of this assignment. */ - final Expr getRightOperand() { result = range.getRightOperand() } + Expr getRightOperand() { none() } + + final override Expr getAnOperand() { + result = this.getLeftOperand() or result = this.getRightOperand() + } + + final override string toString() { result = "... " + this.getOperator() + " ..." } + + override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) + or + pred = "getLeftOperand" and result = getLeftOperand() + or + pred = "getRightOperand" and result = getRightOperand() + } } /** @@ -457,8 +454,16 @@ class Assignment extends Operation { * x = 123 * ``` */ -class AssignExpr extends Assignment { - override AssignExpr::Range range; +class AssignExpr extends Assignment, TAssignExpr { + private Generated::Assignment g; + + AssignExpr() { this = TAssignExpr(g) } + + final override Pattern getLeftOperand() { toGenerated(result) = g.getLeft() } + + final override Expr getRightOperand() { toGenerated(result) = g.getRight() } + + final override string getOperator() { result = "=" } override string getAPrimaryQlClass() { result = "AssignExpr" } } @@ -466,16 +471,22 @@ class AssignExpr extends Assignment { /** * A binary assignment operation other than `=`. */ -class AssignOperation extends Assignment { - override AssignOperation::Range range; +class AssignOperation extends Assignment, TAssignOperation { + private Generated::OperatorAssignment g; + + AssignOperation() { g = toGenerated(this) } + + final override string getOperator() { result = g.getOperator() } + + final override LhsExpr getLeftOperand() { toGenerated(result) = g.getLeft() } + + final override Expr getRightOperand() { toGenerated(result) = g.getRight() } } /** * An arithmetic assignment operation: `+=`, `-=`, `*=`, `/=`, `**=`, and `%=`. */ -class AssignArithmeticOperation extends AssignOperation { - override AssignArithmeticOperation::Range range; -} +class AssignArithmeticOperation extends AssignOperation, TAssignArithmeticOperation { } /** * A `+=` assignment expression. @@ -483,9 +494,7 @@ class AssignArithmeticOperation extends AssignOperation { * x += 1 * ``` */ -class AssignAddExpr extends AssignArithmeticOperation, @operator_assignment_plusequal { - final override AssignAddExpr::Range range; - +class AssignAddExpr extends AssignArithmeticOperation, TAssignAddExpr { final override string getAPrimaryQlClass() { result = "AssignAddExpr" } } @@ -495,9 +504,7 @@ class AssignAddExpr extends AssignArithmeticOperation, @operator_assignment_plus * x -= 3 * ``` */ -class AssignSubExpr extends AssignArithmeticOperation, @operator_assignment_minusequal { - final override AssignSubExpr::Range range; - +class AssignSubExpr extends AssignArithmeticOperation, TAssignSubExpr { final override string getAPrimaryQlClass() { result = "AssignSubExpr" } } @@ -507,9 +514,7 @@ class AssignSubExpr extends AssignArithmeticOperation, @operator_assignment_minu * x *= 10 * ``` */ -class AssignMulExpr extends AssignArithmeticOperation, @operator_assignment_starequal { - final override AssignMulExpr::Range range; - +class AssignMulExpr extends AssignArithmeticOperation, TAssignMulExpr { final override string getAPrimaryQlClass() { result = "AssignMulExpr" } } @@ -519,9 +524,7 @@ class AssignMulExpr extends AssignArithmeticOperation, @operator_assignment_star * x /= y * ``` */ -class AssignDivExpr extends AssignArithmeticOperation, @operator_assignment_slashequal { - final override AssignDivExpr::Range range; - +class AssignDivExpr extends AssignArithmeticOperation, TAssignDivExpr { final override string getAPrimaryQlClass() { result = "AssignDivExpr" } } @@ -531,9 +534,7 @@ class AssignDivExpr extends AssignArithmeticOperation, @operator_assignment_slas * x %= 4 * ``` */ -class AssignModuloExpr extends AssignArithmeticOperation, @operator_assignment_percentequal { - final override AssignModuloExpr::Range range; - +class AssignModuloExpr extends AssignArithmeticOperation, TAssignModuloExpr { final override string getAPrimaryQlClass() { result = "AssignModuloExpr" } } @@ -543,20 +544,14 @@ class AssignModuloExpr extends AssignArithmeticOperation, @operator_assignment_p * x **= 2 * ``` */ -class AssignExponentExpr extends AssignArithmeticOperation, @operator_assignment_starstarequal { - final override AssignExponentExpr::Range range; - +class AssignExponentExpr extends AssignArithmeticOperation, TAssignExponentExpr { final override string getAPrimaryQlClass() { result = "AssignExponentExpr" } } /** * A logical assignment operation: `&&=` and `||=`. */ -class AssignLogicalOperation extends AssignOperation { - override AssignLogicalOperation::Range range; - - final override LhsExpr getLeftOperand() { result = super.getLeftOperand() } -} +class AssignLogicalOperation extends AssignOperation, TAssignLogicalOperation { } /** * A logical AND assignment operation. @@ -564,10 +559,7 @@ class AssignLogicalOperation extends AssignOperation { * x &&= y.even? * ``` */ -class AssignLogicalAndExpr extends AssignLogicalOperation, - @operator_assignment_ampersandampersandequal { - final override AssignLogicalAndExpr::Range range; - +class AssignLogicalAndExpr extends AssignLogicalOperation, TAssignLogicalAndExpr { final override string getAPrimaryQlClass() { result = "AssignLogicalAndExpr" } } @@ -577,18 +569,14 @@ class AssignLogicalAndExpr extends AssignLogicalOperation, * x ||= y * ``` */ -class AssignLogicalOrExpr extends AssignLogicalOperation, @operator_assignment_pipepipeequal { - final override AssignLogicalOrExpr::Range range; - +class AssignLogicalOrExpr extends AssignLogicalOperation, TAssignLogicalOrExpr { final override string getAPrimaryQlClass() { result = "AssignLogicalOrExpr" } } /** * A bitwise assignment operation: `<<=`, `>>=`, `&=`, `|=` and `^=`. */ -class AssignBitwiseOperation extends AssignOperation { - override AssignBitwiseOperation::Range range; -} +class AssignBitwiseOperation extends AssignOperation, TAssignBitwiseOperation { } /** * A left-shift assignment operation. @@ -596,9 +584,7 @@ class AssignBitwiseOperation extends AssignOperation { * x <<= 3 * ``` */ -class AssignLShiftExpr extends AssignBitwiseOperation, @operator_assignment_langlelangleequal { - final override AssignLShiftExpr::Range range; - +class AssignLShiftExpr extends AssignBitwiseOperation, TAssignLShiftExpr { final override string getAPrimaryQlClass() { result = "AssignLShiftExpr" } } @@ -608,9 +594,7 @@ class AssignLShiftExpr extends AssignBitwiseOperation, @operator_assignment_lang * x >>= 3 * ``` */ -class AssignRShiftExpr extends AssignBitwiseOperation, @operator_assignment_ranglerangleequal { - final override AssignRShiftExpr::Range range; - +class AssignRShiftExpr extends AssignBitwiseOperation, TAssignRShiftExpr { final override string getAPrimaryQlClass() { result = "AssignRShiftExpr" } } @@ -620,9 +604,7 @@ class AssignRShiftExpr extends AssignBitwiseOperation, @operator_assignment_rang * x &= 0xff * ``` */ -class AssignBitwiseAndExpr extends AssignBitwiseOperation, @operator_assignment_ampersandequal { - final override AssignBitwiseAndExpr::Range range; - +class AssignBitwiseAndExpr extends AssignBitwiseOperation, TAssignBitwiseAndExpr { final override string getAPrimaryQlClass() { result = "AssignBitwiseAndExpr" } } @@ -632,9 +614,7 @@ class AssignBitwiseAndExpr extends AssignBitwiseOperation, @operator_assignment_ * x |= 0x01 * ``` */ -class AssignBitwiseOrExpr extends AssignBitwiseOperation, @operator_assignment_pipeequal { - final override AssignBitwiseOrExpr::Range range; - +class AssignBitwiseOrExpr extends AssignBitwiseOperation, TAssignBitwiseOrExpr { final override string getAPrimaryQlClass() { result = "AssignBitwiseOrExpr" } } @@ -644,8 +624,6 @@ class AssignBitwiseOrExpr extends AssignBitwiseOperation, @operator_assignment_p * x ^= y * ``` */ -class AssignBitwiseXorExpr extends AssignBitwiseOperation, @operator_assignment_caretequal { - final override AssignBitwiseXorExpr::Range range; - +class AssignBitwiseXorExpr extends AssignBitwiseOperation, TAssignBitwiseXorExpr { final override string getAPrimaryQlClass() { result = "AssignBitwiseXorExpr" } } diff --git a/ql/src/codeql_ruby/ast/Parameter.qll b/ql/src/codeql_ruby/ast/Parameter.qll index e81c9c930f6..7f9c11b417b 100644 --- a/ql/src/codeql_ruby/ast/Parameter.qll +++ b/ql/src/codeql_ruby/ast/Parameter.qll @@ -1,20 +1,19 @@ private import codeql_ruby.AST -private import internal.Pattern +private import internal.AST private import internal.Variable private import internal.Parameter +private import internal.TreeSitter /** A parameter. */ -class Parameter extends AstNode { - override Parameter::Range range; - +class Parameter extends AstNode, TParameter { /** Gets the callable that this parameter belongs to. */ final Callable getCallable() { result.getAParameter() = this } /** Gets the zero-based position of this parameter. */ - final int getPosition() { result = range.getPosition() } + final int getPosition() { this = any(Callable c).getParameter(result) } /** Gets a variable introduced by this parameter. */ - LocalVariable getAVariable() { result = range.getAVariable() } + LocalVariable getAVariable() { none() } /** Gets the variable named `name` introduced by this parameter. */ final LocalVariable getVariable(string name) { @@ -28,44 +27,61 @@ class Parameter extends AstNode { * * This includes both simple parameters and tuple parameters. */ -class PatternParameter extends Parameter, Pattern { - override PatternParameter::Range range; - +class PatternParameter extends Parameter, Pattern, TPatternParameter { override LocalVariable getAVariable() { result = Pattern.super.getAVariable() } } /** A parameter defined using a tuple pattern. */ -class TuplePatternParameter extends PatternParameter, TuplePattern { - override TuplePatternParameter::Range range; +class TuplePatternParameter extends PatternParameter, TuplePattern, TTuplePatternParameter { + final override LocalVariable getAVariable() { result = TuplePattern.super.getAVariable() } final override string getAPrimaryQlClass() { result = "TuplePatternParameter" } + + override AstNode getAChild(string pred) { + result = PatternParameter.super.getAChild(pred) or + result = TuplePattern.super.getAChild(pred) + } } /** A named parameter. */ -class NamedParameter extends Parameter { - override NamedParameter::Range range; - +class NamedParameter extends Parameter, TNamedParameter { /** Gets the name of this parameter. */ - final string getName() { result = range.getName() } + string getName() { none() } /** Gets the variable introduced by this parameter. */ - LocalVariable getVariable() { result = range.getVariable() } + LocalVariable getVariable() { none() } override LocalVariable getAVariable() { result = this.getVariable() } /** Gets an access to this parameter. */ final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() } + + /** Gets the access that defines the underlying local variable. */ + final VariableAccess getDefiningAccess() { result = this.getVariable().getDefiningAccess() } + + override AstNode getAChild(string pred) { + result = Parameter.super.getAChild(pred) + or + pred = "getDefiningAccess" and + result = this.getDefiningAccess() + } } /** A simple (normal) parameter. */ -class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern { - override SimpleParameter::Range range; +class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, TSimpleParameter { + private Generated::Identifier g; - final override LocalVariable getVariable() { result = range.getVariable() } + SimpleParameter() { this = TSimpleParameter(g) } - final override LocalVariable getAVariable() { result = range.getAVariable() } + final override string getName() { result = g.getValue() } + + final override LocalVariable getVariable() { result = TLocalVariable(_, _, g) } + + final override LocalVariable getAVariable() { result = this.getVariable() } final override string getAPrimaryQlClass() { result = "SimpleParameter" } + + final override string toString() { result = this.getName() } } /** @@ -76,10 +92,16 @@ class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern * end * ``` */ -class BlockParameter extends @block_parameter, NamedParameter { - final override BlockParameter::Range range; +class BlockParameter extends NamedParameter, TBlockParameter { + private Generated::BlockParameter g; - final override LocalVariable getVariable() { result = range.getVariable() } + BlockParameter() { this = TBlockParameter(g) } + + final override string getName() { result = g.getName().getValue() } + + final override LocalVariable getVariable() { result = TLocalVariable(_, _, g.getName()) } + + final override string toString() { result = "&" + this.getName() } final override string getAPrimaryQlClass() { result = "BlockParameter" } } @@ -93,10 +115,18 @@ class BlockParameter extends @block_parameter, NamedParameter { * end * ``` */ -class HashSplatParameter extends @hash_splat_parameter, NamedParameter { - final override HashSplatParameter::Range range; +class HashSplatParameter extends NamedParameter, THashSplatParameter { + private Generated::HashSplatParameter g; + + HashSplatParameter() { this = THashSplatParameter(g) } final override string getAPrimaryQlClass() { result = "HashSplatParameter" } + + final override LocalVariable getVariable() { result = TLocalVariable(_, _, g.getName()) } + + final override string toString() { result = "**" + this.getName() } + + final override string getName() { result = g.getName().getValue() } } /** @@ -110,23 +140,39 @@ class HashSplatParameter extends @hash_splat_parameter, NamedParameter { * end * ``` */ -class KeywordParameter extends @keyword_parameter, NamedParameter { - final override KeywordParameter::Range range; +class KeywordParameter extends NamedParameter, TKeywordParameter { + private Generated::KeywordParameter g; + + KeywordParameter() { this = TKeywordParameter(g) } final override string getAPrimaryQlClass() { result = "KeywordParameter" } + final override LocalVariable getVariable() { result = TLocalVariable(_, _, g.getName()) } + /** * Gets the default value, i.e. the value assigned to the parameter when one * is not provided by the caller. If the parameter is mandatory and does not * have a default value, this predicate has no result. */ - final Expr getDefaultValue() { result = range.getDefaultValue() } + final Expr getDefaultValue() { toGenerated(result) = g.getValue() } /** * Holds if the parameter is optional. That is, there is a default value that * is used when the caller omits this parameter. */ final predicate isOptional() { exists(this.getDefaultValue()) } + + final override string toString() { result = this.getName() } + + final override string getName() { result = g.getName().getValue() } + + final override Location getLocation() { result = g.getName().getLocation() } + + final override AstNode getAChild(string pred) { + result = NamedParameter.super.getAChild(pred) + or + pred = "getDefaultValue" and result = this.getDefaultValue() + } } /** @@ -138,8 +184,10 @@ class KeywordParameter extends @keyword_parameter, NamedParameter { * end * ``` */ -class OptionalParameter extends @optional_parameter, NamedParameter { - final override OptionalParameter::Range range; +class OptionalParameter extends NamedParameter, TOptionalParameter { + private Generated::OptionalParameter g; + + OptionalParameter() { this = TOptionalParameter(g) } final override string getAPrimaryQlClass() { result = "OptionalParameter" } @@ -147,7 +195,21 @@ class OptionalParameter extends @optional_parameter, NamedParameter { * Gets the default value, i.e. the value assigned to the parameter when one * is not provided by the caller. */ - final Expr getDefaultValue() { result = range.getDefaultValue() } + final Expr getDefaultValue() { toGenerated(result) = g.getValue() } + + final override LocalVariable getVariable() { result = TLocalVariable(_, _, g.getName()) } + + final override string toString() { result = this.getName() } + + final override string getName() { result = g.getName().getValue() } + + final override Location getLocation() { result = g.getName().getLocation() } + + final override AstNode getAChild(string pred) { + result = NamedParameter.super.getAChild(pred) + or + pred = "getDefaultValue" and result = this.getDefaultValue() + } } /** @@ -158,8 +220,16 @@ class OptionalParameter extends @optional_parameter, NamedParameter { * end * ``` */ -class SplatParameter extends @splat_parameter, NamedParameter { - final override SplatParameter::Range range; +class SplatParameter extends NamedParameter, TSplatParameter { + private Generated::SplatParameter g; + + SplatParameter() { this = TSplatParameter(g) } final override string getAPrimaryQlClass() { result = "SplatParameter" } + + final override LocalVariable getVariable() { result = TLocalVariable(_, _, g.getName()) } + + final override string toString() { result = "*" + this.getName() } + + final override string getName() { result = g.getName().getValue() } } diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 78edd4ad0d3..3f5d335ec05 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -1,18 +1,25 @@ private import codeql_ruby.AST private import codeql.Locations -private import internal.Pattern +private import internal.AST +private import internal.TreeSitter private import internal.Variable /** A pattern. */ class Pattern extends AstNode { - override Pattern::Range range; - - Pattern() { range = this } + Pattern() { + explicitAssignmentNode(toGenerated(this), _) or + implicitAssignmentNode(toGenerated(this)) or + implicitParameterAssignmentNode(toGenerated(this), _) + } /** Gets a variable used in (or introduced by) this pattern. */ - Variable getAVariable() { result = range.getAVariable() } + Variable getAVariable() { none() } } +private class LhsExpr_ = + TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or + TSimpleParameter; + /** * A "left-hand-side" expression. An `LhsExpr` can occur on the left-hand side of * operator assignments (`AssignOperation`), in patterns (`Pattern`) on the left-hand side of @@ -29,17 +36,14 @@ class Pattern extends AstNode { * rescue E => var * ``` */ -class LhsExpr extends Pattern, Expr { - override LhsExpr::Range range; +class LhsExpr extends Pattern, LhsExpr_, Expr { + override Variable getAVariable() { result = this.(VariableAccess).getVariable() } } +private class TVariablePattern = TVariableAccess or TSimpleParameter; + /** A simple variable pattern. */ -class VariablePattern extends Pattern, VariablePattern::VariableToken { - override VariablePattern::Range range; - - /** Gets the variable used in (or introduced by) this pattern. */ - Variable getVariable() { access(this, result) } -} +class VariablePattern extends Pattern, LhsExpr, TVariablePattern { } /** * A tuple pattern. @@ -51,13 +55,32 @@ class VariablePattern extends Pattern, VariablePattern::VariableToken { * a, b, *rest, c, d = value * ``` */ -class TuplePattern extends Pattern { - override TuplePattern::Range range; - +class TuplePattern extends Pattern, TTuplePattern { override string getAPrimaryQlClass() { result = "TuplePattern" } + private Generated::AstNode getChild(int i) { + result = toGenerated(this).(Generated::DestructuredParameter).getChild(i) + or + result = toGenerated(this).(Generated::DestructuredLeftAssignment).getChild(i) + or + toGenerated(this) = + any(Generated::LeftAssignmentList lal | + if + strictcount(int j | exists(lal.getChild(j))) = 1 and + lal.getChild(0) instanceof Generated::DestructuredLeftAssignment + then result = lal.getChild(0).(Generated::DestructuredLeftAssignment).getChild(i) + else result = lal.getChild(i) + ) + } + /** Gets the `i`th pattern in this tuple pattern. */ - final Pattern getElement(int i) { result = range.getElement(i) } + final Pattern getElement(int i) { + exists(Generated::AstNode c | c = this.getChild(i) | + toGenerated(result) = c.(Generated::RestAssignment).getChild() + or + toGenerated(result) = c + ) + } /** Gets a sub pattern in this tuple pattern. */ final Pattern getAnElement() { result = this.getElement(_) } @@ -69,5 +92,13 @@ class TuplePattern extends Pattern { * a, b, *rest, c, d = value * ``` */ - final int getRestIndex() { result = range.getRestIndex() } + final int getRestIndex() { + result = unique(int i | getChild(i) instanceof Generated::RestAssignment) + } + + override Variable getAVariable() { result = this.getElement(_).getAVariable() } + + override string toString() { result = "(..., ...)" } + + override AstNode getAChild(string pred) { pred = "getElement" and result = getElement(_) } } diff --git a/ql/src/codeql_ruby/ast/Scope.qll b/ql/src/codeql_ruby/ast/Scope.qll new file mode 100644 index 00000000000..36b23e880e5 --- /dev/null +++ b/ql/src/codeql_ruby/ast/Scope.qll @@ -0,0 +1,28 @@ +private import codeql_ruby.AST +private import internal.AST +private import internal.Scope +private import internal.TreeSitter + +class Scope extends AstNode, TScopeType { + private Scope::Range range; + + Scope() { range = toGenerated(this) } + + /** Gets the enclosing module, if any. */ + ModuleBase getEnclosingModule() { toGenerated(result) = range.getEnclosingModule() } + + /** Gets the enclosing method, if any. */ + MethodBase getEnclosingMethod() { toGenerated(result) = range.getEnclosingMethod() } + + /** Gets the scope in which this scope is nested, if any. */ + Scope getOuterScope() { toGenerated(result) = range.getOuterScope() } + + /** Gets a variable that is declared in this scope. */ + final Variable getAVariable() { result.getDeclaringScope() = this } + + /** Gets the variable declared in this scope with the given name, if any. */ + final Variable getVariable(string name) { + result = this.getAVariable() and + result.getName() = name + } +} diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index 2e6e6c37509..c75d4e07221 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -1,7 +1,7 @@ private import codeql_ruby.AST private import codeql_ruby.CFG -private import internal.Expr -private import internal.Statement +private import internal.AST +private import internal.TreeSitter private import internal.Variable private import codeql_ruby.controlflow.internal.ControlFlowGraphImpl @@ -10,18 +10,13 @@ private import codeql_ruby.controlflow.internal.ControlFlowGraphImpl * * This is the root QL class for all statements. */ -class Stmt extends AstNode { - override Stmt::Range range; - +class Stmt extends AstNode, TStmt { /** Gets a control-flow node for this statement, if any. */ CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } /** Gets the control-flow scope of this statement, if any. */ CfgScope getCfgScope() { result = getCfgScope(this) } - /** Gets the variable scope that this statement belongs to. */ - VariableScope getVariableScope() { result = enclosingScope(this) } - /** Gets the enclosing callable, if any. */ Callable getEnclosingCallable() { result = this.getCfgScope() } } @@ -29,10 +24,10 @@ class Stmt extends AstNode { /** * An empty statement (`;`). */ -class EmptyStmt extends Stmt, @token_empty_statement { - final override EmptyStmt::Range range; - +class EmptyStmt extends Stmt, TEmptyStmt { final override string getAPrimaryQlClass() { result = "EmptyStmt" } + + final override string toString() { result = ";" } } /** @@ -43,10 +38,10 @@ class EmptyStmt extends Stmt, @token_empty_statement { * end * ``` */ -class BeginExpr extends BodyStatement, @begin { - final override Begin::Range range; - +class BeginExpr extends BodyStmt, TBeginExpr { final override string getAPrimaryQlClass() { result = "BeginExpr" } + + final override string toString() { result = "begin ... " } } /** @@ -55,10 +50,16 @@ class BeginExpr extends BodyStatement, @begin { * BEGIN { puts "starting ..." } * ``` */ -class BeginBlock extends StmtSequence, @begin_block { - final override BeginBlock::Range range; +class BeginBlock extends StmtSequence, TBeginBlock { + private Generated::BeginBlock g; + + BeginBlock() { this = TBeginBlock(g) } final override string getAPrimaryQlClass() { result = "BeginBlock" } + + final override string toString() { result = "BEGIN { ... }" } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -67,10 +68,16 @@ class BeginBlock extends StmtSequence, @begin_block { * END { puts "shutting down" } * ``` */ -class EndBlock extends StmtSequence, @end_block { - final override EndBlock::Range range; +class EndBlock extends StmtSequence, TEndBlock { + private Generated::EndBlock g; + + EndBlock() { this = TEndBlock(g) } final override string getAPrimaryQlClass() { result = "EndBlock" } + + final override string toString() { result = "END { ... }" } + + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -81,16 +88,24 @@ class EndBlock extends StmtSequence, @end_block { * - undef :"method_#{ name }" * ``` */ -class UndefStmt extends Stmt, @undef { - final override UndefStmt::Range range; +class UndefStmt extends Stmt, TUndefStmt { + private Generated::Undef g; + + UndefStmt() { this = TUndefStmt(g) } /** Gets the `n`th method name to undefine. */ - final MethodName getMethodName(int n) { result = range.getMethodName(n) } + final MethodName getMethodName(int n) { toGenerated(result) = g.getChild(n) } /** Gets a method name to undefine. */ final MethodName getAMethodName() { result = getMethodName(_) } final override string getAPrimaryQlClass() { result = "UndefStmt" } + + final override string toString() { result = "undef ..." } + + final override AstNode getAChild(string pred) { + pred = "getMethodName" and result = this.getMethodName(_) + } } /** @@ -101,16 +116,26 @@ class UndefStmt extends Stmt, @undef { * - alias bar :"method_#{ name }" * ``` */ -class AliasStmt extends Stmt, @alias { - final override AliasStmt::Range range; +class AliasStmt extends Stmt, TAliasStmt { + private Generated::Alias g; + + AliasStmt() { this = TAliasStmt(g) } /** Gets the new method name. */ - final MethodName getNewName() { result = range.getNewName() } + final MethodName getNewName() { toGenerated(result) = g.getName() } /** Gets the original method name. */ - final MethodName getOldName() { result = range.getOldName() } + final MethodName getOldName() { toGenerated(result) = g.getAlias() } final override string getAPrimaryQlClass() { result = "AliasStmt" } + + final override string toString() { result = "alias ..." } + + final override AstNode getAChild(string pred) { + pred = "getNewName" and result = this.getNewName() + or + pred = "getOldName" and result = this.getOldName() + } } /** @@ -125,11 +150,30 @@ class AliasStmt extends Stmt, @alias { * next value * ``` */ -class ReturningStmt extends Stmt { - override ReturningStmt::Range range; +class ReturningStmt extends Stmt, TReturningStmt { + private Generated::ArgumentList getArgumentList() { + result = any(Generated::Return g | this = TReturnStmt(g)).getChild() + or + result = any(Generated::Break g | this = TBreakStmt(g)).getChild() + or + result = any(Generated::Next g | this = TNextStmt(g)).getChild() + } /** Gets the returned value, if any. */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { + toGenerated(result) = + any(Generated::AstNode res | + exists(Generated::ArgumentList a, int c | + a = this.getArgumentList() and c = count(a.getChild(_)) + | + res = a.getChild(0) and c = 1 + or + res = a and c > 1 + ) + ) + } + + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } /** @@ -139,10 +183,10 @@ class ReturningStmt extends Stmt { * return value * ``` */ -class ReturnStmt extends ReturningStmt, @return { - final override ReturnStmt::Range range; - +class ReturnStmt extends ReturningStmt, TReturnStmt { final override string getAPrimaryQlClass() { result = "ReturnStmt" } + + final override string toString() { result = "return" } } /** @@ -152,10 +196,10 @@ class ReturnStmt extends ReturningStmt, @return { * break value * ``` */ -class BreakStmt extends ReturningStmt, @break { - final override BreakStmt::Range range; - +class BreakStmt extends ReturningStmt, TBreakStmt { final override string getAPrimaryQlClass() { result = "BreakStmt" } + + final override string toString() { result = "break" } } /** @@ -165,10 +209,10 @@ class BreakStmt extends ReturningStmt, @break { * next value * ``` */ -class NextStmt extends ReturningStmt, @next { - final override NextStmt::Range range; - +class NextStmt extends ReturningStmt, TNextStmt { final override string getAPrimaryQlClass() { result = "NextStmt" } + + final override string toString() { result = "next" } } /** @@ -177,10 +221,10 @@ class NextStmt extends ReturningStmt, @next { * redo * ``` */ -class RedoStmt extends Stmt, @redo { - final override RedoStmt::Range range; - +class RedoStmt extends Stmt, TRedoStmt { final override string getAPrimaryQlClass() { result = "RedoStmt" } + + final override string toString() { result = "redo" } } /** @@ -189,8 +233,8 @@ class RedoStmt extends Stmt, @redo { * retry * ``` */ -class RetryStmt extends Stmt, @retry { - final override RetryStmt::Range range; - +class RetryStmt extends Stmt, TRetryStmt { final override string getAPrimaryQlClass() { result = "RetryStmt" } + + final override string toString() { result = "retry" } } diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index d848763b71b..bc2ab278e6c 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -2,36 +2,10 @@ private import codeql_ruby.AST private import codeql.Locations +private import internal.AST +private import internal.TreeSitter private import internal.Variable -/** A scope in which variables can be declared. */ -class VariableScope extends TScope { - VariableScope::Range range; - - VariableScope() { range = this } - - /** Gets a textual representation of this element. */ - final string toString() { result = range.toString() } - - /** Gets the program element this scope is associated with, if any. */ - final AstNode getScopeElement() { result = range.getScopeElement() } - - /** Gets the location of the program element this scope is associated with. */ - final Location getLocation() { result = getScopeElement().getLocation() } - - /** Gets a variable that is declared in this scope. */ - final Variable getAVariable() { result.getDeclaringScope() = this } - - /** Gets the variable with the given name that is declared in this scope. */ - final Variable getVariable(string name) { - result = this.getAVariable() and - result.getName() = name - } - - /** Gets the scope in which this scope is nested, if any. */ - VariableScope getOuterScope() { result = enclosingScope(this.getScopeElement()) } -} - /** A variable declared in a scope. */ class Variable extends TVariable { Variable::Range range; @@ -48,7 +22,7 @@ class Variable extends TVariable { final Location getLocation() { result = range.getLocation() } /** Gets the scope this variable is declared in. */ - final VariableScope getDeclaringScope() { result = range.getDeclaringScope() } + final Scope getDeclaringScope() { toGenerated(result) = range.getDeclaringScope() } /** Gets an access to this variable. */ VariableAccess getAnAccess() { result.getVariable() = this } @@ -105,11 +79,9 @@ class ClassVariable extends Variable, TClassVariable { } /** An access to a variable. */ -class VariableAccess extends Expr { - override VariableAccess::Range range; - +class VariableAccess extends Expr, TVariableAccess { /** Gets the variable this identifier refers to. */ - Variable getVariable() { result = range.getVariable() } + Variable getVariable() { none() } /** * Holds if this access is a write access belonging to the explicit @@ -121,7 +93,9 @@ class VariableAccess extends Expr { * * both `a` and `b` are write accesses belonging to the same assignment. */ - predicate isExplicitWrite(AstNode assignment) { range.isExplicitWrite(assignment) } + predicate isExplicitWrite(AstNode assignment) { + explicitWriteAccess(toGenerated(this), toGenerated(assignment)) + } /** * Holds if this access is a write access belonging to an implicit assignment. @@ -138,7 +112,7 @@ class VariableAccess extends Expr { * the access to `elements` in the parameter list is an implicit assignment, * as is the first access to `e`. */ - predicate isImplicitWrite() { range.isImplicitWrite() } + predicate isImplicitWrite() { implicitWriteAccess(toGenerated(this)) } } /** An access to a variable where the value is updated. */ @@ -160,14 +134,15 @@ class VariableReadAccess extends VariableAccess { } /** An access to a local variable. */ -class LocalVariableAccess extends VariableAccess, LocalVariableAccess::LocalVariableRange { - final override LocalVariableAccess::Range range; +class LocalVariableAccess extends VariableAccess, TLocalVariableAccess { + private Generated::Identifier g; + private LocalVariable v; - final override LocalVariable getVariable() { result = range.getVariable() } + LocalVariableAccess() { this = TLocalVariableAccess(g, v) } - final override string getAPrimaryQlClass() { - not this instanceof NamedParameter and result = "LocalVariableAccess" - } + final override LocalVariable getVariable() { result = v } + + final override string getAPrimaryQlClass() { result = "LocalVariableAccess" } /** * Holds if this access is a captured variable access. For example in @@ -185,6 +160,8 @@ class LocalVariableAccess extends VariableAccess, LocalVariableAccess::LocalVari * the access to `x` in the second `puts x` is not. */ final predicate isCapturedAccess() { isCapturedAccess(this) } + + final override string toString() { result = g.getValue() } } /** An access to a local variable where the value is updated. */ @@ -194,12 +171,17 @@ class LocalVariableWriteAccess extends LocalVariableAccess, VariableWriteAccess class LocalVariableReadAccess extends LocalVariableAccess, VariableReadAccess { } /** An access to a global variable. */ -class GlobalVariableAccess extends VariableAccess, @token_global_variable { - final override GlobalVariableAccess::Range range; +class GlobalVariableAccess extends VariableAccess, TGlobalVariableAccess { + private Generated::GlobalVariable g; + private GlobalVariable v; - final override GlobalVariable getVariable() { result = range.getVariable() } + GlobalVariableAccess() { this = TGlobalVariableAccess(g, v) } + + final override GlobalVariable getVariable() { result = v } final override string getAPrimaryQlClass() { result = "GlobalVariableAccess" } + + final override string toString() { result = g.getValue() } } /** An access to a global variable where the value is updated. */ @@ -209,19 +191,29 @@ class GlobalVariableWriteAccess extends GlobalVariableAccess, VariableWriteAcces class GlobalVariableReadAccess extends GlobalVariableAccess, VariableReadAccess { } /** An access to an instance variable. */ -class InstanceVariableAccess extends VariableAccess, @token_instance_variable { - final override InstanceVariableAccess::Range range; +class InstanceVariableAccess extends VariableAccess, TInstanceVariableAccess { + private Generated::InstanceVariable g; + private InstanceVariable v; - final override InstanceVariable getVariable() { result = range.getVariable() } + InstanceVariableAccess() { this = TInstanceVariableAccess(g, v) } + + final override InstanceVariable getVariable() { result = v } final override string getAPrimaryQlClass() { result = "InstanceVariableAccess" } + + final override string toString() { result = g.getValue() } } /** An access to a class variable. */ -class ClassVariableAccess extends VariableAccess, @token_class_variable { - final override ClassVariableAccess::Range range; +class ClassVariableAccess extends VariableAccess, TClassVariableAccess { + private Generated::ClassVariable g; + private ClassVariable v; - final override ClassVariable getVariable() { result = range.getVariable() } + ClassVariableAccess() { this = TClassVariableAccess(g, v) } + + final override ClassVariable getVariable() { result = v } final override string getAPrimaryQlClass() { result = "ClassVariableAccess" } + + final override string toString() { result = g.getValue() } } diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index 6cced8abe30..37ef071d507 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -1,17 +1,502 @@ import codeql.Locations private import TreeSitter +private import codeql_ruby.ast.internal.Parameter +private import codeql_ruby.ast.internal.Variable +private import codeql_ruby.AST as AST -module AstNode { - abstract class Range extends @ast_node { - Generated::AstNode generated; +module MethodName { + predicate range(Generated::UnderscoreMethodName g) { + exists(Generated::Undef u | u.getChild(_) = g) + or + exists(Generated::Alias a | a.getName() = g or a.getAlias() = g) + } - Range() { this = generated } + class Token = + @setter or @token_class_variable or @token_constant or @token_global_variable or + @token_identifier or @token_instance_variable or @token_operator; +} - cached - abstract string toString(); +cached +private module Cached { + cached + newtype TAstNode = + TAddExpr(Generated::Binary g) { g instanceof @binary_plus } or + TAliasStmt(Generated::Alias g) or + TArgumentList(Generated::AstNode g) { + ( + g.getParent() instanceof Generated::Break or + g.getParent() instanceof Generated::Return or + g.getParent() instanceof Generated::Next or + g.getParent() instanceof Generated::Assignment or + g.getParent() instanceof Generated::OperatorAssignment + ) and + ( + strictcount(g.(Generated::ArgumentList).getChild(_)) > 1 + or + g instanceof Generated::RightAssignmentList + ) + } or + TAssignAddExpr(Generated::OperatorAssignment g) { g instanceof @operator_assignment_plusequal } or + TAssignBitwiseAndExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_ampersandequal + } or + TAssignBitwiseOrExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_pipeequal + } or + TAssignBitwiseXorExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_caretequal + } or + TAssignDivExpr(Generated::OperatorAssignment g) { g instanceof @operator_assignment_slashequal } or + TAssignExponentExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_starstarequal + } or + TAssignExpr(Generated::Assignment g) or + TAssignLShiftExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_langlelangleequal + } or + TAssignLogicalAndExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_ampersandampersandequal + } or + TAssignLogicalOrExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_pipepipeequal + } or + TAssignModuloExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_percentequal + } or + TAssignMulExpr(Generated::OperatorAssignment g) { g instanceof @operator_assignment_starequal } or + TAssignRShiftExpr(Generated::OperatorAssignment g) { + g instanceof @operator_assignment_ranglerangleequal + } or + TAssignSubExpr(Generated::OperatorAssignment g) { g instanceof @operator_assignment_minusequal } or + TBareStringLiteral(Generated::BareString g) or + TBareSymbolLiteral(Generated::BareSymbol g) or + TBeginBlock(Generated::BeginBlock g) or + TBeginExpr(Generated::Begin g) or + TBitwiseAndExpr(Generated::Binary g) { g instanceof @binary_ampersand } or + TBitwiseOrExpr(Generated::Binary g) { g instanceof @binary_pipe } or + TBitwiseXorExpr(Generated::Binary g) { g instanceof @binary_caret } or + TBlockArgument(Generated::BlockArgument g) or + TBlockParameter(Generated::BlockParameter g) or + TBraceBlock(Generated::Block g) { not g.getParent() instanceof Generated::Lambda } or + TBreakStmt(Generated::Break g) or + TCaseEqExpr(Generated::Binary g) { g instanceof @binary_equalequalequal } or + TCaseExpr(Generated::Case g) or + TCharacterLiteral(Generated::Character g) or + TClass(Generated::Class g) or + TClassVariableAccess(Generated::ClassVariable g, AST::ClassVariable v) { + ClassVariableAccess::range(g, v) + } or + TComplementExpr(Generated::Unary g) { g instanceof @unary_tilde } or + TComplexLiteral(Generated::Complex g) or + TDefinedExpr(Generated::Unary g) { g instanceof @unary_definedquestion } or + TDelimitedSymbolLiteral(Generated::DelimitedSymbol g) or + TDestructuredLeftAssignment(Generated::DestructuredLeftAssignment g) { + not strictcount(int i | exists(g.getParent().(Generated::LeftAssignmentList).getChild(i))) = 1 + } or + TDivExpr(Generated::Binary g) { g instanceof @binary_slash } or + TDo(Generated::Do g) or + TDoBlock(Generated::DoBlock g) { not g.getParent() instanceof Generated::Lambda } or + TElementReference(Generated::ElementReference g) or + TElse(Generated::Else g) or + TElsif(Generated::Elsif g) or + TEmptyStmt(Generated::EmptyStatement g) or + TEndBlock(Generated::EndBlock g) or + TEnsure(Generated::Ensure g) or + TEqExpr(Generated::Binary g) { g instanceof @binary_equalequal } or + TExponentExpr(Generated::Binary g) { g instanceof @binary_starstar } or + TFalseLiteral(Generated::False g) or + TFloatLiteral(Generated::Float g) { not any(Generated::Rational r).getChild() = g } or + TForExpr(Generated::For g) or + TForIn(Generated::In g) or // TODO REMOVE + TGEExpr(Generated::Binary g) { g instanceof @binary_rangleequal } or + TGTExpr(Generated::Binary g) { g instanceof @binary_rangle } or + TGlobalVariableAccess(Generated::GlobalVariable g, AST::GlobalVariable v) { + GlobalVariableAccess::range(g, v) + } or + THashKeySymbolLiteral(Generated::HashKeySymbol g) or + THashLiteral(Generated::Hash g) or + THashSplatArgument(Generated::HashSplatArgument g) or + THashSplatParameter(Generated::HashSplatParameter g) or + THereDoc(Generated::HeredocBeginning g) or + TIdentifierMethodCall(Generated::Identifier g) { vcall(g) and not access(g, _) } or + TIf(Generated::If g) or + TIfModifierExpr(Generated::IfModifier g) or + TInstanceVariableAccess(Generated::InstanceVariable g, AST::InstanceVariable v) { + InstanceVariableAccess::range(g, v) + } or + TIntegerLiteral(Generated::Integer g) { not any(Generated::Rational r).getChild() = g } or + TKeywordParameter(Generated::KeywordParameter g) or + TLEExpr(Generated::Binary g) { g instanceof @binary_langleequal } or + TLShiftExpr(Generated::Binary g) { g instanceof @binary_langlelangle } or + TLTExpr(Generated::Binary g) { g instanceof @binary_langle } or + TLambda(Generated::Lambda g) or + TLeftAssignmentList(Generated::LeftAssignmentList g) or + TLocalVariableAccess(Generated::Identifier g, AST::LocalVariable v) { + LocalVariableAccess::range(g, v) + } or + TLogicalAndExpr(Generated::Binary g) { + g instanceof @binary_and or g instanceof @binary_ampersandampersand + } or + TLogicalOrExpr(Generated::Binary g) { g instanceof @binary_or or g instanceof @binary_pipepipe } or + TMethod(Generated::Method g) or + TModule(Generated::Module g) or + TModuloExpr(Generated::Binary g) { g instanceof @binary_percent } or + TMulExpr(Generated::Binary g) { g instanceof @binary_star } or + TNEExpr(Generated::Binary g) { g instanceof @binary_bangequal } or + TNextStmt(Generated::Next g) or + TNilLiteral(Generated::Nil g) or + TNoRegexMatchExpr(Generated::Binary g) { g instanceof @binary_bangtilde } or + TNotExpr(Generated::Unary g) { g instanceof @unary_bang or g instanceof @unary_not } or + TOptionalParameter(Generated::OptionalParameter g) or + TPair(Generated::Pair g) or + TParenthesizedExpr(Generated::ParenthesizedStatements g) or + TRShiftExpr(Generated::Binary g) { g instanceof @binary_ranglerangle } or + TRangeLiteral(Generated::Range g) or + TRationalLiteral(Generated::Rational g) or + TRedoStmt(Generated::Redo g) or + TRegexLiteral(Generated::Regex g) or + TRegexMatchExpr(Generated::Binary g) { g instanceof @binary_equaltilde } or + TRegularArrayLiteral(Generated::Array g) or + TRegularMethodCall(Generated::Call g) { not g.getMethod() instanceof Generated::Super } or + TRegularStringLiteral(Generated::String g) or + TRegularSuperCall(Generated::Call g) { g.getMethod() instanceof Generated::Super } or + TRescueClause(Generated::Rescue g) or + TRescueModifierExpr(Generated::RescueModifier g) or + TRetryStmt(Generated::Retry g) or + TReturnStmt(Generated::Return g) or + TScopeResolutionConstantAccess(Generated::ScopeResolution g, Generated::Constant constant) { + constant = g.getName() and + ( + // 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. + vcall(g) + or + explicitAssignmentNode(g, _) + ) + } or + TScopeResolutionMethodCall(Generated::ScopeResolution g, Generated::Identifier i) { + i = g.getName() and + not exists(Generated::Call c | c.getMethod() = g) + } or + TSelf(Generated::Self g) or + TSimpleParameter(Generated::Identifier g) { g instanceof Parameter::Range } or + TSimpleSymbolLiteral(Generated::SimpleSymbol g) or + TSingletonClass(Generated::SingletonClass g) or + TSingletonMethod(Generated::SingletonMethod g) or + TSpaceshipExpr(Generated::Binary g) { g instanceof @binary_langleequalrangle } or + TSplatArgument(Generated::SplatArgument g) or + TSplatParameter(Generated::SplatParameter g) or + TStringArrayLiteral(Generated::StringArray g) or + TStringConcatenation(Generated::ChainedString g) or + TStringEscapeSequenceComponent(Generated::EscapeSequence g) or + TStringInterpolationComponent(Generated::Interpolation g) or + TStringTextComponent(Generated::Token g) { + g instanceof Generated::StringContent or g instanceof Generated::HeredocContent + } or + TSubExpr(Generated::Binary g) { g instanceof @binary_minus } or + TSubshellLiteral(Generated::Subshell g) or + TSymbolArrayLiteral(Generated::SymbolArray g) or + TTernaryIfExpr(Generated::Conditional g) or + TThen(Generated::Then g) or + TTokenConstantAccess(Generated::Constant g) { + // A tree-sitter `constant` token is a read of that constant in any context + // where an identifier would be a vcall. + vcall(g) + or + explicitAssignmentNode(g, _) + } or + TTokenMethodName(MethodName::Token g) { MethodName::range(g) } or + TTokenSuperCall(Generated::Super g) { vcall(g) } or + TToplevel(Generated::Program g) { g.getLocation().getFile().getExtension() != "erb" } or + TTrueLiteral(Generated::True g) or + TTuplePatternParameter(Generated::DestructuredParameter g) or + TUnaryMinusExpr(Generated::Unary g) { g instanceof @unary_minus } or + TUnaryPlusExpr(Generated::Unary g) { g instanceof @unary_plus } or + TUndefStmt(Generated::Undef g) or + TUnlessExpr(Generated::Unless g) or + TUnlessModifierExpr(Generated::UnlessModifier g) or + TUntilExpr(Generated::Until g) or + TUntilModifierExpr(Generated::UntilModifier g) or + TWhenExpr(Generated::When g) or + TWhileExpr(Generated::While g) or + TWhileModifierExpr(Generated::WhileModifier g) or + TYieldCall(Generated::Yield g) - Location getLocation() { result = generated.getLocation() } - - predicate child(string label, AstNode::Range child) { none() } + /** Gets the underlying TreeSitter entity for a given AST node. */ + cached + Generated::AstNode toGenerated(AST::AstNode n) { + n = TAddExpr(result) or + n = TAliasStmt(result) or + n = TArgumentList(result) or + n = TAssignAddExpr(result) or + n = TAssignBitwiseAndExpr(result) or + n = TAssignBitwiseOrExpr(result) or + n = TAssignBitwiseXorExpr(result) or + n = TAssignDivExpr(result) or + n = TAssignExponentExpr(result) or + n = TAssignExpr(result) or + n = TAssignLShiftExpr(result) or + n = TAssignLogicalAndExpr(result) or + n = TAssignLogicalOrExpr(result) or + n = TAssignModuloExpr(result) or + n = TAssignMulExpr(result) or + n = TAssignRShiftExpr(result) or + n = TAssignSubExpr(result) or + n = TBareStringLiteral(result) or + n = TBareSymbolLiteral(result) or + n = TBeginBlock(result) or + n = TBeginExpr(result) or + n = TBitwiseAndExpr(result) or + n = TBitwiseOrExpr(result) or + n = TBitwiseXorExpr(result) or + n = TBlockArgument(result) or + n = TBlockParameter(result) or + n = TBraceBlock(result) or + n = TBreakStmt(result) or + n = TCaseEqExpr(result) or + n = TCaseExpr(result) or + n = TCharacterLiteral(result) or + n = TClass(result) or + n = TClassVariableAccess(result, _) or + n = TComplementExpr(result) or + n = TComplexLiteral(result) or + n = TDefinedExpr(result) or + n = TDelimitedSymbolLiteral(result) or + n = TDestructuredLeftAssignment(result) or + n = TDivExpr(result) or + n = TDo(result) or + n = TDoBlock(result) or + n = TElementReference(result) or + n = TElse(result) or + n = TElsif(result) or + n = TEmptyStmt(result) or + n = TEndBlock(result) or + n = TEnsure(result) or + n = TEqExpr(result) or + n = TExponentExpr(result) or + n = TFalseLiteral(result) or + n = TFloatLiteral(result) or + n = TForExpr(result) or + n = TForIn(result) or // TODO REMOVE + n = TGEExpr(result) or + n = TGTExpr(result) or + n = TGlobalVariableAccess(result, _) or + n = THashKeySymbolLiteral(result) or + n = THashLiteral(result) or + n = THashSplatArgument(result) or + n = THashSplatParameter(result) or + n = THereDoc(result) or + n = TIdentifierMethodCall(result) or + n = TIf(result) or + n = TIfModifierExpr(result) or + n = TInstanceVariableAccess(result, _) or + n = TIntegerLiteral(result) or + n = TKeywordParameter(result) or + n = TLEExpr(result) or + n = TLShiftExpr(result) or + n = TLTExpr(result) or + n = TLambda(result) or + n = TLeftAssignmentList(result) or + n = TLocalVariableAccess(result, _) or + n = TLogicalAndExpr(result) or + n = TLogicalOrExpr(result) or + n = TMethod(result) or + n = TModule(result) or + n = TModuloExpr(result) or + n = TMulExpr(result) or + n = TNEExpr(result) or + n = TNextStmt(result) or + n = TNilLiteral(result) or + n = TNoRegexMatchExpr(result) or + n = TNotExpr(result) or + n = TOptionalParameter(result) or + n = TPair(result) or + n = TParenthesizedExpr(result) or + n = TRShiftExpr(result) or + n = TRangeLiteral(result) or + n = TRationalLiteral(result) or + n = TRedoStmt(result) or + n = TRegexLiteral(result) or + n = TRegexMatchExpr(result) or + n = TRegularArrayLiteral(result) or + n = TRegularMethodCall(result) or + n = TRegularStringLiteral(result) or + n = TRegularSuperCall(result) or + n = TRescueClause(result) or + n = TRescueModifierExpr(result) or + n = TRetryStmt(result) or + n = TReturnStmt(result) or + n = TScopeResolutionConstantAccess(result, _) or + n = TScopeResolutionMethodCall(result, _) or + n = TSelf(result) or + n = TSimpleParameter(result) or + n = TSimpleSymbolLiteral(result) or + n = TSingletonClass(result) or + n = TSingletonMethod(result) or + n = TSpaceshipExpr(result) or + n = TSplatArgument(result) or + n = TSplatParameter(result) or + n = TStringArrayLiteral(result) or + n = TStringConcatenation(result) or + n = TStringEscapeSequenceComponent(result) or + n = TStringInterpolationComponent(result) or + n = TStringTextComponent(result) or + n = TSubExpr(result) or + n = TSubshellLiteral(result) or + n = TSymbolArrayLiteral(result) or + n = TTernaryIfExpr(result) or + n = TThen(result) or + n = TTokenConstantAccess(result) or + n = TTokenMethodName(result) or + n = TTokenSuperCall(result) or + n = TToplevel(result) or + n = TTrueLiteral(result) or + n = TTuplePatternParameter(result) or + n = TUnaryMinusExpr(result) or + n = TUnaryPlusExpr(result) or + n = TUndefStmt(result) or + n = TUnlessExpr(result) or + n = TUnlessModifierExpr(result) or + n = TUntilExpr(result) or + n = TUntilModifierExpr(result) or + n = TWhenExpr(result) or + n = TWhileExpr(result) or + n = TWhileModifierExpr(result) or + n = TYieldCall(result) } } + +import Cached + +TAstNode fromGenerated(Generated::AstNode n) { n = toGenerated(result) } + +class TCall = TMethodCall or TYieldCall; + +class TMethodCall = + TIdentifierMethodCall or TScopeResolutionMethodCall or TRegularMethodCall or TElementReference or + TSuperCall; + +class TSuperCall = TTokenSuperCall or TRegularSuperCall; + +class TConstantAccess = TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace; + +class TControlExpr = TConditionalExpr or TCaseExpr or TLoop; + +class TConditionalExpr = + TIfExpr or TUnlessExpr or TIfModifierExpr or TUnlessModifierExpr or TTernaryIfExpr; + +class TIfExpr = TIf or TElsif; + +class TConditionalLoop = TWhileExpr or TUntilExpr or TWhileModifierExpr or TUntilModifierExpr; + +class TLoop = TConditionalLoop or TForExpr; + +class TExpr = + TSelf or TArgumentList or TRescueClause or TRescueModifierExpr or TPair or TStringConcatenation or + TCall or TBlockArgument or TSplatArgument or THashSplatArgument or TConstantAccess or + TControlExpr or TWhenExpr or TLiteral or TCallable or TVariableAccess or TStmtSequence or + TOperation or TSimpleParameter; + +class TStmtSequence = + TBeginBlock or TEndBlock or TThen or TElse or TDo or TEnsure or TStringInterpolationComponent or + TBlock or TBodyStmt or TParenthesizedExpr; + +class TBodyStmt = TBeginExpr or TModuleBase or TMethod or TLambda or TDoBlock or TSingletonMethod; + +class TLiteral = + TNumericLiteral or TNilLiteral or TBooleanLiteral or TStringlikeLiteral or TCharacterLiteral or + TArrayLiteral or THashLiteral or TRangeLiteral or TTokenMethodName; + +class TNumericLiteral = TIntegerLiteral or TFloatLiteral or TRationalLiteral or TComplexLiteral; + +class TBooleanLiteral = TTrueLiteral or TFalseLiteral; + +class TStringComponent = + TStringTextComponent or TStringEscapeSequenceComponent or TStringInterpolationComponent; + +class TStringlikeLiteral = + TStringLiteral or TRegexLiteral or TSymbolLiteral or TSubshellLiteral or THereDoc; + +class TStringLiteral = TRegularStringLiteral or TBareStringLiteral; + +class TSymbolLiteral = TSimpleSymbolLiteral or TComplexSymbolLiteral or THashKeySymbolLiteral; + +class TComplexSymbolLiteral = TDelimitedSymbolLiteral or TBareSymbolLiteral; + +class TArrayLiteral = TRegularArrayLiteral or TStringArrayLiteral or TSymbolArrayLiteral; + +class TCallable = TMethodBase or TLambda or TBlock; + +class TMethodBase = TMethod or TSingletonMethod; + +class TBlock = TDoBlock or TBraceBlock; + +class TModuleBase = TToplevel or TNamespace or TSingletonClass; + +class TNamespace = TClass or TModule; + +class TOperation = TUnaryOperation or TBinaryOperation or TAssignment; + +class TUnaryOperation = + TUnaryLogicalOperation or TUnaryArithmeticOperation or TUnaryBitwiseOperation or TDefinedExpr; + +class TUnaryLogicalOperation = TNotExpr; + +class TUnaryArithmeticOperation = TUnaryPlusExpr or TUnaryMinusExpr; + +class TUnaryBitwiseOperation = TComplementExpr; + +class TBinaryOperation = + TBinaryArithmeticOperation or TBinaryLogicalOperation or TBinaryBitwiseOperation or + TComparisonOperation or TSpaceshipExpr or TRegexMatchExpr or TNoRegexMatchExpr; + +class TBinaryArithmeticOperation = + TAddExpr or TSubExpr or TMulExpr or TDivExpr or TModuloExpr or TExponentExpr; + +class TBinaryLogicalOperation = TLogicalAndExpr or TLogicalOrExpr; + +class TBinaryBitwiseOperation = + TLShiftExpr or TRShiftExpr or TBitwiseAndExpr or TBitwiseOrExpr or TBitwiseXorExpr; + +class TComparisonOperation = TEqualityOperation or TRelationalOperation; + +class TEqualityOperation = TEqExpr or TNEExpr or TCaseEqExpr; + +class TRelationalOperation = TGTExpr or TGEExpr or TLTExpr or TLEExpr; + +class TAssignment = TAssignExpr or TAssignOperation; + +class TAssignOperation = + TAssignArithmeticOperation or TAssignLogicalOperation or TAssignBitwiseOperation; + +class TAssignArithmeticOperation = + TAssignAddExpr or TAssignSubExpr or TAssignMulExpr or TAssignDivExpr or TAssignModuloExpr or + TAssignExponentExpr; + +class TAssignLogicalOperation = TAssignLogicalAndExpr or TAssignLogicalOrExpr; + +class TAssignBitwiseOperation = + TAssignLShiftExpr or TAssignRShiftExpr or TAssignBitwiseAndExpr or TAssignBitwiseOrExpr or + TAssignBitwiseXorExpr; + +class TStmt = + TEmptyStmt or TBodyStmt or TStmtSequence or TUndefStmt or TAliasStmt or TReturningStmt or + TRedoStmt or TRetryStmt or TExpr; + +class TReturningStmt = TReturnStmt or TBreakStmt or TNextStmt; + +class TParameter = + TPatternParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or + TOptionalParameter or TSplatParameter; + +class TPatternParameter = TSimpleParameter or TTuplePatternParameter; + +class TNamedParameter = + TSimpleParameter or TBlockParameter or THashSplatParameter or TKeywordParameter or + TOptionalParameter or TSplatParameter; + +class TTuplePattern = TTuplePatternParameter or TDestructuredLeftAssignment or TLeftAssignmentList; + +class TVariableAccess = + TLocalVariableAccess or TGlobalVariableAccess or TInstanceVariableAccess or TClassVariableAccess; diff --git a/ql/src/codeql_ruby/ast/internal/Call.qll b/ql/src/codeql_ruby/ast/internal/Call.qll deleted file mode 100644 index 697b57653e9..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Call.qll +++ /dev/null @@ -1,262 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -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 Call { - abstract class Range extends Expr::Range { - abstract Expr getArgument(int n); - - override predicate child(string label, AstNode::Range child) { - label = "getArgument" and child = getArgument(_) - } - } -} - -module MethodCall { - class Range extends Call::Range { - MethodCallRange::Range range; - - Range() { this = range } - - final Block getBlock() { result = range.getBlock() } - - final Expr getReceiver() { result = range.getReceiver() } - - final override Expr getArgument(int n) { result = range.getArgument(n) } - - abstract string getMethodName(); - - override string toString() { - result = range.toString() - or - not exists(range.toString()) and result = "call to " + concat(this.getMethodName(), "/") - } - - final override predicate child(string label, AstNode::Range child) { - super.child(label, child) - or - label = "getReceiver" and child = getReceiver() - or - label = "getBlock" and child = getBlock() - } - } -} - -module MethodCallRange { - abstract class Range extends @ast_node { - Generated::AstNode generated; - - Range() { this = generated } - - abstract Block getBlock(); - - abstract Expr getReceiver(); - - abstract string getMethod(); - - abstract Expr getArgument(int n); - - string toString() { none() } - } - - private class IdentifierCallRange extends MethodCallRange::Range, @token_identifier { - final override Generated::Identifier generated; - - IdentifierCallRange() { vcall(this) and not access(this, _) } - - final override Expr getReceiver() { none() } - - final override string getMethod() { result = generated.getValue() } - - final override Expr getArgument(int n) { none() } - - final override Block getBlock() { none() } - } - - private class ScopeResolutionIdentifierCallRange extends MethodCallRange::Range, @scope_resolution { - final override Generated::ScopeResolution generated; - Generated::Identifier identifier; - - ScopeResolutionIdentifierCallRange() { - identifier = generated.getName() and - not exists(Generated::Call c | c.getMethod() = this) - } - - final override Expr getReceiver() { result = generated.getScope() } - - final override string getMethod() { result = identifier.getValue() } - - final override Expr getArgument(int n) { none() } - - final override Block getBlock() { none() } - } - - private class RegularCallRange extends MethodCallRange::Range, @call { - final override Generated::Call generated; - - final override Expr getReceiver() { - result = generated.getReceiver() - or - not exists(generated.getReceiver()) and - result = generated.getMethod().(Generated::ScopeResolution).getScope() - } - - final override string getMethod() { - result = "call" and generated.getMethod() instanceof Generated::ArgumentList - or - result = generated.getMethod().(Generated::Token).getValue() - or - result = - generated.getMethod().(Generated::ScopeResolution).getName().(Generated::Token).getValue() - } - - final override Expr getArgument(int n) { - result = generated.getArguments().getChild(n) - or - not exists(generated.getArguments()) and - result = generated.getMethod().(Generated::ArgumentList).getChild(n) - } - - final override Block getBlock() { result = generated.getBlock() } - } -} - -module ElementReferenceRange { - class Range extends MethodCallRange::Range, @element_reference { - final override Generated::ElementReference generated; - - final override Expr getReceiver() { result = generated.getObject() } - - final override string getMethod() { result = "[]" } - - final override string toString() { result = "...[...]" } - - final override Expr getArgument(int n) { result = generated.getChild(n) } - - final override Block getBlock() { none() } - } -} - -module NormalMethodCall { - class Range extends MethodCall::Range { - Range() { - not this instanceof LhsExpr::Range or - generated.getParent() instanceof AssignOperation - } - - final override string getMethodName() { result = range.getMethod() } - } -} - -module SetterMethodCall { - class Range extends MethodCall::Range, LhsExpr::Range { - final override string getMethodName() { result = range.getMethod() + "=" } - - final override string toString() { result = MethodCall::Range.super.toString() } - } -} - -module ElementReference { - class Range extends MethodCall::Range { - override ElementReferenceRange::Range range; - - final override string getMethodName() { none() } - } -} - -module SuperCall { - class Range extends NormalMethodCall::Range { - override SuperCallRange::Range range; - } -} - -module YieldCall { - class Range extends Call::Range, @yield { - final override Generated::Yield generated; - - final override Expr getArgument(int n) { result = generated.getChild().getChild(n) } - - final override string toString() { result = "yield ..." } - } -} - -module SuperCallRange { - abstract class Range extends MethodCallRange::Range { } - - private class SuperTokenCallRange extends SuperCallRange::Range, @token_super { - final override Generated::Super generated; - - // N.B. `super` tokens can never be accesses, so any vcall with `super` must - // be a call. - SuperTokenCallRange() { vcall(this) } - - final override Expr getReceiver() { none() } - - final override string getMethod() { result = generated.getValue() } - - final override Expr getArgument(int n) { none() } - - final override Block getBlock() { none() } - } - - private class RegularSuperCallRange extends SuperCallRange::Range, @call { - final override Generated::Call generated; - - RegularSuperCallRange() { generated.getMethod() instanceof Generated::Super } - - final override Expr getReceiver() { none() } - - final override string getMethod() { - result = generated.getMethod().(Generated::Super).getValue() - } - - final override Expr getArgument(int n) { result = generated.getArguments().getChild(n) } - - final override Block getBlock() { result = generated.getBlock() } - } -} - -module BlockArgument { - class Range extends Expr::Range, @block_argument { - final override Generated::BlockArgument generated; - - final Expr getValue() { result = generated.getChild() } - - final override string toString() { result = "&..." } - - final override predicate child(string label, AstNode::Range child) { - label = "getValue" and child = getValue() - } - } -} - -module SplatArgument { - class Range extends Expr::Range, @splat_argument { - final override Generated::SplatArgument generated; - - final Expr getValue() { result = generated.getChild() } - - final override string toString() { result = "*..." } - - final override predicate child(string label, AstNode::Range child) { - label = "getValue" and child = getValue() - } - } -} - -module HashSplatArgument { - class Range extends Expr::Range, @hash_splat_argument { - final override Generated::HashSplatArgument generated; - - final Expr getValue() { result = generated.getChild() } - - final override string toString() { result = "**..." } - - final override predicate child(string label, AstNode::Range child) { - label = "getValue" and child = getValue() - } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Constant.qll b/ql/src/codeql_ruby/ast/internal/Constant.qll deleted file mode 100644 index 87082ae35c7..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Constant.qll +++ /dev/null @@ -1,93 +0,0 @@ -private import codeql_ruby.ast.internal.AST -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(); - - override predicate child(string label, AstNode::Range child) { - label = "getScopeExpr" and child = getScopeExpr() - } - } -} - -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, LhsExpr::Range { - Range() { explicitAssignmentNode(this, _) } - - override string toString() { result = ConstantWriteAccess::Range.super.toString() } - } - - private class TokenConstantAssignmentRange extends ConstantAssignment::Range, @token_constant { - final override Generated::Constant generated; - - 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() } - - 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/Control.qll b/ql/src/codeql_ruby/ast/internal/Control.qll deleted file mode 100644 index 3f034ce20b3..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Control.qll +++ /dev/null @@ -1,299 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.Pattern -private import codeql_ruby.ast.internal.TreeSitter - -module ControlExpr { - abstract class Range extends Expr::Range { } -} - -module ConditionalExpr { - abstract class Range extends ControlExpr::Range { - abstract Expr getCondition(); - - abstract Stmt getBranch(boolean cond); - - override predicate child(string label, AstNode::Range child) { - label = "getCondition" and child = getCondition() - or - label = "getBranch" and child = getBranch(_) - } - } -} - -module IfExpr { - abstract class Range extends ConditionalExpr::Range { - abstract Stmt getThen(); - - abstract Stmt getElse(); - - final override string toString() { - if this instanceof @elsif then result = "elsif ..." else result = "if ..." - } - - override predicate child(string label, AstNode::Range child) { - super.child(label, child) - or - label = "getThen" and child = getThen() - or - label = "getElse" and child = getElse() - } - } - - private class IfRange extends IfExpr::Range, @if { - final override Generated::If generated; - - final override Expr getCondition() { result = generated.getCondition() } - - final override Stmt getThen() { result = generated.getConsequence() } - - final override Stmt getElse() { result = generated.getAlternative() } - - final override Stmt getBranch(boolean cond) { - cond = true and result = getThen() - or - cond = false and result = getElse() - } - } - - private class ElsifRange extends IfExpr::Range, @elsif { - final override Generated::Elsif generated; - - final override Expr getCondition() { result = generated.getCondition() } - - final override Stmt getThen() { result = generated.getConsequence() } - - final override Stmt getElse() { result = generated.getAlternative() } - - final override Expr getBranch(boolean cond) { - cond = true and result = getThen() - or - cond = false and result = getElse() - } - } -} - -module UnlessExpr { - class Range extends ConditionalExpr::Range, @unless { - final override Generated::Unless generated; - - final override Expr getCondition() { result = generated.getCondition() } - - final Stmt getThen() { result = generated.getConsequence() } - - final Stmt getElse() { result = generated.getAlternative() } - - final override Expr getBranch(boolean cond) { - cond = false and result = getThen() - or - cond = true and result = getElse() - } - - final override string toString() { result = "unless ..." } - - override predicate child(string label, AstNode::Range child) { - ConditionalExpr::Range.super.child(label, child) - or - label = "getThen" and child = getThen() - or - label = "getElse" and child = getElse() - } - } -} - -module IfModifierExpr { - class Range extends ConditionalExpr::Range, @if_modifier { - final override Generated::IfModifier generated; - - final override Expr getCondition() { result = generated.getCondition() } - - final Stmt getBody() { result = generated.getBody() } - - final override Stmt getBranch(boolean cond) { cond = true and result = getBody() } - - final override string toString() { result = "... if ..." } - - override predicate child(string label, AstNode::Range child) { - ConditionalExpr::Range.super.child(label, child) - or - label = "getBody" and child = getBody() - } - } -} - -module UnlessModifierExpr { - class Range extends ConditionalExpr::Range, @unless_modifier { - final override Generated::UnlessModifier generated; - - final override Expr getCondition() { result = generated.getCondition() } - - final Stmt getBody() { result = generated.getBody() } - - final override Stmt getBranch(boolean cond) { cond = false and result = getBody() } - - final override string toString() { result = "... unless ..." } - - override predicate child(string label, AstNode::Range child) { - ConditionalExpr::Range.super.child(label, child) - or - label = "getBody" and child = getBody() - } - } -} - -module TernaryIfExpr { - class Range extends ConditionalExpr::Range, @conditional { - final override Generated::Conditional generated; - - final override Expr getCondition() { result = generated.getCondition() } - - final Stmt getThen() { result = generated.getConsequence() } - - final Stmt getElse() { result = generated.getAlternative() } - - final override Stmt getBranch(boolean cond) { - cond = true and result = getThen() - or - cond = false and result = getElse() - } - - final override string toString() { result = "... ? ... : ..." } - - override predicate child(string label, AstNode::Range child) { - ConditionalExpr::Range.super.child(label, child) - or - label = "getThen" and child = getThen() - or - label = "getElse" and child = getElse() - } - } -} - -module CaseExpr { - class Range extends ControlExpr::Range, @case__ { - final override Generated::Case generated; - - final Expr getValue() { result = generated.getValue() } - - final Expr getBranch(int n) { result = generated.getChild(n) } - - final override string toString() { result = "case ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getValue" and child = getValue() - or - label = "getBranch" and child = getBranch(_) - } - } -} - -module WhenExpr { - class Range extends Expr::Range, @when { - final override Generated::When generated; - - final Stmt getBody() { result = generated.getBody() } - - final Expr getPattern(int n) { result = generated.getPattern(n).getChild() } - - final override string toString() { result = "when ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getBody" and child = getBody() - or - label = "getPattern" and child = getPattern(_) - } - } -} - -module Loop { - abstract class Range extends ControlExpr::Range { - abstract Stmt getBody(); - - override predicate child(string label, AstNode::Range child) { - label = "getBody" and child = getBody() - } - } -} - -module ConditionalLoop { - abstract class Range extends Loop::Range { - abstract Expr getCondition(); - - override predicate child(string label, AstNode::Range child) { - super.child(label, child) - or - label = "getCondition" and child = getCondition() - } - } -} - -module WhileExpr { - class Range extends ConditionalLoop::Range, @while { - final override Generated::While generated; - - final override Stmt getBody() { result = generated.getBody() } - - final override Expr getCondition() { result = generated.getCondition() } - - final override string toString() { result = "while ..." } - } -} - -module UntilExpr { - class Range extends ConditionalLoop::Range, @until { - final override Generated::Until generated; - - final override Stmt getBody() { result = generated.getBody() } - - final override Expr getCondition() { result = generated.getCondition() } - - final override string toString() { result = "until ..." } - } -} - -module WhileModifierExpr { - class Range extends ConditionalLoop::Range, @while_modifier { - final override Generated::WhileModifier generated; - - final override Stmt getBody() { result = generated.getBody() } - - final override Expr getCondition() { result = generated.getCondition() } - - final override string toString() { result = "... while ..." } - } -} - -module UntilModifierExpr { - class Range extends ConditionalLoop::Range, @until_modifier { - final override Generated::UntilModifier generated; - - final override Stmt getBody() { result = generated.getBody() } - - final override Expr getCondition() { result = generated.getCondition() } - - final override string toString() { result = "... until ..." } - } -} - -module ForExpr { - class Range extends Loop::Range, @for { - final override Generated::For generated; - - final override StmtSequence getBody() { result = generated.getBody() } - - final Pattern getPattern() { result = generated.getPattern() } - - final Expr getValue() { result = generated.getValue().getChild() } - - final override string toString() { result = "for ... in ..." } - - override predicate child(string label, AstNode::Range child) { - Loop::Range.super.child(label, child) - or - label = "getPattern" and child = getPattern() - or - label = "getValue" and child = getValue() - } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Expr.qll b/ql/src/codeql_ruby/ast/internal/Expr.qll deleted file mode 100644 index f2014dcdd54..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Expr.qll +++ /dev/null @@ -1,227 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Literal -private import codeql_ruby.ast.internal.Statement -private import codeql_ruby.ast.internal.TreeSitter - -module Expr { - abstract class Range extends Stmt::Range { } -} - -module Self { - class Range extends Expr::Range, @token_self { - final override Generated::Self generated; - - final override string toString() { result = "self" } - } -} - -module ArgumentList { - private class ValidParent = @break or @return or @next or @assignment or @operator_assignment; - - abstract class Range extends Expr::Range { - Range() { generated.getParent() instanceof ValidParent } - - abstract Expr getElement(int i); - - final override string toString() { result = "..., ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getElement" and child = getElement(_) - } - } - - private class ArgArgumentList extends ArgumentList::Range, @argument_list { - final override Generated::ArgumentList generated; - - ArgArgumentList() { strictcount(generated.getChild(_)) > 1 } - - final override Expr getElement(int i) { result = generated.getChild(i) } - } - - private class AssignmentList extends ArgumentList::Range, @right_assignment_list { - final override Generated::RightAssignmentList generated; - - final override Expr getElement(int i) { result = generated.getChild(i) } - } -} - -module StmtSequence { - abstract class Range extends Expr::Range { - abstract Stmt getStmt(int n); - - int getNumberOfStatements() { result = count(this.getStmt(_)) } - - override string toString() { - exists(int c | c = this.getNumberOfStatements() | - c = 0 and result = ";" - or - c = 1 and result = this.getStmt(0).toString() - or - c > 1 and result = "...; ..." - ) - } - - override predicate child(string label, AstNode::Range child) { - label = "getStmt" and child = getStmt(_) - } - } -} - -module BodyStatement { - abstract class Range extends StmtSequence::Range { - final override Stmt getStmt(int n) { - result = - rank[n + 1](Generated::AstNode node, int i | - node = getChild(i) and - not node instanceof Generated::Else and - not node instanceof Generated::Rescue and - not node instanceof Generated::Ensure - | - node order by i - ) - } - - final RescueClause getRescue(int n) { - result = rank[n + 1](Generated::Rescue node, int i | node = getChild(i) | node order by i) - } - - final StmtSequence getElse() { result = unique(Generated::Else s | s = getChild(_)) } - - final StmtSequence getEnsure() { result = unique(Generated::Ensure s | s = getChild(_)) } - - abstract Generated::AstNode getChild(int i); - - override predicate child(string label, AstNode::Range child) { - StmtSequence::Range.super.child(label, child) - or - label = "getRescue" and child = getRescue(_) - or - label = "getElse" and child = getElse() - or - label = "getEnsure" and child = getEnsure() - } - } -} - -module ParenthesizedExpr { - class Range extends StmtSequence::Range, @parenthesized_statements { - final override Generated::ParenthesizedStatements generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - - final override string toString() { - exists(int c | c = this.getNumberOfStatements() | - c = 0 and result = "()" - or - c > 0 and result = "(" + StmtSequence::Range.super.toString() + ")" - ) - } - } -} - -module ThenExpr { - class Range extends StmtSequence::Range, @then { - final override Generated::Then generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - } -} - -module ElseExpr { - class Range extends StmtSequence::Range, @else { - final override Generated::Else generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - } -} - -module DoExpr { - class Range extends StmtSequence::Range, @do { - final override Generated::Do generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - } -} - -module Ensure { - class Range extends StmtSequence::Range, @ensure { - final override Generated::Ensure generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - - final override string toString() { result = "ensure ..." } - } -} - -module RescueClause { - class Range extends Expr::Range, @rescue { - final override Generated::Rescue generated; - - final Expr getException(int n) { result = generated.getExceptions().getChild(n) } - - final LhsExpr getVariableExpr() { result = generated.getVariable().getChild() } - - final StmtSequence getBody() { result = generated.getBody() } - - final override string toString() { result = "rescue ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getException" and child = getException(_) - or - label = "getVariableExpr" and child = getVariableExpr() - or - label = "getBody" and child = getBody() - } - } -} - -module RescueModifierExpr { - class Range extends Expr::Range, @rescue_modifier { - final override Generated::RescueModifier generated; - - final Stmt getBody() { result = generated.getBody() } - - final Stmt getHandler() { result = generated.getHandler() } - - final override string toString() { result = "... rescue ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getBody" and child = getBody() - or - label = "getHandler" and child = getHandler() - } - } -} - -module Pair { - class Range extends Expr::Range, @pair { - final override Generated::Pair generated; - - final Expr getKey() { result = generated.getKey() } - - final Expr getValue() { result = generated.getValue() } - - final override string toString() { result = "Pair" } - - override predicate child(string label, AstNode::Range child) { - label = "getKey" and child = getKey() - or - label = "getValue" and child = getValue() - } - } -} - -module StringConcatenation { - class Range extends Expr::Range, @chained_string { - final override Generated::ChainedString generated; - - final StringLiteral::Range getString(int i) { result = generated.getChild(i) } - - final override string toString() { result = "\"...\" \"...\"" } - - override predicate child(string label, AstNode::Range child) { - label = "getString" and child = getString(_) - } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Literal.qll b/ql/src/codeql_ruby/ast/internal/Literal.qll deleted file mode 100644 index 0f46a1fabd9..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Literal.qll +++ /dev/null @@ -1,446 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.TreeSitter - -module Literal { - abstract class Range extends Expr::Range { - abstract string getValueText(); - - override string toString() { result = this.getValueText() } - } -} - -module NumericLiteral { - abstract class Range extends Literal::Range { } -} - -module IntegerLiteral { - class Range extends NumericLiteral::Range, @token_integer { - final override Generated::Integer generated; - - Range() { not any(Generated::Rational r).getChild() = this } - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = this.getValueText() } - } -} - -module FloatLiteral { - class Range extends NumericLiteral::Range, @token_float { - final override Generated::Float generated; - - Range() { not any(Generated::Rational r).getChild() = this } - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = this.getValueText() } - } -} - -module RationalLiteral { - class Range extends NumericLiteral::Range, @rational { - final override Generated::Rational generated; - - final override string getValueText() { - result = generated.getChild().(Generated::Token).getValue() + "r" - } - - final override string toString() { result = this.getValueText() } - } -} - -module ComplexLiteral { - class Range extends NumericLiteral::Range, @token_complex { - final override Generated::Complex generated; - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = this.getValueText() } - } -} - -module NilLiteral { - class Range extends Literal::Range, @token_nil { - final override Generated::Nil generated; - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = this.getValueText() } - } -} - -module BooleanLiteral { - class DbUnion = @token_true or @token_false; - - class Range extends Literal::Range, DbUnion { - final override Generated::Token generated; - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = this.getValueText() } - - predicate isTrue() { this instanceof @token_true } - - predicate isFalse() { this instanceof @token_false } - } -} - -module StringComponent { - abstract class Range extends AstNode::Range { - abstract string getValueText(); - } -} - -module StringTextComponent { - class StringContentToken = @token_string_content or @token_heredoc_content; - - class Range extends StringComponent::Range, StringContentToken { - final override Generated::Token generated; - - final override string toString() { result = generated.getValue() } - - final override string getValueText() { result = generated.getValue() } - } -} - -module StringEscapeSequenceComponent { - class Range extends StringComponent::Range, @token_escape_sequence { - final override Generated::EscapeSequence generated; - - final override string toString() { result = generated.getValue() } - - final override string getValueText() { result = generated.getValue() } - } -} - -module StringInterpolationComponent { - class Range extends StringComponent::Range, StmtSequence::Range, @interpolation { - final override Generated::Interpolation generated; - - final override string toString() { result = "#{...}" } - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - - final override string getValueText() { none() } - - override predicate child(string label, AstNode::Range child) { - StmtSequence::Range.super.child(label, child) - } - } -} - -module StringlikeLiteral { - abstract class Range extends Literal::Range { - abstract StringComponent::Range getComponent(int i); - - string getStartDelimiter() { result = "" } - - string getEndDelimiter() { result = "" } - - final predicate isSimple() { count(this.getComponent(_)) <= 1 } - - override string getValueText() { - // 0 components should result in the empty string - // if there are any interpolations, there should be no result - // otherwise, concatenate all the components - forall(StringComponent c | c = this.getComponent(_) | - not c instanceof StringInterpolationComponent::Range - ) and - result = - concat(StringComponent::Range c, int i | - c = this.getComponent(i) - | - c.getValueText() order by i - ) - } - - override string toString() { - exists(string full, string summary | - full = - concat(StringComponent::Range c, int i, string s | - c = this.getComponent(i) and - if c instanceof Generated::Token - then s = c.(Generated::Token).getValue() - else s = "#{...}" - | - s order by i - ) and - ( - // summary should be 32 chars max (incl. ellipsis) - full.length() > 32 and summary = full.substring(0, 29) + "..." - or - full.length() <= 32 and summary = full - ) and - result = this.getStartDelimiter() + summary + this.getEndDelimiter() - ) - } - - override predicate child(string label, AstNode::Range child) { - label = "getComponent" and child = getComponent(_) - } - } -} - -module StringLiteral { - abstract class Range extends StringlikeLiteral::Range { - final override string getStartDelimiter() { result = "\"" } - - final override string getEndDelimiter() { result = "\"" } - } - - private class RegularStringRange extends StringLiteral::Range, @string__ { - final override Generated::String generated; - - final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) } - } - - private class BareStringRange extends StringLiteral::Range, @bare_string { - final override Generated::BareString generated; - - final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) } - } -} - -module RegexLiteral { - class Range extends StringlikeLiteral::Range, @regex { - final override Generated::Regex generated; - - final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) } - - final override string getStartDelimiter() { result = "/" } - - final override string getEndDelimiter() { result = "/" } - - final string getFlagString() { - // For `/foo/i`, there should be an `/i` token in the database with `this` - // as its parents. Strip the delimiter, which can vary. - result = - max(Generated::Token t | - t.getParent() = this - | - t.getValue().suffix(1) order by t.getParentIndex() - ) - } - } -} - -module SymbolLiteral { - abstract class Range extends StringlikeLiteral::Range { } - - class SimpleSymbolRange extends SymbolLiteral::Range { - final override Generated::SimpleSymbol generated; - - final override StringComponent::Range getComponent(int i) { none() } - - final override string getStartDelimiter() { result = ":" } - - // Tree-sitter gives us value text including the colon, which we skip. - final override string getValueText() { result = generated.getValue().suffix(1) } - - final override string toString() { result = generated.getValue() } - } - - abstract private class ComplexSymbolRange extends SymbolLiteral::Range { - final override string getStartDelimiter() { result = ":\"" } - - final override string getEndDelimiter() { result = "\"" } - } - - class DelimitedSymbolRange extends ComplexSymbolRange, @delimited_symbol { - final override Generated::DelimitedSymbol generated; - - final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) } - } - - class BareSymbolRange extends ComplexSymbolRange, @bare_symbol { - final override Generated::BareSymbol generated; - - final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) } - } - - class HashKeySymbolRange extends SymbolLiteral::Range, @token_hash_key_symbol { - final override Generated::HashKeySymbol generated; - - final override StringComponent::Range getComponent(int i) { none() } - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = ":" + this.getValueText() } - } -} - -module SubshellLiteral { - class Range extends StringlikeLiteral::Range, @subshell { - final override Generated::Subshell generated; - - final override StringComponent::Range getComponent(int i) { result = generated.getChild(i) } - - final override string getStartDelimiter() { result = "`" } - - final override string getEndDelimiter() { result = "`" } - } -} - -module CharacterLiteral { - class Range extends Literal::Range, @token_character { - final override Generated::Character generated; - - final override string getValueText() { result = generated.getValue() } - - final override string toString() { result = generated.getValue() } - } -} - -module HereDoc { - private Generated::HeredocBody heredoc(Generated::HeredocBeginning start) { - exists(int i, File f | - start = - rank[i](Generated::HeredocBeginning b | - f = b.getLocation().getFile() - | - b order by b.getLocation().getStartLine(), b.getLocation().getStartColumn() - ) and - result = - rank[i](Generated::HeredocBody b | - f = b.getLocation().getFile() - | - b order by b.getLocation().getStartLine(), b.getLocation().getStartColumn() - ) - ) - } - - class Range extends StringlikeLiteral::Range, @token_heredoc_beginning { - final override Generated::HeredocBeginning generated; - private Generated::HeredocBody body; - - Range() { body = heredoc(this) } - - final override StringComponent::Range getComponent(int n) { result = body.getChild(n) } - - final string getQuoteStyle() { - exists(string s | - s = generated.getValue() and - s.charAt(s.length() - 1) = result and - result = ["'", "`", "\""] - ) - } - - final string getIndentationModifier() { - exists(string s | - s = generated.getValue() and - s.charAt(2) = result and - result = ["-", "~"] - ) - } - - final override string toString() { result = generated.getValue() } - } -} - -module ArrayLiteral { - abstract class Range extends Literal::Range { - final override string getValueText() { none() } - - abstract Expr getElement(int i); - - override predicate child(string label, AstNode::Range child) { - label = "getElement" and child = getElement(_) - } - } - - private class RegularArrayRange extends ArrayLiteral::Range, @array { - final override Generated::Array generated; - - final override Expr getElement(int i) { result = generated.getChild(i) } - - final override string toString() { result = "[...]" } - } - - private class StringArrayRange extends ArrayLiteral::Range, @string_array { - final override Generated::StringArray generated; - - final override Expr getElement(int i) { result = generated.getChild(i) } - - final override string toString() { result = "%w(...)" } - } - - private class SymbolArrayRange extends ArrayLiteral::Range, @symbol_array { - final override Generated::SymbolArray generated; - - final override Expr getElement(int i) { result = generated.getChild(i) } - - final override string toString() { result = "%i(...)" } - } -} - -module HashLiteral { - class Range extends Literal::Range, @hash { - final override Generated::Hash generated; - - final override string getValueText() { none() } - - final Expr getElement(int i) { result = generated.getChild(i) } - - final override string toString() { result = "{...}" } - - override predicate child(string label, AstNode::Range child) { - label = "getElement" and child = getElement(_) - } - } -} - -module RangeLiteral { - class Range extends Literal::Range, @range { - final override Generated::Range generated; - - final override string getValueText() { none() } - - final override string toString() { result = "_ " + generated.getOperator() + " _" } - - final Expr getBegin() { result = generated.getBegin() } - - final Expr getEnd() { result = generated.getEnd() } - - final predicate isInclusive() { this instanceof @range_dotdot } - - final predicate isExclusive() { this instanceof @range_dotdotdot } - - override predicate child(string label, AstNode::Range child) { - label = "getBegin" and child = getBegin() - or - label = "getEnd" and child = getEnd() - } - } -} - -module MethodName { - private class TokenTypes = - @setter or @token_class_variable or @token_constant or @token_global_variable or - @token_identifier or @token_instance_variable or @token_operator; - - abstract class Range extends Literal::Range, @underscore_method_name { - Range() { - exists(Generated::Undef u | u.getChild(_) = generated) - or - exists(Generated::Alias a | a.getName() = generated or a.getAlias() = generated) - } - } - - private class TokenMethodName extends MethodName::Range, TokenTypes { - final override Generated::UnderscoreMethodName generated; - - final override string getValueText() { - result = generated.(Generated::Token).getValue() - or - result = generated.(Generated::Setter).getName().getValue() + "=" - } - } - - private class SimpleSymbolMethodName extends MethodName::Range, SymbolLiteral::SimpleSymbolRange, - @token_simple_symbol { } - - private class DelimitedSymbolMethodName extends MethodName::Range, - SymbolLiteral::DelimitedSymbolRange, @delimited_symbol { } -} diff --git a/ql/src/codeql_ruby/ast/internal/Method.qll b/ql/src/codeql_ruby/ast/internal/Method.qll deleted file mode 100644 index 49540f9b788..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Method.qll +++ /dev/null @@ -1,135 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.Parameter -private import TreeSitter - -module Callable { - abstract class Range extends Expr::Range { - abstract Parameter::Range getParameter(int n); - - override predicate child(string label, AstNode::Range child) { - label = "getParameter" and child = getParameter(_) - } - } -} - -module Method { - class Range extends Callable::Range, BodyStatement::Range, @method { - final override Generated::Method generated; - - override Parameter::Range getParameter(int n) { result = generated.getParameters().getChild(n) } - - string getName() { - result = generated.getName().(Generated::Token).getValue() or - result = generated.getName().(Generated::Setter).getName().getValue() + "=" - } - - final predicate isSetter() { generated.getName() instanceof Generated::Setter } - - final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - - final override string toString() { result = this.getName() } - - override predicate child(string label, AstNode::Range child) { - Callable::Range.super.child(label, child) or BodyStatement::Range.super.child(label, child) - } - } -} - -module SingletonMethod { - class Range extends Callable::Range, BodyStatement::Range, @singleton_method { - final override Generated::SingletonMethod generated; - - override Parameter::Range getParameter(int n) { result = generated.getParameters().getChild(n) } - - string getName() { - result = generated.getName().(Generated::Token).getValue() or - result = generated.getName().(SymbolLiteral).getValueText() or - result = generated.getName().(Generated::Setter).getName().getValue() + "=" - } - - final Generated::AstNode getObject() { result = generated.getObject() } - - final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - - final override string toString() { result = this.getName() } - - override predicate child(string label, AstNode::Range child) { - Callable::Range.super.child(label, child) - or - BodyStatement::Range.super.child(label, child) - or - label = "getObject" and child = getObject() - } - } -} - -module Lambda { - class Range extends Callable::Range, BodyStatement::Range, @lambda { - final override Generated::Lambda generated; - - final override Parameter::Range getParameter(int n) { - result = generated.getParameters().getChild(n) - } - - final override Generated::AstNode getChild(int i) { - result = generated.getBody().(Generated::DoBlock).getChild(i) or - result = generated.getBody().(Generated::Block).getChild(i) - } - - final override string toString() { result = "-> { ... }" } - - override predicate child(string label, AstNode::Range child) { - Callable::Range.super.child(label, child) - or - BodyStatement::Range.super.child(label, child) - } - } -} - -module Block { - abstract class Range extends Callable::Range, StmtSequence::Range { - Range() { not generated.getParent() instanceof Generated::Lambda } - - override predicate child(string label, AstNode::Range child) { - Callable::Range.super.child(label, child) - or - StmtSequence::Range.super.child(label, child) - } - } -} - -module DoBlock { - class Range extends Block::Range, BodyStatement::Range, @do_block { - final override Generated::DoBlock generated; - - final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - - final override Parameter::Range getParameter(int n) { - result = generated.getParameters().getChild(n) - } - - final override string toString() { result = "do ... end" } - - override predicate child(string label, AstNode::Range child) { - Block::Range.super.child(label, child) - or - BodyStatement::Range.super.child(label, child) - } - } -} - -module BraceBlock { - class Range extends Block::Range, @block { - final override Generated::Block generated; - - final override Parameter::Range getParameter(int n) { - result = generated.getParameters().getChild(n) - } - - final override Stmt getStmt(int i) { result = generated.getChild(i) } - - final override string toString() { result = "{ ... }" } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Module.qll b/ql/src/codeql_ruby/ast/internal/Module.qll deleted file mode 100644 index e5b28b92df2..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Module.qll +++ /dev/null @@ -1,129 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Constant -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.TreeSitter - -module ModuleBase { - abstract class Range extends BodyStatement::Range { } -} - -module Namespace { - abstract class Range extends ModuleBase::Range, ConstantWriteAccess::Range { - override predicate child(string label, AstNode::Range child) { - ModuleBase::Range.super.child(label, child) or - ConstantWriteAccess::Range.super.child(label, child) - } - - override string toString() { result = ModuleBase::Range.super.toString() } - } -} - -module Toplevel { - class Range extends ModuleBase::Range, @program { - final override Generated::Program generated; - - Range() { generated.getLocation().getFile().getExtension() != "erb" } - - final override Generated::AstNode getChild(int i) { - result = generated.getChild(i) and - not result instanceof Generated::BeginBlock - } - - final StmtSequence getBeginBlock(int n) { - result = rank[n](int i, Generated::BeginBlock b | b = generated.getChild(i) | b order by i) - } - - final override string toString() { result = generated.getLocation().getFile().getBaseName() } - - override predicate child(string label, AstNode::Range child) { - ModuleBase::Range.super.child(label, child) - or - label = "getBeginBlock" and child = getBeginBlock(_) - } - } -} - -module Class { - class Range extends Namespace::Range, @class { - final override Generated::Class generated; - - final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - - final override string getName() { - result = generated.getName().(Generated::Token).getValue() or - result = - generated.getName().(Generated::ScopeResolution).getName().(Generated::Token).getValue() - } - - 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() } - - final override string toString() { result = this.getName() } - - override predicate child(string label, AstNode::Range child) { - Namespace::Range.super.child(label, child) - or - label = "getSuperclassExpr" and child = getSuperclassExpr() - } - } -} - -module SingletonClass { - class Range extends ModuleBase::Range, @singleton_class { - final override Generated::SingletonClass generated; - - final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - - final Expr getValue() { result = generated.getValue() } - - final override string toString() { result = "class << ..." } - - override predicate child(string label, AstNode::Range child) { - ModuleBase::Range.super.child(label, child) - or - label = "getValue" and child = getValue() - } - } -} - -module Module { - class Range extends Namespace::Range, @module { - final override Generated::Module generated; - - final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } - - final override string getName() { - result = generated.getName().(Generated::Token).getValue() or - result = - generated.getName().(Generated::ScopeResolution).getName().(Generated::Token).getValue() - } - - 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() } - - override predicate child(string label, AstNode::Range child) { - Namespace::Range.super.child(label, child) - } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Operation.qll b/ql/src/codeql_ruby/ast/internal/Operation.qll deleted file mode 100644 index ee11ad805ad..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Operation.qll +++ /dev/null @@ -1,355 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.TreeSitter -private import codeql_ruby.ast.internal.Expr - -module Operation { - abstract class Range extends Expr::Range { - abstract string getOperator(); - - abstract Stmt getAnOperand(); - - override predicate child(string label, AstNode::Range child) { - label = "getAnOperand" and child = getAnOperand() - } - } -} - -module UnaryOperation { - abstract class Range extends Operation::Range, @unary { - final override Generated::Unary generated; - - final override string getOperator() { result = generated.getOperator() } - - Expr getOperand() { result = generated.getOperand() } - - final override Expr getAnOperand() { result = this.getOperand() } - - override string toString() { result = this.getOperator() + " ..." } - - override predicate child(string label, AstNode::Range child) { - Operation::Range.super.child(label, child) - or - label = "getOperand" and child = getOperand() - } - } -} - -module UnaryLogicalOperation { - abstract class Range extends UnaryOperation::Range { } -} - -module UnaryArithmeticOperation { - abstract class Range extends UnaryOperation::Range { } -} - -module UnaryBitwiseOperation { - abstract class Range extends UnaryOperation::Range { } -} - -module NotExpr { - class DbUnion = @unary_bang or @unary_not; - - class Range extends UnaryLogicalOperation::Range, DbUnion { } -} - -module UnaryPlusExpr { - class Range extends UnaryArithmeticOperation::Range, @unary_plus { } -} - -module UnaryMinusExpr { - class Range extends UnaryArithmeticOperation::Range, @unary_minus { } -} - -module ComplementExpr { - class Range extends UnaryBitwiseOperation::Range, @unary_tilde { } -} - -module DefinedExpr { - class Range extends UnaryOperation::Range, @unary_definedquestion { } -} - -module BinaryOperation { - abstract class Range extends Operation::Range, @binary { - final override Generated::Binary generated; - - final override string getOperator() { result = generated.getOperator() } - - final Expr getLeftOperand() { result = generated.getLeft() } - - final Stmt getRightOperand() { result = generated.getRight() } - - final override Stmt getAnOperand() { - result = this.getLeftOperand() or result = this.getRightOperand() - } - - override string toString() { result = "... " + this.getOperator() + " ..." } - - override predicate child(string label, AstNode::Range child) { - Operation::Range.super.child(label, child) - or - label = "getLeftOperand" and child = getLeftOperand() - or - label = "getRightOperand" and child = getRightOperand() - } - } -} - -module BinaryArithmeticOperation { - abstract class Range extends BinaryOperation::Range { } -} - -module BinaryLogicalOperation { - abstract class Range extends BinaryOperation::Range { } -} - -module BinaryBitwiseOperation { - abstract class Range extends BinaryOperation::Range { } -} - -module ComparisonOperation { - abstract class Range extends BinaryOperation::Range, @binary { } -} - -module AddExpr { - class Range extends BinaryArithmeticOperation::Range, @binary_plus { } -} - -module SubExpr { - class Range extends BinaryArithmeticOperation::Range, @binary_minus { } -} - -module MulExpr { - class Range extends BinaryArithmeticOperation::Range, @binary_star { } -} - -module DivExpr { - class Range extends BinaryArithmeticOperation::Range, @binary_slash { } -} - -module ModuloExpr { - class Range extends BinaryArithmeticOperation::Range, @binary_percent { } -} - -module ExponentExpr { - class Range extends BinaryArithmeticOperation::Range, @binary_starstar { } -} - -module LogicalAndExpr { - class DbUnion = @binary_and or @binary_ampersandampersand; - - class Range extends BinaryLogicalOperation::Range, DbUnion { } -} - -module LogicalOrExpr { - class DbUnion = @binary_or or @binary_pipepipe; - - class Range extends BinaryLogicalOperation::Range, DbUnion { } -} - -module LShiftExpr { - class Range extends BinaryBitwiseOperation::Range, @binary_langlelangle { } -} - -module RShiftExpr { - class Range extends BinaryBitwiseOperation::Range, @binary_ranglerangle { } -} - -module BitwiseAndExpr { - class Range extends BinaryBitwiseOperation::Range, @binary_ampersand { } -} - -module BitwiseOrExpr { - class Range extends BinaryBitwiseOperation::Range, @binary_pipe { } -} - -module BitwiseXorExpr { - class Range extends BinaryBitwiseOperation::Range, @binary_caret { } -} - -module EqualityOperation { - abstract class Range extends ComparisonOperation::Range { } -} - -module EqExpr { - class Range extends EqualityOperation::Range, @binary_equalequal { } -} - -module NEExpr { - class Range extends EqualityOperation::Range, @binary_bangequal { } -} - -module CaseEqExpr { - class Range extends EqualityOperation::Range, @binary_equalequalequal { } -} - -module RelationalOperation { - abstract class Range extends ComparisonOperation::Range { - abstract Expr getGreaterOperand(); - - abstract Expr getLesserOperand(); - - override predicate child(string label, AstNode::Range child) { - ComparisonOperation::Range.super.child(label, child) - or - label = "getGreaterOperand" and child = getGreaterOperand() - or - label = "getLesserOperand" and child = getLesserOperand() - } - } -} - -module GTExpr { - class Range extends RelationalOperation::Range, @binary_rangle { - final override Expr getGreaterOperand() { result = this.getLeftOperand() } - - final override Expr getLesserOperand() { result = this.getRightOperand() } - } -} - -module GEExpr { - class Range extends RelationalOperation::Range, @binary_rangleequal { - final override Expr getGreaterOperand() { result = this.getLeftOperand() } - - final override Expr getLesserOperand() { result = this.getRightOperand() } - } -} - -module LTExpr { - class Range extends RelationalOperation::Range, @binary_langle { - final override Expr getGreaterOperand() { result = this.getRightOperand() } - - final override Expr getLesserOperand() { result = this.getLeftOperand() } - } -} - -module LEExpr { - class Range extends RelationalOperation::Range, @binary_langleequal { - final override Expr getGreaterOperand() { result = this.getRightOperand() } - - final override Expr getLesserOperand() { result = this.getLeftOperand() } - } -} - -module SpaceshipExpr { - class Range extends BinaryOperation::Range, @binary_langleequalrangle { } -} - -module RegexMatchExpr { - class Range extends BinaryOperation::Range, @binary_equaltilde { } -} - -module NoRegexMatchExpr { - class Range extends BinaryOperation::Range, @binary_bangtilde { } -} - -module Assignment { - abstract class Range extends Operation::Range { - abstract Pattern getLeftOperand(); - - abstract Expr getRightOperand(); - - final override Expr getAnOperand() { - result = this.getLeftOperand() or result = this.getRightOperand() - } - - override string toString() { result = "... " + this.getOperator() + " ..." } - - override predicate child(string label, AstNode::Range child) { - Operation::Range.super.child(label, child) - or - label = "getLeftOperand" and child = getLeftOperand() - or - label = "getRightOperand" and child = getRightOperand() - } - } -} - -module AssignExpr { - class Range extends Assignment::Range, @assignment { - final override Generated::Assignment generated; - - final override Pattern getLeftOperand() { result = generated.getLeft() } - - final override Expr getRightOperand() { result = generated.getRight() } - - final override string getOperator() { result = "=" } - } -} - -module AssignOperation { - abstract class Range extends Assignment::Range, @operator_assignment { - final override Generated::OperatorAssignment generated; - - final override string getOperator() { result = generated.getOperator() } - - final override LhsExpr getLeftOperand() { result = generated.getLeft() } - - final override Expr getRightOperand() { result = generated.getRight() } - } -} - -module AssignArithmeticOperation { - abstract class Range extends AssignOperation::Range { } -} - -module AssignLogicalOperation { - abstract class Range extends AssignOperation::Range { } -} - -module AssignBitwiseOperation { - abstract class Range extends AssignOperation::Range { } -} - -module AssignAddExpr { - class Range extends AssignArithmeticOperation::Range, @operator_assignment_plusequal { } -} - -module AssignSubExpr { - class Range extends AssignArithmeticOperation::Range, @operator_assignment_minusequal { } -} - -module AssignMulExpr { - class Range extends AssignArithmeticOperation::Range, @operator_assignment_starequal { } -} - -module AssignDivExpr { - class Range extends AssignArithmeticOperation::Range, @operator_assignment_slashequal { } -} - -module AssignExponentExpr { - class Range extends AssignArithmeticOperation::Range, @operator_assignment_starstarequal { } -} - -module AssignModuloExpr { - class Range extends AssignArithmeticOperation::Range, @operator_assignment_percentequal { } -} - -module AssignLogicalAndExpr { - class Range extends AssignLogicalOperation::Range, @operator_assignment_ampersandampersandequal { - } -} - -module AssignLogicalOrExpr { - class Range extends AssignLogicalOperation::Range, @operator_assignment_pipepipeequal { } -} - -module AssignLShiftExpr { - class Range extends AssignBitwiseOperation::Range, @operator_assignment_langlelangleequal { } -} - -module AssignRShiftExpr { - class Range extends AssignBitwiseOperation::Range, @operator_assignment_ranglerangleequal { } -} - -module AssignBitwiseAndExpr { - class Range extends AssignBitwiseOperation::Range, @operator_assignment_ampersandequal { } -} - -module AssignBitwiseOrExpr { - class Range extends AssignBitwiseOperation::Range, @operator_assignment_pipeequal { } -} - -module AssignBitwiseXorExpr { - class Range extends AssignBitwiseOperation::Range, @operator_assignment_caretequal { } -} diff --git a/ql/src/codeql_ruby/ast/internal/Parameter.qll b/ql/src/codeql_ruby/ast/internal/Parameter.qll index 6595f38be33..69f8068635e 100644 --- a/ql/src/codeql_ruby/ast/internal/Parameter.qll +++ b/ql/src/codeql_ruby/ast/internal/Parameter.qll @@ -1,14 +1,9 @@ private import codeql_ruby.AST +private import AST private import TreeSitter -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.Variable -private import codeql_ruby.ast.internal.Method -private import codeql_ruby.ast.internal.Pattern -private import codeql.Locations module Parameter { - class Range extends AstNode::Range { + class Range extends Generated::AstNode { private int pos; Range() { @@ -19,143 +14,6 @@ module Parameter { this = any(Generated::LambdaParameters lp).getChild(pos) } - final int getPosition() { result = pos } - - LocalVariable getAVariable() { none() } - - override string toString() { none() } - } -} - -module NamedParameter { - abstract class Range extends Parameter::Range { - abstract string getName(); - - abstract LocalVariable getVariable(); - - override LocalVariable getAVariable() { result = this.getVariable() } - } -} - -module SimpleParameter { - class Range extends NamedParameter::Range, PatternParameter::Range, VariablePattern::Range { - final override string getName() { result = this.getVariableName() } - - final override LocalVariable getVariable() { result = TLocalVariable(_, _, this) } - - final override LocalVariable getAVariable() { result = this.getVariable() } - - final override string toString() { result = this.getName() } - } -} - -module PatternParameter { - class Range extends Parameter::Range, Pattern::Range { - override LocalVariable getAVariable() { result = this.(Pattern::Range).getAVariable() } - - override string toString() { none() } - } -} - -module TuplePatternParameter { - class Range extends PatternParameter::Range, TuplePattern::Range { - override LocalVariable getAVariable() { result = TuplePattern::Range.super.getAVariable() } - - override string toString() { result = TuplePattern::Range.super.toString() } - - override predicate child(string label, AstNode::Range child) { - PatternParameter::Range.super.child(label, child) or - TuplePattern::Range.super.child(label, child) - } - } -} - -module BlockParameter { - class Range extends NamedParameter::Range, @block_parameter { - final override Generated::BlockParameter generated; - - final override string getName() { result = generated.getName().getValue() } - - final override LocalVariable getVariable() { - result = TLocalVariable(_, _, generated.getName()) - } - - final override string toString() { result = "&" + this.getName() } - } -} - -module HashSplatParameter { - class Range extends NamedParameter::Range, @hash_splat_parameter { - final override Generated::HashSplatParameter generated; - - final override LocalVariable getVariable() { - result = TLocalVariable(_, _, generated.getName()) - } - - final override string toString() { result = "**" + this.getName() } - - final override string getName() { result = generated.getName().getValue() } - } -} - -module KeywordParameter { - class Range extends NamedParameter::Range, @keyword_parameter { - final override Generated::KeywordParameter generated; - - final override LocalVariable getVariable() { - result = TLocalVariable(_, _, generated.getName()) - } - - final Expr::Range getDefaultValue() { result = generated.getValue() } - - final override string toString() { result = this.getName() } - - final override string getName() { result = generated.getName().getValue() } - - final override Location getLocation() { result = generated.getName().getLocation() } - - final override predicate child(string label, AstNode::Range child) { - NamedParameter::Range.super.child(label, child) - or - label = "getDefaultValue" and child = getDefaultValue() - } - } -} - -module OptionalParameter { - class Range extends NamedParameter::Range, @optional_parameter { - final override Generated::OptionalParameter generated; - - final override LocalVariable getVariable() { - result = TLocalVariable(_, _, generated.getName()) - } - - final Expr::Range getDefaultValue() { result = generated.getValue() } - - final override string toString() { result = this.getName() } - - final override string getName() { result = generated.getName().getValue() } - - final override Location getLocation() { result = generated.getName().getLocation() } - - final override predicate child(string label, AstNode::Range child) { - NamedParameter::Range.super.child(label, child) - or - label = "getDefaultValue" and child = getDefaultValue() - } - } -} - -module SplatParameter { - class Range extends NamedParameter::Range, @splat_parameter { - final override Generated::SplatParameter generated; - - final override LocalVariable getVariable() { - result = TLocalVariable(_, _, generated.getName()) - } - - final override string toString() { result = "*" + this.getName() } - - final override string getName() { result = generated.getName().getValue() } + int getPosition() { result = pos } } } diff --git a/ql/src/codeql_ruby/ast/internal/Pattern.qll b/ql/src/codeql_ruby/ast/internal/Pattern.qll deleted file mode 100644 index 275e483107a..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Pattern.qll +++ /dev/null @@ -1,112 +0,0 @@ -private import codeql_ruby.AST -private import TreeSitter -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.Variable -private import codeql_ruby.ast.internal.Method -private import codeql.Locations - -/** - * Holds if `n` is in the left-hand-side of an explicit assignment `assignment`. - */ -predicate explicitAssignmentNode(Generated::AstNode n, Generated::AstNode assignment) { - n = assignment.(Generated::Assignment).getLeft() - or - n = assignment.(Generated::OperatorAssignment).getLeft() - or - exists(Generated::AstNode parent | - parent = n.getParent() and - explicitAssignmentNode(parent, assignment) - | - parent instanceof Generated::DestructuredLeftAssignment - or - parent instanceof Generated::LeftAssignmentList - or - parent instanceof Generated::RestAssignment - ) -} - -/** Holds if `n` is inside an implicit assignment. */ -predicate implicitAssignmentNode(Generated::AstNode n) { - n = any(Generated::ExceptionVariable ev).getChild() - or - n = any(Generated::For for).getPattern() - or - implicitAssignmentNode(n.getParent()) -} - -/** Holds if `n` is inside a parameter. */ -predicate implicitParameterAssignmentNode(Generated::AstNode n, Callable::Range c) { - n = c.getParameter(_) - or - implicitParameterAssignmentNode(n.getParent().(Generated::DestructuredParameter), c) -} - -module Pattern { - abstract class Range extends AstNode::Range { - cached - Range() { - explicitAssignmentNode(this, _) - or - implicitAssignmentNode(this) - or - implicitParameterAssignmentNode(this, _) - } - - Variable getAVariable() { none() } - - override string toString() { none() } - } -} - -module LhsExpr { - abstract class Range extends Pattern::Range, Expr::Range { } -} - -module VariablePattern { - class VariableToken = - @token_identifier or @token_instance_variable or @token_class_variable or @token_global_variable; - - class Range extends LhsExpr::Range, VariableToken { - override Generated::Token generated; - - string getVariableName() { result = generated.getValue() } - - override Variable getAVariable() { access(this, result) } - - override string toString() { result = this.getVariableName() } - } -} - -module TuplePattern { - private class Range_ = - @destructured_parameter or @destructured_left_assignment or @left_assignment_list; - - class Range extends Pattern::Range, Range_ { - Pattern::Range getElement(int i) { - exists(Generated::AstNode c | c = getChild(i) | - result = c.(Generated::RestAssignment).getChild() - or - not c instanceof Generated::RestAssignment and result = c - ) - } - - private Generated::AstNode getChild(int i) { - result = this.(Generated::DestructuredParameter).getChild(i) - or - result = this.(Generated::DestructuredLeftAssignment).getChild(i) - or - result = this.(Generated::LeftAssignmentList).getChild(i) - } - - int getRestIndex() { result = unique(int i | getChild(i) instanceof Generated::RestAssignment) } - - override Variable getAVariable() { result = this.getElement(_).getAVariable() } - - override string toString() { result = "(..., ...)" } - - override predicate child(string label, AstNode::Range child) { - label = "getElement" and child = getElement(_) - } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Scope.qll b/ql/src/codeql_ruby/ast/internal/Scope.qll new file mode 100644 index 00000000000..7c76ef8affa --- /dev/null +++ b/ql/src/codeql_ruby/ast/internal/Scope.qll @@ -0,0 +1,85 @@ +private import TreeSitter +private import codeql_ruby.ast.Scope +private import codeql_ruby.ast.internal.AST +private import codeql_ruby.ast.internal.Parameter + +class TScopeType = TMethodBase or TModuleLike or TBlockLike; + +private class TBlockLike = TDoBlock or TLambda or TBlock or TEndBlock; + +private class TModuleLike = TToplevel or TModule or TClass or TSingletonClass; + +module Scope { + class TypeRange = Callable::TypeRange or ModuleBase::TypeRange or @end_block; + + class Range extends Generated::AstNode, TypeRange { + Range() { not this = any(Generated::Lambda l).getBody() } + + ModuleBase::Range getEnclosingModule() { + result = this + or + not this instanceof ModuleBase::Range and result = this.getOuterScope().getEnclosingModule() + } + + MethodBase::Range getEnclosingMethod() { + result = this + or + not this instanceof MethodBase::Range and + not this instanceof ModuleBase::Range and + result = this.getOuterScope().getEnclosingMethod() + } + + Range getOuterScope() { result = scopeOf(this) } + } +} + +module MethodBase { + class TypeRange = @method or @singleton_method; + + class Range extends Scope::Range, TypeRange { } +} + +module Callable { + class TypeRange = MethodBase::TypeRange or @do_block or @lambda or @block; + + class Range extends Scope::Range, TypeRange { + Parameter::Range getParameter(int i) { + result = this.(Generated::Method).getParameters().getChild(i) or + result = this.(Generated::SingletonMethod).getParameters().getChild(i) or + result = this.(Generated::DoBlock).getParameters().getChild(i) or + result = this.(Generated::Lambda).getParameters().getChild(i) or + result = this.(Generated::Block).getParameters().getChild(i) + } + } +} + +module ModuleBase { + class TypeRange = @program or @module or @class or @singleton_class; + + class Range extends Scope::Range, TypeRange { } +} + +private Generated::AstNode parentOf(Generated::AstNode n) { + exists(Generated::AstNode parent | parent = n.getParent() | + if + n = + [ + parent.(Generated::Module).getName(), parent.(Generated::Class).getName(), + parent.(Generated::Class).getSuperclass(), parent.(Generated::SingletonClass).getValue(), + parent.(Generated::Method).getName(), parent.(Generated::SingletonMethod).getName(), + parent.(Generated::SingletonMethod).getObject() + ] + then result = parent.getParent() + else result = parent + ) +} + +/** Gets the enclosing scope of a node */ +cached +Scope::Range scopeOf(Generated::AstNode n) { + exists(Generated::AstNode p | p = parentOf(n) | + p = result + or + not p instanceof Scope::Range and result = scopeOf(p) + ) +} diff --git a/ql/src/codeql_ruby/ast/internal/Statement.qll b/ql/src/codeql_ruby/ast/internal/Statement.qll deleted file mode 100644 index 08d5d2a19bc..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Statement.qll +++ /dev/null @@ -1,146 +0,0 @@ -private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.TreeSitter - -module Stmt { - abstract class Range extends AstNode::Range { } -} - -module EmptyStmt { - class Range extends Stmt::Range, @token_empty_statement { - final override Generated::EmptyStatement generated; - - final override string toString() { result = ";" } - - override predicate child(string label, AstNode::Range child) { none() } - } -} - -module Begin { - class Range extends BodyStatement::Range, @begin { - final override Generated::Begin generated; - - final override Generated::AstNode getChild(int n) { result = generated.getChild(n) } - - final override string toString() { result = "begin ... " } - } -} - -module BeginBlock { - class Range extends StmtSequence::Range, @begin_block { - final override Generated::BeginBlock generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - - final override string toString() { result = "BEGIN { ... }" } - } -} - -module EndBlock { - class Range extends StmtSequence::Range, @end_block { - final override Generated::EndBlock generated; - - final override Stmt getStmt(int n) { result = generated.getChild(n) } - - final override string toString() { result = "END { ... }" } - } -} - -module UndefStmt { - class Range extends Stmt::Range, @undef { - final override Generated::Undef generated; - - final MethodName getMethodName(int n) { result = generated.getChild(n) } - - final override string toString() { result = "undef ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getMethodName" and child = getMethodName(_) - } - } -} - -module AliasStmt { - class Range extends Stmt::Range, @alias { - final override Generated::Alias generated; - - final MethodName getNewName() { result = generated.getName() } - - final MethodName getOldName() { result = generated.getAlias() } - - final override string toString() { result = "alias ..." } - - override predicate child(string label, AstNode::Range child) { - label = "getNewName" and child = getNewName() - or - label = "getOldName" and child = getOldName() - } - } -} - -module ReturningStmt { - abstract class Range extends Stmt::Range { - abstract Generated::ArgumentList getArgumentList(); - - final Expr getValue() { - exists(Generated::ArgumentList a, int c | - a = this.getArgumentList() and c = count(a.getChild(_)) - | - result = a.getChild(0) and c = 1 - or - result = a and c > 1 - ) - } - - override predicate child(string label, AstNode::Range child) { - label = "getValue" and child = getValue() - } - } -} - -module ReturnStmt { - class Range extends ReturningStmt::Range, @return { - final override Generated::Return generated; - - final override string toString() { result = "return" } - - final override Generated::ArgumentList getArgumentList() { result = generated.getChild() } - } -} - -module BreakStmt { - class Range extends ReturningStmt::Range, @break { - final override Generated::Break generated; - - final override string toString() { result = "break" } - - final override Generated::ArgumentList getArgumentList() { result = generated.getChild() } - } -} - -module NextStmt { - class Range extends ReturningStmt::Range, @next { - final override Generated::Next generated; - - final override string toString() { result = "next" } - - final override Generated::ArgumentList getArgumentList() { result = generated.getChild() } - } -} - -module RedoStmt { - class Range extends Stmt::Range, @redo { - final override Generated::Redo generated; - - final override string toString() { result = "redo" } - } -} - -module RetryStmt { - class Range extends Stmt::Range, @retry { - final override Generated::Retry generated; - - final override string toString() { result = "retry" } - } -} diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index ee2e11a7d91..fcd1e428862 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -2,83 +2,74 @@ private import TreeSitter private import codeql.Locations private import codeql_ruby.AST private import codeql_ruby.ast.internal.AST -private import codeql_ruby.ast.internal.Expr -private import codeql_ruby.ast.internal.Method -private import codeql_ruby.ast.internal.Module -private import codeql_ruby.ast.internal.Pattern private import codeql_ruby.ast.internal.Parameter +private import codeql_ruby.ast.internal.Scope -Generated::AstNode parentOf(Generated::AstNode n) { - exists(Generated::AstNode parent | parent = n.getParent() | - if - n = - [ - parent.(Generated::Module).getName(), parent.(Generated::Class).getName(), - parent.(Generated::Class).getSuperclass(), parent.(Generated::SingletonClass).getValue(), - parent.(Generated::Method).getName(), parent.(Generated::SingletonMethod).getName(), - parent.(Generated::SingletonMethod).getObject() - ] - then result = parent.getParent() - else result = parent +/** + * Holds if `n` is in the left-hand-side of an explicit assignment `assignment`. + */ +predicate explicitAssignmentNode(Generated::AstNode n, Generated::AstNode assignment) { + n = assignment.(Generated::Assignment).getLeft() + or + n = assignment.(Generated::OperatorAssignment).getLeft() + or + exists(Generated::AstNode parent | + parent = n.getParent() and + explicitAssignmentNode(parent, assignment) + | + parent instanceof Generated::DestructuredLeftAssignment + or + parent instanceof Generated::LeftAssignmentList + or + parent instanceof Generated::RestAssignment ) } -private Generated::AstNode parentOfNoScope(Generated::AstNode n) { - result = parentOf(n) and - not n = any(VariableScope::Range s).getScopeElement() +/** Holds if `n` is inside an implicit assignment. */ +predicate implicitAssignmentNode(Generated::AstNode n) { + n = any(Generated::ExceptionVariable ev).getChild() + or + n = any(Generated::For for).getPattern() + or + implicitAssignmentNode(n.getParent()) +} + +/** Holds if `n` is inside a parameter. */ +predicate implicitParameterAssignmentNode(Generated::AstNode n, Callable::Range c) { + n = c.getParameter(_) + or + implicitParameterAssignmentNode(n.getParent().(Generated::DestructuredParameter), c) } private predicate instanceVariableAccess( - Generated::InstanceVariable var, string name, VariableScope scope, boolean instance + Generated::InstanceVariable var, string name, Scope::Range scope, boolean instance ) { name = var.getValue() and scope = enclosingModuleOrClass(var) and if hasEnclosingMethod(var) then instance = true else instance = false } -private predicate classVariableAccess(Generated::ClassVariable var, string name, VariableScope scope) { +private predicate classVariableAccess(Generated::ClassVariable var, string name, Scope::Range scope) { name = var.getValue() and scope = enclosingModuleOrClass(var) } -predicate hasEnclosingMethod(Generated::AstNode node) { - exists(Callable::Range method | - parentCallableScope*(enclosingScope(node)) = TCallableScope(method) - | - method instanceof Method::Range or - method instanceof SingletonMethod::Range - ) +private predicate hasEnclosingMethod(Generated::AstNode node) { + exists(Scope::Range s | scopeOf(node) = s and exists(s.getEnclosingMethod())) } -private TCallableScope parentCallableScope(TCallableScope scope) { - exists(Callable::Range c | - scope = TCallableScope(c) and - not c instanceof Method::Range and - not c instanceof SingletonMethod::Range - | - result = scope.(VariableScope::Range).getOuterScope() - ) +private ModuleBase::Range enclosingModuleOrClass(Generated::AstNode node) { + exists(Scope::Range s | scopeOf(node) = s and result = s.getEnclosingModule()) } -private VariableScope::Range parentScope(VariableScope::Range scope) { - not scope instanceof ModuleOrClassScope and - result = scope.getOuterScope() -} - -private ModuleOrClassScope enclosingModuleOrClass(Generated::AstNode node) { - result = parentScope*(enclosingScope(node)) -} - -private predicate parameterAssignment( - CallableScope::Range scope, string name, Generated::Identifier i -) { - implicitParameterAssignmentNode(i, scope.getScopeElement()) and +private predicate parameterAssignment(Callable::Range scope, string name, Generated::Identifier i) { + implicitParameterAssignmentNode(i, scope) and name = i.getValue() } /** Holds if `scope` defines `name` in its parameter declaration at `i`. */ private predicate scopeDefinesParameterVariable( - CallableScope::Range scope, string name, Generated::Identifier i + Callable::Range scope, string name, Generated::Identifier i ) { // In case of overlapping parameter names (e.g. `_`), only the first // parameter will give rise to a variable @@ -90,8 +81,8 @@ private predicate scopeDefinesParameterVariable( ) or exists(Parameter::Range p | - p = scope.getScopeElement().(Callable::Range).getParameter(_) and - name = p.(NamedParameter::Range).getName() + p = scope.getParameter(_) and + name = i.getValue() | i = p.(Generated::BlockParameter).getName() or i = p.(Generated::HashSplatParameter).getName() or @@ -102,10 +93,10 @@ private predicate scopeDefinesParameterVariable( } /** Holds if `name` is assigned in `scope` at `i`. */ -private predicate scopeAssigns(VariableScope scope, string name, Generated::Identifier i) { +private predicate scopeAssigns(Scope::Range scope, string name, Generated::Identifier i) { (explicitAssignmentNode(i, _) or implicitAssignmentNode(i)) and name = i.getValue() and - scope = enclosingScope(i) + scope = scopeOf(i) } /** Holds if location `one` starts strictly before location `two` */ @@ -116,45 +107,12 @@ private predicate strictlyBefore(Location one, Location two) { one.getStartLine() = two.getStartLine() and one.getStartColumn() < two.getStartColumn() } -private Generated::AstNode getNodeForIdentifier(Generated::Identifier id) { - exists(Generated::AstNode parent | parent = id.getParent() | - if - parent instanceof Generated::BlockParameter - or - parent instanceof Generated::SplatParameter - or - parent instanceof Generated::HashSplatParameter - or - parent instanceof Generated::KeywordParameter - or - parent instanceof Generated::OptionalParameter - then result = parent - else result = id - ) -} - cached private module Cached { - /** Gets the enclosing scope for `node`. */ - cached - VariableScope::Range enclosingScope(Generated::AstNode node) { - result.getScopeElement() = parentOfNoScope*(parentOf(node)) - } - - cached - newtype TScope = - TGlobalScope() or - TTopLevelScope(Generated::Program node) or - TModuleScope(Generated::Module node) or - TClassScope(Generated::AstNode cls) { - cls instanceof Generated::Class or cls instanceof Generated::SingletonClass - } or - TCallableScope(Callable::Range c) - cached newtype TVariable = TGlobalVariable(string name) { name = any(Generated::GlobalVariable var).getValue() } or - TClassVariable(VariableScope scope, string name, Generated::AstNode decl) { + TClassVariable(Scope::Range scope, string name, Generated::AstNode decl) { decl = min(Generated::ClassVariable other | classVariableAccess(other, name, scope) @@ -162,7 +120,7 @@ private module Cached { other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() ) } or - TInstanceVariable(VariableScope scope, string name, boolean instance, Generated::AstNode decl) { + TInstanceVariable(Scope::Range scope, string name, boolean instance, Generated::AstNode decl) { decl = min(Generated::InstanceVariable other | instanceVariableAccess(other, name, scope, instance) @@ -170,7 +128,7 @@ private module Cached { other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() ) } or - TLocalVariable(VariableScope::Range scope, string name, Generated::Identifier i) { + TLocalVariable(Scope::Range scope, string name, Generated::Identifier i) { scopeDefinesParameterVariable(scope, name, i) or i = @@ -180,7 +138,7 @@ private module Cached { other order by other.getLocation().getStartLine(), other.getLocation().getStartColumn() ) and not scopeDefinesParameterVariable(scope, name, _) and - not scope.inherits(name, _) + not inherits(scope, name, _) } // Db types that can be vcalls @@ -330,10 +288,12 @@ private module Cached { } cached - predicate access(Generated::Identifier access, Variable variable) { - exists(string name | name = access.getValue() | - variable.getDeclaringScope() = enclosingScope(access) and + predicate access(Generated::Identifier access, Variable::Range variable) { + exists(string name | variable.getName() = name and + name = access.getValue() + | + variable.getDeclaringScope() = scopeOf(access) and not strictlyBefore(access.getLocation(), variable.getLocation()) and // In case of overlapping parameter names, later parameters should not // be considered accesses to the first parameter @@ -341,15 +301,20 @@ private module Cached { then scopeDefinesParameterVariable(_, _, access) else any() or - exists(VariableScope declScope | - variable = declScope.getVariable(name) and - enclosingScope(access).inherits(name, declScope) + exists(Scope::Range declScope | + variable.getDeclaringScope() = declScope and + inherits(scopeOf(access), name, declScope) ) ) } private class Access extends Generated::Token { - Access() { access(this, _) or this instanceof Generated::GlobalVariable } + Access() { + access(this, _) or + this instanceof Generated::GlobalVariable or + this instanceof Generated::InstanceVariable or + this instanceof Generated::ClassVariable + } } cached @@ -365,13 +330,13 @@ private module Cached { } cached - predicate isCapturedAccess(LocalVariableAccess::Range access) { - access.getVariable().getDeclaringScope() != enclosingScope(access) + predicate isCapturedAccess(LocalVariableAccess access) { + toGenerated(access.getVariable().getDeclaringScope()) != scopeOf(toGenerated(access)) } cached predicate instanceVariableAccess(Generated::InstanceVariable var, InstanceVariable v) { - exists(string name, VariableScope scope, boolean instance | + exists(string name, Scope::Range scope, boolean instance | v = TInstanceVariable(scope, name, instance, _) and instanceVariableAccess(var, name, scope, instance) ) @@ -379,7 +344,7 @@ private module Cached { cached predicate classVariableAccess(Generated::ClassVariable var, ClassVariable variable) { - exists(VariableScope scope, string name | + exists(Scope::Range scope, string name | variable = TClassVariable(scope, name, _) and classVariableAccess(var, name, scope) ) @@ -388,125 +353,23 @@ private module Cached { import Cached -module VariableScope { - abstract class Range extends TScope { - abstract string toString(); - - abstract Generated::AstNode getScopeElement(); - - abstract predicate isCapturing(); - - VariableScope::Range getOuterScope() { result = enclosingScope(this.getScopeElement()) } - - /** Holds if this scope inherits `name` from an outer scope `outer`. */ - predicate inherits(string name, VariableScope::Range outer) { - this.isCapturing() and - not scopeDefinesParameterVariable(this, name, _) and - ( - outer = this.getOuterScope() and - ( - scopeDefinesParameterVariable(outer, name, _) - or - exists(Generated::Identifier i | - scopeAssigns(outer, name, i) and - strictlyBefore(i.getLocation(), this.getLocation()) - ) - ) - or - this.getOuterScope().inherits(name, outer) +/** Holds if this scope inherits `name` from an outer scope `outer`. */ +private predicate inherits(Scope::Range scope, string name, Scope::Range outer) { + (scope instanceof Generated::Block or scope instanceof Generated::DoBlock) and + not scopeDefinesParameterVariable(scope, name, _) and + ( + outer = scope.getOuterScope() and + ( + scopeDefinesParameterVariable(outer, name, _) + or + exists(Generated::Identifier i | + scopeAssigns(outer, name, i) and + strictlyBefore(i.getLocation(), scope.getLocation()) ) - } - - final Location getLocation() { result = getScopeElement().getLocation() } - } -} - -module GlobalScope { - class Range extends VariableScope::Range, TGlobalScope { - override string toString() { result = "global scope" } - - override Generated::AstNode getScopeElement() { none() } - - override predicate isCapturing() { none() } - } -} - -module TopLevelScope { - class Range extends VariableScope::Range, TTopLevelScope { - Generated::Program program; - - Range() { TTopLevelScope(program) = this } - - override string toString() { - result = "top-level scope for " + program.getLocation().getFile().getBaseName() - } - - override Generated::AstNode getScopeElement() { result = program } - - override predicate isCapturing() { none() } - } -} - -module ModuleScope { - class Range extends VariableScope::Range, TModuleScope { - Generated::Module mdl; - - Range() { TModuleScope(mdl) = this } - - override string toString() { result = "module scope for " + mdl.(Module::Range).getName() } - - override Generated::AstNode getScopeElement() { result = mdl } - - override predicate isCapturing() { none() } - } -} - -module ClassScope { - class Range extends VariableScope::Range, TClassScope { - Generated::AstNode cls; - - Range() { TClassScope(cls) = this } - - override string toString() { - result = "class scope for " + cls.(Class::Range).getName() - or - cls instanceof Generated::SingletonClass and result = "class scope" - } - - override Generated::AstNode getScopeElement() { result = cls } - - override predicate isCapturing() { none() } - } -} - -module CallableScope { - class Range extends VariableScope::Range, TCallableScope { - private Generated::AstNode c; - - Range() { this = TCallableScope(c) } - - override string toString() { - result = "method scope for " + c.(SingletonMethod).getName() - or - result = "method scope for " + c.(Method).getName() - or - c instanceof Lambda and - result = "lambda scope" - or - c instanceof Block and - result = "block scope" - } - - override Generated::AstNode getScopeElement() { TCallableScope(result) = this } - - override predicate isCapturing() { - c instanceof Generated::Lambda - or - c instanceof Generated::Block - or - c instanceof Generated::DoBlock - } - } + ) + or + inherits(scope.getOuterScope(), name, outer) + ) } module Variable { @@ -517,13 +380,13 @@ module Variable { abstract Location getLocation(); - abstract VariableScope getDeclaringScope(); + abstract Scope::Range getDeclaringScope(); } } module LocalVariable { class Range extends Variable::Range, TLocalVariable { - private VariableScope scope; + private Scope::Range scope; private string name; private Generated::Identifier i; @@ -533,9 +396,9 @@ module LocalVariable { final override Location getLocation() { result = i.getLocation() } - final override VariableScope getDeclaringScope() { result = scope } + final override Scope::Range getDeclaringScope() { result = scope } - final VariableAccess getDefiningAccess() { result = getNodeForIdentifier(i) } + final VariableAccess getDefiningAccess() { toGenerated(result) = i } } } @@ -549,15 +412,13 @@ module GlobalVariable { final override Location getLocation() { none() } - final override VariableScope getDeclaringScope() { result = TGlobalScope() } + final override Scope::Range getDeclaringScope() { none() } } } -private class ModuleOrClassScope = TClassScope or TModuleScope or TTopLevelScope; - module InstanceVariable { class Range extends Variable::Range, TInstanceVariable { - private ModuleOrClassScope scope; + private ModuleBase::Range scope; private boolean instance; private string name; private Generated::AstNode decl; @@ -570,13 +431,13 @@ module InstanceVariable { final override Location getLocation() { result = decl.getLocation() } - final override VariableScope getDeclaringScope() { result = scope } + final override Scope::Range getDeclaringScope() { result = scope } } } module ClassVariable { class Range extends Variable::Range, TClassVariable { - private ModuleOrClassScope scope; + private ModuleBase::Range scope; private string name; private Generated::AstNode decl; @@ -586,90 +447,33 @@ module ClassVariable { final override Location getLocation() { result = decl.getLocation() } - final override VariableScope getDeclaringScope() { result = scope } - } -} - -module VariableAccess { - abstract class Range extends Expr::Range { - abstract Variable getVariable(); - - final predicate isExplicitWrite(AstNode assignment) { - exists(Generated::Identifier i | this = getNodeForIdentifier(i) | - explicitWriteAccess(i, assignment) - ) - or - not this = getNodeForIdentifier(_) and explicitWriteAccess(this, assignment) - } - - final predicate isImplicitWrite() { - exists(Generated::Identifier i | this = getNodeForIdentifier(i) | implicitWriteAccess(i)) - or - not this = getNodeForIdentifier(_) and implicitWriteAccess(this) - } + final override Scope::Range getDeclaringScope() { result = scope } } } module LocalVariableAccess { - class LocalVariableRange = - @token_identifier or @splat_parameter or @keyword_parameter or @optional_parameter or - @hash_splat_parameter or @block_parameter; - - class Range extends VariableAccess::Range, LocalVariableRange { - LocalVariable variable; - - Range() { - exists(Generated::Identifier id | - this = getNodeForIdentifier(id) and - access(id, variable) and - ( - explicitWriteAccess(id, _) - or - implicitWriteAccess(id) - or - vcall(id) - ) - ) - } - - override string toString() { result = generated.(Generated::Identifier).getValue() } - - final override LocalVariable getVariable() { result = variable } + predicate range(Generated::Identifier id, LocalVariable v) { + access(id, v) and + ( + explicitWriteAccess(id, _) + or + implicitWriteAccess(id) + or + vcall(id) + ) } } module GlobalVariableAccess { - class Range extends VariableAccess::Range, @token_global_variable { - GlobalVariable variable; - - Range() { this.(Generated::GlobalVariable).getValue() = variable.getName() } - - final override GlobalVariable getVariable() { result = variable } - - override string toString() { result = generated.(Generated::GlobalVariable).getValue() } - } + predicate range(Generated::GlobalVariable n, GlobalVariable v) { n.getValue() = v.getName() } } module InstanceVariableAccess { - class Range extends VariableAccess::Range, @token_instance_variable { - InstanceVariable variable; - - Range() { instanceVariableAccess(this, variable) } - - final override InstanceVariable getVariable() { result = variable } - - override string toString() { result = generated.(Generated::InstanceVariable).getValue() } + predicate range(Generated::InstanceVariable n, InstanceVariable v) { + instanceVariableAccess(n, v) } } module ClassVariableAccess { - class Range extends VariableAccess::Range, @token_class_variable { - ClassVariable variable; - - Range() { classVariableAccess(this, variable) } - - final override ClassVariable getVariable() { result = variable } - - override string toString() { result = generated.(Generated::ClassVariable).getValue() } - } + predicate range(Generated::ClassVariable n, ClassVariable v) { classVariableAccess(n, v) } } diff --git a/ql/src/codeql_ruby/controlflow/BasicBlocks.qll b/ql/src/codeql_ruby/controlflow/BasicBlocks.qll index 08001011625..2926d00fe18 100644 --- a/ql/src/codeql_ruby/controlflow/BasicBlocks.qll +++ b/ql/src/codeql_ruby/controlflow/BasicBlocks.qll @@ -2,6 +2,8 @@ private import codeql.Locations private import codeql_ruby.AST +private import codeql_ruby.ast.internal.AST +private import codeql_ruby.ast.internal.TreeSitter private import codeql_ruby.controlflow.ControlFlowGraph private import internal.ControlFlowGraphImpl private import CfgNodes @@ -351,14 +353,14 @@ class ExitBasicBlock extends BasicBlock { } private module JoinBlockPredecessors { - private predicate id(AstNode x, AstNode y) { x = y } + private predicate id(Generated::AstNode x, Generated::AstNode y) { x = y } - private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y) + private predicate idOf(Generated::AstNode x, int y) = equivalenceRelation(id/2)(x, y) int getId(JoinBlockPredecessor jbp) { - idOf(jbp.getFirstNode().(AstCfgNode).getNode(), result) + idOf(toGenerated(jbp.getFirstNode().(AstCfgNode).getNode()), result) or - idOf(jbp.(EntryBasicBlock).getScope(), result) + idOf(toGenerated(jbp.(EntryBasicBlock).getScope()), result) } string getSplitString(JoinBlockPredecessor jbp) { diff --git a/ql/src/codeql_ruby/controlflow/CfgNodes.qll b/ql/src/codeql_ruby/controlflow/CfgNodes.qll index 1004e092d6a..10449ed7c91 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -62,11 +62,11 @@ class ExitNode extends CfgNode, TExitNode { * (dead) code or not important for control flow, and multiple when there are different * splits for the AST node. */ -class AstCfgNode extends CfgNode, TAstNode { +class AstCfgNode extends CfgNode, TAstCfgNode { private Splits splits; private AstNode n; - AstCfgNode() { this = TAstNode(n, splits) } + AstCfgNode() { this = TAstCfgNode(n, splits) } final override AstNode getNode() { result = n } @@ -131,7 +131,7 @@ abstract private class ExprChildMapping extends Expr { pragma[noinline] private BasicBlock getABasicBlockInScope() { - result.getANode() = TAstNode(this.getAChildStar(), _) + result.getANode() = TAstCfgNode(this.getAChildStar(), _) } pragma[nomagic] @@ -283,7 +283,7 @@ module ExprNodes { } private class StmtSequenceChildMapping extends ExprChildMapping, StmtSequence { - override predicate relevantChild(Expr e) { e = this.getLastExpr() } + override predicate relevantChild(Expr e) { e = this.getLastStmt() } } /** A control-flow node that wraps a `StmtSequence` AST expression. */ @@ -292,8 +292,8 @@ module ExprNodes { final override StmtSequence getExpr() { result = ExprCfgNode.super.getExpr() } - /** Gets the last expression in this sequence, if any. */ - final ExprCfgNode getLastExpr() { e.hasCfgChild(e.getLastExpr(), this, result) } + /** Gets the last statement in this sequence, if any. */ + final ExprCfgNode getLastStmt() { e.hasCfgChild(e.getLastStmt(), this, result) } } private class ForExprChildMapping extends ExprChildMapping, ForExpr { diff --git a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll index 5465e03e247..519d2b30dad 100644 --- a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll +++ b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll @@ -1,7 +1,7 @@ /** Provides classes representing the control flow graph. */ private import codeql.Locations -private import codeql_ruby.AST as AST +private import codeql_ruby.AST private import codeql_ruby.controlflow.BasicBlocks private import SuccessorTypes private import internal.ControlFlowGraphImpl @@ -9,12 +9,12 @@ private import internal.Splitting private import internal.Completion /** An AST node with an associated control-flow graph. */ -class CfgScope extends AST::AstNode { +class CfgScope extends Scope { CfgScope() { this instanceof CfgScope::Range_ } /** Gets the CFG scope that this scope is nested under, if any. */ final CfgScope getOuterCfgScope() { - exists(AST::AstNode parent | + exists(AstNode parent | parent = this.getParent() and result = getCfgScope(parent) ) @@ -34,7 +34,7 @@ class CfgNode extends TCfgNode { string toString() { none() } /** Gets the AST node that this node corresponds to, if any. */ - AST::AstNode getNode() { none() } + AstNode getNode() { none() } /** Gets the location of this control flow node. */ Location getLocation() { none() } diff --git a/ql/src/codeql_ruby/controlflow/internal/AstNodes.qll b/ql/src/codeql_ruby/controlflow/internal/AstNodes.qll deleted file mode 100644 index a8c5a710b41..00000000000 --- a/ql/src/codeql_ruby/controlflow/internal/AstNodes.qll +++ /dev/null @@ -1,149 +0,0 @@ -/** - * Provides various helper classes for AST nodes. The definitions in this file - * will likely be part of the hand-written user-facing AST layer. - */ - -private import codeql_ruby.ast.internal.TreeSitter::Generated -private import codeql_ruby.controlflow.internal.Completion - -class LogicalNotAstNode extends Unary { - AstNode operand; - - LogicalNotAstNode() { - this.getOperator().toString() in ["!", "not"] and - operand = this.getOperand() - } -} - -class LogicalAndAstNode extends Binary { - AstNode left; - AstNode right; - - LogicalAndAstNode() { - this.getOperator().toString() in ["&&", "and"] and - left = this.getLeft() and - right = this.getRight() - } - - AstNode getAnOperand() { result in [left, right] } -} - -class LogicalOrAstNode extends Binary { - AstNode left; - AstNode right; - - LogicalOrAstNode() { - this.getOperator().toString() in ["||", "or"] and - left = this.getLeft() and - right = this.getRight() - } - - AstNode getAnOperand() { result in [left, right] } -} - -private class If_or_elisif = - @if or @elsif or @conditional or @if_modifier or @unless or @unless_modifier; - -class IfElsifAstNode extends AstNode, If_or_elisif { - AstNode getConditionNode() { none() } - - AstNode getBranch(boolean b) { none() } -} - -private class IfAstNode extends IfElsifAstNode, If { - override AstNode getConditionNode() { result = this.getCondition() } - - override AstNode getBranch(boolean b) { - b = true and result = this.getConsequence() - or - b = false and result = this.getAlternative() - } -} - -private class ElsifAstNode extends IfElsifAstNode, Elsif { - override AstNode getConditionNode() { result = this.getCondition() } - - override AstNode getBranch(boolean b) { - b = true and result = this.getConsequence() - or - b = false and result = this.getAlternative() - } -} - -private class ConditionalAstNode extends IfElsifAstNode, Conditional { - override AstNode getConditionNode() { result = this.getCondition() } - - override AstNode getBranch(boolean b) { - b = true and result = this.getConsequence() - or - b = false and result = this.getAlternative() - } -} - -private class IfModifierAstNode extends IfElsifAstNode, IfModifier { - override AstNode getConditionNode() { result = this.getCondition() } - - override AstNode getBranch(boolean b) { b = true and result = this.getBody() } -} - -private class UnlessAstNode extends IfElsifAstNode, Unless { - override AstNode getConditionNode() { result = this.getCondition() } - - override AstNode getBranch(boolean b) { - b = false and result = this.getConsequence() - or - b = true and result = this.getAlternative() - } -} - -private class UnlessModifierAstNode extends IfElsifAstNode, UnlessModifier { - override AstNode getConditionNode() { result = this.getCondition() } - - override AstNode getBranch(boolean b) { b = false and result = this.getBody() } -} - -private class CondLoop = @while or @while_modifier or @until or @until_modifier; - -class ConditionalLoopAstNode extends AstNode, CondLoop { - AstNode getConditionNode() { none() } - - AstNode getBodyNode() { none() } - - predicate continueLoop(BooleanCompletion c) { c instanceof TrueCompletion } - - final predicate endLoop(BooleanCompletion c) { continueLoop(c.getDual()) } -} - -private class WhileLoop extends ConditionalLoopAstNode, While { - override UnderscoreStatement getConditionNode() { result = this.getCondition() } - - override Do getBodyNode() { result = this.getBody() } -} - -private class WhileModifierLoop extends ConditionalLoopAstNode, WhileModifier { - override AstNode getConditionNode() { result = this.getCondition() } - - override UnderscoreStatement getBodyNode() { result = this.getBody() } -} - -private class UntilLoop extends ConditionalLoopAstNode, Until { - override UnderscoreStatement getConditionNode() { result = this.getCondition() } - - override Do getBodyNode() { result = this.getBody() } - - override predicate continueLoop(BooleanCompletion c) { c instanceof FalseCompletion } -} - -private class UntilModifierLoop extends ConditionalLoopAstNode, UntilModifier { - override AstNode getConditionNode() { result = this.getCondition() } - - override UnderscoreStatement getBodyNode() { result = this.getBody() } - - override predicate continueLoop(BooleanCompletion c) { c instanceof FalseCompletion } -} - -class ParenthesizedStatement extends ParenthesizedStatements { - ParenthesizedStatement() { strictcount(int i | exists(this.getChild(i))) = 1 } - - AstNode getChild() { result = this.getChild(0) } -} diff --git a/ql/src/codeql_ruby/controlflow/internal/Cfg.ql b/ql/src/codeql_ruby/controlflow/internal/Cfg.ql index ef06ba17a97..ded6b103222 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Cfg.ql +++ b/ql/src/codeql_ruby/controlflow/internal/Cfg.ql @@ -14,7 +14,7 @@ query predicate nodes(CfgNode n, string attr, string val) { p order by p.getLocation().getFile().getBaseName(), p.getLocation().getFile().getAbsolutePath(), - p.getLocation().getStartLine() + p.getLocation().getStartLine(), p.getLocation().getStartColumn() ) ).toString() } diff --git a/ql/src/codeql_ruby/controlflow/internal/Completion.qll b/ql/src/codeql_ruby/controlflow/internal/Completion.qll index c1148d430e3..dc80bbb98f2 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Completion.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Completion.qll @@ -5,6 +5,7 @@ */ private import codeql_ruby.AST +private import codeql_ruby.ast.internal.AST private import codeql_ruby.controlflow.ControlFlowGraph private import ControlFlowGraphImpl private import NonReturning @@ -48,12 +49,12 @@ private predicate nestedEnsureCompletion(Completion outer, int nestLevel) { or outer = TExitCompletion() ) and - nestLevel = any(Trees::RescueEnsureBlockTree t).nestLevel() + nestLevel = any(Trees::BodyStmtTree t).getNestLevel() } pragma[noinline] private predicate completionIsValidForStmt(AstNode n, Completion c) { - n instanceof InRange and + n = TForIn(_) and c instanceof EmptinessCompletion or n instanceof BreakStmt and @@ -175,7 +176,7 @@ private predicate inBooleanContext(AstNode n) { or n = any(NotExpr parent | inBooleanContext(parent)).getOperand() or - n = any(StmtSequence parent | inBooleanContext(parent)).getLastExpr() + n = any(StmtSequence parent | inBooleanContext(parent)).getLastStmt() or exists(CaseExpr c, WhenExpr w | not exists(c.getValue()) and @@ -205,7 +206,7 @@ private predicate inMatchingContext(AstNode n) { w.getPattern(_) = n ) or - n = any(Trees::DefaultValueParameterTree t | t.hasDefaultValue()) + n.(Trees::DefaultValueParameterTree).hasDefaultValue() } /** diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 931f7020666..83091c2f87b 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -31,16 +31,16 @@ * caught up by its surrounding loop and turned into a `NormalCompletion`. */ +private import codeql_ruby.AST private import codeql_ruby.ast.internal.AST as ASTInternal -private import codeql_ruby.ast.internal.Control as Control -private import codeql_ruby.ast.internal.TreeSitter::Generated -private import AstNodes +private import codeql_ruby.ast.internal.Scope +private import codeql_ruby.ast.Scope +private import codeql_ruby.ast.internal.TreeSitter private import codeql_ruby.ast.internal.Variable private import codeql_ruby.controlflow.ControlFlowGraph private import Completion private import SuccessorTypes private import Splitting -private import codeql.files.FileSystem module CfgScope { abstract class Range_ extends AstNode { @@ -49,22 +49,12 @@ module CfgScope { abstract predicate exit(AstNode last, Completion c); } - private class ProgramScope extends Range_, Program { + private class ToplevelScope extends Range_, Toplevel { final override predicate entry(AstNode first) { first(this, first) } final override predicate exit(AstNode last, Completion c) { last(this, last, c) } } - private class BeginBlockScope extends Range_, BeginBlock { - final override predicate entry(AstNode first) { - first(this.(Trees::BeginBlockTree).getFirstChildNode(), first) - } - - final override predicate exit(AstNode last, Completion c) { - last(this.(Trees::BeginBlockTree).getLastChildNode(), last, c) - } - } - private class EndBlockScope extends Range_, EndBlock { final override predicate entry(AstNode first) { first(this.(Trees::EndBlockTree).getFirstChildNode(), first) @@ -75,82 +65,23 @@ module CfgScope { } } - private class MethodScope extends Range_, AstNode { - MethodScope() { this instanceof Method } - - final override predicate entry(AstNode first) { - this.(Trees::RescueEnsureBlockTree).firstInner(first) - } + private class BodyStmtCallableScope extends Range_, ASTInternal::TBodyStmt, Callable { + final override predicate entry(AstNode first) { this.(Trees::BodyStmtTree).firstInner(first) } final override predicate exit(AstNode last, Completion c) { - this.(Trees::RescueEnsureBlockTree).lastInner(last, c) + this.(Trees::BodyStmtTree).lastInner(last, c) } } - private class SingletonMethodScope extends Range_, AstNode { - SingletonMethodScope() { this instanceof SingletonMethod } - + private class BraceBlockScope extends Range_, BraceBlock { final override predicate entry(AstNode first) { - this.(Trees::RescueEnsureBlockTree).firstInner(first) + first(this.(Trees::BraceBlockTree).getFirstChildNode(), first) } final override predicate exit(AstNode last, Completion c) { - this.(Trees::RescueEnsureBlockTree).lastInner(last, c) + last(this.(Trees::BraceBlockTree).getLastChildNode(), last, c) } } - - private class DoBlockScope extends Range_, DoBlock { - DoBlockScope() { not this.getParent() instanceof Lambda } - - final override predicate entry(AstNode first) { - this.(Trees::RescueEnsureBlockTree).firstInner(first) - } - - final override predicate exit(AstNode last, Completion c) { - this.(Trees::RescueEnsureBlockTree).lastInner(last, c) - } - } - - private class BlockScope extends Range_, Block { - BlockScope() { not this.getParent() instanceof Lambda } - - final override predicate entry(AstNode first) { - first(this.(Trees::BlockTree).getFirstChildNode(), first) - } - - final override predicate exit(AstNode last, Completion c) { - last(this.(Trees::BlockTree).getLastChildNode(), last, c) - } - } - - private class LambdaScope extends Range_, Lambda { - final override predicate entry(AstNode first) { - first(this.getParameters(), first) - or - not exists(this.getParameters()) and - ( - this.getBody().(Trees::DoBlockTree).firstInner(first) - or - first(this.getBody().(Trees::BlockTree).getFirstChildNode(), first) - ) - } - - final override predicate exit(AstNode last, Completion c) { - last(this.getParameters(), last, c) and - not c instanceof NormalCompletion - or - last(this.getBody().(Trees::BlockTree).getLastChildNode(), last, c) - or - this.getBody().(Trees::RescueEnsureBlockTree).lastInner(last, c) - or - not exists(this.getBody()) and last(this.getParameters(), last, c) - } - } -} - -private AstNode parent(AstNode n) { - result = parentOf(n) and - not n instanceof CfgScope } abstract private class ControlFlowTree extends AstNode { @@ -205,11 +136,7 @@ private predicate succImpl(AstNode pred, AstNode succ, Completion c) { any(ControlFlowTree cft).succ(pred, succ, c) } -private predicate isHidden(ControlFlowTree t) { - not t instanceof ASTInternal::AstNode::Range - or - t.isHidden() -} +private predicate isHidden(ControlFlowTree t) { t.isHidden() } private predicate succImplIfHidden(AstNode pred, AstNode succ) { isHidden(pred) and @@ -257,10 +184,7 @@ predicate succExit(CfgScope::Range_ scope, AstNode last, Completion c) { */ abstract private class StandardNode extends ControlFlowTree { /** Gets the `i`th child node, in order of evaluation. */ - ControlFlowTree getChildNode(int i) { - result = this.getAFieldOrChild() and - i = result.getParentIndex() - } + abstract ControlFlowTree getChildNode(int i); private AstNode getChildNodeRanked(int i) { result = rank[i + 1](AstNode child, int j | child = this.getChildNode(j) | child order by j) @@ -294,16 +218,21 @@ abstract private class PreOrderTree extends ControlFlowTree { } // TODO: remove this class; it should be replaced with an implicit non AST node -class InRange extends ASTInternal::AstNode::Range, @in { +private class ForIn extends AstNode, ASTInternal::TForIn { final override string toString() { result = "In" } } // TODO: remove this class; it should be replaced with an implicit non AST node -class ForRange extends Control::ForExpr::Range, @for { - override predicate child(string label, ASTInternal::AstNode::Range child) { - Control::ForExpr::Range.super.child(label, child) +private class ForRange extends ForExpr { + override AstNode getAChild(string pred) { + result = ForExpr.super.getAChild(pred) or - label = "" and this.(AstNode).getAFieldOrChild().(In) = child + pred = "" and + result = this.getIn() + } + + ForIn getIn() { + result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(Generated::For).getValue()) } } @@ -339,49 +268,6 @@ abstract private class PostOrderTree extends ControlFlowTree { } } -private class LeftToRightPostOrderNodes = - @argument_list or @array or @bare_string or @bare_symbol or @binary or @block_argument or - @break or @call or @chained_string or @delimited_symbol or @destructured_left_assignment or - @destructured_parameter or @element_reference or @exception_variable or @hash or - @hash_splat_argument or @interpolation or @left_assignment_list or @next or - @operator_assignment or @pair or @parenthesized_statements or @range or @redo or @regex or - @rest_assignment or @retry or @return or @right_assignment_list or @scope_resolution or - @token_simple_symbol or @splat_argument or @string__ or @string_array or @subshell or - @superclass or @symbol_array or @token_hash_key_symbol or @unary; - -private class LeftToRightPostOrderTree extends StandardPostOrderTree, LeftToRightPostOrderNodes { - LeftToRightPostOrderTree() { - not this instanceof LogicalNotAstNode and - not this instanceof LogicalAndAstNode and - not this instanceof LogicalOrAstNode - } - - override predicate isHidden() { - this instanceof ArgumentList or - this instanceof ChainedString or - this instanceof ExceptionVariable or - this instanceof LeftAssignmentList or - this instanceof RightAssignmentList - } -} - -private class LeftToRightPreOrderNodes = - @alias or @block_parameters or @class or @do or @else or @ensure or @lambda_parameters or - @method_parameters or @pattern or @program or @then or @undef or @yield; - -private class LeftToRightPreOrderTree extends StandardPreOrderTree, LeftToRightPreOrderNodes { - override predicate isHidden() { - this instanceof BlockParameters or - this instanceof Do or - this instanceof Else or - this instanceof LambdaParameters or - this instanceof MethodParameters or - this instanceof Pattern or - this instanceof Program or - this instanceof Then - } -} - abstract private class StandardPostOrderTree extends StandardNode, PostOrderTree { final override predicate first(AstNode first) { first(this.getFirstChildNode(), first) @@ -415,584 +301,152 @@ abstract class ScopeTree extends StandardNode, LeafTree { /** Defines the CFG by dispatch on the various AST types. */ module Trees { - private class AssignmentTree extends StandardPostOrderTree, Assignment { + private class AliasStmtTree extends StandardPreOrderTree, AliasStmt { final override ControlFlowTree getChildNode(int i) { - result = this.getRight() and i = 0 + result = this.getNewName() and i = 0 or - result = this.getLeft() and i = 1 + result = this.getOldName() and i = 1 } } - private class BeginTree extends RescueEnsureBlockTree, PreOrderTree, Begin { - final override AstNode getChildNode(int i, boolean rescuable) { - result = this.getChild(i) and rescuable = true - } - - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + private class ArgumentListTree extends StandardPostOrderTree, ArgumentList { + final override ControlFlowTree getChildNode(int i) { result = this.getElement(i) } override predicate isHidden() { any() } } - class BeginBlockTree extends ScopeTree, BeginBlock { - final override ControlFlowTree getChildNode(int i) { result = this.getChild(i) } + private class ArrayLiteralTree extends StandardPostOrderTree, ArrayLiteral { + final override ControlFlowTree getChildNode(int i) { result = this.getElement(i) } } - class BlockTree extends ScopeTree, Block { + private class AssignOperationTree extends StandardPostOrderTree, AssignOperation { final override ControlFlowTree getChildNode(int i) { - result = this.getParameters() and i = 0 + result = this.getLeftOperand() and i = 0 or - result = this.getChild(i - 1) + result = this.getRightOperand() and i = 1 } } - private class BlockParameterTree extends LeafTree, BlockParameter { } - - private class CaseTree extends PreOrderTree, Case { - final override predicate propagatesAbnormal(AstNode child) { - child = this.getValue() or child = this.getChild(_) - } - - final override predicate last(AstNode last, Completion c) { - last(this.getValue(), last, c) and not exists(this.getChild(_)) + private class AssignmentTree extends StandardPostOrderTree, AssignExpr { + final override ControlFlowTree getChildNode(int i) { + result = this.getLeftOperand() and i = 0 or - last(this.getChild(_).(When).getBody(), last, c) - or - exists(int i, ControlFlowTree lastBranch | - lastBranch = this.getChild(i) and - not exists(this.getChild(i + 1)) and - last(lastBranch, last, c) - ) - } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - exists(AstNode next | - pred = this and - first(next, succ) and - c instanceof SimpleCompletion - | - next = this.getValue() - or - not exists(this.getValue()) and - next = this.getChild(0) - ) - or - last(this.getValue(), pred, c) and - first(this.getChild(0), succ) and - c instanceof SimpleCompletion - or - exists(int i, WhenTree branch | branch = this.getChild(i) | - last(branch.getLastPattern(), pred, c) and - first(this.getChild(i + 1), succ) and - c.(ConditionalCompletion).getValue() = false - ) + result = this.getRightOperand() and i = 1 } } - private class CharacterTree extends LeafTree, Character { } - - private class ClassTree extends RescueEnsureBlockTree, PreOrderTree, Class { - final override AstNode getChildNode(int i, boolean rescuable) { - result = this.getName() and i = 0 and rescuable = false - or - result = this.getSuperclass() and i = 1 and rescuable = true - or - result = this.getChild(i - 2) and rescuable = true - } - - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } - } - - private class ClassVariableTree extends LeafTree, ClassVariable { } - - private class ComplexTree extends LeafTree, Complex { } - - private class ConstantTree extends LeafTree, Constant { } - - /** A parameter that may have a default value. */ - abstract class DefaultValueParameterTree extends PreOrderTree { - abstract AstNode getDefaultValue(); - - predicate hasDefaultValue() { exists(this.getDefaultValue()) } - - final override predicate propagatesAbnormal(AstNode child) { child = this.getDefaultValue() } - - final override predicate last(AstNode last, Completion c) { - last(this.getDefaultValue(), last, c) and - c instanceof NormalCompletion - or - last = this and - ( - not this.hasDefaultValue() and - c instanceof SimpleCompletion - or - this.hasDefaultValue() and - c.(MatchingCompletion).getValue() = true - ) - } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - pred = this and - first(this.getDefaultValue(), succ) and - c.(MatchingCompletion).getValue() = false - } - } - - class DoBlockTree extends RescueEnsureBlockTree, PostOrderTree, DoBlock { - final override predicate first(AstNode first) { first = this } - - final override AstNode getChildNode(int i, boolean rescuable) { - result = this.getParameters() and i = 0 and rescuable = false - or - result = this.getChild(i - 1) and rescuable = true - } - } - - private class EmptyStatementTree extends LeafTree, EmptyStatement { } - - class EndBlockTree extends ScopeTree, EndBlock { - final override ControlFlowTree getChildNode(int i) { result = this.getChild(i) } - } - - private class ExceptionsTree extends PreOrderTree, Exceptions { - final override predicate propagatesAbnormal(AstNode child) { none() } - - final override predicate last(AstNode last, Completion c) { - last(this.getChild(_), last, c) and - c.(MatchingCompletion).getValue() = true - or - exists(int lst | - last(this.getChild(lst), last, c) and - not exists(this.getChild(lst + 1)) - ) - } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - pred = this and - first(this.getChild(0), succ) and - c instanceof SimpleCompletion - or - exists(int i | - last(this.getChild(i), pred, c) and - c.(MatchingCompletion).getValue() = false and - first(this.getChild(i + 1), succ) - ) - } - + private class BeginTree extends BodyStmtPreOrderTree, BeginExpr { override predicate isHidden() { any() } } - private class FalseTree extends LeafTree, False { } + private class BinaryOperationTree extends StandardPostOrderTree, BinaryOperation { + // Logical AND and OR are handled separately + BinaryOperationTree() { not this instanceof BinaryLogicalOperation } - private class FloatTree extends LeafTree, Float { } + final override ControlFlowTree getChildNode(int i) { + result = this.getLeftOperand() and i = 0 + or + result = this.getRightOperand() and i = 1 + } + } + + private class BlockArgumentTree extends StandardPostOrderTree, BlockArgument { + final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } + } + + abstract private class NonDefaultValueParameterTree extends ControlFlowTree, NamedParameter { + final override predicate first(AstNode first) { + this.getDefiningAccess().(ControlFlowTree).first(first) + } + + final override predicate last(AstNode last, Completion c) { + this.getDefiningAccess().(ControlFlowTree).last(last, c) + } + + override predicate propagatesAbnormal(AstNode child) { + this.getDefiningAccess().(ControlFlowTree).propagatesAbnormal(child) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } + } + + private class BlockParameterTree extends NonDefaultValueParameterTree, BlockParameter { } /** - * Control flow of a for-in loop - * - * For example, this program fragment: - * - * ```rb - * for arg in args do - * puts arg - * end - * puts "done"; - * ``` - * - * has the following control flow graph: - * - * ``` - * args - * | - * in------<----- - * / \ \ - * / \ | - * / \ | - * / \ | - * empty non-empty | - * | \ | - * for \ | - * | arg | - * | | | - * puts "done" puts arg | - * \___/ - * ``` + * TODO: make all StmtSequence tree classes post-order, and simplify class + * hierarchy. */ - private class ForTree extends PostOrderTree, For { - final override predicate propagatesAbnormal(AstNode child) { - child = this.getPattern() or child = this.getArray() - } - - final override predicate first(AstNode first) { first(this.getArray(), first) } - - private In getIn() { result = this.getValue() } - - private UnderscoreArg getArray() { result = this.getValue().getChild() } - - /** - * for pattern in array do body end - * ``` - * array +-> in +--[non empty]--> pattern -> body -> in - * |--[empty]--> for - * ``` - */ - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getArray(), pred, c) and - first(this.getIn(), succ) and - c instanceof SimpleCompletion + abstract class BodyStmtTree extends StmtSequenceTree, BodyStmt { + predicate firstInner(AstNode first) { + first(this.getBodyChild(0, _), first) or - last(this.getIn(), pred, c) and - first(this.getPattern(), succ) and - c.(EmptinessCompletion).getValue() = false - or - last(this.getPattern(), pred, c) and - first(this.getBody(), succ) and - c instanceof NormalCompletion - or - last(this.getBody(), pred, c) and - first(this.getIn(), succ) and - c.continuesLoop() - or - last(this.getBody(), pred, c) and - first(this.getBody(), succ) and - c instanceof RedoCompletion - or - succ = this and + not exists(this.getBodyChild(_, _)) and ( - last(this.getIn(), pred, c) and - c.(EmptinessCompletion).getValue() = true + first(this.getRescue(_), first) or - last(this.getBody(), pred, c) and - not c.continuesLoop() and - not c instanceof BreakCompletion and - not c instanceof RedoCompletion - or - last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + not exists(this.getRescue(_)) and + first(this.getEnsure(), first) ) } - } - private class GlobalVariableTree extends LeafTree, GlobalVariable { } - - private class HashSplatParameterTree extends LeafTree, HashSplatParameter { } - - private HeredocBody heredoc(HeredocBeginning start) { - exists(int i, File f | - start = - rank[i](HeredocBeginning b | - f = b.getLocation().getFile() - | - b order by b.getLocation().getStartLine(), b.getLocation().getStartColumn() - ) and - result = - rank[i](HeredocBody b | - f = b.getLocation().getFile() - | - b order by b.getLocation().getStartLine(), b.getLocation().getStartColumn() + predicate lastInner(AstNode last, Completion c) { + exists(boolean ensurable | last = this.getAnEnsurePredecessor(c, ensurable) | + not this.hasEnsure() + or + ensurable = false + ) + or + // If the body completes normally, take the completion from the `ensure` block + this.lastEnsure(last, c, any(NormalCompletion nc), _) + or + // If the `ensure` block completes normally, it inherits any non-normal + // completion from the body + c = + any(NestedEnsureCompletion nec | + this.lastEnsure(last, nec.getAnInnerCompatibleCompletion(), nec.getOuterCompletion(), + nec.getNestLevel()) ) - ) - } - - private class HeredocBeginningTree extends StandardPreOrderTree, HeredocBeginning { - final override ControlFlowTree getChildNode(int i) { result = heredoc(this).getChild(i) } - } - - private class IdentifierTree extends LeafTree, Identifier { } - - private class IfElsifTree extends PostOrderTree, IfElsifAstNode { - final override predicate propagatesAbnormal(AstNode child) { - child = this.getConditionNode() or child = this.getBranch(_) - } - - final override predicate first(AstNode first) { first(this.getConditionNode(), first) } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - exists(boolean b | - last(this.getConditionNode(), pred, c) and - b = c.(BooleanCompletion).getValue() - | - first(this.getBranch(b), succ) - or - not exists(this.getBranch(b)) and - succ = this - ) or - last(this.getBranch(_), pred, c) and - succ = this and - c instanceof NormalCompletion - } - } - - private class InTree extends LeafTree, In { } - - private class InstanceVariableTree extends LeafTree, InstanceVariable { } - - private class IntegerTree extends LeafTree, Integer { } - - private class KeywordParameterTree extends DefaultValueParameterTree, KeywordParameter { - final override AstNode getDefaultValue() { result = this.getValue() } - } - - class LambdaTree extends LeafTree, Lambda { - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getParameters(), pred, c) and - c instanceof NormalCompletion and - ( - this.getBody().(DoBlockTree).firstInner(succ) - or - first(this.getBody().(BlockTree).getFirstChildNode(), succ) - ) - } - } - - class LogicalAndTree extends PostOrderTree, LogicalAndAstNode { - final override predicate propagatesAbnormal(AstNode child) { child in [left, right] } - - final override predicate first(AstNode first) { first(left, first) } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(left, pred, c) and - c instanceof TrueCompletion and - first(right, succ) + not exists(this.getBodyChild(_, _)) and + not exists(this.getRescue(_)) and + this.lastEnsure0(last, c) or - last(left, pred, c) and - c instanceof FalseCompletion and - succ = this - or - last(right, pred, c) and - c instanceof NormalCompletion and - succ = this + last([this.getEnsure(), this.getBodyChild(_, false)], last, c) and + not c instanceof NormalCompletion } - } - - class LogicalOrTree extends PostOrderTree, LogicalOrAstNode { - final override predicate propagatesAbnormal(AstNode child) { child in [left, right] } - - final override predicate first(AstNode first) { first(left, first) } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(left, pred, c) and - c instanceof FalseCompletion and - first(right, succ) - or - last(left, pred, c) and - c instanceof TrueCompletion and - succ = this - or - last(right, pred, c) and - c instanceof NormalCompletion and - succ = this - } - } - - class LogicalNotTree extends PostOrderTree, LogicalNotAstNode { - final override predicate propagatesAbnormal(AstNode child) { child = operand } - - final override predicate first(AstNode first) { first(operand, first) } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - succ = this and - last(operand, pred, c) and - c instanceof NormalCompletion - } - } - - private class MethodTree extends RescueEnsureBlockTree, PostOrderTree, Method { - final override AstNode getChildNode(int i, boolean rescuable) { - result = this.getParameters() and i = 0 and rescuable = false - or - result = this.getChild(i - 1) and rescuable = true - } - - final override predicate first(AstNode first) { first(this.getName(), first) } override predicate succ(AstNode pred, AstNode succ, Completion c) { - RescueEnsureBlockTree.super.succ(pred, succ, c) + this instanceof PreOrderTree and + pred = this and + c instanceof SimpleCompletion and + this.firstInner(succ) or - last(this.getName(), pred, c) and - succ = this and + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + or + // Exceptional flow from body to first `rescue` + this.lastBody(pred, c, true) and + first(this.getRescue(0), succ) and + c instanceof RaiseCompletion + or + // Flow from one `rescue` clause to the next when there is no match + exists(RescueTree rescue, int i | rescue = this.getRescue(i) | + rescue.lastNoMatch(pred, c) and + first(this.getRescue(i + 1), succ) + ) + or + // Flow from body to `else` block when no exception + this.lastBody(pred, c, _) and + first(this.getElse(), succ) and c instanceof NormalCompletion - } - } - - private class ModuleTree extends RescueEnsureBlockTree, PreOrderTree, Module { - final override AstNode getChildNode(int i, boolean rescuable) { - result = this.getName() and i = 0 and rescuable = false or - result = this.getChild(i - 1) and rescuable = true - } - - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } - } - - private class NilTree extends LeafTree, Nil { } - - private class OptionalParameterTree extends DefaultValueParameterTree, OptionalParameter { - final override AstNode getDefaultValue() { result = this.getValue() } - } - - private class RationalTree extends LeafTree, Rational { } - - private class RescueTree extends PreOrderTree, Rescue { - final override predicate propagatesAbnormal(AstNode child) { child = this.getExceptions() } - - predicate lastMatch(AstNode last, Completion c) { - last(this.getBody(), last, c) - or - not exists(this.getBody()) and - ( - last(this.getVariable(), last, c) - or - not exists(this.getVariable()) and - ( - last(this.getExceptions(), last, c) and - c.(MatchingCompletion).getValue() = true - or - not exists(this.getExceptions()) and - last = this and - isValidFor(c, this) - ) - ) - } - - predicate lastNoMatch(AstNode last, Completion c) { - last(this.getExceptions(), last, c) and - c.(MatchingCompletion).getValue() = false - } - - final override predicate last(AstNode last, Completion c) { - this.lastNoMatch(last, c) - or - this.lastMatch(last, c) - } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - exists(AstNode next | - pred = this and - first(next, succ) and - c instanceof SimpleCompletion - | - next = this.getExceptions() - or - not exists(this.getExceptions()) and - ( - next = this.getVariable() - or - not exists(this.getVariable()) and - next = this.getBody() - ) - ) - or - exists(AstNode next | - last(this.getExceptions(), pred, c) and - first(next, succ) and - c.(MatchingCompletion).getValue() = true - | - next = this.getVariable() - or - not exists(this.getVariable()) and - next = this.getBody() - ) - or - last(this.getVariable(), pred, c) and - first(this.getBody(), succ) and - c instanceof NormalCompletion - } - } - - /** Gets a child of `n` that is in CFG scope `scope`. */ - pragma[noinline] - private AstNode getAChildInScope(AstNode n, CfgScope scope) { - result.getParent() = n and - scope = getCfgScope(result) - } - - /** A block that may contain `rescue`/`ensure`. */ - abstract class RescueEnsureBlockTree extends ControlFlowTree { - /** - * Gets the `i`th child of this block. `rescuable` indicates whether exceptional - * execution of the child can be caught by `rescue`/`ensure`. - */ - abstract AstNode getChildNode(int i, boolean rescuable); - - /** Gets the `i`th child in the body of this block. */ - final private AstNode getBodyChild(int i, boolean rescuable) { - result = this.getChildNode(_, rescuable) and - result = - rank[i + 1](AstNode child, int j | - child = this.getChildNode(j, _) and - not result instanceof Rescue and - not result instanceof Ensure and - not result instanceof Else - | - child order by j - ) - } - - /** Gets the `i`th `rescue` block in this block. */ - final Rescue getRescue(int i) { - result = rank[i + 1](Rescue s | s = this.getAFieldOrChild() | s order by s.getParentIndex()) - } - - /** Gets the `else` block in this block, if any. */ - final private Else getElse() { result = unique(Else s | s = this.getAFieldOrChild()) } - - /** Gets the `ensure` block in this block, if any. */ - final Ensure getEnsure() { result = unique(Ensure s | s = this.getAFieldOrChild()) } - - final private predicate hasEnsure() { exists(this.getEnsure()) } - - final override predicate propagatesAbnormal(AstNode child) { none() } - - /** - * Gets a descendant that belongs to the `ensure` block of this block, if any. - * Nested `ensure` blocks are not included. - */ - AstNode getAnEnsureDescendant() { - result = this.getEnsure() - or - exists(AstNode mid | - mid = this.getAnEnsureDescendant() and - result = getAChildInScope(mid, getCfgScope(mid)) and - not exists(RescueEnsureBlockTree nestedBlock | - result = nestedBlock.getEnsure() and - nestedBlock != this - ) - ) - } - - /** - * Holds if `innerBlock` has an `ensure` block and is immediately nested inside the - * `ensure` block of this block. - */ - private predicate nestedEnsure(RescueEnsureBlockTree innerBlock) { - exists(Ensure innerEnsure | - innerEnsure = getAChildInScope(this.getAnEnsureDescendant(), getCfgScope(this)) and - innerEnsure = innerBlock.getEnsure() - ) - } - - /** - * Gets the `ensure`-nesting level of this block. That is, the number of `ensure` - * blocks that this block is nested under. - */ - int nestLevel() { result = count(RescueEnsureBlockTree outer | outer.nestedEnsure+(this)) } - - /** - * Holds if `last` is a last element in the body of this block. `ensurable` - * indicates whether `last` may be a predecessor of an `ensure` block. - */ - pragma[nomagic] - private predicate lastBody(AstNode last, Completion c, boolean ensurable) { - exists(boolean rescuable | - if c instanceof RaiseCompletion then ensurable = rescuable else ensurable = true - | - last(this.getBodyChild(_, rescuable), last, c) and - not c instanceof NormalCompletion - or - exists(int lst | - last(this.getBodyChild(lst, rescuable), last, c) and - not exists(this.getBodyChild(lst + 1, _)) - ) - ) + // Flow into `ensure` block + pred = getAnEnsurePredecessor(c, true) and + first(this.getEnsure(), succ) } /** @@ -1040,6 +494,40 @@ module Trees { pragma[nomagic] private predicate lastEnsure0(AstNode last, Completion c) { last(this.getEnsure(), last, c) } + /** + * Gets a descendant that belongs to the `ensure` block of this block, if any. + * Nested `ensure` blocks are not included. + */ + AstNode getAnEnsureDescendant() { + result = this.getEnsure() + or + exists(AstNode mid | + mid = this.getAnEnsureDescendant() and + result = getAChildInScope(mid, getCfgScope(mid)) and + not exists(BodyStmt nestedBlock | + result = nestedBlock.getEnsure() and + nestedBlock != this + ) + ) + } + + /** + * Holds if `innerBlock` has an `ensure` block and is immediately nested inside the + * `ensure` block of this block. + */ + private predicate nestedEnsure(BodyStmtTree innerBlock) { + exists(StmtSequence innerEnsure | + innerEnsure = getAChildInScope(this.getAnEnsureDescendant(), getCfgScope(this)) and + innerEnsure = innerBlock.(BodyStmt).getEnsure() + ) + } + + /** + * Gets the `ensure`-nesting level of this block. That is, the number of `ensure` + * blocks that this block is nested under. + */ + int getNestLevel() { result = count(BodyStmtTree outer | outer.nestedEnsure+(this)) } + pragma[nomagic] private predicate lastEnsure( AstNode last, NormalCompletion ensure, Completion outer, int nestLevel @@ -1048,83 +536,470 @@ module Trees { exists( this.getAnEnsurePredecessor(any(Completion c0 | outer = c0.getOuterCompletion()), true) ) and - nestLevel = this.nestLevel() + nestLevel = this.getNestLevel() } - predicate lastInner(AstNode last, Completion c) { - exists(boolean ensurable | last = this.getAnEnsurePredecessor(c, ensurable) | - not this.hasEnsure() + /** + * Holds if `last` is a last element in the body of this block. `ensurable` + * indicates whether `last` may be a predecessor of an `ensure` block. + */ + pragma[nomagic] + private predicate lastBody(AstNode last, Completion c, boolean ensurable) { + exists(boolean rescuable | + if c instanceof RaiseCompletion then ensurable = rescuable else ensurable = true + | + last(this.getBodyChild(_, rescuable), last, c) and + not c instanceof NormalCompletion or - ensurable = false - ) - or - // If the body completes normally, take the completion from the `ensure` block - this.lastEnsure(last, c, any(NormalCompletion nc), _) - or - // If the `ensure` block completes normally, it inherits any non-normal - // completion from the body - c = - any(NestedEnsureCompletion nec | - this.lastEnsure(last, nec.getAnInnerCompatibleCompletion(), nec.getOuterCompletion(), - nec.getNestLevel()) + exists(int lst | + last(this.getBodyChild(lst, rescuable), last, c) and + not exists(this.getBodyChild(lst + 1, _)) ) - or - not exists(this.getBodyChild(_, _)) and - not exists(this.getRescue(_)) and - this.lastEnsure0(last, c) - or - last([this.getEnsure(), this.getBodyChild(_, false)], last, c) and - not c instanceof NormalCompletion - } - - predicate firstInner(AstNode first) { - first(this.getBodyChild(0, _), first) - or - not exists(this.getBodyChild(_, _)) and - ( - first(this.getRescue(_), first) - or - not exists(this.getRescue(_)) and - first(this.getEnsure(), first) ) } - - override predicate succ(AstNode pred, AstNode succ, Completion c) { - this instanceof PreOrderTree and - pred = this and - c instanceof SimpleCompletion and - this.firstInner(succ) - or - // Normal left-to-right evaluation in the body - exists(int i | - last(this.getBodyChild(i, _), pred, c) and - first(this.getBodyChild(i + 1, _), succ) and - c instanceof NormalCompletion - ) - or - // Exceptional flow from body to first `rescue` - this.lastBody(pred, c, true) and - first(this.getRescue(0), succ) and - c instanceof RaiseCompletion - or - // Flow from one `rescue` clause to the next when there is no match - exists(RescueTree rescue, int i | rescue = this.getRescue(i) | - rescue.lastNoMatch(pred, c) and - first(this.getRescue(i + 1), succ) - ) - or - // Flow from body to `else` block when no exception - this.lastBody(pred, c, _) and - first(this.getElse(), succ) and - c instanceof NormalCompletion - or - // Flow into `ensure` block - pred = getAnEnsurePredecessor(c, true) and - first(this.getEnsure(), succ) - } } - private class RescueModifierTree extends PreOrderTree, RescueModifier { + abstract class BodyStmtPreOrderTree extends BodyStmtTree, PreOrderTree { + final override predicate last(AstNode last, Completion c) { + this.lastInner(last, c) + or + not exists(this.getAChild(_)) and + last = this and + isValidFor(c, this) + } + } + + abstract class BodyStmtPostOrderTree extends BodyStmtTree, PostOrderTree { + override predicate first(AstNode first) { first = this } + } + + private class BooleanLiteralTree extends LeafTree, BooleanLiteral { } + + class BraceBlockTree extends ScopeTree, BraceBlock { + final override ControlFlowTree getChildNode(int i) { + result = this.getParameter(i) + or + result = this.getStmt(i - this.getNumberOfParameters()) + } + } + + private class CaseTree extends PreOrderTree, CaseExpr { + final override predicate propagatesAbnormal(AstNode child) { + child = this.getValue() or child = this.getABranch() + } + + final override predicate last(AstNode last, Completion c) { + last(this.getValue(), last, c) and not exists(this.getABranch()) + or + last(this.getAWhenBranch().getBody(), last, c) + or + exists(int i, ControlFlowTree lastBranch | + lastBranch = this.getBranch(i) and + not exists(this.getBranch(i + 1)) and + last(lastBranch, last, c) + ) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(AstNode next | + pred = this and + first(next, succ) and + c instanceof SimpleCompletion + | + next = this.getValue() + or + not exists(this.getValue()) and + next = this.getBranch(0) + ) + or + last(this.getValue(), pred, c) and + first(this.getBranch(0), succ) and + c instanceof SimpleCompletion + or + exists(int i, WhenTree branch | branch = this.getBranch(i) | + last(branch.getLastPattern(), pred, c) and + first(this.getBranch(i + 1), succ) and + c.(ConditionalCompletion).getValue() = false + ) + } + } + + private class CharacterTree extends LeafTree, CharacterLiteral { } + + private class ClassTree extends BodyStmtPreOrderTree, Class { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getScopeExpr() and i = 0 and rescuable = false + or + result = this.getSuperclassExpr() and + i = count(this.getScopeExpr()) and + rescuable = true + or + result = this.getStmt(i - count(this.getScopeExpr()) - count(this.getSuperclassExpr())) and + rescuable = true + } + } + + private class ClassVariableTree extends LeafTree, ClassVariableAccess { } + + private class ConditionalExprTree extends PostOrderTree, ConditionalExpr { + final override predicate propagatesAbnormal(AstNode child) { + child = this.getCondition() or child = this.getBranch(_) + } + + final override predicate first(AstNode first) { first(this.getCondition(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(boolean b | + last(this.getCondition(), pred, c) and + b = c.(BooleanCompletion).getValue() + | + first(this.getBranch(b), succ) + or + not exists(this.getBranch(b)) and + succ = this + ) + or + last(this.getBranch(_), pred, c) and + succ = this and + c instanceof NormalCompletion + } + } + + private class ConditionalLoopTree extends PostOrderTree, ConditionalLoop { + final override predicate propagatesAbnormal(AstNode child) { child = this.getCondition() } + + final override predicate first(AstNode first) { first(this.getCondition(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getCondition(), pred, c) and + this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue()) and + first(this.getBody(), succ) + or + last(this.getBody(), pred, c) and + first(this.getCondition(), succ) and + c.continuesLoop() + or + last(this.getBody(), pred, c) and + first(this.getBody(), succ) and + c instanceof RedoCompletion + or + succ = this and + ( + last(this.getCondition(), pred, c) and + this.entersLoopWhenConditionIs(c.(BooleanCompletion).getValue().booleanNot()) + or + last(this.getBody(), pred, c) and + not c.continuesLoop() and + not c instanceof BreakCompletion and + not c instanceof RedoCompletion + or + last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + ) + } + } + + private class ConstantAccessTree extends PostOrderTree, ConstantAccess { + ConstantAccessTree() { + not this instanceof Class and + not this instanceof Module + } + + final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() } + + final override predicate first(AstNode first) { + first(this.getScopeExpr(), first) + or + not exists(this.getScopeExpr()) and + first = this + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getScopeExpr(), pred, c) and + succ = this and + c instanceof NormalCompletion + } + } + + /** A parameter that may have a default value. */ + abstract class DefaultValueParameterTree extends ControlFlowTree { + abstract Expr getDefaultValueExpr(); + + abstract AstNode getAccessNode(); + + predicate hasDefaultValue() { exists(this.getDefaultValueExpr()) } + + final override predicate propagatesAbnormal(AstNode child) { + child = this.getDefaultValueExpr() or child = this.getAccessNode() + } + + final override predicate first(AstNode first) { first = this.getAccessNode() } + + final override predicate last(AstNode last, Completion c) { + last(this.getDefaultValueExpr(), last, c) and + c instanceof NormalCompletion + or + last = this.getAccessNode() and + ( + not this.hasDefaultValue() and + c instanceof SimpleCompletion + or + this.hasDefaultValue() and + c.(MatchingCompletion).getValue() = true + ) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + pred = this.getAccessNode() and + first(this.getDefaultValueExpr(), succ) and + c.(MatchingCompletion).getValue() = false + } + } + + private class DoBlockTree extends BodyStmtPostOrderTree, DoBlock { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + } + + private class EmptyStatementTree extends LeafTree, EmptyStmt { } + + class EndBlockTree extends ScopeTree, EndBlock { + final override ControlFlowTree getChildNode(int i) { result = this.getStmt(i) } + } + + private class ForInTree extends LeafTree, ForIn { } + + /** + * Control flow of a for-in loop + * + * For example, this program fragment: + * + * ```rb + * for arg in args do + * puts arg + * end + * puts "done"; + * ``` + * + * has the following control flow graph: + * + * ``` + * args + * | + * in------<----- + * / \ \ + * / \ | + * / \ | + * / \ | + * empty non-empty | + * | \ | + * for \ | + * | arg | + * | | | + * puts "done" puts arg | + * \___/ + * ``` + */ + private class ForTree extends PostOrderTree, ForRange { + final override predicate propagatesAbnormal(AstNode child) { + child = this.getPattern() or child = this.getValue() + } + + final override predicate first(AstNode first) { first(this.getValue(), first) } + + /** + * for pattern in array do body end + * ``` + * array +-> in +--[non empty]--> pattern -> body -> in + * |--[empty]--> for + * ``` + */ + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getValue(), pred, c) and + first(this.getIn(), succ) and + c instanceof SimpleCompletion + or + last(this.getIn(), pred, c) and + first(this.getPattern(), succ) and + c.(EmptinessCompletion).getValue() = false + or + last(this.getPattern(), pred, c) and + first(this.getBody(), succ) and + c instanceof NormalCompletion + or + last(this.getBody(), pred, c) and + first(this.getIn(), succ) and + c.continuesLoop() + or + last(this.getBody(), pred, c) and + first(this.getBody(), succ) and + c instanceof RedoCompletion + or + succ = this and + ( + last(this.getIn(), pred, c) and + c.(EmptinessCompletion).getValue() = true + or + last(this.getBody(), pred, c) and + not c.continuesLoop() and + not c instanceof BreakCompletion and + not c instanceof RedoCompletion + or + last(this.getBody(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + ) + } + } + + private class GlobalVariableTree extends LeafTree, GlobalVariableAccess { } + + private class HashLiteralTree extends StandardPostOrderTree, HashLiteral { + final override ControlFlowTree getChildNode(int i) { result = this.getElement(i) } + } + + private class HashSplatArgumentTree extends StandardPostOrderTree, HashSplatArgument { + final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } + } + + private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { } + + private class HereDocTree extends StandardPreOrderTree, HereDoc { + final override ControlFlowTree getChildNode(int i) { result = this.getComponent(i) } + } + + private class InstanceVariableTree extends LeafTree, InstanceVariableAccess { } + + private class KeywordParameterTree extends DefaultValueParameterTree, KeywordParameter { + final override Expr getDefaultValueExpr() { result = this.getDefaultValue() } + + final override AstNode getAccessNode() { result = this.getDefiningAccess() } + } + + private class LambdaTree extends BodyStmtPostOrderTree, Lambda { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + } + + private class LocalVariableAccessTree extends LeafTree, LocalVariableAccess { } + + private class LogicalAndTree extends PostOrderTree, LogicalAndExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnOperand() } + + final override predicate first(AstNode first) { first(this.getLeftOperand(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getLeftOperand(), pred, c) and + c instanceof TrueCompletion and + first(this.getRightOperand(), succ) + or + last(this.getLeftOperand(), pred, c) and + c instanceof FalseCompletion and + succ = this + or + last(this.getRightOperand(), pred, c) and + c instanceof NormalCompletion and + succ = this + } + } + + private class LogicalNotTree extends PostOrderTree, NotExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getOperand() } + + final override predicate first(AstNode first) { first(this.getOperand(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + succ = this and + last(this.getOperand(), pred, c) and + c instanceof NormalCompletion + } + } + + private class LogicalOrTree extends PostOrderTree, LogicalOrExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnOperand() } + + final override predicate first(AstNode first) { first(this.getLeftOperand(), first) } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(this.getLeftOperand(), pred, c) and + c instanceof FalseCompletion and + first(this.getRightOperand(), succ) + or + last(this.getLeftOperand(), pred, c) and + c instanceof TrueCompletion and + succ = this + or + last(this.getRightOperand(), pred, c) and + c instanceof NormalCompletion and + succ = this + } + } + + private class MethodCallTree extends StandardPostOrderTree, MethodCall { + final override ControlFlowTree getChildNode(int i) { + result = this.getReceiver() and i = 0 + or + result = this.getArgument(i - count(this.getReceiver())) + or + result = this.getBlock() and i = count(this.getReceiver()) + this.getNumberOfArguments() + } + } + + private class MethodNameTree extends LeafTree, MethodName, ASTInternal::TTokenMethodName { } + + private class MethodTree extends BodyStmtPostOrderTree, Method { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + } + + private class ModuleTree extends BodyStmtPreOrderTree, Module { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getScopeExpr() and i = 0 and rescuable = false + or + result = BodyStmtPreOrderTree.super.getBodyChild(i - count(this.getScopeExpr()), rescuable) + } + } + + private class NilTree extends LeafTree, NilLiteral { } + + private class NumericLiteralTree extends LeafTree, NumericLiteral { } + + private class OptionalParameterTree extends DefaultValueParameterTree, OptionalParameter { + final override Expr getDefaultValueExpr() { result = this.getDefaultValue() } + + final override AstNode getAccessNode() { result = this.getDefiningAccess() } + } + + private class PairTree extends StandardPostOrderTree, Pair { + final override ControlFlowTree getChildNode(int i) { + result = this.getKey() and i = 0 + or + result = this.getValue() and i = 1 + } + } + + private class RangeLiteralTree extends StandardPostOrderTree, RangeLiteral { + final override ControlFlowTree getChildNode(int i) { + result = this.getBegin() and i = 0 + or + result = this.getEnd() and i = 1 + } + } + + private class RedoStmtTree extends LeafTree, RedoStmt { } + + private class RescueModifierTree extends PreOrderTree, RescueModifierExpr { final override predicate propagatesAbnormal(AstNode child) { child = this.getHandler() } final override predicate last(AstNode last, Completion c) { @@ -1145,58 +1020,279 @@ module Trees { } } - private class SelfTree extends LeafTree, Self { } + private class RescueTree extends PreOrderTree, RescueClause { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnException() } - private class SetterTree extends LeafTree, Setter { } + private Expr getLastException() { + exists(int i | result = this.getException(i) and not exists(this.getException(i + 1))) + } - private class SingletonClassTree extends RescueEnsureBlockTree, PreOrderTree, SingletonClass { - final override AstNode getChildNode(int i, boolean rescuable) { - rescuable = true and + predicate lastMatch(AstNode last, Completion c) { + last(this.getBody(), last, c) + or + not exists(this.getBody()) and ( - result = this.getValue() and i = 0 + last(this.getVariableExpr(), last, c) or - result = this.getChild(i - 1) + not exists(this.getVariableExpr()) and + ( + last(this.getAnException(), last, c) and + c.(MatchingCompletion).getValue() = true + or + not exists(this.getAnException()) and + last = this and + isValidFor(c, this) + ) ) } - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } - } - - private class SingletonMethodTree extends RescueEnsureBlockTree, PostOrderTree, SingletonMethod { - final override AstNode getChildNode(int i, boolean rescuable) { - result = this.getParameters() and - i = 0 and - rescuable = false - or - result = this.getChild(i - 1) and - rescuable = true + predicate lastNoMatch(AstNode last, Completion c) { + last(this.getLastException(), last, c) and + c.(MatchingCompletion).getValue() = false } - final override predicate first(AstNode first) { first(this.getObject(), first) } + final override predicate last(AstNode last, Completion c) { + this.lastNoMatch(last, c) + or + this.lastMatch(last, c) + } + + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + exists(AstNode next | + pred = this and + first(next, succ) and + c instanceof SimpleCompletion + | + next = this.getException(0) + or + not exists(this.getException(0)) and + ( + next = this.getVariableExpr() + or + not exists(this.getVariableExpr()) and + next = this.getBody() + ) + ) + or + exists(AstNode next | + last(this.getAnException(), pred, c) and + first(next, succ) and + c.(MatchingCompletion).getValue() = true + | + next = this.getVariableExpr() + or + not exists(this.getVariableExpr()) and + next = this.getBody() + ) + or + exists(int i | + last(this.getException(i), pred, c) and + c.(MatchingCompletion).getValue() = false and + first(this.getException(i + 1), succ) + ) + or + last(this.getVariableExpr(), pred, c) and + first(this.getBody(), succ) and + c instanceof NormalCompletion + } + } + + private class RetryStmtTree extends LeafTree, RetryStmt { } + + private class ReturningStmtTree extends StandardPostOrderTree, ReturningStmt { + final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } + } + + private class SelfTree extends LeafTree, Self { } + + private class SimpleParameterTree extends NonDefaultValueParameterTree, SimpleParameter { } + + // Corner case: For duplicated '_' parameters, only the first occurence has a defining + // access. For subsequent parameters we simply include the parameter itself in the CFG + private class SimpleParameterTreeDupUnderscore extends LeafTree, SimpleParameter { + SimpleParameterTreeDupUnderscore() { not exists(this.getDefiningAccess()) } + } + + /** + * Control-flow tree for any post-order StmtSequence that doesn't have a more + * specific implementation. + * TODO: make all StmtSequence tree classes post-order, and simplify class + * hierarchy. + */ + private class SimplePostOrderStmtSequenceTree extends StmtSequenceTree, PostOrderTree { + SimplePostOrderStmtSequenceTree() { + this instanceof StringInterpolationComponent or + this instanceof ParenthesizedExpr + } + + final override predicate first(AstNode first) { first(this.getStmt(0), first) } + + override predicate propagatesAbnormal(AstNode child) { child = this.getAStmt() } + } + + /** + * Control-flow tree for any pre-order StmtSequence that doesn't have a more + * specific implementation. + * TODO: make all StmtSequence tree classes post-order, and simplify class + * hierarchy. + */ + private class SimplePreOrderStmtSequenceTree extends StmtSequenceTree, PreOrderTree { + SimplePreOrderStmtSequenceTree() { + not this instanceof BodyStmtTree and + not this instanceof EndBlock and + not this instanceof StringInterpolationComponent and + not this instanceof Block and + not this instanceof ParenthesizedExpr + } + + override predicate propagatesAbnormal(AstNode child) { child = this.getAStmt() } + + override predicate isHidden() { + this instanceof ASTInternal::TElse or + this instanceof ASTInternal::TThen or + this instanceof ASTInternal::TDo + } + + final AstNode getLastChildNode() { result = this.getStmt(this.getNumberOfStatements() - 1) } + + final override predicate last(AstNode last, Completion c) { + last(this.getLastChildNode(), last, c) + or + not exists(this.getLastChildNode()) and + isValidFor(c, this) and + last = this + } override predicate succ(AstNode pred, AstNode succ, Completion c) { - RescueEnsureBlockTree.super.succ(pred, succ, c) + StmtSequenceTree.super.succ(pred, succ, c) + } + } + + private class SingletonClassTree extends BodyStmtPreOrderTree, SingletonClass { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + ( + result = this.getValue() and i = 0 and rescuable = false + or + result = BodyStmtPreOrderTree.super.getBodyChild(i - 1, rescuable) + ) + } + } + + private class SingletonMethodTree extends BodyStmtPostOrderTree, SingletonMethod { + /** Gets the `i`th child in the body of this block. */ + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getParameter(i) and rescuable = false + or + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + + override predicate first(AstNode first) { first(this.getObject(), first) } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + BodyStmtPostOrderTree.super.succ(pred, succ, c) or last(this.getObject(), pred, c) and - first(this.getName(), succ) and - c instanceof NormalCompletion - or - last(this.getName(), pred, c) and succ = this and c instanceof NormalCompletion } } - private class SplatParameterTree extends LeafTree, SplatParameter { } + private class SplatArgumentTree extends StandardPostOrderTree, SplatArgument { + final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } + } - private class SuperTree extends LeafTree, Super { } + private class SplatParameterTree extends NonDefaultValueParameterTree, SplatParameter { } - private class TrueTree extends LeafTree, True { } + abstract class StmtSequenceTree extends ControlFlowTree, StmtSequence { + override predicate propagatesAbnormal(AstNode child) { none() } - private class WhenTree extends PreOrderTree, When { - final override predicate propagatesAbnormal(AstNode child) { child = this.getPattern(_) } + /** Gets the `i`th child in the body of this body statement. */ + AstNode getBodyChild(int i, boolean rescuable) { + result = this.getStmt(i) and + rescuable = true + } - final Pattern getLastPattern() { + AstNode getLastBodyChild() { + exists(int i | + result = this.getBodyChild(i, _) and + not exists(this.getBodyChild(i + 1, _)) + ) + } + + override predicate succ(AstNode pred, AstNode succ, Completion c) { + this instanceof PreOrderTree and + pred = this and + first(this.getBodyChild(0, _), succ) and + c instanceof SimpleCompletion + or + this instanceof PostOrderTree and + succ = this and + last(this.getLastBodyChild(), pred, c) and + c instanceof NormalCompletion + or + // Normal left-to-right evaluation in the body + exists(int i | + last(this.getBodyChild(i, _), pred, c) and + first(this.getBodyChild(i + 1, _), succ) and + c instanceof NormalCompletion + ) + } + } + + private class StringConcatenationTree extends StandardPostOrderTree, StringConcatenation { + final override ControlFlowTree getChildNode(int i) { result = this.getString(i) } + + override predicate isHidden() { any() } + } + + private class StringTextComponentTree extends LeafTree, StringTextComponent { + override predicate isHidden() { any() } + } + + private class StringEscapeSequenceComponentTree extends LeafTree, StringEscapeSequenceComponent { + override predicate isHidden() { any() } + } + + private class StringlikeLiteralTree extends StandardPostOrderTree, StringlikeLiteral { + StringlikeLiteralTree() { not this instanceof HereDoc } + + final override ControlFlowTree getChildNode(int i) { result = this.getComponent(i) } + } + + private class SuperCallTree extends StandardPostOrderTree, SuperCall { + final override ControlFlowTree getChildNode(int i) { result = this.getArgument(i) } + } + + private class ToplevelTree extends BodyStmtPreOrderTree, Toplevel { + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getBeginBlock(i) and rescuable = true + or + result = BodyStmtPreOrderTree.super.getBodyChild(i - count(this.getABeginBlock()), rescuable) + } + + override predicate isHidden() { any() } + } + + private class TuplePatternTree extends StandardPostOrderTree, TuplePattern { + final override ControlFlowTree getChildNode(int i) { result = this.getElement(i) } + } + + private class UnaryOperationTree extends StandardPostOrderTree, UnaryOperation { + // Logical NOT is handled separately + UnaryOperationTree() { not this instanceof NotExpr } + + final override ControlFlowTree getChildNode(int i) { result = this.getOperand() and i = 0 } + } + + private class UndefStmtTree extends StandardPreOrderTree, UndefStmt { + final override ControlFlowTree getChildNode(int i) { result = this.getMethodName(i) } + } + + private class WhenTree extends PreOrderTree, WhenExpr { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAPattern() } + + final Expr getLastPattern() { exists(int i | result = this.getPattern(i) and not exists(this.getPattern(i + 1)) @@ -1215,7 +1311,7 @@ module Trees { first(this.getPattern(0), succ) and c instanceof SimpleCompletion or - exists(int i, Pattern p, boolean b | + exists(int i, Expr p, boolean b | p = this.getPattern(i) and last(p, pred, c) and b = c.(ConditionalCompletion).getValue() @@ -1229,45 +1325,31 @@ module Trees { } } - private class ConditionalLoopTree extends PostOrderTree, ConditionalLoopAstNode { - final override predicate propagatesAbnormal(AstNode child) { child = this.getConditionNode() } - - final override predicate first(AstNode first) { first(this.getConditionNode(), first) } - - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getConditionNode(), pred, c) and - this.continueLoop(c) and - first(this.getBodyNode(), succ) - or - last(this.getBodyNode(), pred, c) and - first(this.getConditionNode(), succ) and - c.continuesLoop() - or - last(this.getBodyNode(), pred, c) and - first(this.getBodyNode(), succ) and - c instanceof RedoCompletion - or - succ = this and - ( - last(this.getConditionNode(), pred, c) and - this.endLoop(c) - or - last(this.getBodyNode(), pred, c) and - not c.continuesLoop() and - not c instanceof BreakCompletion and - not c instanceof RedoCompletion - or - last(this.getBodyNode(), pred, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) - ) - } + // TODO: make post-order + private class YieldCallTree extends StandardPreOrderTree, YieldCall { + final override ControlFlowTree getChildNode(int i) { result = this.getArgument(i) } } + + /** Gets a child of `n` that is in CFG scope `scope`. */ + pragma[noinline] + private AstNode getAChildInScope(AstNode n, CfgScope scope) { + result.getParent() = n and + scope = getCfgScope(result) + } +} + +private Scope parent(Scope n) { + result = n.getOuterScope() and + not n instanceof CfgScope::Range_ } cached private module Cached { /** Gets the CFG scope of node `n`. */ cached - CfgScope getCfgScope(AstNode n) { result = unique(CfgScope scope | scope = parent*(parentOf(n))) } + CfgScope getCfgScope(AstNode n) { + result = parent*(ASTInternal::fromGenerated(scopeOf(ASTInternal::toGenerated(n)))) + } private predicate isAbnormalExitType(SuccessorType t) { t instanceof RaiseSuccessor or t instanceof ExitSuccessor @@ -1287,7 +1369,7 @@ private module Cached { succExitSplits(b.getANode(), _, scope, _) ) } or - TAstNode(AstNode n, Splits splits) { + TAstCfgNode(AstNode n, Splits splits) { exists(Reachability::SameSplitsBlock b | b.isReachable(splits) | n = b.getANode()) } @@ -1311,10 +1393,10 @@ private module Cached { exists(CfgScope scope, AstNode succElement, Splits succSplits | pred = TEntryNode(scope) and succEntrySplits(scope, succElement, succSplits, t) and - result = TAstNode(succElement, succSplits) + result = TAstCfgNode(succElement, succSplits) ) or - exists(AstNode predNode, Splits predSplits | pred = TAstNode(predNode, predSplits) | + exists(AstNode predNode, Splits predSplits | pred = TAstCfgNode(predNode, predSplits) | exists(CfgScope scope, boolean normal | succExitSplits(predNode, predSplits, scope, t) and (if isAbnormalExitType(t) then normal = false else normal = true) and @@ -1324,7 +1406,7 @@ private module Cached { exists(AstNode succElement, Splits succSplits, Completion c | succSplits(predNode, predSplits, succElement, succSplits, c) and t = c.getAMatchingSuccessorType() and - result = TAstNode(succElement, succSplits) + result = TAstCfgNode(succElement, succSplits) ) ) or diff --git a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll index c54cbc27b65..8543c64f055 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll @@ -16,13 +16,13 @@ private module Cached { cached newtype TSplitKind = TConditionalCompletionSplitKind() or - TEnsureSplitKind(int nestLevel) { nestLevel = any(Trees::RescueEnsureBlockTree t).nestLevel() } + TEnsureSplitKind(int nestLevel) { nestLevel = any(Trees::BodyStmtTree t).getNestLevel() } cached newtype TSplit = TConditionalCompletionSplit(ConditionalCompletion c) or TEnsureSplit(EnsureSplitting::EnsureSplitType type, int nestLevel) { - nestLevel = any(Trees::RescueEnsureBlockTree t).nestLevel() + nestLevel = any(Trees::BodyStmtTree t).getNestLevel() } cached @@ -226,7 +226,7 @@ private module ConditionalCompletionSplitting { last(succ.(LogicalOrExpr).getAnOperand(), pred, c) and completion = c or - last(succ.(ParenthesizedExpr).getLastExpr(), pred, c) and + last(succ.(StmtSequence).getLastStmt(), pred, c) and completion = c or last(succ.(ConditionalExpr).getBranch(_), pred, c) and @@ -277,12 +277,11 @@ module EnsureSplitting { /** A node that belongs to an `ensure` block. */ private class EnsureNode extends AstNode { - private Trees::RescueEnsureBlockTree block; + private Trees::BodyStmtTree block; EnsureNode() { this = block.getAnEnsureDescendant() } - /** Gets the immediate block that this node belongs to. */ - Trees::RescueEnsureBlockTree getBlock() { result = block } + int getNestLevel() { result = block.getNestLevel() } /** Holds if this node is the entry node in the `ensure` block it belongs to. */ predicate isEntryNode() { first(block.getEnsure(), this) } @@ -355,7 +354,7 @@ module EnsureSplitting { pragma[noinline] private predicate hasEntry0(AstNode pred, EnsureNode succ, int nestLevel, Completion c) { succ.isEntryNode() and - nestLevel = succ.getBlock().nestLevel() and + nestLevel = succ.getNestLevel() and succ(pred, succ, c) } @@ -378,11 +377,9 @@ module EnsureSplitting { } pragma[noinline] - private predicate exit0( - AstNode pred, Trees::RescueEnsureBlockTree block, int nestLevel, Completion c - ) { + private predicate exit0(AstNode pred, Trees::BodyStmtTree block, int nestLevel, Completion c) { this.appliesToPredecessor(pred) and - nestLevel = block.nestLevel() and + nestLevel = block.getNestLevel() and block.lastInner(pred, c) } @@ -391,9 +388,7 @@ module EnsureSplitting { * `inherited` indicates whether `c` is an inherited completion from the * body. */ - private predicate exit( - Trees::RescueEnsureBlockTree block, AstNode pred, Completion c, boolean inherited - ) { + private predicate exit(Trees::BodyStmtTree block, AstNode pred, Completion c, boolean inherited) { exists(EnsureSplitType type | exit0(pred, block, this.getNestLevel(), c) and type = this.getType() @@ -477,10 +472,10 @@ module EnsureSplitting { if en.isEntryNode() then // entering a nested `ensure` block - en.getBlock().nestLevel() > this.getNestLevel() + en.getNestLevel() > this.getNestLevel() else // staying in the same (possibly nested) `ensure` block as `pred` - en.getBlock().nestLevel() >= this.getNestLevel() + en.getNestLevel() >= this.getNestLevel() ) } } diff --git a/ql/src/codeql_ruby/dataflow/internal/DataFlowPrivate.qll b/ql/src/codeql_ruby/dataflow/internal/DataFlowPrivate.qll index 222fa8ac7bb..2352f9c4693 100644 --- a/ql/src/codeql_ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ql/src/codeql_ruby/dataflow/internal/DataFlowPrivate.qll @@ -126,7 +126,7 @@ private module Cached { or nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::AssignExprCfgNode).getRhs() or - nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StmtSequenceCfgNode).getLastExpr() + nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::StmtSequenceCfgNode).getLastStmt() or nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::ConditionalExprCfgNode).getBranch(_) or diff --git a/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll b/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll index f8d4671e563..ad2d2932044 100644 --- a/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll +++ b/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll @@ -6,7 +6,7 @@ private import CfgNodes::ExprNodes /** Holds if `v` is uninitialized at index `i` in entry block `bb`. */ predicate uninitializedWrite(EntryBasicBlock bb, int i, LocalVariable v) { - v.getDeclaringScope().getScopeElement() = bb.getScope() and + v.getDeclaringScope() = bb.getScope() and i = -1 } @@ -49,7 +49,7 @@ private predicate capturedExitRead(AnnotatedExitBasicBlock bb, int i, LocalVaria i = bb.length() } -private CfgScope getCaptureOuterCfgScope(Callable scope) { +private CfgScope getCaptureOuterCfgScope(CfgScope scope) { result = scope.getOuterCfgScope() and ( scope instanceof Block diff --git a/ql/src/codeql_ruby/printAst.qll b/ql/src/codeql_ruby/printAst.qll index 3556e20d45c..2737e456251 100644 --- a/ql/src/codeql_ruby/printAst.qll +++ b/ql/src/codeql_ruby/printAst.qll @@ -27,6 +27,19 @@ class PrintAstNode extends AstNode { string getProperty(string key) { key = "semmle.label" and result = "[" + concat(this.getAPrimaryQlClass(), ", ") + "] " + this.toString() + or + key = "semmle.order" and + result = + any(int i | + this = + rank[i](AstNode p | + | + p + order by + p.getLocation().getFile().getBaseName(), p.getLocation().getFile().getAbsolutePath(), + p.getLocation().getStartLine(), p.getLocation().getStartColumn() + ) + ).toString() } /** @@ -39,7 +52,7 @@ class PrintAstNode extends AstNode { /** * Gets the child node that is accessed using the predicate `edgeName`. */ - PrintAstNode getChild(string edgeName) { range.child(edgeName, result) } + PrintAstNode getChild(string edgeName) { result = this.getAChild(edgeName) } } private predicate shouldPrintNode(AstNode n) { @@ -62,9 +75,14 @@ query predicate nodes(PrintAstNode node, string key, string value) { query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) { source.shouldPrint() and target.shouldPrint() and - key = "semmle.label" and target = source.getAChild() and - value = concat(string name | source.getChild(name) = target | name, "/") + ( + key = "semmle.label" and + value = concat(string name | source.getChild(name) = target | name, "/") + or + key = "semmle.order" and + value = target.getProperty("semmle.order") + ) } /** diff --git a/ql/src/queries/performance/UseDetect.ql b/ql/src/queries/performance/UseDetect.ql new file mode 100644 index 00000000000..d17f5d6f8f9 --- /dev/null +++ b/ql/src/queries/performance/UseDetect.ql @@ -0,0 +1,65 @@ +/** + * @name Use detect + * @description Use 'detect' instead of 'select' followed by 'first' or 'last'. + * @kind problem + * @problem.severity warning + * @id rb/use-detect + * @tags performance rubocop + * @precision high + * + * This is an implementation of Rubocop rule + * https://github.com/rubocop/rubocop-performance/blob/master/lib/rubocop/cop/performance/detect.rb + */ + +import ruby +import codeql_ruby.dataflow.SSA + +/** A call that extracts the first or last element of a list. */ +class EndCall extends MethodCall { + string detect; + + EndCall() { + detect = "detect" and + ( + this.getMethodName() = "first" and + this.getNumberOfArguments() = 0 + or + this.getNumberOfArguments() = 1 and + this.getArgument(0).(IntegerLiteral).getValueText() = "0" + ) + or + detect = "reverse_detect" and + ( + this.getMethodName() = "last" and + this.getNumberOfArguments() = 0 + or + this.getNumberOfArguments() = 1 and + this.getArgument(0).(UnaryMinusExpr).getOperand().(IntegerLiteral).getValueText() = "1" + ) + } + + string detectCall() { result = detect } +} + +Expr getUniqueRead(Expr e) { + exists(AssignExpr ae | + e = ae.getRightOperand() and + forex(Ssa::WriteDefinition def | def.getWriteAccess() = ae.getLeftOperand() | + strictcount(def.getARead()) = 1 and + not def = any(Ssa::PhiNode phi).getAnInput() and + def.getARead() = result.getAControlFlowNode() + ) + ) +} + +class SelectBlock extends MethodCall { + SelectBlock() { + this.getMethodName() in ["select", "filter", "find_all"] and + exists(this.getBlock()) + } +} + +from EndCall call, SelectBlock selectBlock +where getUniqueRead*(selectBlock) = call.getReceiver() +select call, "Replace this call and $@ with '" + call.detectCall() + "'.", selectBlock, + "'select' call" diff --git a/ql/test/library-tests/ast/Ast.expected b/ql/test/library-tests/ast/Ast.expected index 5df02154eb5..8b74d275522 100644 --- a/ql/test/library-tests/ast/Ast.expected +++ b/ql/test/library-tests/ast/Ast.expected @@ -13,12 +13,14 @@ calls/calls.rb: # 17| getStmt: [MethodCall] call to foo # 17| getBlock: [BraceBlock] { ... } # 17| getParameter: [SimpleParameter] x +# 17| getDefiningAccess: [LocalVariableAccess] x # 17| getStmt: [AddExpr] ... + ... # 17| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 17| getAnOperand/getRightOperand: [IntegerLiteral] 1 # 20| getStmt: [MethodCall] call to foo # 20| getBlock: [DoBlock] do ... end # 20| getParameter: [SimpleParameter] x +# 20| getDefiningAccess: [LocalVariableAccess] x # 21| getStmt: [AddExpr] ... + ... # 21| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 21| getAnOperand/getRightOperand: [IntegerLiteral] 1 @@ -28,6 +30,7 @@ calls/calls.rb: # 25| getComponent: [StringTextComponent] foo # 25| getBlock: [DoBlock] do ... end # 25| getParameter: [SimpleParameter] x +# 25| getDefiningAccess: [LocalVariableAccess] x # 26| getStmt: [AddExpr] ... + ... # 26| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 26| getAnOperand/getRightOperand: [IntegerLiteral] 1 @@ -153,16 +156,20 @@ calls/calls.rb: # 144| getReceiver: [ConstantReadAccess] X # 148| getStmt: [Method] method_with_keyword_param # 148| getParameter: [KeywordParameter] keyword +# 148| getDefiningAccess: [LocalVariableAccess] keyword # 148| getDefaultValue: [MethodCall] call to foo # 150| getStmt: [Method] method_with_keyword_param2 # 150| getParameter: [KeywordParameter] keyword +# 150| getDefiningAccess: [LocalVariableAccess] keyword # 150| getDefaultValue: [MethodCall] call to foo # 150| getReceiver: [ConstantReadAccess] X # 154| getStmt: [Method] method_with_optional_param # 154| getParameter: [OptionalParameter] param +# 154| getDefiningAccess: [LocalVariableAccess] param # 154| getDefaultValue: [MethodCall] call to foo # 156| getStmt: [Method] method_with_optional_param2 # 156| getParameter: [OptionalParameter] param +# 156| getDefiningAccess: [LocalVariableAccess] param # 156| getDefaultValue: [MethodCall] call to foo # 156| getReceiver: [ConstantReadAccess] X # 160| getStmt: [Module] SomeModule @@ -383,12 +390,14 @@ calls/calls.rb: # 290| getStmt: [SuperCall] call to super # 290| getBlock: [BraceBlock] { ... } # 290| getParameter: [SimpleParameter] x +# 290| getDefiningAccess: [LocalVariableAccess] x # 290| getStmt: [AddExpr] ... + ... # 290| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 290| getAnOperand/getRightOperand: [IntegerLiteral] 1 # 291| getStmt: [SuperCall] call to super # 291| getBlock: [DoBlock] do ... end # 291| getParameter: [SimpleParameter] x +# 291| getDefiningAccess: [LocalVariableAccess] x # 291| getStmt: [MulExpr] ... * ... # 291| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 291| getAnOperand/getRightOperand: [IntegerLiteral] 2 @@ -397,6 +406,7 @@ calls/calls.rb: # 292| getArgument: [IntegerLiteral] 5 # 292| getBlock: [BraceBlock] { ... } # 292| getParameter: [SimpleParameter] x +# 292| getDefiningAccess: [LocalVariableAccess] x # 292| getStmt: [AddExpr] ... + ... # 292| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 292| getAnOperand/getRightOperand: [IntegerLiteral] 100 @@ -405,6 +415,7 @@ calls/calls.rb: # 293| getArgument: [IntegerLiteral] 7 # 293| getBlock: [DoBlock] do ... end # 293| getParameter: [SimpleParameter] x +# 293| getDefiningAccess: [LocalVariableAccess] x # 293| getStmt: [AddExpr] ... + ... # 293| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 293| getAnOperand/getRightOperand: [IntegerLiteral] 200 @@ -689,6 +700,7 @@ constants/constants.rb: # 19| getReceiver: [ConstantReadAccess] Names # 19| getBlock: [DoBlock] do ... end # 19| getParameter: [SimpleParameter] name +# 19| getDefiningAccess: [LocalVariableAccess] name # 20| getStmt: [MethodCall] call to puts # 20| getArgument: [StringLiteral] "#{...} #{...}" # 20| getComponent: [StringInterpolationComponent] #{...} @@ -1173,9 +1185,8 @@ control/loops.rb: # 24| getAnOperand/getRightOperand: [LocalVariableAccess] value # 28| getStmt: [ForExpr] for ... in ... # 28| getPattern: [TuplePattern] (..., ...) -# 28| getElement: [TuplePattern] (..., ...) -# 28| getElement: [LocalVariableAccess] key -# 28| getElement: [LocalVariableAccess] value +# 28| getElement: [LocalVariableAccess] key +# 28| getElement: [LocalVariableAccess] value # 28| : [???] In # 28| getValue: [HashLiteral] {...} # 28| getElement: [Pair] Pair @@ -1528,8 +1539,11 @@ params/params.rb: # 1| [Toplevel] params.rb # 4| getStmt: [Method] identifier_method_params # 4| getParameter: [SimpleParameter] foo +# 4| getDefiningAccess: [LocalVariableAccess] foo # 4| getParameter: [SimpleParameter] bar +# 4| getDefiningAccess: [LocalVariableAccess] bar # 4| getParameter: [SimpleParameter] baz +# 4| getDefiningAccess: [LocalVariableAccess] baz # 8| getStmt: [AssignExpr] ... = ... # 8| getAnOperand/getLeftOperand: [LocalVariableAccess] hash # 8| getAnOperand/getRightOperand: [HashLiteral] {...} @@ -1537,7 +1551,9 @@ params/params.rb: # 9| getReceiver: [LocalVariableAccess] hash # 9| getBlock: [DoBlock] do ... end # 9| getParameter: [SimpleParameter] key +# 9| getDefiningAccess: [LocalVariableAccess] key # 9| getParameter: [SimpleParameter] value +# 9| getDefiningAccess: [LocalVariableAccess] value # 10| getStmt: [MethodCall] call to puts # 10| getArgument: [StringLiteral] "#{...} -> #{...}" # 10| getComponent: [StringInterpolationComponent] #{...} @@ -1549,7 +1565,9 @@ params/params.rb: # 14| getAnOperand/getLeftOperand: [LocalVariableAccess] sum # 14| getAnOperand/getRightOperand: [Lambda] -> { ... } # 14| getParameter: [SimpleParameter] foo +# 14| getDefiningAccess: [LocalVariableAccess] foo # 14| getParameter: [SimpleParameter] bar +# 14| getDefiningAccess: [LocalVariableAccess] bar # 14| getStmt: [AddExpr] ... + ... # 14| getAnOperand/getLeftOperand: [LocalVariableAccess] foo # 14| getAnOperand/getRightOperand: [LocalVariableAccess] bar @@ -1589,24 +1607,36 @@ params/params.rb: # 26| getAnOperand/getRightOperand: [LocalVariableAccess] fourth # 30| getStmt: [Method] method_with_splat # 30| getParameter: [SimpleParameter] wibble +# 30| getDefiningAccess: [LocalVariableAccess] wibble # 30| getParameter: [SplatParameter] *splat +# 30| getDefiningAccess: [LocalVariableAccess] splat # 30| getParameter: [HashSplatParameter] **double_splat +# 30| getDefiningAccess: [LocalVariableAccess] double_splat # 34| getStmt: [MethodCall] call to each # 34| getReceiver: [LocalVariableAccess] array # 34| getBlock: [DoBlock] do ... end # 34| getParameter: [SimpleParameter] val +# 34| getDefiningAccess: [LocalVariableAccess] val # 34| getParameter: [SplatParameter] *splat +# 34| getDefiningAccess: [LocalVariableAccess] splat # 34| getParameter: [HashSplatParameter] **double_splat +# 34| getDefiningAccess: [LocalVariableAccess] double_splat # 38| getStmt: [AssignExpr] ... = ... # 38| getAnOperand/getLeftOperand: [LocalVariableAccess] lambda_with_splats # 38| getAnOperand/getRightOperand: [Lambda] -> { ... } # 38| getParameter: [SimpleParameter] x +# 38| getDefiningAccess: [LocalVariableAccess] x # 38| getParameter: [SplatParameter] *blah +# 38| getDefiningAccess: [LocalVariableAccess] blah # 38| getParameter: [HashSplatParameter] **wibble +# 38| getDefiningAccess: [LocalVariableAccess] wibble # 41| getStmt: [Method] method_with_keyword_params # 41| getParameter: [SimpleParameter] x +# 41| getDefiningAccess: [LocalVariableAccess] x # 41| getParameter: [KeywordParameter] foo +# 41| getDefiningAccess: [LocalVariableAccess] foo # 41| getParameter: [KeywordParameter] bar +# 41| getDefiningAccess: [LocalVariableAccess] bar # 41| getDefaultValue: [IntegerLiteral] 7 # 42| getStmt: [AddExpr] ... + ... # 42| getAnOperand/getLeftOperand: [AddExpr] ... + ... @@ -1615,6 +1645,7 @@ params/params.rb: # 42| getAnOperand/getRightOperand: [LocalVariableAccess] bar # 46| getStmt: [Method] use_block_with_keyword # 46| getParameter: [BlockParameter] &block +# 46| getDefiningAccess: [LocalVariableAccess] block # 47| getStmt: [MethodCall] call to puts # 47| getArgument: [MethodCall] call to call # 47| getReceiver: [LocalVariableAccess] block @@ -1627,7 +1658,9 @@ params/params.rb: # 49| getStmt: [MethodCall] call to use_block_with_keyword # 49| getBlock: [DoBlock] do ... end # 49| getParameter: [KeywordParameter] xx +# 49| getDefiningAccess: [LocalVariableAccess] xx # 49| getParameter: [KeywordParameter] yy +# 49| getDefiningAccess: [LocalVariableAccess] yy # 49| getDefaultValue: [IntegerLiteral] 100 # 50| getStmt: [AddExpr] ... + ... # 50| getAnOperand/getLeftOperand: [LocalVariableAccess] xx @@ -1636,8 +1669,11 @@ params/params.rb: # 53| getAnOperand/getLeftOperand: [LocalVariableAccess] lambda_with_keyword_params # 53| getAnOperand/getRightOperand: [Lambda] -> { ... } # 53| getParameter: [SimpleParameter] x +# 53| getDefiningAccess: [LocalVariableAccess] x # 53| getParameter: [KeywordParameter] y +# 53| getDefiningAccess: [LocalVariableAccess] y # 53| getParameter: [KeywordParameter] z +# 53| getDefiningAccess: [LocalVariableAccess] z # 53| getDefaultValue: [IntegerLiteral] 3 # 54| getStmt: [AddExpr] ... + ... # 54| getAnOperand/getLeftOperand: [AddExpr] ... + ... @@ -1646,12 +1682,16 @@ params/params.rb: # 54| getAnOperand/getRightOperand: [LocalVariableAccess] z # 58| getStmt: [Method] method_with_optional_params # 58| getParameter: [SimpleParameter] val1 +# 58| getDefiningAccess: [LocalVariableAccess] val1 # 58| getParameter: [OptionalParameter] val2 +# 58| getDefiningAccess: [LocalVariableAccess] val2 # 58| getDefaultValue: [IntegerLiteral] 0 # 58| getParameter: [OptionalParameter] val3 +# 58| getDefiningAccess: [LocalVariableAccess] val3 # 58| getDefaultValue: [IntegerLiteral] 100 # 62| getStmt: [Method] use_block_with_optional # 62| getParameter: [BlockParameter] &block +# 62| getDefiningAccess: [LocalVariableAccess] block # 63| getStmt: [MethodCall] call to call # 63| getReceiver: [LocalVariableAccess] block # 63| getArgument: [StringLiteral] "Zeus" @@ -1659,7 +1699,9 @@ params/params.rb: # 65| getStmt: [MethodCall] call to use_block_with_optional # 65| getBlock: [DoBlock] do ... end # 65| getParameter: [SimpleParameter] name +# 65| getDefiningAccess: [LocalVariableAccess] name # 65| getParameter: [OptionalParameter] age +# 65| getDefiningAccess: [LocalVariableAccess] age # 65| getDefaultValue: [IntegerLiteral] 99 # 66| getStmt: [MethodCall] call to puts # 66| getArgument: [StringLiteral] "#{...} is #{...} years old" @@ -1673,9 +1715,12 @@ params/params.rb: # 70| getAnOperand/getLeftOperand: [LocalVariableAccess] lambda_with_optional_params # 70| getAnOperand/getRightOperand: [Lambda] -> { ... } # 70| getParameter: [SimpleParameter] a +# 70| getDefiningAccess: [LocalVariableAccess] a # 70| getParameter: [OptionalParameter] b +# 70| getDefiningAccess: [LocalVariableAccess] b # 70| getDefaultValue: [IntegerLiteral] 1000 # 70| getParameter: [OptionalParameter] c +# 70| getDefiningAccess: [LocalVariableAccess] c # 70| getDefaultValue: [IntegerLiteral] 20 # 70| getStmt: [AddExpr] ... + ... # 70| getAnOperand/getLeftOperand: [AddExpr] ... + ... diff --git a/ql/test/library-tests/ast/Ast.ql b/ql/test/library-tests/ast/Ast.ql index f851015a1e2..babafaac548 100644 --- a/ql/test/library-tests/ast/Ast.ql +++ b/ql/test/library-tests/ast/Ast.ql @@ -3,22 +3,3 @@ */ import codeql_ruby.printAst - -class OrderedAstNode extends PrintAstNode { - override string getProperty(string key) { - result = super.getProperty(key) - or - key = "semmle.order" and - result = - any(int i | - this = - rank[i](AstNode p | - | - p - order by - p.getLocation().getFile().getBaseName(), p.getLocation().getFile().getAbsolutePath(), - p.getLocation().getStartLine() - ) - ).toString() - } -} diff --git a/ql/test/library-tests/ast/calls/calls.ql b/ql/test/library-tests/ast/calls/calls.ql index 32b1183317f..4d244c2cf4c 100644 --- a/ql/test/library-tests/ast/calls/calls.ql +++ b/ql/test/library-tests/ast/calls/calls.ql @@ -1,5 +1,4 @@ import ruby -import codeql_ruby.ast.internal.TreeSitter private string getMethodName(Call c) { result = c.(MethodCall).getMethodName() diff --git a/ql/test/library-tests/ast/control/Loop.expected b/ql/test/library-tests/ast/control/Loop.expected index f93e73f5ee8..6ed5a497ec4 100644 --- a/ql/test/library-tests/ast/control/Loop.expected +++ b/ql/test/library-tests/ast/control/Loop.expected @@ -29,7 +29,8 @@ forExprs forExprsTuplePatterns | loops.rb:22:1:25:3 | for ... in ... | loops.rb:22:5:22:14 | (..., ...) | 0 | loops.rb:22:5:22:7 | key | | loops.rb:22:1:25:3 | for ... in ... | loops.rb:22:5:22:14 | (..., ...) | 1 | loops.rb:22:10:22:14 | value | -| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | 0 | loops.rb:28:5:28:16 | (..., ...) | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | 0 | loops.rb:28:6:28:8 | key | +| loops.rb:28:1:32:3 | for ... in ... | loops.rb:28:5:28:16 | (..., ...) | 1 | loops.rb:28:11:28:15 | value | whileExprs | loops.rb:35:1:39:3 | while ... | loops.rb:35:7:35:11 | ... < ... | loops.rb:35:12:39:3 | ...; ... | 0 | loops.rb:36:3:36:8 | ... += ... | | loops.rb:35:1:39:3 | while ... | loops.rb:35:7:35:11 | ... < ... | loops.rb:35:12:39:3 | ...; ... | 1 | loops.rb:37:3:37:8 | ... += ... | diff --git a/ql/test/library-tests/ast/modules/toplevel.expected b/ql/test/library-tests/ast/modules/toplevel.expected index 0eca47938a3..9ea0f83b3df 100644 --- a/ql/test/library-tests/ast/modules/toplevel.expected +++ b/ql/test/library-tests/ast/modules/toplevel.expected @@ -3,6 +3,6 @@ toplevel | modules.rb:1:1:61:3 | modules.rb | Toplevel | | toplevel.rb:1:1:5:23 | toplevel.rb | Toplevel | beginBlocks -| toplevel.rb:1:1:5:23 | toplevel.rb | 1 | toplevel.rb:5:1:5:22 | BEGIN { ... } | +| toplevel.rb:1:1:5:23 | toplevel.rb | 0 | toplevel.rb:5:1:5:22 | BEGIN { ... } | endBlocks | toplevel.rb:1:1:5:23 | toplevel.rb | toplevel.rb:3:1:3:18 | END { ... } | diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index d6f687ecb90..b585b8ac4f0 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1,25 +1,25 @@ break_ensure.rb: -# 1| enter break_ensure.rb -#-----| -> m1 - # 1| enter m1 #-----| -> elements +# 1| enter break_ensure.rb +#-----| -> m1 + # 1| m1 #-----| -> m2 -# 1| elements -#-----| -> elements +# 1| exit m1 # 1| exit break_ensure.rb -# 1| exit m1 +# 1| exit m1 (normal) +#-----| -> exit m1 # 1| exit break_ensure.rb (normal) #-----| -> exit break_ensure.rb -# 1| exit m1 (normal) -#-----| -> exit m1 +# 1| elements +#-----| -> elements # 2| for ... in ... #-----| -> ensure ... @@ -38,8 +38,8 @@ break_ensure.rb: #-----| -> In # 3| ... > ... -#-----| false -> if ... #-----| true -> break +#-----| false -> if ... # 3| element #-----| -> 0 @@ -57,8 +57,8 @@ break_ensure.rb: #-----| -> exit m1 (normal) # 8| call to nil? -#-----| false -> if ... #-----| true -> "elements nil" +#-----| false -> if ... # 8| elements #-----| -> call to nil? @@ -75,14 +75,14 @@ break_ensure.rb: # 13| m2 #-----| -> m3 -# 13| elements -#-----| -> elements - # 13| exit m2 # 13| exit m2 (normal) #-----| -> exit m2 +# 13| elements +#-----| -> elements + # 14| for ... in ... #-----| -> exit m2 (normal) @@ -100,8 +100,8 @@ break_ensure.rb: #-----| -> ensure ... # 16| ... > ... -#-----| false -> if ... #-----| true -> break +#-----| false -> if ... # 16| element #-----| -> 0 @@ -125,12 +125,12 @@ break_ensure.rb: #-----| break -> for ... in ... # 20| call to nil? -#-----| false -> if ... #-----| true -> "elements nil" +#-----| false -> if ... # 20| [ensure: break] call to nil? -#-----| false -> [ensure: break] if ... #-----| true -> [ensure: break] "elements nil" +#-----| false -> [ensure: break] if ... # 20| elements #-----| -> call to nil? @@ -156,20 +156,20 @@ break_ensure.rb: # 27| m3 #-----| -> m4 -# 27| elements -#-----| -> elements - # 27| exit m3 # 27| exit m3 (normal) #-----| -> exit m3 +# 27| elements +#-----| -> elements + # 29| if ... #-----| -> ensure ... # 29| call to nil? -#-----| false -> if ... #-----| true -> return +#-----| false -> if ... # 29| elements #-----| -> call to nil? @@ -216,12 +216,12 @@ break_ensure.rb: #-----| -> [ensure: return] In # 35| ... > ... -#-----| false -> if ... #-----| true -> break +#-----| false -> if ... # 35| [ensure: return] ... > ... -#-----| false -> [ensure: return] if ... #-----| true -> [ensure: return] break +#-----| false -> [ensure: return] if ... # 35| call to x #-----| -> 0 @@ -253,14 +253,14 @@ break_ensure.rb: # 44| m4 #-----| -> exit break_ensure.rb (normal) -# 44| elements -#-----| -> elements - # 44| exit m4 # 44| exit m4 (normal) #-----| -> exit m4 +# 44| elements +#-----| -> elements + # 45| for ... in ... #-----| -> exit m4 (normal) @@ -278,8 +278,8 @@ break_ensure.rb: #-----| -> ensure ... # 47| ... > ... -#-----| false -> if ... #-----| true -> "" +#-----| false -> if ... # 47| element #-----| -> 1 @@ -306,12 +306,12 @@ break_ensure.rb: #-----| raise -> for ... in ... # 51| ... > ... -#-----| false -> if ... #-----| true -> 10 +#-----| false -> if ... # 51| [ensure: raise] ... > ... -#-----| false -> [ensure: raise] if ... #-----| true -> [ensure: raise] 10 +#-----| false -> [ensure: raise] if ... # 51| element #-----| -> 0 @@ -338,25 +338,25 @@ break_ensure.rb: #-----| -> [ensure: raise] break case.rb: -# 1| enter case.rb -#-----| -> if_in_case - # 1| enter if_in_case #-----| -> case ... +# 1| enter case.rb +#-----| -> if_in_case + # 1| if_in_case #-----| -> exit case.rb (normal) -# 1| exit case.rb - # 1| exit if_in_case -# 1| exit case.rb (normal) -#-----| -> exit case.rb +# 1| exit case.rb # 1| exit if_in_case (normal) #-----| -> exit if_in_case +# 1| exit case.rb (normal) +#-----| -> exit case.rb + # 2| case ... #-----| -> call to x1 @@ -377,8 +377,8 @@ case.rb: #-----| -> (if ...) # 3| call to x2 -#-----| false -> if ... #-----| true -> "x2" +#-----| false -> if ... # 3| call to puts #-----| -> if ... @@ -401,7 +401,7 @@ case.rb: cfg.rb: # 1| enter cfg.rb -#-----| -> bar +#-----| -> BEGIN { ... } # 1| bar #-----| -> alias ... @@ -418,16 +418,16 @@ cfg.rb: #-----| -> bar # 3| bar -#-----| -> 42 +#-----| -> b # 5| ... = ... #-----| -> b # 5| b -#-----| -> ... = ... +#-----| -> 42 # 5| 42 -#-----| -> b +#-----| -> ... = ... # 7| %i(...) #-----| -> b @@ -460,24 +460,16 @@ cfg.rb: #-----| -> %w(...) # 12| call to puts -#-----| -> BEGIN { ... } +#-----| -> END { ... } # 12| 4 #-----| -> call to puts -# 15| enter BEGIN { ... } +# 15| BEGIN { ... } #-----| -> "hello" -# 15| BEGIN { ... } -#-----| -> END { ... } - -# 15| exit BEGIN { ... } - -# 15| exit BEGIN { ... } (normal) -#-----| -> exit BEGIN { ... } - # 16| call to puts -#-----| -> exit BEGIN { ... } (normal) +#-----| -> bar # 16| "hello" #-----| -> call to puts @@ -508,18 +500,23 @@ cfg.rb: # 23| 1 #-----| -> ... + ... -# 25| enter { ... } -#-----| -> x - # 25| call to times #-----| -> :puts # 25| 2 #-----| -> { ... } +# 25| enter { ... } +#-----| -> x + # 25| { ... } #-----| -> call to times +# 25| exit { ... } + +# 25| exit { ... } (normal) +#-----| -> exit { ... } + # 25| x #-----| -> x @@ -529,11 +526,6 @@ cfg.rb: # 25| x #-----| -> call to puts -# 25| exit { ... } - -# 25| exit { ... } (normal) -#-----| -> exit { ... } - # 27| call to puts #-----| -> Proc @@ -543,19 +535,24 @@ cfg.rb: # 27| :puts #-----| -> &... -# 29| enter { ... } -#-----| -> &x - # 29| call to new #-----| -> true # 29| Proc #-----| -> { ... } +# 29| enter { ... } +#-----| -> x + # 29| { ... } #-----| -> call to new -# 29| &x +# 29| exit { ... } + +# 29| exit { ... } (normal) +#-----| -> exit { ... } + +# 29| x #-----| -> x # 29| call to call @@ -564,11 +561,6 @@ cfg.rb: # 29| x #-----| -> call to call -# 29| exit { ... } - -# 29| exit { ... } (normal) -#-----| -> exit { ... } - # 31| while ... #-----| -> false @@ -587,12 +579,12 @@ cfg.rb: # 35| false #-----| false -> if ... -# 39| call to puts -#-----| -> case ... - # 39| self #-----| -> 42 +# 39| call to puts +#-----| -> case ... + # 39| 42 #-----| -> call to puts @@ -619,12 +611,12 @@ cfg.rb: #-----| -> 2 # 43| 2 -#-----| no-match -> 3 #-----| match -> "some" +#-----| no-match -> 3 # 43| 3 -#-----| no-match -> 4 #-----| match -> "some" +#-----| no-match -> 4 # 43| 4 #-----| match -> "some" @@ -659,7 +651,7 @@ cfg.rb: #-----| -> ... == ... # 48| call to puts -#-----| -> "a" +#-----| -> chained # 48| "one" #-----| -> call to puts @@ -668,8 +660,8 @@ cfg.rb: #-----| -> b # 49| ... == ... -#-----| false -> b #-----| true -> "some" +#-----| false -> b # 49| b #-----| -> 0 @@ -679,7 +671,7 @@ cfg.rb: # 49| ... > ... #-----| true -> "some" -#-----| false -> "a" +#-----| false -> chained # 49| b #-----| -> 1 @@ -688,16 +680,16 @@ cfg.rb: #-----| -> ... > ... # 49| call to puts -#-----| -> "a" +#-----| -> chained # 49| "some" #-----| -> call to puts # 52| ... = ... -#-----| -> ?\x40 +#-----| -> character # 52| chained -#-----| -> ... = ... +#-----| -> "a" # 52| "a" #-----| -> chained @@ -712,40 +704,40 @@ cfg.rb: #-----| -> #{...} # 52| "string" -#-----| -> chained +#-----| -> ... = ... # 54| ... = ... #-----| -> Silly # 54| character -#-----| -> ... = ... +#-----| -> ?\x40 # 54| ?\x40 -#-----| -> character +#-----| -> ... = ... # 58| Silly #-----| -> Object # 58| Object -#-----| -> 10-2i - -# 59| ... = ... -#-----| -> call to b - -# 59| complex -#-----| -> ... = ... - -# 59| 10-2i #-----| -> complex -# 60| ... = ... -#-----| -> "constant" +# 59| ... = ... +#-----| -> conditional -# 60| conditional +# 59| complex +#-----| -> 10-2i + +# 59| 10-2i #-----| -> ... = ... +# 60| ... = ... +#-----| -> C + +# 60| conditional +#-----| -> call to b + # 60| ... ? ... : ... -#-----| -> conditional +#-----| -> ... = ... # 60| ... < ... #-----| true -> "hello" @@ -764,19 +756,19 @@ cfg.rb: #-----| -> ... ? ... : ... # 61| ... = ... -#-----| -> 1 +#-----| -> x # 61| C -#-----| -> ... = ... +#-----| -> "constant" # 61| "constant" -#-----| -> C +#-----| -> ... = ... # 62| ... = ... #-----| -> pattern # 62| (..., ...) -#-----| -> ... = ... +#-----| -> 1 # 62| x #-----| -> y @@ -791,7 +783,7 @@ cfg.rb: #-----| -> (..., ...) # 62| [...] -#-----| -> x +#-----| -> ... = ... # 62| 1 #-----| -> 2 @@ -809,7 +801,12 @@ cfg.rb: #-----| -> a # 63| pattern -#-----| -> 1 +#-----| -> items + +# 63| exit pattern + +# 63| exit pattern (normal) +#-----| -> exit pattern # 63| (..., ...) #-----| -> a @@ -820,11 +817,6 @@ cfg.rb: # 63| b #-----| -> (..., ...) -# 63| exit pattern - -# 63| exit pattern (normal) -#-----| -> exit pattern - # 64| call to puts #-----| -> b @@ -841,10 +833,10 @@ cfg.rb: #-----| -> items # 67| items -#-----| -> ... = ... +#-----| -> 1 # 67| [...] -#-----| -> items +#-----| -> ... = ... # 67| 1 #-----| -> 2 @@ -871,7 +863,7 @@ cfg.rb: #-----| -> "silly" # 69| print -#-----| -> 42 +#-----| -> x # 69| exit print @@ -888,10 +880,10 @@ cfg.rb: #-----| -> x # 74| x -#-----| -> ... = ... +#-----| -> 42 # 74| 42 -#-----| -> x +#-----| -> ... = ... # 75| if ... #-----| -> ; @@ -941,7 +933,7 @@ cfg.rb: #-----| -> "end" # 85| call to puts -#-----| -> x +#-----| -> escape # 85| "end" #-----| -> call to puts @@ -950,10 +942,10 @@ cfg.rb: #-----| -> 1.4 # 88| escape -#-----| -> ... = ... +#-----| -> x # 88| "\u1234#{...}\n" -#-----| -> escape +#-----| -> ... = ... # 88| #{...} #-----| -> "\u1234#{...}\n" @@ -962,7 +954,7 @@ cfg.rb: #-----| -> #{...} # 90| for ... in ... -#-----| -> 42 +#-----| -> $global # 90| x #-----| -> x @@ -987,8 +979,8 @@ cfg.rb: #-----| -> x # 91| ... > ... -#-----| false -> if ... #-----| true -> next +#-----| false -> if ... # 91| x #-----| -> 3 @@ -1006,38 +998,38 @@ cfg.rb: #-----| -> call to puts # 95| ... = ... -#-----| -> "a" +#-----| -> map1 # 95| $global -#-----| -> ... = ... +#-----| -> 42 # 95| 42 -#-----| -> $global - -# 97| ... = ... -#-----| -> map1 - -# 97| map1 #-----| -> ... = ... -# 97| {...} -#-----| -> map1 +# 97| ... = ... +#-----| -> map2 -# 97| Pair -#-----| -> "c" +# 97| map1 +#-----| -> "a" + +# 97| {...} +#-----| -> ... = ... # 97| "a" #-----| -> "b" +# 97| Pair +#-----| -> "c" + # 97| "b" #-----| -> Pair -# 97| Pair -#-----| -> :e - # 97| "c" #-----| -> "d" +# 97| Pair +#-----| -> :e + # 97| "d" #-----| -> Pair @@ -1054,10 +1046,10 @@ cfg.rb: #-----| -> parameters # 98| map2 -#-----| -> ... = ... +#-----| -> map1 # 98| {...} -#-----| -> map2 +#-----| -> ... = ... # 98| **... #-----| -> "x" @@ -1065,12 +1057,12 @@ cfg.rb: # 98| map1 #-----| -> **... -# 98| Pair -#-----| -> map1 - # 98| "x" #-----| -> "y" +# 98| Pair +#-----| -> map1 + # 98| "y" #-----| -> Pair @@ -1084,7 +1076,12 @@ cfg.rb: #-----| -> value # 101| parameters -#-----| -> "healthy" +#-----| -> type + +# 101| exit parameters + +# 101| exit parameters (normal) +#-----| -> exit parameters # 101| value #-----| no-match -> 42 @@ -1094,16 +1091,11 @@ cfg.rb: #-----| -> key # 101| key -#-----| -> **kwargs +#-----| -> kwargs -# 101| **kwargs +# 101| kwargs #-----| -> value -# 101| exit parameters - -# 101| exit parameters (normal) -#-----| -> exit parameters - # 102| call to puts #-----| -> kwargs @@ -1123,22 +1115,22 @@ cfg.rb: #-----| -> ...[...] # 106| ... = ... -#-----| -> "food" +#-----| -> table # 106| type -#-----| -> ... = ... +#-----| -> "healthy" # 106| "healthy" -#-----| -> type +#-----| -> ... = ... # 107| ... = ... #-----| -> < ... = ... +#-----| -> "food" # 107| "food" -#-----| -> table +#-----| -> ... = ... # 108| call to puts #-----| -> b @@ -1161,18 +1153,18 @@ cfg.rb: # 110| call to type #-----| -> #{...} -# 113| ... if ... -#-----| -> C - # 113| call to puts #-----| -> ... if ... +# 113| ... if ... +#-----| -> C + # 113| "hi" #-----| -> call to puts # 113| ... > ... -#-----| false -> ... if ... #-----| true -> "hi" +#-----| false -> ... if ... # 113| b #-----| -> 10 @@ -1181,37 +1173,42 @@ cfg.rb: #-----| -> ... > ... # 115| C -#-----| -> 42 - -# 116| ... = ... -#-----| -> 10 - -# 116| @field -#-----| -> ... = ... - -# 116| 42 #-----| -> @field -# 117| ... = ... -#-----| -> -> { ... } - -# 117| @@static_field -#-----| -> ... = ... - -# 117| 10 +# 116| ... = ... #-----| -> @@static_field -# 120| enter -> { ... } -#-----| -> x +# 116| @field +#-----| -> 42 + +# 116| 42 +#-----| -> ... = ... + +# 117| ... = ... +#-----| -> swap + +# 117| @@static_field +#-----| -> 10 + +# 117| 10 +#-----| -> ... = ... # 120| ... = ... #-----| -> M # 120| swap -#-----| -> ... = ... +#-----| -> -> { ... } + +# 120| enter -> { ... } +#-----| -> x # 120| -> { ... } -#-----| -> swap +#-----| -> ... = ... + +# 120| exit -> { ... } + +# 120| exit -> { ... } (normal) +#-----| -> exit -> { ... } # 120| (..., ...) #-----| -> y @@ -1231,34 +1228,29 @@ cfg.rb: # 120| x #-----| -> [...] -# 120| exit -> { ... } - -# 120| exit -> { ... } (normal) -#-----| -> exit -> { ... } - # 122| M -#-----| -> nil +#-----| -> nothing # 123| ... = ... -#-----| -> 2 +#-----| -> some # 123| nothing -#-----| -> ... = ... +#-----| -> nil # 123| nil -#-----| -> nothing +#-----| -> ... = ... # 124| ... = ... #-----| -> some # 124| some -#-----| -> ... = ... +#-----| -> 2 # 124| 2 -#-----| -> some +#-----| -> ... = ... # 125| ... += ... -#-----| -> 2 +#-----| -> last # 125| some #-----| -> 10 @@ -1267,13 +1259,13 @@ cfg.rb: #-----| -> ... += ... # 126| ... = ... -#-----| -> 0 +#-----| -> range # 126| last -#-----| -> ... = ... +#-----| -> 2 # 126| (...; ...) -#-----| -> last +#-----| -> ... = ... # 126| 2 #-----| -> 4 @@ -1285,13 +1277,13 @@ cfg.rb: #-----| -> (...; ...) # 127| ... = ... -#-----| -> 1 +#-----| -> half # 127| range -#-----| -> ... = ... +#-----| -> 0 # 127| _ .. _ -#-----| -> range +#-----| -> ... = ... # 127| 0 #-----| -> 9 @@ -1300,13 +1292,13 @@ cfg.rb: #-----| -> _ .. _ # 128| ... = ... -#-----| -> range +#-----| -> regex # 128| half -#-----| -> ... = ... +#-----| -> 1 # 128| ... + ... -#-----| -> half +#-----| -> ... = ... # 128| ... / ... #-----| -> 1 @@ -1327,13 +1319,13 @@ cfg.rb: #-----| -> ... / ... # 129| ... = ... -#-----| -> 5 +#-----| -> Constant # 129| regex -#-----| -> ... = ... +#-----| -> range # 129| /hello\s+[#{...}]/ -#-----| -> regex +#-----| -> ... = ... # 129| #{...} #-----| -> /hello\s+[#{...}]/ @@ -1342,507 +1334,513 @@ cfg.rb: #-----| -> #{...} # 130| ... = ... -#-----| -> ... rescue ... +#-----| -> EmptyClass # 130| Constant -#-----| -> ... = ... +#-----| -> 5 # 130| 5 -#-----| -> Constant - -# 133| ... rescue ... -#-----| -> 1 - -# 133| ... / ... -#-----| raise -> "div by zero" -#-----| -> 1 - -# 133| 1 -#-----| -> 0 - -# 133| 0 -#-----| -> ... / ... - -# 133| call to puts -#-----| -> 1 - -# 133| "div by zero" -#-----| -> call to puts - -# 135| ... = ... -#-----| -> M - -# 135| (..., ...) #-----| -> ... = ... -# 135| init -#-----| -> last +# 133| EmptyClass +#-----| -> EmptyModule -# 135| last -#-----| -> (..., ...) +# 134| EmptyModule +#-----| -> ... rescue ... -# 135| 1 -#-----| -> 2 +# 136| ... rescue ... +#-----| -> 1 -# 135| 2 -#-----| -> 3 - -# 135| 3 +# 136| ... / ... +#-----| raise -> "div by zero" #-----| -> init -# 137| Constant +# 136| 1 +#-----| -> 0 + +# 136| 0 +#-----| -> ... / ... + +# 136| call to puts +#-----| -> init + +# 136| "div by zero" +#-----| -> call to puts + +# 138| ... = ... #-----| -> M -# 137| M -#-----| -> Constant - -# 138| Constant -#-----| -> class << ... - -# 138| call to itself -#-----| -> Constant - -# 138| M -#-----| -> call to itself - -# 140| class << ... -#-----| -> Silly - -# 140| call to itself -#-----| -> setter= - -# 140| Silly -#-----| -> call to itself - -# 141| setter= -#-----| -> print - -# 142| enter print -#-----| -> "singleton" - -# 142| print -#-----| -> Silly - -# 142| exit print - -# 142| exit print (normal) -#-----| -> exit print - -# 143| call to puts -#-----| -> call to super - -# 143| "singleton" -#-----| -> call to puts - -# 144| call to puts -#-----| -> exit print (normal) - -# 144| call to print -#-----| -> call to puts - -# 144| call to super -#-----| -> call to print - -# 148| ... = ... -#-----| -> silly - -# 148| silly -#-----| -> ... = ... - -# 148| call to new -#-----| -> silly - -# 148| Silly -#-----| -> call to new - -# 149| enter method -#-----| -> *x - -# 149| method -#-----| -> two_parameters - -# 149| silly -#-----| -> method - -# 149| *x -#-----| -> x - -# 149| exit method - -# 149| exit method (normal) -#-----| -> exit method - -# 150| call to puts -#-----| -> exit method (normal) - -# 150| x -#-----| -> call to puts - -# 153| enter two_parameters -#-----| -> a - -# 153| two_parameters +# 138| (..., ...) #-----| -> 1 -# 153| a -#-----| -> b +# 138| init +#-----| -> last -# 153| b -#-----| -> exit two_parameters (normal) +# 138| last +#-----| -> (..., ...) -# 153| exit two_parameters - -# 153| exit two_parameters (normal) -#-----| -> exit two_parameters - -# 155| call to two_parameters -#-----| -> call to __FILE__ - -# 155| *... -#-----| -> call to two_parameters - -# 155| [...] -#-----| -> *... - -# 155| 1 +# 138| 1 #-----| -> 2 -# 155| 2 -#-----| -> [...] +# 138| 2 +#-----| -> 3 -# 157| ... = ... -#-----| -> :hello - -# 157| scriptfile +# 138| 3 #-----| -> ... = ... -# 157| `cat "#{...}"` -#-----| -> scriptfile +# 140| M +#-----| -> Constant -# 157| #{...} -#-----| -> `cat "#{...}"` +# 140| Constant +#-----| -> M -# 157| call to __FILE__ -#-----| -> #{...} +# 141| call to itself +#-----| -> Constant -# 159| ... = ... -#-----| -> 12 +# 141| M +#-----| -> call to itself -# 159| symbol +# 141| Constant +#-----| -> class << ... + +# 143| class << ... +#-----| -> Silly + +# 143| call to itself +#-----| -> setter= + +# 143| Silly +#-----| -> call to itself + +# 144| setter= +#-----| -> print + +# 145| enter print +#-----| -> "singleton" + +# 145| print +#-----| -> silly + +# 145| exit print + +# 145| exit print (normal) +#-----| -> exit print + +# 146| call to puts +#-----| -> call to super + +# 146| "singleton" +#-----| -> call to puts + +# 147| call to puts +#-----| -> exit print (normal) + +# 147| call to print +#-----| -> call to puts + +# 147| call to super +#-----| -> call to print + +# 151| ... = ... +#-----| -> silly + +# 151| silly +#-----| -> Silly + +# 151| call to new #-----| -> ... = ... -# 159| :hello -#-----| -> symbol +# 151| Silly +#-----| -> call to new -# 161| ... = ... -#-----| -> true - -# 161| delimited_symbol -#-----| -> ... = ... - -# 161| :"goodbye-#{...}" -#-----| -> delimited_symbol - -# 161| #{...} -#-----| -> :"goodbye-#{...}" - -# 161| ... + ... -#-----| -> #{...} - -# 161| 12 -#-----| -> 13 - -# 161| 13 -#-----| -> ... + ... - -# 163| ... = ... -#-----| -> true - -# 163| x -#-----| -> ... = ... - -# 163| true +# 152| enter method #-----| -> x -# 164| ... = ... -#-----| -> 42 - -# 164| x -#-----| -> ... = ... - -# 164| ! ... -#-----| -> x - -# 164| true -#-----| -> ! ... - -# 165| ... = ... -#-----| -> undef ... - -# 165| x -#-----| -> ... = ... - -# 165| - ... -#-----| -> x - -# 165| 42 -#-----| -> - ... - -# 167| undef ... +# 152| method #-----| -> two_parameters -# 167| two_parameters +# 152| exit method + +# 152| exit method (normal) +#-----| -> exit method + +# 152| silly +#-----| -> method + +# 152| x #-----| -> x -# 169| unless ... +# 153| call to puts +#-----| -> exit method (normal) + +# 153| x +#-----| -> call to puts + +# 156| enter two_parameters +#-----| -> a + +# 156| two_parameters +#-----| -> 1 + +# 156| exit two_parameters + +# 156| exit two_parameters (normal) +#-----| -> exit two_parameters + +# 156| a +#-----| -> b + +# 156| b +#-----| -> exit two_parameters (normal) + +# 158| call to two_parameters +#-----| -> scriptfile + +# 158| *... +#-----| -> call to two_parameters + +# 158| [...] +#-----| -> *... + +# 158| 1 +#-----| -> 2 + +# 158| 2 +#-----| -> [...] + +# 160| ... = ... +#-----| -> symbol + +# 160| scriptfile +#-----| -> call to __FILE__ + +# 160| `cat "#{...}"` +#-----| -> ... = ... + +# 160| #{...} +#-----| -> `cat "#{...}"` + +# 160| call to __FILE__ +#-----| -> #{...} + +# 162| ... = ... +#-----| -> delimited_symbol + +# 162| symbol +#-----| -> :hello + +# 162| :hello +#-----| -> ... = ... + +# 164| ... = ... #-----| -> x -# 169| ... == ... +# 164| delimited_symbol +#-----| -> 12 + +# 164| :"goodbye-#{...}" +#-----| -> ... = ... + +# 164| #{...} +#-----| -> :"goodbye-#{...}" + +# 164| ... + ... +#-----| -> #{...} + +# 164| 12 +#-----| -> 13 + +# 164| 13 +#-----| -> ... + ... + +# 166| ... = ... +#-----| -> x + +# 166| x +#-----| -> true + +# 166| true +#-----| -> ... = ... + +# 167| ... = ... +#-----| -> x + +# 167| x +#-----| -> true + +# 167| ! ... +#-----| -> ... = ... + +# 167| true +#-----| -> ! ... + +# 168| ... = ... +#-----| -> undef ... + +# 168| x +#-----| -> 42 + +# 168| - ... +#-----| -> ... = ... + +# 168| 42 +#-----| -> - ... + +# 170| undef ... +#-----| -> two_parameters + +# 170| two_parameters +#-----| -> x + +# 172| unless ... +#-----| -> x + +# 172| ... == ... #-----| false -> "hi" #-----| true -> "bye" -# 169| x +# 172| x #-----| -> 10 -# 169| 10 +# 172| 10 #-----| -> ... == ... -# 169| call to puts +# 172| call to puts #-----| -> unless ... -# 169| "hi" +# 172| "hi" #-----| -> call to puts -# 169| call to puts +# 172| call to puts #-----| -> unless ... -# 169| "bye" +# 172| "bye" #-----| -> call to puts -# 171| ... unless ... -#-----| -> x - -# 171| call to puts +# 174| call to puts #-----| -> ... unless ... -# 171| "hi" +# 174| ... unless ... +#-----| -> x + +# 174| "hi" #-----| -> call to puts -# 171| ... == ... -#-----| true -> ... unless ... +# 174| ... == ... #-----| false -> "hi" +#-----| true -> ... unless ... -# 171| x +# 174| x #-----| -> 0 -# 171| 0 +# 174| 0 #-----| -> ... == ... -# 173| until ... -#-----| -> 0 +# 176| until ... +#-----| -> i -# 173| ... > ... +# 176| ... > ... #-----| true -> until ... #-----| false -> x -# 173| x +# 176| x #-----| -> 10 -# 173| 10 +# 176| 10 #-----| -> ... > ... -# 173| ... += ... +# 176| ... += ... #-----| -> "hello" -# 173| x +# 176| x #-----| -> 10 -# 173| 10 +# 176| 10 #-----| -> ... += ... -# 173| call to puts -#-----| -> x - -# 173| "hello" -#-----| -> call to puts - -# 175| ... = ... -#-----| -> i - -# 175| i -#-----| -> ... = ... - -# 175| 0 -#-----| -> i - -# 176| ... until ... -#-----| -> 0 - -# 176| (...; ...) -#-----| -> i - # 176| call to puts -#-----| -> i +#-----| -> x # 176| "hello" #-----| -> call to puts -# 176| ... += ... -#-----| -> (...; ...) - -# 176| i -#-----| -> 1 - -# 176| 1 -#-----| -> ... += ... - -# 176| ... == ... -#-----| true -> ... until ... -#-----| false -> "hello" - -# 176| i -#-----| -> 10 - -# 176| 10 -#-----| -> ... == ... - # 178| ... = ... -#-----| -> x - -# 178| x -#-----| -> ... = ... - -# 178| 0 -#-----| -> x - -# 179| while ... #-----| -> i -# 179| ... < ... -#-----| false -> while ... -#-----| true -> x +# 178| i +#-----| -> 0 -# 179| x +# 178| 0 +#-----| -> ... = ... + +# 179| (...; ...) +#-----| -> i + +# 179| ... until ... +#-----| -> x + +# 179| call to puts +#-----| -> i + +# 179| "hello" +#-----| -> call to puts + +# 179| ... += ... +#-----| -> (...; ...) + +# 179| i +#-----| -> 1 + +# 179| 1 +#-----| -> ... += ... + +# 179| ... == ... +#-----| false -> "hello" +#-----| true -> ... until ... + +# 179| i #-----| -> 10 # 179| 10 -#-----| -> ... < ... - -# 180| ... += ... -#-----| -> x - -# 180| x -#-----| -> 1 - -# 180| 1 -#-----| -> ... += ... - -# 181| if ... -#-----| -> x - -# 181| ... == ... -#-----| false -> if ... -#-----| true -> redo - -# 181| x -#-----| -> 5 - -# 181| 5 #-----| -> ... == ... -# 181| redo -#-----| redo -> x - -# 182| call to puts +# 181| ... = ... #-----| -> x -# 182| x -#-----| -> call to puts - -# 185| ... while ... -#-----| -> run_block - -# 185| (...; ...) -#-----| -> i - -# 185| call to puts -#-----| -> i - -# 185| "hello" -#-----| -> call to puts - -# 185| ... -= ... -#-----| -> (...; ...) - -# 185| i -#-----| -> 1 - -# 185| 1 -#-----| -> ... -= ... - -# 185| ... != ... -#-----| false -> ... while ... -#-----| true -> "hello" - -# 185| i +# 181| x #-----| -> 0 -# 185| 0 -#-----| -> ... != ... +# 181| 0 +#-----| -> ... = ... -# 187| enter run_block -#-----| -> yield ... +# 182| while ... +#-----| -> i -# 187| run_block -#-----| -> { ... } +# 182| ... < ... +#-----| true -> x +#-----| false -> while ... -# 188| yield ... -#-----| -> 42 +# 182| x +#-----| -> 10 -# 188| 42 +# 182| 10 +#-----| -> ... < ... -# 191| enter { ... } +# 183| ... += ... #-----| -> x -# 191| call to run_block -#-----| -> exit cfg.rb (normal) +# 183| x +#-----| -> 1 -# 191| { ... } -#-----| -> call to run_block +# 183| 1 +#-----| -> ... += ... -# 191| x +# 184| if ... #-----| -> x -# 191| call to puts -#-----| -> exit { ... } (normal) +# 184| ... == ... +#-----| true -> redo +#-----| false -> if ... -# 191| x +# 184| x +#-----| -> 5 + +# 184| 5 +#-----| -> ... == ... + +# 184| redo +#-----| redo -> x + +# 185| call to puts +#-----| -> x + +# 185| x #-----| -> call to puts -# 191| exit { ... } +# 188| (...; ...) +#-----| -> i -# 191| exit { ... } (normal) +# 188| ... while ... +#-----| -> run_block + +# 188| call to puts +#-----| -> i + +# 188| "hello" +#-----| -> call to puts + +# 188| ... -= ... +#-----| -> (...; ...) + +# 188| i +#-----| -> 1 + +# 188| 1 +#-----| -> ... -= ... + +# 188| ... != ... +#-----| true -> "hello" +#-----| false -> ... while ... + +# 188| i +#-----| -> 0 + +# 188| 0 +#-----| -> ... != ... + +# 190| enter run_block +#-----| -> yield ... + +# 190| run_block +#-----| -> { ... } + +# 190| exit run_block + +# 190| exit run_block (normal) +#-----| -> exit run_block + +# 191| yield ... +#-----| -> 42 + +# 191| 42 +#-----| -> exit run_block (normal) + +# 194| call to run_block +#-----| -> exit cfg.rb (normal) + +# 194| enter { ... } +#-----| -> x + +# 194| { ... } +#-----| -> call to run_block + +# 194| exit { ... } + +# 194| exit { ... } (normal) #-----| -> exit { ... } -exit.rb: -# 1| enter exit.rb -#-----| -> m1 +# 194| x +#-----| -> x +# 194| call to puts +#-----| -> exit { ... } (normal) + +# 194| x +#-----| -> call to puts + +exit.rb: # 1| enter m1 #-----| -> x +# 1| enter exit.rb +#-----| -> m1 + # 1| m1 #-----| -> m2 -# 1| x -#-----| -> x - -# 1| exit exit.rb - # 1| exit m1 -# 1| exit exit.rb (normal) -#-----| -> exit exit.rb +# 1| exit exit.rb # 1| exit m1 (abnormal) #-----| -> exit m1 @@ -1850,12 +1848,18 @@ exit.rb: # 1| exit m1 (normal) #-----| -> exit m1 +# 1| exit exit.rb (normal) +#-----| -> exit exit.rb + +# 1| x +#-----| -> x + # 2| if ... #-----| -> "x <= 2" # 2| ... > ... -#-----| false -> if ... #-----| true -> 1 +#-----| false -> if ... # 2| x #-----| -> 2 @@ -1881,9 +1885,6 @@ exit.rb: # 8| m2 #-----| -> exit exit.rb (normal) -# 8| x -#-----| -> x - # 8| exit m2 # 8| exit m2 (abnormal) @@ -1892,12 +1893,15 @@ exit.rb: # 8| exit m2 (normal) #-----| -> exit m2 +# 8| x +#-----| -> x + # 9| if ... #-----| -> "x <= 2" # 9| ... > ... -#-----| false -> if ... #-----| true -> "abort!" +#-----| false -> if ... # 9| x #-----| -> 2 @@ -1918,25 +1922,25 @@ exit.rb: #-----| -> call to puts heredoc.rb: -# 1| enter heredoc.rb -#-----| -> double_heredoc - # 1| enter double_heredoc #-----| -> < double_heredoc + # 1| double_heredoc #-----| -> exit heredoc.rb (normal) -# 1| exit heredoc.rb - # 1| exit double_heredoc -# 1| exit heredoc.rb (normal) -#-----| -> exit heredoc.rb +# 1| exit heredoc.rb # 1| exit double_heredoc (normal) #-----| -> exit double_heredoc +# 1| exit heredoc.rb (normal) +#-----| -> exit heredoc.rb + # 2| call to puts #-----| -> exit double_heredoc (normal) @@ -1947,27 +1951,27 @@ heredoc.rb: #-----| -> call to puts ifs.rb: -# 1| enter ifs.rb -#-----| -> m1 - # 1| enter m1 #-----| -> x +# 1| enter ifs.rb +#-----| -> m1 + # 1| m1 #-----| -> m2 -# 1| x -#-----| -> x +# 1| exit m1 # 1| exit ifs.rb -# 1| exit m1 +# 1| exit m1 (normal) +#-----| -> exit m1 # 1| exit ifs.rb (normal) #-----| -> exit ifs.rb -# 1| exit m1 (normal) -#-----| -> exit m1 +# 1| x +#-----| -> x # 2| if ... #-----| -> exit m1 (normal) @@ -2063,20 +2067,20 @@ ifs.rb: # 11| m2 #-----| -> m3 -# 11| b -#-----| -> b - # 11| exit m2 # 11| exit m2 (normal) #-----| -> exit m2 +# 11| b +#-----| -> b + # 12| if ... #-----| -> 1 # 12| b -#-----| false -> if ... #-----| true -> 0 +#-----| false -> if ... # 13| return #-----| return -> exit m2 (normal) @@ -2096,14 +2100,14 @@ ifs.rb: # 18| m3 #-----| -> m4 -# 18| x -#-----| -> x - # 18| exit m3 # 18| exit m3 (normal) #-----| -> exit m3 +# 18| x +#-----| -> x + # 19| if ... #-----| -> x @@ -2121,10 +2125,10 @@ ifs.rb: #-----| -> x # 20| x -#-----| -> ... = ... +#-----| -> x # 20| - ... -#-----| -> x +#-----| -> ... = ... # 20| x #-----| -> - ... @@ -2146,10 +2150,10 @@ ifs.rb: #-----| -> if ... # 22| x -#-----| -> ... = ... +#-----| -> x # 22| ... - ... -#-----| -> x +#-----| -> ... = ... # 22| x #-----| -> 1 @@ -2169,6 +2173,11 @@ ifs.rb: # 28| m4 #-----| -> m5 +# 28| exit m4 + +# 28| exit m4 (normal) +#-----| -> exit m4 + # 28| b1 #-----| -> b2 @@ -2178,11 +2187,6 @@ ifs.rb: # 28| b3 #-----| -> b1 -# 28| exit m4 - -# 28| exit m4 (normal) -#-----| -> exit m4 - # 29| return #-----| return -> exit m4 (normal) @@ -2225,6 +2229,11 @@ ifs.rb: # 32| m5 #-----| -> 1 +# 32| exit m5 + +# 32| exit m5 (normal) +#-----| -> exit m5 + # 32| b1 #-----| -> b2 @@ -2240,11 +2249,6 @@ ifs.rb: # 32| b5 #-----| -> b1 -# 32| exit m5 - -# 32| exit m5 (normal) -#-----| -> exit m5 - # 33| if ... #-----| -> exit m5 (normal) @@ -2295,12 +2299,12 @@ ifs.rb: # 36| enter conditional_method_def #-----| -> "bla" -# 36| ... unless ... -#-----| -> constant_condition - # 36| conditional_method_def #-----| -> ... unless ... +# 36| ... unless ... +#-----| -> constant_condition + # 36| exit conditional_method_def # 36| exit conditional_method_def (normal) @@ -2313,8 +2317,8 @@ ifs.rb: #-----| -> call to puts # 38| ... == ... -#-----| true -> ... unless ... #-----| false -> conditional_method_def +#-----| true -> ... unless ... # 38| 1 #-----| -> 2 @@ -2343,34 +2347,34 @@ ifs.rb: #-----| true -> [false] ! ... loops.rb: -# 1| enter loops.rb -#-----| -> m1 - # 1| enter m1 #-----| -> x +# 1| enter loops.rb +#-----| -> m1 + # 1| m1 #-----| -> m2 -# 1| x -#-----| -> x +# 1| exit m1 # 1| exit loops.rb -# 1| exit m1 +# 1| exit m1 (normal) +#-----| -> exit m1 # 1| exit loops.rb (normal) #-----| -> exit loops.rb -# 1| exit m1 (normal) -#-----| -> exit m1 +# 1| x +#-----| -> x # 2| while ... #-----| -> exit m1 (normal) # 2| ... >= ... -#-----| false -> while ... #-----| true -> x +#-----| false -> while ... # 2| x #-----| -> 0 @@ -2399,20 +2403,20 @@ loops.rb: # 8| m2 #-----| -> m3 -# 8| x -#-----| -> x - # 8| exit m2 # 8| exit m2 (normal) #-----| -> exit m2 +# 8| x +#-----| -> x + # 9| while ... #-----| -> "Done" # 9| ... >= ... -#-----| false -> while ... #-----| true -> x +#-----| false -> while ... # 9| x #-----| -> 0 @@ -2471,8 +2475,8 @@ loops.rb: #-----| -> elsif ... # 16| ... > ... -#-----| false -> elsif ... #-----| true -> redo +#-----| false -> elsif ... # 16| x #-----| -> 10 @@ -2506,15 +2510,12 @@ loops.rb: # 24| exit m3 (normal) #-----| -> exit m3 -# 25| enter do ... end -#-----| -> x +# 25| [...] +#-----| -> do ... end # 25| call to each #-----| -> exit m3 (normal) -# 25| [...] -#-----| -> do ... end - # 25| 1 #-----| -> 2 @@ -2524,17 +2525,20 @@ loops.rb: # 25| 3 #-----| -> [...] +# 25| enter do ... end +#-----| -> x + # 25| do ... end #-----| -> call to each -# 25| x -#-----| -> x - # 25| exit do ... end # 25| exit do ... end (normal) #-----| -> exit do ... end +# 25| x +#-----| -> x + # 26| call to puts #-----| -> exit do ... end (normal) @@ -2548,14 +2552,14 @@ raise.rb: # 1| ExceptionA #-----| -> Exception -# 1| Exception -#-----| -> ExceptionB - # 1| exit raise.rb # 1| exit raise.rb (normal) #-----| -> exit raise.rb +# 1| Exception +#-----| -> ExceptionB + # 4| ExceptionB #-----| -> Exception @@ -2568,9 +2572,6 @@ raise.rb: # 7| m1 #-----| -> m2 -# 7| x -#-----| -> x - # 7| exit m1 # 7| exit m1 (abnormal) @@ -2579,12 +2580,15 @@ raise.rb: # 7| exit m1 (normal) #-----| -> exit m1 +# 7| x +#-----| -> x + # 8| if ... #-----| -> "x <= 2" # 8| ... > ... -#-----| false -> if ... #-----| true -> "x > 2" +#-----| false -> if ... # 8| x #-----| -> 2 @@ -2610,9 +2614,6 @@ raise.rb: # 14| m2 #-----| -> m3 -# 14| b -#-----| -> b - # 14| exit m2 # 14| exit m2 (abnormal) @@ -2621,6 +2622,9 @@ raise.rb: # 14| exit m2 (normal) #-----| -> exit m2 +# 14| b +#-----| -> b + # 16| if ... #-----| -> "End m2" @@ -2659,14 +2663,14 @@ raise.rb: # 25| m3 #-----| -> m4 -# 25| b -#-----| -> b - # 25| exit m3 # 25| exit m3 (normal) #-----| -> exit m3 +# 25| b +#-----| -> b + # 27| if ... #-----| -> "End m3" @@ -2701,14 +2705,14 @@ raise.rb: # 36| m4 #-----| -> m5 -# 36| b -#-----| -> b - # 36| exit m4 # 36| exit m4 (normal) #-----| -> exit m4 +# 36| b +#-----| -> b + # 38| if ... #-----| -> "End m4" @@ -2746,14 +2750,14 @@ raise.rb: # 47| m5 #-----| -> m6 -# 47| b -#-----| -> b - # 47| exit m5 # 47| exit m5 (normal) #-----| -> exit m5 +# 47| b +#-----| -> b + # 49| if ... #-----| -> "End m5" @@ -2785,9 +2789,6 @@ raise.rb: # 57| m6 #-----| -> m7 -# 57| b -#-----| -> b - # 57| exit m6 # 57| exit m6 (abnormal) @@ -2796,6 +2797,9 @@ raise.rb: # 57| exit m6 (normal) #-----| -> exit m6 +# 57| b +#-----| -> b + # 59| if ... #-----| -> "End m6" @@ -2841,9 +2845,6 @@ raise.rb: # 68| m7 #-----| -> m8 -# 68| x -#-----| -> x - # 68| exit m7 # 68| exit m7 (abnormal) @@ -2852,6 +2853,9 @@ raise.rb: # 68| exit m7 (normal) #-----| -> exit m7 +# 68| x +#-----| -> x + # 69| if ... #-----| -> "0 <= x <= 2" @@ -2875,8 +2879,8 @@ raise.rb: #-----| -> if ... # 71| ... < ... -#-----| false -> elsif ... #-----| true -> "x < 0" +#-----| false -> elsif ... # 71| x #-----| -> 0 @@ -2929,9 +2933,6 @@ raise.rb: # 79| m8 #-----| -> m9 -# 79| x -#-----| -> "Begin m8" - # 79| exit m8 # 79| exit m8 (abnormal) @@ -2940,6 +2941,9 @@ raise.rb: # 79| exit m8 (normal) #-----| -> exit m8 +# 79| x +#-----| -> "Begin m8" + # 80| call to puts #-----| -> x @@ -2969,8 +2973,8 @@ raise.rb: #-----| -> if ... # 84| ... < ... -#-----| false -> elsif ... #-----| true -> "x < 0" +#-----| false -> elsif ... # 84| x #-----| -> 0 @@ -3029,6 +3033,14 @@ raise.rb: # 94| m9 #-----| -> m10 +# 94| exit m9 + +# 94| exit m9 (abnormal) +#-----| -> exit m9 + +# 94| exit m9 (normal) +#-----| -> exit m9 + # 94| x #-----| -> b1 @@ -3038,14 +3050,6 @@ raise.rb: # 94| b2 #-----| -> "Begin m9" -# 94| exit m9 - -# 94| exit m9 (abnormal) -#-----| -> exit m9 - -# 94| exit m9 (normal) -#-----| -> exit m9 - # 95| call to puts #-----| -> x @@ -3075,8 +3079,8 @@ raise.rb: #-----| -> if ... # 99| ... < ... -#-----| false -> elsif ... #-----| true -> "x < 0" +#-----| false -> elsif ... # 99| x #-----| -> 0 @@ -3133,16 +3137,16 @@ raise.rb: #-----| -> [ensure: raise] ensure ... # 106| b1 -#-----| false -> if ... #-----| true -> "b1 is true" +#-----| false -> if ... # 106| [ensure: return] b1 -#-----| false -> [ensure: return] if ... #-----| true -> [ensure: return] "b1 is true" +#-----| false -> [ensure: return] if ... # 106| [ensure: raise] b1 -#-----| false -> [ensure: raise] if ... #-----| true -> [ensure: raise] "b1 is true" +#-----| false -> [ensure: raise] if ... # 107| call to raise #-----| raise -> [ensure(1): raise] ensure ... @@ -3259,16 +3263,16 @@ raise.rb: #-----| raise -> exit m9 (abnormal) # 116| b2 -#-----| false -> if ... #-----| true -> "b2 is true" +#-----| false -> if ... # 116| [ensure: return] b2 -#-----| false -> [ensure: return] if ... #-----| true -> [ensure: return] "b2 is true" +#-----| false -> [ensure: return] if ... # 116| [ensure: raise] b2 -#-----| false -> [ensure: raise] if ... #-----| true -> [ensure: raise] "b2 is true" +#-----| false -> [ensure: raise] if ... # 117| call to raise #-----| raise -> exit m9 (abnormal) @@ -3294,6 +3298,14 @@ raise.rb: # 121| m10 #-----| -> m11 +# 121| exit m10 + +# 121| exit m10 (abnormal) +#-----| -> exit m10 + +# 121| exit m10 (normal) +#-----| -> exit m10 + # 121| p #-----| no-match -> "Exception" #-----| match -> ensure ... @@ -3304,14 +3316,6 @@ raise.rb: # 121| "Exception" #-----| -> call to raise -# 121| exit m10 - -# 121| exit m10 (abnormal) -#-----| -> exit m10 - -# 121| exit m10 (normal) -#-----| -> exit m10 - # 124| ensure ... #-----| -> "Will not get executed if p is..." @@ -3327,9 +3331,6 @@ raise.rb: # 128| m11 #-----| -> m12 -# 128| b -#-----| -> b - # 128| exit m11 # 128| exit m11 (abnormal) @@ -3338,6 +3339,9 @@ raise.rb: # 128| exit m11 (normal) #-----| -> exit m11 +# 128| b +#-----| -> b + # 130| if ... #-----| -> ensure ... @@ -3401,20 +3405,20 @@ raise.rb: # 142| m12 #-----| -> m13 -# 142| b -#-----| -> b - # 142| exit m12 # 142| exit m12 (normal) #-----| -> exit m12 +# 142| b +#-----| -> b + # 143| if ... #-----| -> ensure ... # 143| b -#-----| false -> if ... #-----| true -> "" +#-----| false -> if ... # 144| call to raise #-----| raise -> [ensure: raise] ensure ... @@ -3460,16 +3464,13 @@ raise.rb: # 154| m14 #-----| -> exit raise.rb (normal) -# 154| element -#-----| -> element - # 154| exit m14 # 154| exit m14 (normal) #-----| -> exit m14 -# 155| enter { ... } -#-----| -> elem +# 154| element +#-----| -> element # 155| call to each #-----| -> exit m14 (normal) @@ -3477,28 +3478,12 @@ raise.rb: # 155| element #-----| -> { ... } +# 155| enter { ... } +#-----| -> elem + # 155| { ... } #-----| -> call to each -# 155| elem -#-----| -> element - -# 155| ... if ... -#-----| -> exit { ... } (normal) - -# 155| call to raise -#-----| raise -> exit { ... } (abnormal) - -# 155| "" -#-----| -> call to raise - -# 155| call to nil? -#-----| false -> ... if ... -#-----| true -> "" - -# 155| element -#-----| -> call to nil? - # 155| exit { ... } # 155| exit { ... } (abnormal) @@ -3506,3 +3491,22 @@ raise.rb: # 155| exit { ... } (normal) #-----| -> exit { ... } + +# 155| elem +#-----| -> element + +# 155| call to raise +#-----| raise -> exit { ... } (abnormal) + +# 155| ... if ... +#-----| -> exit { ... } (normal) + +# 155| "" +#-----| -> call to raise + +# 155| call to nil? +#-----| true -> "" +#-----| false -> ... if ... + +# 155| element +#-----| -> call to nil? diff --git a/ql/test/library-tests/controlflow/graph/cfg.rb b/ql/test/library-tests/controlflow/graph/cfg.rb index ff3305df60a..aa43ec6c9ae 100644 --- a/ql/test/library-tests/controlflow/graph/cfg.rb +++ b/ql/test/library-tests/controlflow/graph/cfg.rb @@ -130,6 +130,9 @@ module M Constant = 5 end +class EmptyClass; end +module EmptyModule; end + 1/0 rescue puts "div by zero" (*init, last) = 1, 2, 3 diff --git a/ql/test/library-tests/variables/ssa.expected b/ql/test/library-tests/variables/ssa.expected index cd3ce3e3047..513d0029e8c 100644 --- a/ql/test/library-tests/variables/ssa.expected +++ b/ql/test/library-tests/variables/ssa.expected @@ -13,11 +13,11 @@ definition | parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | | parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | -| parameters.rb:15:15:15:19 | **map | parameters.rb:15:17:15:19 | map | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | | parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | | parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | -| parameters.rb:21:16:21:21 | &block | parameters.rb:21:17:21:21 | block | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | | parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | | parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | @@ -100,12 +100,12 @@ read | parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas | -| parameters.rb:15:15:15:19 | **map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | | parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key | | parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value | -| parameters.rb:21:16:21:21 | &block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | | parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size | @@ -181,11 +181,11 @@ firstRead | parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | -| parameters.rb:15:15:15:19 | **map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | | parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key | | parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value | -| parameters.rb:21:16:21:21 | &block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | | parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size | @@ -250,12 +250,12 @@ lastRead | parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:9:25:9:30 | client | | parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas | -| parameters.rb:15:15:15:19 | **map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map | | parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key | | parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value | -| parameters.rb:21:16:21:21 | &block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:26:8:26:11 | name | | parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:27:8:27:11 | size | | parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:31:11:31:15 | first | @@ -306,7 +306,7 @@ lastRead | ssa.rb:84:10:86:8 | | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured | adjacentReads | nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:15:11:15:11 | a | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | parameters.rb:11:14:11:19 | pizzas | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | parameters.rb:11:14:11:19 | pizzas | | parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | parameters.rb:26:8:26:11 | name | | scopes.rb:9:9:18:3 | | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a | | scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x | scopes.rb:28:8:28:8 | x | scopes.rb:31:10:31:10 | x | diff --git a/ql/test/library-tests/variables/varaccess.expected b/ql/test/library-tests/variables/varaccess.expected index 596bd750fea..55ce10e6324 100644 --- a/ql/test/library-tests/variables/varaccess.expected +++ b/ql/test/library-tests/variables/varaccess.expected @@ -1,182 +1,193 @@ variableAccess -| class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | top-level scope for class_variables.rb | -| class_variables.rb:3:3:3:5 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | top-level scope for class_variables.rb | -| class_variables.rb:6:4:6:6 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | top-level scope for class_variables.rb | -| class_variables.rb:11:7:11:9 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | class scope for X | -| class_variables.rb:14:6:14:8 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | class scope for X | -| class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:5 | @@x | class_variables.rb:18:1:20:3 | class scope for Y | -| class_variables.rb:23:3:23:5 | @@x | class_variables.rb:23:3:23:5 | @@x | class_variables.rb:22:1:24:3 | module scope for M | -| class_variables.rb:28:5:28:7 | @@x | class_variables.rb:28:5:28:7 | @@x | class_variables.rb:26:1:29:3 | module scope for N | -| instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:8:8:8:11 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:11:6:11:9 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:13:1:18:3 | class scope for X | -| instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:13:1:18:3 | class scope for X | -| instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:20:1:25:3 | module scope for M | -| instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:20:1:25:3 | module scope for M | -| instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:35:1:44:4 | class scope for C | -| instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | class scope for C | -| instance_variables.rb:42:6:42:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | class scope for C | -| nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | class scope for C | -| nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | module scope for M | -| nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | module scope for N | -| nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:10:7:26:9 | class scope for D | -| nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | method scope for show_a | -| nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | method scope for show_a | -| nested_scopes.rb:15:11:15:11 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | method scope for show_a | -| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:19:20:13 | block scope | -| nested_scopes.rb:16:13:16:13 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:19:20:13 | block scope | -| nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:21:19:15 | block scope | -| nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | block scope | -| nested_scopes.rb:17:15:17:15 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | block scope | -| nested_scopes.rb:18:15:18:15 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | block scope | -| nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:23:18:36 | block scope | -| nested_scopes.rb:18:34:18:34 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | block scope | -| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:9:24:11 | method scope for show_a2 | -| nested_scopes.rb:23:16:23:16 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:9:24:11 | method scope for show_a2 | -| nested_scopes.rb:25:14:25:14 | a | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:10:7:26:9 | class scope for D | -| nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:30:7:33:9 | class scope | -| nested_scopes.rb:32:16:32:16 | a | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:30:7:33:9 | class scope | -| nested_scopes.rb:34:12:34:12 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | module scope for N | -| nested_scopes.rb:36:10:36:10 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | module scope for M | -| nested_scopes.rb:38:8:38:8 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | class scope for C | -| nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:1:1:42:1 | top-level scope for nested_scopes.rb | -| nested_scopes.rb:41:1:41:1 | d | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:1:1:42:1 | top-level scope for nested_scopes.rb | -| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:1:9:5:3 | block scope | -| parameters.rb:1:18:1:18 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | block scope | -| parameters.rb:2:4:2:4 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | block scope | -| parameters.rb:3:9:3:9 | x | parameters.rb:1:14:1:14 | x | parameters.rb:1:9:5:3 | block scope | -| parameters.rb:4:9:4:9 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | block scope | -| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:7:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:8:6:8:11 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:9:25:9:30 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:11:14:11:19 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:11:41:11:46 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:15:15:15:19 | **map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | method scope for print_map | -| parameters.rb:16:3:16:5 | map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | method scope for print_map | -| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:16:12:18:5 | block scope | -| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:16:12:18:5 | block scope | -| parameters.rb:17:13:17:15 | key | parameters.rb:16:16:16:18 | key | parameters.rb:16:12:18:5 | block scope | -| parameters.rb:17:22:17:26 | value | parameters.rb:16:21:16:25 | value | parameters.rb:16:12:18:5 | block scope | -| parameters.rb:21:16:21:21 | &block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | method scope for call_block | -| parameters.rb:22:3:22:7 | block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | method scope for call_block | -| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | method scope for opt_param | -| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:25:1:28:3 | method scope for opt_param | -| parameters.rb:25:40:25:43 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | method scope for opt_param | -| parameters.rb:26:8:26:11 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | method scope for opt_param | -| parameters.rb:27:8:27:11 | size | parameters.rb:25:33:25:36 | size | parameters.rb:25:1:28:3 | method scope for opt_param | -| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:31:11:31:15 | first | parameters.rb:30:15:30:19 | first | parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:31:20:31:25 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:31:30:31:33 | last | parameters.rb:30:36:30:39 | last | parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:34:1:34:1 | b | parameters.rb:34:1:34:1 | b | parameters.rb:1:1:58:1 | top-level scope for parameters.rb | -| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:35:1:38:3 | method scope for multi | -| parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | method scope for multi | -| parameters.rb:37:11:37:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:35:1:38:3 | method scope for multi | -| parameters.rb:37:16:37:16 | b | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | method scope for multi | -| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:40:1:43:3 | method scope for multi2 | -| parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | method scope for multi2 | -| parameters.rb:42:11:42:11 | d | parameters.rb:40:12:40:12 | d | parameters.rb:40:1:43:3 | method scope for multi2 | -| parameters.rb:42:16:42:16 | e | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | method scope for multi2 | -| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:45:1:47:3 | method scope for dup_underscore | -| parameters.rb:46:8:46:8 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:45:1:47:3 | method scope for dup_underscore | -| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:49:1:51:3 | method scope for tuples | -| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:49:1:51:3 | method scope for tuples | -| parameters.rb:50:11:50:11 | a | parameters.rb:49:13:49:13 | a | parameters.rb:49:1:51:3 | method scope for tuples | -| parameters.rb:50:16:50:16 | b | parameters.rb:49:15:49:15 | b | parameters.rb:49:1:51:3 | method scope for tuples | -| parameters.rb:53:1:53:1 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | top-level scope for parameters.rb | -| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:54:9:57:3 | block scope | -| parameters.rb:54:19:54:19 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | top-level scope for parameters.rb | -| parameters.rb:55:9:55:9 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | top-level scope for parameters.rb | -| parameters.rb:56:9:56:9 | y | parameters.rb:54:14:54:14 | y | parameters.rb:54:9:57:3 | block scope | -| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | scopes.rb:2:9:6:3 | block scope | -| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | block scope | -| scopes.rb:5:9:5:9 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | block scope | -| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:15:9:15:9 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:16:9:16:9 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:17:9:17:9 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | block scope | -| scopes.rb:21:1:21:7 | $global | file://:0:0:0:0 | $global | file://:0:0:0:0 | global scope | -| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:24:10:24:11 | $0 | file://:0:0:0:0 | $0 | file://:0:0:0:0 | global scope | -| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:29:3:29:3 | x | scopes.rb:29:3:29:3 | x | scopes.rb:28:1:30:3 | module scope for B | -| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:32:3:32:3 | x | scopes.rb:32:3:32:3 | x | scopes.rb:31:1:33:3 | class scope | -| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:35:3:35:3 | x | scopes.rb:35:3:35:3 | x | scopes.rb:34:1:36:3 | class scope for C | -| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:38:3:38:3 | x | scopes.rb:38:3:38:3 | x | scopes.rb:37:1:39:3 | method scope for foo | -| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:3:8:3:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:4:8:4:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:5:6:5:6 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:6:5:6:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:7:10:7:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:8:10:8:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:10:5:10:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:11:10:11:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:12:10:12:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:15:8:15:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope for m1 | -| ssa.rb:19:9:19:9 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope for m1 | -| ssa.rb:20:10:20:10 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope for m1 | -| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | method scope for m1 | -| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | method scope for m2 | -| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | method scope for m2 | -| ssa.rb:26:15:26:22 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | method scope for m2 | -| ssa.rb:27:10:27:13 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | method scope for m2 | -| ssa.rb:29:8:29:11 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | method scope for m2 | -| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | block scope | -| ssa.rb:34:10:34:10 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | block scope | -| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | method scope for m4 | -| ssa.rb:41:8:41:9 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | method scope for m4 | -| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | method scope for m5 | -| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | method scope for m5 | -| ssa.rb:45:12:45:12 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | method scope for m5 | -| ssa.rb:46:8:46:8 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | method scope for m5 | -| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x | ssa.rb:49:1:51:3 | method scope for m6 | -| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | method scope for m6 | -| ssa.rb:50:8:50:8 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | method scope for m6 | -| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | method scope for m7 | -| ssa.rb:54:3:54:3 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | method scope for m7 | -| ssa.rb:54:7:54:9 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | method scope for m7 | -| ssa.rb:55:8:55:8 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | method scope for m7 | -| ssa.rb:59:3:59:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | method scope for m8 | -| ssa.rb:60:3:60:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | method scope for m8 | -| ssa.rb:61:8:61:8 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | method scope for m8 | -| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:65:3:65:10 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:66:3:66:3 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | block scope | -| ssa.rb:67:10:67:10 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | block scope | -| ssa.rb:68:10:68:17 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:69:5:69:12 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:71:8:71:15 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | method scope for m10 | -| ssa.rb:77:15:77:22 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | method scope for m10 | -| ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | method scope for m11 | -| ssa.rb:85:15:85:22 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | method scope for m11 | +| class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:3:3:3:5 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:6:4:6:6 | @@x | class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:11:7:11:9 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | X | +| class_variables.rb:14:6:14:8 | @@x | class_variables.rb:11:7:11:9 | @@x | class_variables.rb:9:1:16:3 | X | +| class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:5 | @@x | class_variables.rb:18:1:20:3 | Y | +| class_variables.rb:23:3:23:5 | @@x | class_variables.rb:23:3:23:5 | @@x | class_variables.rb:22:1:24:3 | M | +| class_variables.rb:28:5:28:7 | @@x | class_variables.rb:28:5:28:7 | @@x | class_variables.rb:26:1:29:3 | N | +| instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:8:8:8:11 | @foo | instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:11:6:11:9 | @top | instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:13:1:18:3 | X | +| instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:13:1:18:3 | X | +| instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:20:1:25:3 | M | +| instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:20:1:25:3 | M | +| instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:35:1:44:4 | C | +| instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | C | +| instance_variables.rb:42:6:42:7 | @x | instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:35:1:44:4 | C | +| nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:15:11:15:11 | a | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:19:20:13 | do ... end | +| nested_scopes.rb:16:13:16:13 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:19:20:13 | do ... end | +| nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:26:16:26 | x | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:17:15:17:15 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:18:15:18:15 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:26:18:26 | x | nested_scopes.rb:18:23:18:36 | { ... } | +| nested_scopes.rb:18:34:18:34 | a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:23:16:23:16 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:25:14:25:14 | a | nested_scopes.rb:11:9:11:9 | a | nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:30:7:33:9 | class << ... | +| nested_scopes.rb:32:16:32:16 | a | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:30:7:33:9 | class << ... | +| nested_scopes.rb:34:12:34:12 | a | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:36:10:36:10 | a | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:38:8:38:8 | a | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:1:1:42:1 | nested_scopes.rb | +| nested_scopes.rb:41:1:41:1 | d | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:1:1:42:1 | nested_scopes.rb | +| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:1:18:1:18 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:2:4:2:4 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:3:9:3:9 | x | parameters.rb:1:14:1:14 | x | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:4:9:4:9 | y | parameters.rb:1:18:1:18 | y | parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:8:6:8:11 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:9:25:9:30 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:11:14:11:19 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:11:41:11:46 | client | parameters.rb:7:17:7:22 | client | parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | print_map | +| parameters.rb:16:3:16:5 | map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | print_map | +| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:17:13:17:15 | key | parameters.rb:16:16:16:18 | key | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:17:22:17:26 | value | parameters.rb:16:21:16:25 | value | parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | call_block | +| parameters.rb:22:3:22:7 | block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | call_block | +| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:25:33:25:36 | size | parameters.rb:25:33:25:36 | size | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:25:40:25:43 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:26:8:26:11 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:27:8:27:11 | size | parameters.rb:25:33:25:36 | size | parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:30:15:30:19 | first | parameters.rb:30:15:30:19 | first | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:30:24:30:29 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:30:36:30:39 | last | parameters.rb:30:36:30:39 | last | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:11:31:15 | first | parameters.rb:30:15:30:19 | first | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:20:31:25 | middle | parameters.rb:30:24:30:29 | middle | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:31:30:31:33 | last | parameters.rb:30:36:30:39 | last | parameters.rb:30:1:32:3 | key_param | +| parameters.rb:34:1:34:1 | b | parameters.rb:34:1:34:1 | b | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:35:11:35:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:35:1:38:3 | multi | +| parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | multi | +| parameters.rb:37:11:37:11 | a | parameters.rb:35:11:35:11 | a | parameters.rb:35:1:38:3 | multi | +| parameters.rb:37:16:37:16 | b | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | multi | +| parameters.rb:40:12:40:12 | d | parameters.rb:40:12:40:12 | d | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:42:11:42:11 | d | parameters.rb:40:12:40:12 | d | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:42:16:42:16 | e | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:45:20:45:20 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:46:8:46:8 | _ | parameters.rb:45:20:45:20 | _ | parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:50:11:50:11 | a | parameters.rb:49:13:49:13 | a | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:50:16:50:16 | b | parameters.rb:49:15:49:15 | b | parameters.rb:49:1:51:3 | tuples | +| parameters.rb:53:1:53:1 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:54:9:57:3 | do ... end | +| parameters.rb:54:19:54:19 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:55:9:55:9 | x | parameters.rb:53:1:53:1 | x | parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:56:9:56:9 | y | parameters.rb:54:14:54:14 | y | parameters.rb:54:9:57:3 | do ... end | +| scopes.rb:2:14:2:14 | x | scopes.rb:2:14:2:14 | x | scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:4:4:4:4 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:5:9:5:9 | a | scopes.rb:4:4:4:4 | a | scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:7:1:7:1 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:8:6:8:6 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:9:14:9:14 | x | scopes.rb:9:14:9:14 | x | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:10:9:10:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:11:4:11:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:12:9:12:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:13:4:13:4 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:13:7:13:7 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:13:11:13:11 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:13:14:13:14 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:14:9:14:9 | a | scopes.rb:7:1:7:1 | a | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:15:9:15:9 | b | scopes.rb:13:7:13:7 | b | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:16:9:16:9 | c | scopes.rb:13:11:13:11 | c | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:17:9:17:9 | d | scopes.rb:13:14:13:14 | d | scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:24:1:24:6 | script | scopes.rb:24:1:24:6 | script | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:27:1:27:1 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:28:8:28:8 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:29:3:29:3 | x | scopes.rb:29:3:29:3 | x | scopes.rb:28:1:30:3 | B | +| scopes.rb:31:10:31:10 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:32:3:32:3 | x | scopes.rb:32:3:32:3 | x | scopes.rb:31:1:33:3 | class << ... | +| scopes.rb:34:7:34:7 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:34:14:34:14 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:35:3:35:3 | x | scopes.rb:35:3:35:3 | x | scopes.rb:34:1:36:3 | C | +| scopes.rb:37:5:37:5 | x | scopes.rb:27:1:27:1 | x | scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:38:3:38:3 | x | scopes.rb:38:3:38:3 | x | scopes.rb:37:1:39:3 | foo | +| ssa.rb:1:7:1:7 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | m | +| ssa.rb:2:3:2:3 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:3:8:3:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:4:8:4:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:5:6:5:6 | b | ssa.rb:1:7:1:7 | b | ssa.rb:1:1:16:3 | m | +| ssa.rb:6:5:6:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:7:10:7:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:8:10:8:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:10:5:10:5 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:11:10:11:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:12:10:12:10 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:15:8:15:8 | i | ssa.rb:2:3:2:3 | i | ssa.rb:1:1:16:3 | m | +| ssa.rb:18:8:18:8 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:19:9:19:9 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:20:10:20:10 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:21:5:21:5 | x | ssa.rb:18:8:18:8 | x | ssa.rb:18:1:23:3 | m1 | +| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:26:7:26:10 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:26:15:26:22 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:27:10:27:13 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:29:8:29:11 | elem | ssa.rb:26:7:26:10 | elem | ssa.rb:25:1:30:3 | m2 | +| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | do ... end | +| ssa.rb:34:10:34:10 | x | ssa.rb:33:20:33:20 | x | ssa.rb:33:16:35:5 | do ... end | +| ssa.rb:40:3:40:4 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:41:8:41:9 | m3 | ssa.rb:40:3:40:4 | m3 | ssa.rb:38:1:42:3 | m4 | +| ssa.rb:44:8:44:8 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:45:3:45:3 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:45:12:45:12 | b | ssa.rb:44:8:44:8 | b | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:46:8:46:8 | x | ssa.rb:45:3:45:3 | x | ssa.rb:44:1:47:3 | m5 | +| ssa.rb:49:9:49:9 | x | ssa.rb:49:9:49:9 | x | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:49:14:49:14 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:50:8:50:8 | y | ssa.rb:49:14:49:14 | y | ssa.rb:49:1:51:3 | m6 | +| ssa.rb:53:8:53:10 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:54:3:54:3 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:54:7:54:9 | foo | ssa.rb:53:8:53:10 | foo | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:55:8:55:8 | x | ssa.rb:54:3:54:3 | x | ssa.rb:53:1:56:3 | m7 | +| ssa.rb:59:3:59:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:60:3:60:3 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:61:8:61:8 | x | ssa.rb:59:3:59:3 | x | ssa.rb:58:1:62:3 | m8 | +| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:65:3:65:10 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:66:3:66:3 | a | ssa.rb:64:8:64:8 | a | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | do ... end | +| ssa.rb:67:10:67:10 | a | ssa.rb:66:15:66:15 | a | ssa.rb:66:11:70:5 | do ... end | +| ssa.rb:68:10:68:17 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:69:5:69:12 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:71:8:71:15 | captured | ssa.rb:65:3:65:10 | captured | ssa.rb:64:1:72:3 | m9 | +| ssa.rb:75:3:75:10 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | m10 | +| ssa.rb:77:15:77:22 | captured | ssa.rb:75:3:75:10 | captured | ssa.rb:74:1:79:3 | m10 | +| ssa.rb:82:3:82:10 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | m11 | +| ssa.rb:85:15:85:22 | captured | ssa.rb:82:3:82:10 | captured | ssa.rb:81:1:88:3 | m11 | explicitWrite +| class_variables.rb:1:1:1:3 | @@x | class_variables.rb:1:1:1:8 | ... = ... | +| class_variables.rb:19:3:19:5 | @@x | class_variables.rb:19:3:19:10 | ... = ... | +| class_variables.rb:23:3:23:5 | @@x | class_variables.rb:23:3:23:10 | ... = ... | +| instance_variables.rb:1:1:1:4 | @top | instance_variables.rb:1:1:1:8 | ... = ... | +| instance_variables.rb:4:3:4:6 | @foo | instance_variables.rb:4:3:4:11 | ... = ... | +| instance_variables.rb:14:3:14:4 | @x | instance_variables.rb:14:3:14:9 | ... = ... | +| instance_variables.rb:16:5:16:6 | @y | instance_variables.rb:16:5:16:10 | ... = ... | +| instance_variables.rb:21:2:21:3 | @m | instance_variables.rb:21:2:21:8 | ... = ... | +| instance_variables.rb:23:4:23:5 | @n | instance_variables.rb:23:4:23:9 | ... = ... | +| instance_variables.rb:28:3:28:4 | @x | instance_variables.rb:28:3:28:10 | ... = ... | +| instance_variables.rb:32:12:32:13 | @x | instance_variables.rb:32:12:32:19 | ... = ... | +| instance_variables.rb:36:3:36:4 | @x | instance_variables.rb:36:3:36:9 | ... = ... | +| instance_variables.rb:39:6:39:7 | @x | instance_variables.rb:39:6:39:12 | ... = ... | | nested_scopes.rb:5:3:5:3 | a | nested_scopes.rb:5:3:5:7 | ... = ... | | nested_scopes.rb:7:5:7:5 | a | nested_scopes.rb:7:5:7:9 | ... = ... | | nested_scopes.rb:9:7:9:7 | a | nested_scopes.rb:9:7:9:11 | ... = ... | @@ -228,11 +239,11 @@ implicitWrite | parameters.rb:1:14:1:14 | x | | parameters.rb:1:18:1:18 | y | | parameters.rb:7:17:7:22 | client | -| parameters.rb:7:25:7:31 | *pizzas | -| parameters.rb:15:15:15:19 | **map | +| parameters.rb:7:26:7:31 | pizzas | +| parameters.rb:15:17:15:19 | map | | parameters.rb:16:16:16:18 | key | | parameters.rb:16:21:16:25 | value | -| parameters.rb:21:16:21:21 | &block | +| parameters.rb:21:17:21:21 | block | | parameters.rb:25:15:25:18 | name | | parameters.rb:25:33:25:36 | size | | parameters.rb:30:15:30:19 | first | @@ -257,26 +268,13 @@ implicitWrite | ssa.rb:64:8:64:8 | a | | ssa.rb:66:15:66:15 | a | readAccess -| class_variables.rb:1:1:1:3 | @@x | | class_variables.rb:3:3:3:5 | @@x | | class_variables.rb:6:4:6:6 | @@x | | class_variables.rb:11:7:11:9 | @@x | | class_variables.rb:14:6:14:8 | @@x | -| class_variables.rb:19:3:19:5 | @@x | -| class_variables.rb:23:3:23:5 | @@x | | class_variables.rb:28:5:28:7 | @@x | -| instance_variables.rb:1:1:1:4 | @top | -| instance_variables.rb:4:3:4:6 | @foo | | instance_variables.rb:8:8:8:11 | @foo | | instance_variables.rb:11:6:11:9 | @top | -| instance_variables.rb:14:3:14:4 | @x | -| instance_variables.rb:16:5:16:6 | @y | -| instance_variables.rb:21:2:21:3 | @m | -| instance_variables.rb:23:4:23:5 | @n | -| instance_variables.rb:28:3:28:4 | @x | -| instance_variables.rb:32:12:32:13 | @x | -| instance_variables.rb:36:3:36:4 | @x | -| instance_variables.rb:39:6:39:7 | @x | | instance_variables.rb:42:6:42:7 | @x | | nested_scopes.rb:14:16:14:16 | a | | nested_scopes.rb:15:11:15:11 | a | diff --git a/ql/test/library-tests/variables/varaccess.ql b/ql/test/library-tests/variables/varaccess.ql index df7f516efb4..fff11bb421f 100644 --- a/ql/test/library-tests/variables/varaccess.ql +++ b/ql/test/library-tests/variables/varaccess.ql @@ -1,7 +1,7 @@ import codeql_ruby.AST import codeql_ruby.ast.Variable -query predicate variableAccess(VariableAccess access, Variable variable, VariableScope scope) { +query predicate variableAccess(VariableAccess access, Variable variable, Scope scope) { variable = access.getVariable() and scope = variable.getDeclaringScope() } diff --git a/ql/test/library-tests/variables/varscopes.expected b/ql/test/library-tests/variables/varscopes.expected index 401ce5a280d..9639caa4cb0 100644 --- a/ql/test/library-tests/variables/varscopes.expected +++ b/ql/test/library-tests/variables/varscopes.expected @@ -1,75 +1,74 @@ -| class_variables.rb:1:1:29:4 | top-level scope for class_variables.rb | -| class_variables.rb:5:1:7:3 | method scope for print | -| class_variables.rb:9:1:16:3 | class scope for X | -| class_variables.rb:10:3:12:5 | method scope for b | -| class_variables.rb:13:3:15:5 | method scope for s | -| class_variables.rb:18:1:20:3 | class scope for Y | -| class_variables.rb:22:1:24:3 | module scope for M | -| class_variables.rb:26:1:29:3 | module scope for N | -| file://:0:0:0:0 | global scope | -| instance_variables.rb:1:1:44:4 | top-level scope for instance_variables.rb | -| instance_variables.rb:3:1:5:3 | method scope for foo | -| instance_variables.rb:7:1:9:3 | method scope for print_foo | -| instance_variables.rb:13:1:18:3 | class scope for X | -| instance_variables.rb:15:3:17:5 | method scope for m | -| instance_variables.rb:20:1:25:3 | module scope for M | -| instance_variables.rb:22:2:24:4 | method scope for n | -| instance_variables.rb:27:6:29:1 | block scope | -| instance_variables.rb:31:1:33:3 | method scope for bar | -| instance_variables.rb:32:10:32:21 | block scope | -| instance_variables.rb:35:1:44:4 | class scope for C | -| instance_variables.rb:37:3:43:5 | method scope for x | -| instance_variables.rb:38:4:40:6 | method scope for y | -| nested_scopes.rb:1:1:3:3 | method scope for a | -| nested_scopes.rb:1:1:42:1 | top-level scope for nested_scopes.rb | -| nested_scopes.rb:4:1:39:3 | class scope for C | -| nested_scopes.rb:6:3:37:5 | module scope for M | -| nested_scopes.rb:8:5:35:7 | module scope for N | -| nested_scopes.rb:10:7:26:9 | class scope for D | -| nested_scopes.rb:12:9:21:11 | method scope for show_a | -| nested_scopes.rb:15:19:20:13 | block scope | -| nested_scopes.rb:16:21:19:15 | block scope | -| nested_scopes.rb:18:23:18:36 | block scope | -| nested_scopes.rb:22:9:24:11 | method scope for show_a2 | -| nested_scopes.rb:27:7:29:9 | method scope for show | -| nested_scopes.rb:30:7:33:9 | class scope | -| parameters.rb:1:1:58:1 | top-level scope for parameters.rb | -| parameters.rb:1:9:5:3 | block scope | -| parameters.rb:7:1:13:3 | method scope for order_pizza | -| parameters.rb:15:1:19:3 | method scope for print_map | -| parameters.rb:16:12:18:5 | block scope | -| parameters.rb:21:1:23:3 | method scope for call_block | -| parameters.rb:25:1:28:3 | method scope for opt_param | -| parameters.rb:30:1:32:3 | method scope for key_param | -| parameters.rb:35:1:38:3 | method scope for multi | -| parameters.rb:40:1:43:3 | method scope for multi2 | -| parameters.rb:45:1:47:3 | method scope for dup_underscore | -| parameters.rb:49:1:51:3 | method scope for tuples | -| parameters.rb:54:9:57:3 | block scope | -| scopes.rb:1:1:1:15 | method scope for a | -| scopes.rb:1:1:40:1 | top-level scope for scopes.rb | -| scopes.rb:2:9:6:3 | block scope | -| scopes.rb:9:9:18:3 | block scope | -| scopes.rb:26:1:26:12 | class scope for A | -| scopes.rb:28:1:30:3 | module scope for B | -| scopes.rb:31:1:33:3 | class scope | -| scopes.rb:34:1:36:3 | class scope for C | -| scopes.rb:37:1:39:3 | method scope for foo | -| ssa.rb:1:1:16:3 | method scope for m | -| ssa.rb:1:1:88:3 | top-level scope for ssa.rb | -| ssa.rb:18:1:23:3 | method scope for m1 | -| ssa.rb:25:1:30:3 | method scope for m2 | -| ssa.rb:32:1:36:3 | method scope for m3 | -| ssa.rb:33:16:35:5 | block scope | -| ssa.rb:38:1:42:3 | method scope for m4 | -| ssa.rb:44:1:47:3 | method scope for m5 | -| ssa.rb:49:1:51:3 | method scope for m6 | -| ssa.rb:53:1:56:3 | method scope for m7 | -| ssa.rb:58:1:62:3 | method scope for m8 | -| ssa.rb:64:1:72:3 | method scope for m9 | -| ssa.rb:66:11:70:5 | block scope | -| ssa.rb:74:1:79:3 | method scope for m10 | -| ssa.rb:76:7:78:5 | block scope | -| ssa.rb:81:1:88:3 | method scope for m11 | -| ssa.rb:83:7:87:5 | block scope | -| ssa.rb:84:10:86:8 | block scope | +| class_variables.rb:1:1:29:4 | class_variables.rb | +| class_variables.rb:5:1:7:3 | print | +| class_variables.rb:9:1:16:3 | X | +| class_variables.rb:10:3:12:5 | b | +| class_variables.rb:13:3:15:5 | s | +| class_variables.rb:18:1:20:3 | Y | +| class_variables.rb:22:1:24:3 | M | +| class_variables.rb:26:1:29:3 | N | +| instance_variables.rb:1:1:44:4 | instance_variables.rb | +| instance_variables.rb:3:1:5:3 | foo | +| instance_variables.rb:7:1:9:3 | print_foo | +| instance_variables.rb:13:1:18:3 | X | +| instance_variables.rb:15:3:17:5 | m | +| instance_variables.rb:20:1:25:3 | M | +| instance_variables.rb:22:2:24:4 | n | +| instance_variables.rb:27:6:29:1 | { ... } | +| instance_variables.rb:31:1:33:3 | bar | +| instance_variables.rb:32:10:32:21 | { ... } | +| instance_variables.rb:35:1:44:4 | C | +| instance_variables.rb:37:3:43:5 | x | +| instance_variables.rb:38:4:40:6 | y | +| nested_scopes.rb:1:1:3:3 | a | +| nested_scopes.rb:1:1:42:1 | nested_scopes.rb | +| nested_scopes.rb:4:1:39:3 | C | +| nested_scopes.rb:6:3:37:5 | M | +| nested_scopes.rb:8:5:35:7 | N | +| nested_scopes.rb:10:7:26:9 | D | +| nested_scopes.rb:12:9:21:11 | show_a | +| nested_scopes.rb:15:19:20:13 | do ... end | +| nested_scopes.rb:16:21:19:15 | do ... end | +| nested_scopes.rb:18:23:18:36 | { ... } | +| nested_scopes.rb:22:9:24:11 | show_a2 | +| nested_scopes.rb:27:7:29:9 | show | +| nested_scopes.rb:30:7:33:9 | class << ... | +| parameters.rb:1:1:58:1 | parameters.rb | +| parameters.rb:1:9:5:3 | do ... end | +| parameters.rb:7:1:13:3 | order_pizza | +| parameters.rb:15:1:19:3 | print_map | +| parameters.rb:16:12:18:5 | do ... end | +| parameters.rb:21:1:23:3 | call_block | +| parameters.rb:25:1:28:3 | opt_param | +| parameters.rb:30:1:32:3 | key_param | +| parameters.rb:35:1:38:3 | multi | +| parameters.rb:40:1:43:3 | multi2 | +| parameters.rb:45:1:47:3 | dup_underscore | +| parameters.rb:49:1:51:3 | tuples | +| parameters.rb:54:9:57:3 | do ... end | +| scopes.rb:1:1:1:15 | a | +| scopes.rb:1:1:40:1 | scopes.rb | +| scopes.rb:2:9:6:3 | do ... end | +| scopes.rb:9:9:18:3 | do ... end | +| scopes.rb:26:1:26:12 | A | +| scopes.rb:28:1:30:3 | B | +| scopes.rb:31:1:33:3 | class << ... | +| scopes.rb:34:1:36:3 | C | +| scopes.rb:37:1:39:3 | foo | +| ssa.rb:1:1:16:3 | m | +| ssa.rb:1:1:88:3 | ssa.rb | +| ssa.rb:18:1:23:3 | m1 | +| ssa.rb:25:1:30:3 | m2 | +| ssa.rb:32:1:36:3 | m3 | +| ssa.rb:33:16:35:5 | do ... end | +| ssa.rb:38:1:42:3 | m4 | +| ssa.rb:44:1:47:3 | m5 | +| ssa.rb:49:1:51:3 | m6 | +| ssa.rb:53:1:56:3 | m7 | +| ssa.rb:58:1:62:3 | m8 | +| ssa.rb:64:1:72:3 | m9 | +| ssa.rb:66:11:70:5 | do ... end | +| ssa.rb:74:1:79:3 | m10 | +| ssa.rb:76:7:78:5 | do ... end | +| ssa.rb:81:1:88:3 | m11 | +| ssa.rb:83:7:87:5 | do ... end | +| ssa.rb:84:10:86:8 | do ... end | diff --git a/ql/test/library-tests/variables/varscopes.ql b/ql/test/library-tests/variables/varscopes.ql index e0272ce17af..8a8af82355b 100644 --- a/ql/test/library-tests/variables/varscopes.ql +++ b/ql/test/library-tests/variables/varscopes.ql @@ -1,3 +1,3 @@ -import codeql_ruby.ast.Variable +import codeql_ruby.ast.Scope -select any(VariableScope x) +select any(Scope x) diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.expected b/ql/test/query-tests/performance/UseDetect/UseDetect.expected new file mode 100644 index 00000000000..d8a5ec8dd41 --- /dev/null +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.expected @@ -0,0 +1,7 @@ +| UseDetect.rb:5:9:5:36 | call to first | Replace this call and $@ with 'detect'. | UseDetect.rb:5:9:5:30 | call to select | 'select' call | +| UseDetect.rb:6:9:6:35 | call to last | Replace this call and $@ with 'reverse_detect'. | UseDetect.rb:6:9:6:30 | call to select | 'select' call | +| UseDetect.rb:7:9:7:33 | ...[...] | Replace this call and $@ with 'detect'. | UseDetect.rb:7:9:7:30 | call to select | 'select' call | +| UseDetect.rb:8:9:8:34 | ...[...] | Replace this call and $@ with 'reverse_detect'. | UseDetect.rb:8:9:8:30 | call to select | 'select' call | +| UseDetect.rb:9:9:9:36 | call to first | Replace this call and $@ with 'detect'. | UseDetect.rb:9:9:9:30 | call to filter | 'select' call | +| UseDetect.rb:10:9:10:37 | call to last | Replace this call and $@ with 'reverse_detect'. | UseDetect.rb:10:9:10:32 | call to find_all | 'select' call | +| UseDetect.rb:12:9:12:24 | call to first | Replace this call and $@ with 'detect'. | UseDetect.rb:11:22:11:43 | call to select | 'select' call | diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.qlref b/ql/test/query-tests/performance/UseDetect/UseDetect.qlref new file mode 100644 index 00000000000..6d920e39cff --- /dev/null +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.qlref @@ -0,0 +1 @@ +queries/performance/UseDetect.ql \ No newline at end of file diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.rb b/ql/test/query-tests/performance/UseDetect/UseDetect.rb new file mode 100644 index 00000000000..e1d2d9b91ba --- /dev/null +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.rb @@ -0,0 +1,21 @@ + +class DetectTest + def test + # These are bad + [].select { |i| true }.first + [].select { |i| true }.last + [].select { |i| true }[0] + [].select { |i| true }[-1] + [].filter { |i| true }.first + [].find_all { |i| true }.last + selection1 = [].select { |i| true } + selection1.first + + # These are good + [].select("").first # Selecting a string + [].select { |i| true }[1] # Selecting the second element + selection2 = [].select { |i| true } + selection2.first # Selection used elsewhere + selection3 = selection2 + end +end