diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index 28a05877c5f..d64e6d6f1b5 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 diff --git a/ql/src/codeql_ruby/ast/Method.qll b/ql/src/codeql_ruby/ast/Method.qll index 3040df238cc..60a17ac340a 100644 --- a/ql/src/codeql_ruby/ast/Method.qll +++ b/ql/src/codeql_ruby/ast/Method.qll @@ -18,13 +18,18 @@ class Callable extends Expr, CfgScope { } /** A method. */ -class Method extends Callable, BodyStatement, @method { - final override Method::Range range; - - final override string getAPrimaryQlClass() { result = "Method" } +class MethodBase extends Callable, BodyStatement, Scope { + override MethodBase::Range range; /** Gets the name of this method. */ final string getName() { result = range.getName() } +} + +/** A normal method. */ +class Method extends MethodBase, @method { + final override Method::Range range; + + final override string getAPrimaryQlClass() { result = "Method" } /** * Holds if this is a setter method, as in the following example: @@ -40,16 +45,13 @@ class Method extends Callable, BodyStatement, @method { } /** A singleton method. */ -class SingletonMethod extends Callable, BodyStatement, @singleton_method { +class SingletonMethod extends MethodBase, @singleton_method { final override SingletonMethod::Range range; final override string getAPrimaryQlClass() { result = "SingletonMethod" } /** Gets the object of this singleton method. */ final Expr getObject() { result = range.getObject() } - - /** Gets the name of this method. */ - final string getName() { result = range.getName() } } /** @@ -65,7 +67,7 @@ class Lambda extends Callable, BodyStatement, @lambda { } /** A block. */ -class Block extends Callable, StmtSequence { +class Block extends Callable, StmtSequence, Scope { override Block::Range range; } diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index 64238e60213..5f3e150e79a 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -5,14 +5,14 @@ private import internal.Module /** * The base class for classes, singleton classes, and modules. */ -class ModuleBase extends BodyStatement { +class ModuleBase extends BodyStatement, Scope { override ModuleBase::Range range; /** 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() } diff --git a/ql/src/codeql_ruby/ast/Scope.qll b/ql/src/codeql_ruby/ast/Scope.qll new file mode 100644 index 00000000000..3969eb0b77f --- /dev/null +++ b/ql/src/codeql_ruby/ast/Scope.qll @@ -0,0 +1,15 @@ +private import codeql_ruby.AST +private import internal.Scope + +class Scope extends AstNode, Scope::ScopeType { + override Scope::Range range; + + AstNode getADescendant() { result = range.getADescendant() } + + ModuleBase getEnclosingModule() { result = range.getEnclosingModule() } + + MethodBase getEnclosingMethod() { result = range.getEnclosingMethod() } + + /** Gets the scope in which this scope is nested, if any. */ + Scope getOuterScope() { result = range.getOuterScope() } +} diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index 2e6e6c37509..2842ba23661 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -2,7 +2,6 @@ private import codeql_ruby.AST private import codeql_ruby.CFG private import internal.Expr private import internal.Statement -private import internal.Variable private import codeql_ruby.controlflow.internal.ControlFlowGraphImpl /** @@ -19,9 +18,6 @@ class Stmt extends AstNode { /** 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() } } diff --git a/ql/src/codeql_ruby/ast/internal/Method.qll b/ql/src/codeql_ruby/ast/internal/Method.qll index 49540f9b788..0b31ca2d22d 100644 --- a/ql/src/codeql_ruby/ast/internal/Method.qll +++ b/ql/src/codeql_ruby/ast/internal/Method.qll @@ -2,6 +2,7 @@ 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 codeql_ruby.ast.internal.Scope private import TreeSitter module Callable { @@ -14,13 +15,25 @@ module Callable { } } +module MethodBase { + abstract class Range extends Callable::Range, BodyStatement::Range, Scope::Range { + abstract string getName(); + + override predicate child(string label, AstNode::Range child) { + Callable::Range.super.child(label, child) or BodyStatement::Range.super.child(label, child) + } + + override string toString() { result = BodyStatement::Range.super.toString() } + } +} + module Method { - class Range extends Callable::Range, BodyStatement::Range, @method { + class Range extends MethodBase::Range, @method { final override Generated::Method generated; override Parameter::Range getParameter(int n) { result = generated.getParameters().getChild(n) } - string getName() { + override string getName() { result = generated.getName().(Generated::Token).getValue() or result = generated.getName().(Generated::Setter).getName().getValue() + "=" } @@ -30,20 +43,16 @@ module Method { 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 { + class Range extends MethodBase::Range, @singleton_method { final override Generated::SingletonMethod generated; override Parameter::Range getParameter(int n) { result = generated.getParameters().getChild(n) } - string getName() { + override string getName() { result = generated.getName().(Generated::Token).getValue() or result = generated.getName().(SymbolLiteral).getValueText() or result = generated.getName().(Generated::Setter).getName().getValue() + "=" @@ -56,9 +65,7 @@ module SingletonMethod { 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) + MethodBase::Range.super.child(label, child) or label = "getObject" and child = getObject() } @@ -89,7 +96,7 @@ module Lambda { } module Block { - abstract class Range extends Callable::Range, StmtSequence::Range { + abstract class Range extends Callable::Range, StmtSequence::Range, Scope::Range { Range() { not generated.getParent() instanceof Generated::Lambda } override predicate child(string label, AstNode::Range child) { @@ -97,6 +104,8 @@ module Block { or StmtSequence::Range.super.child(label, child) } + + override string toString() { result = StmtSequence::Range.super.toString() } } } diff --git a/ql/src/codeql_ruby/ast/internal/Module.qll b/ql/src/codeql_ruby/ast/internal/Module.qll index e5b28b92df2..349e6ced59a 100644 --- a/ql/src/codeql_ruby/ast/internal/Module.qll +++ b/ql/src/codeql_ruby/ast/internal/Module.qll @@ -2,10 +2,13 @@ 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.Scope private import codeql_ruby.ast.internal.TreeSitter module ModuleBase { - abstract class Range extends BodyStatement::Range { } + abstract class Range extends BodyStatement::Range, Scope::Range { + override string toString() { result = BodyStatement::Range.super.toString() } + } } module Namespace { 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..ab057c2d921 --- /dev/null +++ b/ql/src/codeql_ruby/ast/internal/Scope.qll @@ -0,0 +1,63 @@ +private import TreeSitter +private import codeql_ruby.ast.internal.AST +private import codeql_ruby.ast.internal.Module +private import codeql_ruby.ast.internal.Method +private import codeql_ruby.ast.internal.Statement + +module Scope { + class ScopeType = MethodLike or ModuleLike or BlockLike; + + class BlockLike = @do_block or @lambda or @block or @end_block; + + class ModuleLike = @program or @module or @class or @singleton_class; + + class MethodLike = @method or @singleton_method; + + class Range extends AstNode::Range, ScopeType { + Range() { not exists(Generated::Lambda l | l.getBody() = this) } + + Generated::AstNode getADescendant() { this = scopeOf(result) } + + 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() + } + + Scope::Range getOuterScope() { result = scopeOf(this) } + + override string toString() { none() } + } +} + +/** Gets the enclosing scope of a node */ +private Scope::Range scopeOf(Generated::AstNode n) { + exists(Generated::AstNode p | p = parentOf(n) | + p instanceof Scope::Range and result = p + or + not p instanceof Scope::Range and result = scopeOf(p) + ) +} + +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 + ) +} diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 931f7020666..f6d63161d94 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -33,6 +33,7 @@ private import codeql_ruby.ast.internal.AST as ASTInternal private import codeql_ruby.ast.internal.Control as Control +private import codeql_ruby.ast.internal.Scope private import codeql_ruby.ast.internal.TreeSitter::Generated private import AstNodes private import codeql_ruby.ast.internal.Variable @@ -148,11 +149,6 @@ module CfgScope { } } -private AstNode parent(AstNode n) { - result = parentOf(n) and - not n instanceof CfgScope -} - abstract private class ControlFlowTree extends AstNode { /** * Holds if `first` is the first element executed within this AST node. @@ -1263,11 +1259,16 @@ module Trees { } } +private Scope::Range parent(Scope::Range n) { + result = n.getOuterScope() and + not n instanceof CfgScope +} + 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*(any(Scope::Range x | x.getADescendant() = n)) } private predicate isAbnormalExitType(SuccessorType t) { t instanceof RaiseSuccessor or t instanceof ExitSuccessor diff --git a/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll b/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll index f8d4671e563..bdc3c813ca0 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 }