diff --git a/ql/src/codeql_ruby/ast/Method.qll b/ql/src/codeql_ruby/ast/Method.qll index 47cf9d36f09..bf284ea29f8 100644 --- a/ql/src/codeql_ruby/ast/Method.qll +++ b/ql/src/codeql_ruby/ast/Method.qll @@ -4,7 +4,9 @@ private import internal.Method /** A callable. */ class Callable extends AstNode { - Callable() { this instanceof CallableRange } + Callable::Range range; + + Callable() { range = this } /** Gets the number of parameters of this callable. */ final int getNumberOfParameters() { result = count(this.getAParameter()) } @@ -12,12 +14,13 @@ class Callable extends AstNode { /** Gets a parameter of this callable. */ final Parameter getAParameter() { result = this.getParameter(_) } - /** Gets the nth parameter of this callable. */ - final Parameter getParameter(int n) { result = this.(CallableRange).getParameter(n) } + /** Gets the `n`th parameter of this callable. */ + final Parameter getParameter(int n) { result = range.getParameter(n) } } /** A method. */ class Method extends Callable, @method { + final override Method::Range range; final override Generated::Method generated; final override string describeQlClass() { result = "Method" } @@ -25,12 +28,7 @@ class Method extends Callable, @method { final override string toString() { result = this.getName() } /** Gets the name of this method. */ - final string getName() { - result = generated.getName().(Generated::Token).getValue() or - // TODO: use hand-written Symbol class - result = generated.getName().(Generated::Symbol).toString() or - result = generated.getName().(Generated::Setter).getName().getValue() + "=" - } + final string getName() { result = range.getName() } /** * Holds if this is a setter method, as in the following example: @@ -47,19 +45,14 @@ class Method extends Callable, @method { /** A singleton method. */ class SingletonMethod extends Callable, @singleton_method { - final override Generated::SingletonMethod generated; + final override SingletonMethod::Range range; final override string describeQlClass() { result = "SingletonMethod" } final override string toString() { result = this.getName() } /** Gets the name of this method. */ - final string getName() { - result = generated.getName().(Generated::Token).getValue() or - // TODO: use hand-written Symbol class - result = generated.getName().(Generated::Symbol).toString() or - result = generated.getName().(Generated::Setter).getName().getValue() + "=" - } + final string getName() { result = range.getName() } } /** @@ -69,7 +62,7 @@ class SingletonMethod extends Callable, @singleton_method { * ``` */ class Lambda extends Callable, @lambda { - final override Generated::Lambda generated; + final override Lambda::Range range; final override string describeQlClass() { result = "Lambda" } @@ -78,12 +71,12 @@ class Lambda extends Callable, @lambda { /** A block. */ class Block extends AstNode, Callable { - Block() { this instanceof BlockRange } + override Block::Range range; } /** A block enclosed within `do` and `end`. */ class DoBlock extends Block, @do_block { - final override Generated::DoBlock generated; + final override DoBlock::Range range; final override string describeQlClass() { result = "DoBlock" } @@ -97,7 +90,7 @@ class DoBlock extends Block, @do_block { * ``` */ class BraceBlock extends Block, @block { - final override Generated::Block generated; + final override BraceBlock::Range range; final override string describeQlClass() { result = "BraceBlock" } diff --git a/ql/src/codeql_ruby/ast/Parameter.qll b/ql/src/codeql_ruby/ast/Parameter.qll index 87be195530b..a9c3587c523 100644 --- a/ql/src/codeql_ruby/ast/Parameter.qll +++ b/ql/src/codeql_ruby/ast/Parameter.qll @@ -1,6 +1,5 @@ import codeql_ruby.AST -private import Variable -private import Pattern +private import internal.Pattern private import internal.TreeSitter private import internal.Variable @@ -37,15 +36,13 @@ class Parameter extends AstNode { * * This includes both simple parameters and tuple parameters. */ -class PatternParameter extends Parameter, Pattern { } +class PatternParameter extends Parameter, Pattern { + override Variable getAVariable() { result = Pattern.super.getAVariable() } +} /** A parameter defined using a tuple pattern. */ class TuplePatternParameter extends PatternParameter, TuplePattern { final override string describeQlClass() { result = "TuplePatternParameter" } - - final override Variable getAVariable() { - result = this.getAnElement+().(VariablePattern).getVariable() - } } /** A named parameter. */ @@ -58,7 +55,7 @@ class NamedParameter extends Parameter { /** Gets the variable introduced by this parameter. */ Variable getVariable() { none() } - final override Variable getAVariable() { result = this.getVariable() } + override Variable getAVariable() { result = this.getVariable() } /** Gets an access to this parameter. */ final VariableAccess getAnAccess() { result = this.getVariable().getAnAccess() } @@ -66,10 +63,12 @@ class NamedParameter extends Parameter { /** A simple (normal) parameter. */ class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern { - final override string getName() { result = this.getVariableName() } + final override string getName() { result = range.getVariableName() } final override Variable getVariable() { result = TLocalVariable(_, _, this) } + final override Variable getAVariable() { result = this.getVariable() } + final override string describeQlClass() { result = "SimpleParameter" } final override string toString() { result = this.getName() } diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 530a11ea2c7..093cb665740 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -7,20 +7,23 @@ private import Variable /** A pattern. */ class Pattern extends AstNode { - Pattern() { this instanceof PatternRange } + Pattern::Range range; + + Pattern() { range = this } + + /** Gets a variable used in (or introduced by) this pattern. */ + Variable getAVariable() { result = range.getAVariable() } } /** A simple variable pattern. */ class VariablePattern extends Pattern { + final override VariablePattern::Range range; final override Generated::Identifier generated; - /** Gets the name of the variable used in this pattern. */ - final string getVariableName() { result = generated.getValue() } - /** Gets the variable used in (or introduced by) this pattern. */ Variable getVariable() { access(this, result) } - override string toString() { result = this.getVariableName() } + override string toString() { result = range.getVariableName() } } /** @@ -29,10 +32,10 @@ class VariablePattern extends Pattern { * This includes both tuple patterns in parameters and assignments. */ class TuplePattern extends Pattern { - TuplePattern() { this instanceof TuplePatternRange } + final override TuplePattern::Range range; /** Gets the `i`th pattern in this tuple pattern. */ - final Pattern getElement(int i) { result = this.(TuplePatternRange).getElement(i) } + final Pattern getElement(int i) { result = range.getElement(i) } /** Gets a sub pattern in this tuple pattern. */ final Pattern getAnElement() { result = this.getElement(_) } diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index dbc36512d49..6e56ab83c1b 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -7,11 +7,15 @@ 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 = this.(VariableScopeRange).toString() } + final string toString() { result = range.toString() } /** Gets the program element this scope is associated with, if any. */ - final AstNode getScopeElement() { result = this.(VariableScopeRange).getScopeElement() } + final AstNode getScopeElement() { result = range.getScopeElement() } /** Gets the location of the program element this scope is associated with. */ final Location getLocation() { result = getScopeElement().getLocation() } @@ -28,17 +32,21 @@ class VariableScope extends TScope { /** A variable declared in a scope. */ class Variable extends TVariable { + Variable::Range range; + + Variable() { range = this } + /** Gets the name of this variable. */ - final string getName() { result = this.(VariableRange).getName() } + final string getName() { result = range.getName() } /** Gets a textual representation of this variable. */ final string toString() { result = this.getName() } /** Gets the location of this variable. */ - final Location getLocation() { result = this.(VariableRange).getLocation() } + final Location getLocation() { result = range.getLocation() } /** Gets the scope this variable is declared in. */ - final VariableScope getDeclaringScope() { result = this.(VariableRange).getDeclaringScope() } + final VariableScope getDeclaringScope() { result = range.getDeclaringScope() } /** Gets an access to this variable. */ VariableAccess getAnAccess() { result.getVariable() = this } @@ -46,11 +54,7 @@ class Variable extends TVariable { /** A local variable. */ class LocalVariable extends Variable { - private VariableScope scope; - private string name; - private Generated::Identifier i; - - LocalVariable() { this = TLocalVariable(scope, name, i) } + override LocalVariable::Range range; final override LocalVariableAccess getAnAccess() { result.getVariable() = this } } diff --git a/ql/src/codeql_ruby/ast/internal/Method.qll b/ql/src/codeql_ruby/ast/internal/Method.qll index 43602e66dd4..577e1ebdcf0 100644 --- a/ql/src/codeql_ruby/ast/internal/Method.qll +++ b/ql/src/codeql_ruby/ast/internal/Method.qll @@ -1,42 +1,70 @@ import codeql_ruby.AST private import TreeSitter -abstract class CallableRange extends AstNode { - abstract Parameter getParameter(int n); +module Callable { + abstract class Range extends AstNode { + abstract Parameter getParameter(int n); + } } -private class MethodRange extends CallableRange, @method { - final override Generated::Method generated; +module Method { + class Range extends Callable::Range, @method { + final override Generated::Method generated; - override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) } + override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) } + + string getName() { + result = generated.getName().(Generated::Token).getValue() or + // TODO: use hand-written Symbol class + result = generated.getName().(Generated::Symbol).toString() or + result = generated.getName().(Generated::Setter).getName().getValue() + "=" + } + } } -private class SingletonMethodRange extends CallableRange, @singleton_method { - final override Generated::SingletonMethod generated; +module SingletonMethod { + class Range extends Callable::Range, @singleton_method { + final override Generated::SingletonMethod generated; - override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) } + override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) } + + string getName() { + result = generated.getName().(Generated::Token).getValue() or + // TODO: use hand-written Symbol class + result = generated.getName().(Generated::Symbol).toString() or + result = generated.getName().(Generated::Setter).getName().getValue() + "=" + } + } } -private class LambdaRange extends CallableRange, @lambda { - final override Generated::Lambda generated; +module Lambda { + class Range extends Callable::Range, @lambda { + final override Generated::Lambda generated; - final override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) } + final override Parameter getParameter(int n) { result = generated.getParameters().getChild(n) } + } } -abstract class BlockRange extends CallableRange { - Generated::BlockParameters params; +module Block { + abstract class Range extends Callable::Range { + Generated::BlockParameters params; - final override Parameter getParameter(int n) { result = params.getChild(n) } + final override Parameter getParameter(int n) { result = params.getChild(n) } + } } -private class DoBlockRange extends BlockRange, @do_block { - final override Generated::DoBlock generated; +module DoBlock { + class Range extends Block::Range, @do_block { + final override Generated::DoBlock generated; - DoBlockRange() { params = generated.getParameters() } + Range() { params = generated.getParameters() } + } } -private class BraceBlockRange extends BlockRange, @block { - final override Generated::Block generated; +module BraceBlock { + class Range extends Block::Range, @block { + final override Generated::Block generated; - BraceBlockRange() { params = generated.getParameters() } + Range() { params = generated.getParameters() } + } } diff --git a/ql/src/codeql_ruby/ast/internal/Pattern.qll b/ql/src/codeql_ruby/ast/internal/Pattern.qll index eb34db359a5..5f55dd9c664 100644 --- a/ql/src/codeql_ruby/ast/internal/Pattern.qll +++ b/ql/src/codeql_ruby/ast/internal/Pattern.qll @@ -1,5 +1,6 @@ import codeql_ruby.AST private import TreeSitter +private import Variable private import codeql.Locations private predicate tuplePatternNode(Generated::AstNode n, boolean parameter) { @@ -34,32 +35,46 @@ private predicate patternNode(Generated::AstNode n, boolean parameter) { */ predicate assignment(Generated::Identifier i, boolean parameter) { patternNode(i, parameter) } -abstract class PatternRange extends AstNode { - PatternRange() { patternNode(this, _) } +module Pattern { + abstract class Range extends AstNode { + Range() { patternNode(this, _) } + + abstract Variable getAVariable(); + } } -private class VariablePatternRange extends PatternRange { - override Generated::Identifier generated; +module VariablePattern { + class Range extends Pattern::Range { + override Generated::Identifier generated; + + string getVariableName() { result = generated.getValue() } + + override Variable getAVariable() { access(this, result) } + } } -abstract class TuplePatternRange extends PatternRange { - abstract Pattern getElement(int i); -} - -private class ParameterTuplePatternRange extends TuplePatternRange { - override Generated::DestructuredParameter generated; - - override Pattern getElement(int i) { result = generated.getChild(i) } -} - -private class AssignmentTuplePatternRange extends TuplePatternRange { - override Generated::DestructuredLeftAssignment generated; - - override Pattern getElement(int i) { result = generated.getChild(i) } -} - -private class AssignmentListPatternRange extends TuplePatternRange { - override Generated::LeftAssignmentList generated; - - override Pattern getElement(int i) { result = generated.getChild(i) } +module TuplePattern { + abstract class Range extends Pattern::Range { + abstract Pattern::Range getElement(int i); + + override Variable getAVariable() { result = this.getElement(_).getAVariable() } + } + + private class ParameterTuplePatternRange extends Range { + override Generated::DestructuredParameter generated; + + override Pattern::Range getElement(int i) { result = generated.getChild(i) } + } + + private class AssignmentTuplePatternRange extends Range { + override Generated::DestructuredLeftAssignment generated; + + override Pattern::Range getElement(int i) { result = generated.getChild(i) } + } + + private class AssignmentListPatternRange extends Range { + override Generated::LeftAssignmentList generated; + + override Pattern::Range getElement(int i) { result = generated.getChild(i) } + } } diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index 6dab791c9e9..a6d89adda95 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -13,7 +13,9 @@ private VariableScope enclosingScope(Generated::AstNode node) { result.getScopeElement() = parent*(node.getParent()) } -private predicate parameterAssignment(CallableScopeRange scope, string name, Generated::Identifier i) { +private predicate parameterAssignment( + CallableScope::Range scope, string name, Generated::Identifier i +) { assignment(i, true) and scope = enclosingScope(i) and name = i.getValue() @@ -21,7 +23,7 @@ private predicate parameterAssignment(CallableScopeRange scope, string name, Gen /** Holds if `scope` defines `name` in its parameter declaration at `i`. */ private predicate scopeDefinesParameterVariable( - CallableScopeRange scope, string name, Generated::Identifier i + CallableScope::Range scope, string name, Generated::Identifier i ) { parameterAssignment(scope, name, i) and // In case of overlapping parameter names (e.g. `_`), only the first @@ -142,69 +144,83 @@ private module Cached { import Cached -abstract class VariableScopeRange extends TScope { - abstract string toString(); +module VariableScope { + abstract class Range extends TScope { + abstract string toString(); - abstract AstNode getScopeElement(); -} - -class TopLevelScopeRange extends VariableScopeRange, TTopLevelScope { - override string toString() { result = "top-level scope" } - - override AstNode getScopeElement() { TTopLevelScope(result) = this } -} - -class ModuleScopeRange extends VariableScopeRange, TModuleScope { - override string toString() { result = "module scope" } - - override AstNode getScopeElement() { TModuleScope(result) = this } -} - -class ClassScopeRange extends VariableScopeRange, TClassScope { - override string toString() { result = "class scope" } - - override AstNode getScopeElement() { TClassScope(result) = this } -} - -class CallableScopeRange extends VariableScopeRange, TCallableScope { - private Callable c; - - CallableScopeRange() { this = TCallableScope(c) } - - override string toString() { - (c instanceof Method or c instanceof SingletonMethod) and - result = "method scope" - or - c instanceof Lambda and - result = "lambda scope" - or - c instanceof Block and - result = "block scope" + abstract AstNode getScopeElement(); } - - override Callable getScopeElement() { TCallableScope(result) = this } } -class VariableRange extends TVariable { - abstract string getName(); +module TopLevelScope { + class Range extends VariableScope::Range, TTopLevelScope { + override string toString() { result = "top-level scope" } - string toString() { result = this.getName() } - - abstract Location getLocation(); - - abstract VariableScope getDeclaringScope(); + override AstNode getScopeElement() { TTopLevelScope(result) = this } + } } -class LocalVariableRange extends VariableRange { - private VariableScope scope; - private string name; - private Generated::Identifier i; +module ModuleScope { + class Range extends VariableScope::Range, TModuleScope { + override string toString() { result = "module scope" } - LocalVariableRange() { this = TLocalVariable(scope, name, i) } - - final override string getName() { result = name } - - final override Location getLocation() { result = i.getLocation() } - - final override VariableScope getDeclaringScope() { result = scope } + override AstNode getScopeElement() { TModuleScope(result) = this } + } +} + +module ClassScope { + class Range extends VariableScope::Range, TClassScope { + override string toString() { result = "class scope" } + + override AstNode getScopeElement() { TClassScope(result) = this } + } +} + +module CallableScope { + class Range extends VariableScope::Range, TCallableScope { + private Callable c; + + Range() { this = TCallableScope(c) } + + override string toString() { + (c instanceof Method or c instanceof SingletonMethod) and + result = "method scope" + or + c instanceof Lambda and + result = "lambda scope" + or + c instanceof Block and + result = "block scope" + } + + override Callable getScopeElement() { TCallableScope(result) = this } + } +} + +module Variable { + class Range extends TVariable { + abstract string getName(); + + string toString() { result = this.getName() } + + abstract Location getLocation(); + + abstract VariableScope getDeclaringScope(); + } +} + +module LocalVariable { + class Range extends Variable::Range { + private VariableScope scope; + private string name; + private Generated::Identifier i; + + Range() { this = TLocalVariable(scope, name, i) } + + final override string getName() { result = name } + + final override Location getLocation() { result = i.getLocation() } + + final override VariableScope getDeclaringScope() { result = scope } + } }