From 5854b831f3107f6726945e84ad823485f569ff70 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Thu, 4 Mar 2021 13:43:59 +0000 Subject: [PATCH 01/39] Ruby: rb/use-detect query --- ql/src/queries/performance/UseDetect.ql | 63 +++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 ql/src/queries/performance/UseDetect.ql diff --git a/ql/src/queries/performance/UseDetect.ql b/ql/src/queries/performance/UseDetect.ql new file mode 100644 index 00000000000..b5449f30c0c --- /dev/null +++ b/ql/src/queries/performance/UseDetect.ql @@ -0,0 +1,63 @@ +/** + * @name Use detect + * @description Use 'detect' instead of 'first' and '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 + +// Extracts the first or last element of a list +abstract class EndCall extends MethodCall { + abstract string detectCall(); +} + +abstract class First extends EndCall { + override string detectCall() { result = "detect" } +} + +class FirstCall extends First { + FirstCall() { + this.getMethodName() = "first" and + this.getNumberOfArguments() = 0 + } +} + +class FirstElement extends First, ElementReference { + FirstElement() { + this.getNumberOfArguments() = 1 and + this.getArgument(0).(IntegerLiteral).getValueText() = "0" + } +} + +abstract class Last extends EndCall { + override string detectCall() { result = "reverse_detect" } +} + +class LastCall extends Last { + LastCall() { this.getMethodName() = "last" and this.getNumberOfArguments() = 0 } +} + +class LastElement extends Last, ElementReference { + LastElement() { + this.getNumberOfArguments() = 1 and + this.getArgument(0).(UnaryMinusExpr).getOperand().(IntegerLiteral).getValueText() = "1" + } +} + +class SelectBlock extends MethodCall { + SelectBlock() { + this.getMethodName() in ["select", "filter", "find_all"] and + exists(this.getBlock()) + } +} + +from EndCall call, SelectBlock selectBlock +where selectBlock = call.getReceiver() +select call, "Replace this call with '" + call.detectCall() + "'." From 522bcff79d3cc32d461e3d29e480489c4f27f5ff Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Thu, 4 Mar 2021 15:38:09 +0000 Subject: [PATCH 02/39] Ruby: Initial test case --- .../performance/UseDetect/UseDetect.rb | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 ql/test/query-tests/performance/UseDetect/UseDetect.rb 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..267643bac74 --- /dev/null +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.rb @@ -0,0 +1,128 @@ +# frozen_string_literal: true + +require "cases/helper" +require "models/post" +require "models/comment" + +module ActiveRecord + module ConnectionAdapters + class Mysql2SchemaTest < ActiveRecord::Mysql2TestCase + fixtures :posts + + def setup + @connection = ActiveRecord::Base.connection + db = Post.connection_pool.db_config.database + table = Post.table_name + @db_name = db + + @omgpost = Class.new(ActiveRecord::Base) do + self.inheritance_column = :disabled + self.table_name = "#{db}.#{table}" + def self.name; "Post"; end + end + end + + def test_float_limits + @connection.create_table :mysql_doubles do |t| + t.float :float_no_limit + t.float :float_short, limit: 5 + t.float :float_long, limit: 53 + + t.float :float_23, limit: 23 + t.float :float_24, limit: 24 + t.float :float_25, limit: 25 + end + + column_no_limit = @connection.columns(:mysql_doubles).find { |c| c.name == "float_no_limit" } + column_short = @connection.columns(:mysql_doubles).find { |c| c.name == "float_short" } + column_long = @connection.columns(:mysql_doubles).find { |c| c.name == "float_long" } + + column_23 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_23" } + column_24 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_24" } + column_25 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_25" } + + # MySQL floats are precision 0..24, MySQL doubles are precision 25..53 + assert_equal 24, column_no_limit.limit + assert_equal 24, column_short.limit + assert_equal 53, column_long.limit + + assert_equal 24, column_23.limit + assert_equal 24, column_24.limit + assert_equal 53, column_25.limit + ensure + @connection.drop_table "mysql_doubles", if_exists: true + end + + def test_schema + assert @omgpost.first + end + + def test_primary_key + assert_equal "id", @omgpost.primary_key + end + + def test_data_source_exists? + name = @omgpost.table_name + assert @connection.data_source_exists?(name), "#{name} data_source should exist" + end + + def test_data_source_exists_wrong_schema + assert_not(@connection.data_source_exists?("#{@db_name}.zomg"), "data_source should not exist") + end + + def test_dump_indexes + index_a_name = "index_key_tests_on_snack" + index_b_name = "index_key_tests_on_pizza" + index_c_name = "index_key_tests_on_awesome" + + table = "key_tests" + + indexes = @connection.indexes(table).sort_by(&:name) + assert_equal 3, indexes.size + + index_a = indexes.select { |i| i.name == index_a_name }[0] + index_b = indexes.select { |i| i.name == index_b_name }[0] + index_c = indexes.select { |i| i.name == index_c_name }[0] + assert_equal :btree, index_a.using + assert_nil index_a.type + assert_equal :btree, index_b.using + assert_nil index_b.type + + assert_nil index_c.using + assert_equal :fulltext, index_c.type + end + + unless mysql_enforcing_gtid_consistency? + def test_drop_temporary_table + @connection.transaction do + @connection.create_table(:temp_table, temporary: true) + # if it doesn't properly say DROP TEMPORARY TABLE, the transaction commit + # will complain that no transaction is active + @connection.drop_table(:temp_table, temporary: true) + end + end + end + end + end +end + +class Mysql2AnsiQuotesTest < ActiveRecord::Mysql2TestCase + def setup + @connection = ActiveRecord::Base.connection + @connection.execute("SET SESSION sql_mode='ANSI_QUOTES'") + end + + def teardown + @connection.reconnect! + end + + def test_primary_key_method_with_ansi_quotes + assert_equal "id", @connection.primary_key("topics") + end + + def test_foreign_keys_method_with_ansi_quotes + fks = @connection.foreign_keys("lessons_students") + assert_equal([["lessons_students", "students", :cascade]], + fks.map { |fk| [fk.from_table, fk.to_table, fk.on_delete] }) + end +end From ca497479c2bd31676073ec4b2b3af7b8ac0ce0e3 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Thu, 4 Mar 2021 15:44:05 +0000 Subject: [PATCH 03/39] Ruby: Finish the test for UseDetect --- .../performance/UseDetect/UseDetect.expected | 6 + .../performance/UseDetect/UseDetect.qlref | 1 + .../performance/UseDetect/UseDetect.rb | 136 ++---------------- 3 files changed, 19 insertions(+), 124 deletions(-) create mode 100644 ql/test/query-tests/performance/UseDetect/UseDetect.expected create mode 100644 ql/test/query-tests/performance/UseDetect/UseDetect.qlref 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..2fd8b960ed9 --- /dev/null +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.expected @@ -0,0 +1,6 @@ +| UseDetect.rb:5:9:5:36 | call to first | Replace this call with 'detect'. | +| UseDetect.rb:6:9:6:35 | call to last | Replace this call with 'reverse_detect'. | +| UseDetect.rb:7:9:7:33 | ...[...] | Replace this call with 'detect'. | +| UseDetect.rb:8:9:8:34 | ...[...] | Replace this call with 'reverse_detect'. | +| UseDetect.rb:9:9:9:36 | call to first | Replace this call with 'detect'. | +| UseDetect.rb:10:9:10:37 | call to last | Replace this call with 'reverse_detect'. | 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 index 267643bac74..b0053e188d7 100644 --- a/ql/test/query-tests/performance/UseDetect/UseDetect.rb +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.rb @@ -1,128 +1,16 @@ -# frozen_string_literal: true -require "cases/helper" -require "models/post" -require "models/comment" +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 -module ActiveRecord - module ConnectionAdapters - class Mysql2SchemaTest < ActiveRecord::Mysql2TestCase - fixtures :posts - - def setup - @connection = ActiveRecord::Base.connection - db = Post.connection_pool.db_config.database - table = Post.table_name - @db_name = db - - @omgpost = Class.new(ActiveRecord::Base) do - self.inheritance_column = :disabled - self.table_name = "#{db}.#{table}" - def self.name; "Post"; end - end - end - - def test_float_limits - @connection.create_table :mysql_doubles do |t| - t.float :float_no_limit - t.float :float_short, limit: 5 - t.float :float_long, limit: 53 - - t.float :float_23, limit: 23 - t.float :float_24, limit: 24 - t.float :float_25, limit: 25 - end - - column_no_limit = @connection.columns(:mysql_doubles).find { |c| c.name == "float_no_limit" } - column_short = @connection.columns(:mysql_doubles).find { |c| c.name == "float_short" } - column_long = @connection.columns(:mysql_doubles).find { |c| c.name == "float_long" } - - column_23 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_23" } - column_24 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_24" } - column_25 = @connection.columns(:mysql_doubles).find { |c| c.name == "float_25" } - - # MySQL floats are precision 0..24, MySQL doubles are precision 25..53 - assert_equal 24, column_no_limit.limit - assert_equal 24, column_short.limit - assert_equal 53, column_long.limit - - assert_equal 24, column_23.limit - assert_equal 24, column_24.limit - assert_equal 53, column_25.limit - ensure - @connection.drop_table "mysql_doubles", if_exists: true - end - - def test_schema - assert @omgpost.first - end - - def test_primary_key - assert_equal "id", @omgpost.primary_key - end - - def test_data_source_exists? - name = @omgpost.table_name - assert @connection.data_source_exists?(name), "#{name} data_source should exist" - end - - def test_data_source_exists_wrong_schema - assert_not(@connection.data_source_exists?("#{@db_name}.zomg"), "data_source should not exist") - end - - def test_dump_indexes - index_a_name = "index_key_tests_on_snack" - index_b_name = "index_key_tests_on_pizza" - index_c_name = "index_key_tests_on_awesome" - - table = "key_tests" - - indexes = @connection.indexes(table).sort_by(&:name) - assert_equal 3, indexes.size - - index_a = indexes.select { |i| i.name == index_a_name }[0] - index_b = indexes.select { |i| i.name == index_b_name }[0] - index_c = indexes.select { |i| i.name == index_c_name }[0] - assert_equal :btree, index_a.using - assert_nil index_a.type - assert_equal :btree, index_b.using - assert_nil index_b.type - - assert_nil index_c.using - assert_equal :fulltext, index_c.type - end - - unless mysql_enforcing_gtid_consistency? - def test_drop_temporary_table - @connection.transaction do - @connection.create_table(:temp_table, temporary: true) - # if it doesn't properly say DROP TEMPORARY TABLE, the transaction commit - # will complain that no transaction is active - @connection.drop_table(:temp_table, temporary: true) - end - end - end + # These are good + [].select("").first + [].select { |i| true }[1] end - end -end - -class Mysql2AnsiQuotesTest < ActiveRecord::Mysql2TestCase - def setup - @connection = ActiveRecord::Base.connection - @connection.execute("SET SESSION sql_mode='ANSI_QUOTES'") - end - - def teardown - @connection.reconnect! - end - - def test_primary_key_method_with_ansi_quotes - assert_equal "id", @connection.primary_key("topics") - end - - def test_foreign_keys_method_with_ansi_quotes - fks = @connection.foreign_keys("lessons_students") - assert_equal([["lessons_students", "students", :cascade]], - fks.map { |fk| [fk.from_table, fk.to_table, fk.on_delete] }) - end end From 20a62d169ab1c017ec962a7d5838584e5c1ed716 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Thu, 4 Mar 2021 15:48:09 +0000 Subject: [PATCH 04/39] Ruby: Update query description --- ql/src/queries/performance/UseDetect.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/src/queries/performance/UseDetect.ql b/ql/src/queries/performance/UseDetect.ql index b5449f30c0c..d75c4b83c25 100644 --- a/ql/src/queries/performance/UseDetect.ql +++ b/ql/src/queries/performance/UseDetect.ql @@ -1,6 +1,6 @@ /** * @name Use detect - * @description Use 'detect' instead of 'first' and 'last'. + * @description Use 'detect' instead of 'select' followed by 'first' or 'last'. * @kind problem * @problem.severity warning * @id rb/use-detect From 00260db58f84db433c0269eb6a7f6e16fdd0ae0f Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 9 Mar 2021 09:46:42 +0100 Subject: [PATCH 05/39] Add Scope.qll --- ql/src/codeql_ruby/AST.qll | 1 + ql/src/codeql_ruby/ast/Method.qll | 20 +++--- ql/src/codeql_ruby/ast/Module.qll | 6 +- ql/src/codeql_ruby/ast/Scope.qll | 15 +++++ ql/src/codeql_ruby/ast/Statement.qll | 4 -- ql/src/codeql_ruby/ast/internal/Method.qll | 33 ++++++---- ql/src/codeql_ruby/ast/internal/Module.qll | 5 +- ql/src/codeql_ruby/ast/internal/Scope.qll | 63 +++++++++++++++++++ .../internal/ControlFlowGraphImpl.qll | 13 ++-- .../codeql_ruby/dataflow/internal/SsaImpl.qll | 2 +- 10 files changed, 126 insertions(+), 36 deletions(-) create mode 100644 ql/src/codeql_ruby/ast/Scope.qll create mode 100644 ql/src/codeql_ruby/ast/internal/Scope.qll 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 } From 0f829476f4a7ecaf7c28ef190bc21c00b7a41304 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Tue, 9 Mar 2021 10:13:07 +0000 Subject: [PATCH 06/39] Ruby: Refactor EndCall to reduce number of classes --- ql/src/queries/performance/UseDetect.ql | 55 ++++++++++--------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/ql/src/queries/performance/UseDetect.ql b/ql/src/queries/performance/UseDetect.ql index d75c4b83c25..262f7782548 100644 --- a/ql/src/queries/performance/UseDetect.ql +++ b/ql/src/queries/performance/UseDetect.ql @@ -13,42 +13,31 @@ import ruby -// Extracts the first or last element of a list -abstract class EndCall extends MethodCall { - abstract string detectCall(); -} +/** A call that extracts the first or last element of a list. */ +class EndCall extends MethodCall { + string detect; -abstract class First extends EndCall { - override string detectCall() { result = "detect" } -} - -class FirstCall extends First { - FirstCall() { - this.getMethodName() = "first" and - this.getNumberOfArguments() = 0 + 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" + ) } -} -class FirstElement extends First, ElementReference { - FirstElement() { - this.getNumberOfArguments() = 1 and - this.getArgument(0).(IntegerLiteral).getValueText() = "0" - } -} - -abstract class Last extends EndCall { - override string detectCall() { result = "reverse_detect" } -} - -class LastCall extends Last { - LastCall() { this.getMethodName() = "last" and this.getNumberOfArguments() = 0 } -} - -class LastElement extends Last, ElementReference { - LastElement() { - this.getNumberOfArguments() = 1 and - this.getArgument(0).(UnaryMinusExpr).getOperand().(IntegerLiteral).getValueText() = "1" - } + string detectCall() { result = detect } } class SelectBlock extends MethodCall { From 5b4bf584a17d49dbbc4cdaf9ebe6af3a54ee6b1e Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Tue, 9 Mar 2021 10:20:23 +0000 Subject: [PATCH 07/39] Ruby: Update qltest output for new select format --- .../performance/UseDetect/UseDetect.expected | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.expected b/ql/test/query-tests/performance/UseDetect/UseDetect.expected index 2fd8b960ed9..9fa417a983d 100644 --- a/ql/test/query-tests/performance/UseDetect/UseDetect.expected +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.expected @@ -1,6 +1,6 @@ -| UseDetect.rb:5:9:5:36 | call to first | Replace this call with 'detect'. | -| UseDetect.rb:6:9:6:35 | call to last | Replace this call with 'reverse_detect'. | -| UseDetect.rb:7:9:7:33 | ...[...] | Replace this call with 'detect'. | -| UseDetect.rb:8:9:8:34 | ...[...] | Replace this call with 'reverse_detect'. | -| UseDetect.rb:9:9:9:36 | call to first | Replace this call with 'detect'. | -| UseDetect.rb:10:9:10:37 | call to last | Replace this call with 'reverse_detect'. | +| 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 | From 855d190800ac0b45469fbf2f95d397c2155c3e41 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Tue, 9 Mar 2021 10:25:24 +0000 Subject: [PATCH 08/39] Ruby: Test local data flow --- ql/src/queries/performance/UseDetect.ql | 17 +++++++++++++++-- .../performance/UseDetect/UseDetect.expected | 1 + .../performance/UseDetect/UseDetect.rb | 9 +++++++-- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ql/src/queries/performance/UseDetect.ql b/ql/src/queries/performance/UseDetect.ql index 262f7782548..a1cfc9d6b2a 100644 --- a/ql/src/queries/performance/UseDetect.ql +++ b/ql/src/queries/performance/UseDetect.ql @@ -12,6 +12,7 @@ */ import ruby +import codeql_ruby.dataflow.SSA /** A call that extracts the first or last element of a list. */ class EndCall extends MethodCall { @@ -40,6 +41,17 @@ class EndCall extends MethodCall { 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 @@ -48,5 +60,6 @@ class SelectBlock extends MethodCall { } from EndCall call, SelectBlock selectBlock -where selectBlock = call.getReceiver() -select call, "Replace this call with '" + call.detectCall() + "'." +where [selectBlock, getUniqueRead(selectBlock)] = call.getReceiver() +select call, "Replace this call and $@ with '" + call.detectCall() + "'.", selectBlock, + "'select' call" diff --git a/ql/test/query-tests/performance/UseDetect/UseDetect.expected b/ql/test/query-tests/performance/UseDetect/UseDetect.expected index 9fa417a983d..d8a5ec8dd41 100644 --- a/ql/test/query-tests/performance/UseDetect/UseDetect.expected +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.expected @@ -4,3 +4,4 @@ | 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.rb b/ql/test/query-tests/performance/UseDetect/UseDetect.rb index b0053e188d7..e1d2d9b91ba 100644 --- a/ql/test/query-tests/performance/UseDetect/UseDetect.rb +++ b/ql/test/query-tests/performance/UseDetect/UseDetect.rb @@ -8,9 +8,14 @@ class DetectTest [].select { |i| true }[-1] [].filter { |i| true }.first [].find_all { |i| true }.last + selection1 = [].select { |i| true } + selection1.first # These are good - [].select("").first - [].select { |i| true }[1] + [].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 From 86a89ab1fee208a375da46c71ad2e251c217f3c9 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 9 Mar 2021 10:13:43 +0100 Subject: [PATCH 09/39] Remove VariableScope IPA type --- ql/src/codeql_ruby/ast/Variable.qll | 18 +- ql/src/codeql_ruby/ast/internal/Variable.qll | 244 +++--------- .../variables/varaccess.expected | 352 +++++++++--------- .../variables/varscopes.expected | 149 ++++---- 4 files changed, 300 insertions(+), 463 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index d848763b71b..e45c81e1d07 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -5,20 +5,7 @@ private import codeql.Locations 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() } - +class VariableScope extends Scope { /** Gets a variable that is declared in this scope. */ final Variable getAVariable() { result.getDeclaringScope() = this } @@ -27,9 +14,6 @@ class VariableScope extends TScope { 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. */ diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index ee2e11a7d91..7a64e957609 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -7,78 +7,37 @@ 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 - -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 - ) -} - -private Generated::AstNode parentOfNoScope(Generated::AstNode n) { - result = parentOf(n) and - not n = any(VariableScope::Range s).getScopeElement() -} +private import codeql_ruby.ast.internal.Scope 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 | node = s.getADescendant() 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 | node = s.getADescendant() 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,7 +49,7 @@ private predicate scopeDefinesParameterVariable( ) or exists(Parameter::Range p | - p = scope.getScopeElement().(Callable::Range).getParameter(_) and + p = scope.(Callable::Range).getParameter(_) and name = p.(NamedParameter::Range).getName() | i = p.(Generated::BlockParameter).getName() or @@ -102,7 +61,7 @@ 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) @@ -137,9 +96,7 @@ cached private module Cached { /** Gets the enclosing scope for `node`. */ cached - VariableScope::Range enclosingScope(Generated::AstNode node) { - result.getScopeElement() = parentOfNoScope*(parentOf(node)) - } + Scope::Range enclosingScope(Generated::AstNode node) { result.getADescendant() = node } cached newtype TScope = @@ -154,7 +111,7 @@ private module Cached { 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 +119,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 +127,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 +137,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 +287,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() = enclosingScope(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,9 +300,9 @@ 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(enclosingScope(access), name, declScope) ) ) } @@ -371,7 +330,7 @@ private module Cached { 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 +338,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 +347,22 @@ 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(Block::Range scope, string name, Scope::Range outer) { + 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.(Generated::AstNode).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 +373,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,7 +389,7 @@ 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) } } @@ -549,7 +405,7 @@ module GlobalVariable { final override Location getLocation() { none() } - final override VariableScope getDeclaringScope() { result = TGlobalScope() } + final override Scope::Range getDeclaringScope() { none() } } } @@ -557,7 +413,7 @@ 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 +426,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,7 +442,7 @@ module ClassVariable { final override Location getLocation() { result = decl.getLocation() } - final override VariableScope getDeclaringScope() { result = scope } + final override Scope::Range getDeclaringScope() { result = scope } } } diff --git a/ql/test/library-tests/variables/varaccess.expected b/ql/test/library-tests/variables/varaccess.expected index 596bd750fea..ddfa887a139 100644 --- a/ql/test/library-tests/variables/varaccess.expected +++ b/ql/test/library-tests/variables/varaccess.expected @@ -1,181 +1,179 @@ 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:25: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:15: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:16: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 | 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 | ... = ... | 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 | From 600d9c66aeec3cdae199e60422d2f71bca3a3400 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 9 Mar 2021 11:56:17 +0100 Subject: [PATCH 10/39] Remove VariableScope --- ql/src/codeql_ruby/ast/Scope.qll | 9 +++++++++ ql/src/codeql_ruby/ast/Variable.qll | 14 +------------- ql/test/library-tests/variables/varaccess.ql | 2 +- ql/test/library-tests/variables/varscopes.ql | 4 ++-- 4 files changed, 13 insertions(+), 16 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Scope.qll b/ql/src/codeql_ruby/ast/Scope.qll index 3969eb0b77f..bbc34bc81fb 100644 --- a/ql/src/codeql_ruby/ast/Scope.qll +++ b/ql/src/codeql_ruby/ast/Scope.qll @@ -12,4 +12,13 @@ class Scope extends AstNode, Scope::ScopeType { /** Gets the scope in which this scope is nested, if any. */ Scope getOuterScope() { result = range.getOuterScope() } + + /** 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 + } } diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index e45c81e1d07..955723399dd 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -4,18 +4,6 @@ private import codeql_ruby.AST private import codeql.Locations private import internal.Variable -/** A scope in which variables can be declared. */ -class VariableScope extends Scope { - /** 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 - } -} - /** A variable declared in a scope. */ class Variable extends TVariable { Variable::Range range; @@ -32,7 +20,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() { result = range.getDeclaringScope() } /** Gets an access to this variable. */ VariableAccess getAnAccess() { result.getVariable() = this } 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.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) From f28071ceb67ae07b156d78ea0b65d2b595c2c74d Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 9 Mar 2021 13:55:55 +0100 Subject: [PATCH 11/39] Fix VariableRead/WriteAcess for instance and class variables --- ql/src/codeql_ruby/ast/internal/Variable.qll | 7 ++++- .../variables/varaccess.expected | 26 +++++++++---------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index ee2e11a7d91..2e9aa9f1c19 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -349,7 +349,12 @@ private module Cached { } 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 diff --git a/ql/test/library-tests/variables/varaccess.expected b/ql/test/library-tests/variables/varaccess.expected index 596bd750fea..b25e62e339c 100644 --- a/ql/test/library-tests/variables/varaccess.expected +++ b/ql/test/library-tests/variables/varaccess.expected @@ -177,6 +177,19 @@ variableAccess | 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 | 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 | ... = ... | @@ -257,26 +270,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 | From 6a284378d6ba7bb55d0668f3252ce222cfc01e5a Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Tue, 9 Mar 2021 18:57:24 +0100 Subject: [PATCH 12/39] Update ql/src/codeql_ruby/ast/Scope.qll Co-authored-by: Nick Rolfe --- ql/src/codeql_ruby/ast/Scope.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/src/codeql_ruby/ast/Scope.qll b/ql/src/codeql_ruby/ast/Scope.qll index bbc34bc81fb..c0484232a3d 100644 --- a/ql/src/codeql_ruby/ast/Scope.qll +++ b/ql/src/codeql_ruby/ast/Scope.qll @@ -16,7 +16,7 @@ class Scope extends AstNode, Scope::ScopeType { /** 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. */ + /** 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 From cb977cb29091f3e3f4aa5704e4813262bbe6e6b7 Mon Sep 17 00:00:00 2001 From: Calum Grant Date: Wed, 10 Mar 2021 10:56:33 +0000 Subject: [PATCH 13/39] Ruby: Use getAUniqueRead TC --- ql/src/queries/performance/UseDetect.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/src/queries/performance/UseDetect.ql b/ql/src/queries/performance/UseDetect.ql index a1cfc9d6b2a..d17f5d6f8f9 100644 --- a/ql/src/queries/performance/UseDetect.ql +++ b/ql/src/queries/performance/UseDetect.ql @@ -60,6 +60,6 @@ class SelectBlock extends MethodCall { } from EndCall call, SelectBlock selectBlock -where [selectBlock, getUniqueRead(selectBlock)] = call.getReceiver() +where getUniqueRead*(selectBlock) = call.getReceiver() select call, "Replace this call and $@ with '" + call.detectCall() + "'.", selectBlock, "'select' call" From 3e5ff1d042672f47deee7d4ba377f478fe86f47e Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Fri, 12 Mar 2021 16:52:28 +0100 Subject: [PATCH 14/39] AST: order edges by target node When printing a tree CodeQL iterates over the nodes and for each node prints the successor edges as children. If the the successor edges are ordered by target node then the children printe in the right order in the expected output. --- ql/test/library-tests/ast/Ast.ql | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/ql/test/library-tests/ast/Ast.ql b/ql/test/library-tests/ast/Ast.ql index f851015a1e2..9784cdc7d05 100644 --- a/ql/test/library-tests/ast/Ast.ql +++ b/ql/test/library-tests/ast/Ast.ql @@ -2,9 +2,21 @@ * @kind graph */ -import codeql_ruby.printAst +import codeql_ruby.printAst as P -class OrderedAstNode extends PrintAstNode { +query predicate nodes = P::nodes/3; + +query predicate graphProperties = P::graphProperties/2; + +query predicate edges(P::PrintAstNode source, P::PrintAstNode target, string key, string value) { + P::edges(source, target, key, value) + or + P::edges(source, target, _, _) and + key = "semmle.order" and + value = target.(OrderedAstNode).getProperty("semmle.order") +} + +class OrderedAstNode extends P::PrintAstNode { override string getProperty(string key) { result = super.getProperty(key) or @@ -12,12 +24,12 @@ class OrderedAstNode extends PrintAstNode { result = any(int i | this = - rank[i](AstNode p | + rank[i](P::AstNode p | | p order by p.getLocation().getFile().getBaseName(), p.getLocation().getFile().getAbsolutePath(), - p.getLocation().getStartLine() + p.getLocation().getStartLine(), p.getLocation().getStartColumn() ) ).toString() } From d54db292f73139f005d5ac32d48ee06708b20cd8 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 15 Mar 2021 10:27:55 +0100 Subject: [PATCH 15/39] Move semmle.order property to printAst.qll --- ql/src/codeql_ruby/printAst.qll | 22 +++++++++++++++++++-- ql/test/library-tests/ast/Ast.ql | 33 +------------------------------- 2 files changed, 21 insertions(+), 34 deletions(-) diff --git a/ql/src/codeql_ruby/printAst.qll b/ql/src/codeql_ruby/printAst.qll index 3556e20d45c..07cb40e180d 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() } /** @@ -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/test/library-tests/ast/Ast.ql b/ql/test/library-tests/ast/Ast.ql index 9784cdc7d05..babafaac548 100644 --- a/ql/test/library-tests/ast/Ast.ql +++ b/ql/test/library-tests/ast/Ast.ql @@ -2,35 +2,4 @@ * @kind graph */ -import codeql_ruby.printAst as P - -query predicate nodes = P::nodes/3; - -query predicate graphProperties = P::graphProperties/2; - -query predicate edges(P::PrintAstNode source, P::PrintAstNode target, string key, string value) { - P::edges(source, target, key, value) - or - P::edges(source, target, _, _) and - key = "semmle.order" and - value = target.(OrderedAstNode).getProperty("semmle.order") -} - -class OrderedAstNode extends P::PrintAstNode { - override string getProperty(string key) { - result = super.getProperty(key) - or - key = "semmle.order" and - result = - any(int i | - this = - rank[i](P::AstNode p | - | - p - order by - p.getLocation().getFile().getBaseName(), p.getLocation().getFile().getAbsolutePath(), - p.getLocation().getStartLine(), p.getLocation().getStartColumn() - ) - ).toString() - } -} +import codeql_ruby.printAst From 7eaf02a0bf3adfb0d48300904176bfe721e17057 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 9 Mar 2021 10:49:36 +0100 Subject: [PATCH 16/39] Make external `AstNode` an IPA type --- ql/src/codeql_ruby/AST.qll | 20 +- ql/src/codeql_ruby/ast/Call.qll | 191 ++++++- ql/src/codeql_ruby/ast/Constant.qll | 77 ++- ql/src/codeql_ruby/ast/Control.qll | 299 ++++++++--- ql/src/codeql_ruby/ast/Expr.qll | 238 ++++++-- ql/src/codeql_ruby/ast/Literal.qll | 450 +++++++++++++--- ql/src/codeql_ruby/ast/Method.qll | 128 ++++- ql/src/codeql_ruby/ast/Module.qll | 111 +++- ql/src/codeql_ruby/ast/Operation.qll | 350 ++++++------ ql/src/codeql_ruby/ast/Parameter.qll | 136 +++-- ql/src/codeql_ruby/ast/Pattern.qll | 60 ++- ql/src/codeql_ruby/ast/Scope.qll | 16 +- ql/src/codeql_ruby/ast/Statement.qll | 132 +++-- ql/src/codeql_ruby/ast/Variable.qll | 64 ++- ql/src/codeql_ruby/ast/internal/AST.qll | 507 +++++++++++++++++- ql/src/codeql_ruby/ast/internal/Call.qll | 262 --------- ql/src/codeql_ruby/ast/internal/Constant.qll | 93 ---- ql/src/codeql_ruby/ast/internal/Control.qll | 299 ----------- ql/src/codeql_ruby/ast/internal/Expr.qll | 227 -------- ql/src/codeql_ruby/ast/internal/Literal.qll | 446 --------------- ql/src/codeql_ruby/ast/internal/Method.qll | 144 ----- ql/src/codeql_ruby/ast/internal/Module.qll | 132 ----- ql/src/codeql_ruby/ast/internal/Operation.qll | 355 ------------ ql/src/codeql_ruby/ast/internal/Parameter.qll | 148 +---- ql/src/codeql_ruby/ast/internal/Pattern.qll | 112 ---- ql/src/codeql_ruby/ast/internal/Scope.qll | 70 ++- ql/src/codeql_ruby/ast/internal/Statement.qll | 146 ----- ql/src/codeql_ruby/ast/internal/Variable.qll | 181 +++---- .../codeql_ruby/controlflow/BasicBlocks.qll | 10 +- ql/src/codeql_ruby/controlflow/CfgNodes.qll | 7 +- .../controlflow/ControlFlowGraph.qll | 5 +- .../controlflow/internal/Completion.qll | 5 +- .../controlflow/internal/Consistency.qll | 2 +- .../internal/ControlFlowGraphImpl.qll | 64 ++- .../controlflow/internal/Splitting.qll | 33 +- ql/src/codeql_ruby/printAst.qll | 2 +- ql/test/library-tests/ast/Ast.expected | 46 ++ .../controlflow/graph/Cfg.expected | 96 ++-- ql/test/library-tests/variables/ssa.expected | 30 +- .../variables/varaccess.expected | 12 +- 40 files changed, 2496 insertions(+), 3210 deletions(-) delete mode 100644 ql/src/codeql_ruby/ast/internal/Call.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Constant.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Control.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Expr.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Literal.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Method.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Module.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Operation.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Pattern.qll delete mode 100644 ql/src/codeql_ruby/ast/internal/Statement.qll diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index d64e6d6f1b5..ce383d239fe 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -18,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. * @@ -33,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 = toTreeSitter(this).getLocation() } /** Gets a child node of this `AstNode`. */ - final AstNode getAChild() { range.child(_, result) } + final AstNode getAChild() { this.child(_, result) } /** Gets the parent of this `AstNode`, if this node is not a root node. */ final AstNode getParent() { result.getAChild() = this } + + /** + * Holds if `child` is a child of this node, and `child` can be retrieved using + * a predicate named `label`. + */ + cached + predicate child(string label, AstNode child) { none() } } diff --git a/ql/src/codeql_ruby/ast/Call.qll b/ql/src/codeql_ruby/ast/Call.qll index c7a73c1fa7e..a2bf4f6e24a 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,29 @@ class Call extends Expr { * Gets the number of arguments of this method call. */ final int getNumberOfArguments() { result = count(this.getAnArgument()) } + + override predicate child(string label, AstNode child) { + label = "getArgument" and child = 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 +85,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 +96,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 +104,68 @@ 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 predicate child(string label, AstNode child) { + Call.super.child(label, child) + or + label = "getReceiver" and child = getReceiver() + or + label = "getBlock" and child = 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() { toTreeSitter(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() { + toTreeSitter(result) = g.getReceiver() + or + not exists(g.getReceiver()) and + toTreeSitter(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) { + toTreeSitter(result) = g.getArguments().getChild(n) + or + not exists(g.getArguments()) and + toTreeSitter(result) = g.getMethod().(Generated::ArgumentList).getChild(n) + } + + final override Block getBlock() { toTreeSitter(result) = g.getBlock() } } /** @@ -101,8 +176,6 @@ class MethodCall extends Call { * ``` */ class SetterMethodCall extends MethodCall, LhsExpr { - final override SetterMethodCall::Range range; - final override string getAPrimaryQlClass() { result = "SetterMethodCall" } } @@ -112,10 +185,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() { toTreeSitter(result) = g.getObject() } + + final override string getMethodName() { result = getMethodName(this, "[]") } + + final override Expr getArgument(int n) { toTreeSitter(result) = g.getChild(n) } + + final override string toString() { result = "...[...]" } } /** @@ -124,10 +207,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) { toTreeSitter(result) = g.getChild().getChild(n) } + + final override string toString() { result = "yield ..." } } /** @@ -140,20 +229,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) { toTreeSitter(result) = g.getArguments().getChild(n) } + + final override Block getBlock() { toTreeSitter(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 +275,13 @@ class BlockArgument extends Expr, @block_argument { * foo(&bar) * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toTreeSitter(result) = g.getChild() } + + final override string toString() { result = "&..." } + + final override predicate child(string label, AstNode child) { + label = "getValue" and child = getValue() + } } /** @@ -173,8 +290,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 +304,13 @@ class SplatArgument extends Expr, @splat_argument { * foo(*bar) * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toTreeSitter(result) = g.getChild() } + + final override string toString() { result = "*..." } + + final override predicate child(string label, AstNode child) { + label = "getValue" and child = getValue() + } } /** @@ -194,8 +319,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 +333,11 @@ class HashSplatArgument extends Expr, @hash_splat_argument { * foo(**bar) * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toTreeSitter(result) = g.getChild() } + + final override string toString() { result = "**..." } + + final override predicate child(string label, AstNode child) { + label = "getValue" and child = getValue() + } } diff --git a/ql/src/codeql_ruby/ast/Constant.qll b/ql/src/codeql_ruby/ast/Constant.qll index 3c11c0f8e91..f3749d1b42b 100644 --- a/ql/src/codeql_ruby/ast/Constant.qll +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -1,12 +1,11 @@ private import codeql_ruby.AST -private import internal.Constant +private import internal.AST +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 +23,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 +33,13 @@ class ConstantAccess extends Expr { * ::MESSAGE * ``` */ - predicate hasGlobalScope() { range.hasGlobalScope() } + predicate hasGlobalScope() { none() } + + override string toString() { result = this.getName() } + + override predicate child(string label, AstNode child) { + label = "getScopeExpr" and child = getScopeExpr() + } } /** @@ -53,12 +58,36 @@ class ConstantAccess extends Expr { * end * ``` */ -class ConstantReadAccess extends ConstantAccess { - final override ConstantReadAccess::Range range; +class ConstantReadAccess extends ConstantAccess, TConstantReadAccess { + override Expr getScopeExpr() { none() } + + override predicate hasGlobalScope() { none() } final override string getAPrimaryQlClass() { result = "ConstantReadAccess" } } +private class TokenConstantReadAccess extends ConstantReadAccess, TTokenConstantReadAccess { + private Generated::Constant g; + + TokenConstantReadAccess() { this = TTokenConstantReadAccess(g) } + + final override string getName() { result = g.getValue() } +} + +private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, + TScopeResolutionConstantReadAccess { + private Generated::ScopeResolution g; + private Generated::Constant constant; + + ScopeResolutionConstantReadAccess() { this = TScopeResolutionConstantReadAccess(g, constant) } + + final override string getName() { result = constant.getValue() } + + final override Expr getScopeExpr() { toTreeSitter(result) = g.getScope() } + + final override predicate hasGlobalScope() { not exists(this.getScopeExpr()) } +} + /** * A definition of a constant. * @@ -75,12 +104,32 @@ class ConstantReadAccess extends ConstantAccess { * module M::Baz; end # defines constant Baz as a module in module M * ``` */ -class ConstantWriteAccess extends ConstantAccess { - override ConstantWriteAccess::Range range; - +class ConstantWriteAccess extends ConstantAccess, TConstantWriteAccess { override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } } +private class TokenConstantWriteAccess extends ConstantWriteAccess, TTokenConstantWriteAccess { + private Generated::Constant g; + + TokenConstantWriteAccess() { this = TTokenConstantWriteAccess(g) } + + final override string getName() { result = g.getValue() } +} + +private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, + TScopeResolutionConstantWriteAccess { + private Generated::ScopeResolution g; + private Generated::Constant constant; + + ScopeResolutionConstantWriteAccess() { this = TScopeResolutionConstantWriteAccess(g, constant) } + + final override string getName() { result = constant.getValue() } + + final override Expr getScopeExpr() { toTreeSitter(result) = g.getScope() } + + final override predicate hasGlobalScope() { not exists(this.getScopeExpr()) } +} + /** * A definition of a constant via assignment. For example, the left-hand * operand in the following example: @@ -89,8 +138,6 @@ class ConstantWriteAccess extends ConstantAccess { * MAX_SIZE = 100 * ``` */ -class ConstantAssignment extends ConstantWriteAccess { - override ConstantAssignment::Range range; - +class ConstantAssignment extends ConstantWriteAccess, LhsExpr, TConstantAssignment { 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..688726a328e 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 predicate child(string label, AstNode child) { + label = "getCondition" and child = this.getCondition() + or + label = "getBranch" and child = 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 predicate child(string label, AstNode child) { + ConditionalExpr.super.child(label, child) + or + label = "getThen" and child = this.getThen() + or + label = "getElse" and child = this.getElse() + } +} + +private class If extends IfExpr, TIf { + private Generated::If g; + + If() { this = TIf(g) } + + final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + + final override Stmt getThen() { toTreeSitter(result) = g.getConsequence() } + + final override Stmt getElse() { toTreeSitter(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() { toTreeSitter(result) = g.getCondition() } + + final override Stmt getThen() { toTreeSitter(result) = g.getConsequence() } + + final override Stmt getElse() { toTreeSitter(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() { toTreeSitter(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() { toTreeSitter(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() { toTreeSitter(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 predicate child(string label, AstNode child) { + ConditionalExpr.super.child(label, child) + or + label = "getThen" and child = this.getThen() + or + label = "getElse" and child = 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() { toTreeSitter(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() { toTreeSitter(result) = g.getBody() } + + final override string toString() { result = "... if ..." } + + override predicate child(string label, AstNode child) { + ConditionalExpr.super.child(label, child) + or + label = "getBody" and child = 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() { toTreeSitter(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() { toTreeSitter(result) = g.getBody() } + + final override string toString() { result = "... unless ..." } + + override predicate child(string label, AstNode child) { + ConditionalExpr.super.child(label, child) + or + label = "getBody" and child = 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() { toTreeSitter(result) = g.getCondition() } + /** Gets the 'then' branch of this ternary if expression. */ - final Stmt getThen() { result = range.getThen() } + final Stmt getThen() { toTreeSitter(result) = g.getConsequence() } /** Gets the 'else' branch of this ternary if expression. */ - final Stmt getElse() { result = range.getElse() } + final Stmt getElse() { toTreeSitter(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 predicate child(string label, AstNode child) { + ConditionalExpr.super.child(label, child) + or + label = "getThen" and child = this.getThen() + or + label = "getElse" and child = 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() { toTreeSitter(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) { toTreeSitter(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 predicate child(string label, AstNode child) { + label = "getValue" and child = this.getValue() + or + label = "getBranch" and child = 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() { toTreeSitter(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) { toTreeSitter(result) = g.getPattern(n).getChild() } /** * Gets a pattern (or condition) in this case-when expression. @@ -281,28 +406,42 @@ 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 predicate child(string label, AstNode child) { + label = "getBody" and child = this.getBody() + or + label = "getPattern" and child = 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 predicate child(string label, AstNode child) { + label = "getBody" and child = 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 predicate child(string label, AstNode child) { + Loop.super.child(label, child) + or + label = "getCondition" and child = this.getCondition() + } } /** @@ -314,13 +453,19 @@ 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() { toTreeSitter(result) = g.getBody() } + + final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + + final override string toString() { result = "while ..." } } /** @@ -332,13 +477,19 @@ 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() { toTreeSitter(result) = g.getBody() } + + final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + + final override string toString() { result = "until ..." } } /** @@ -347,10 +498,18 @@ 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() { toTreeSitter(result) = g.getBody() } + + final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } final override string getAPrimaryQlClass() { result = "WhileModifierExpr" } + + final override string toString() { result = "... while ..." } } /** @@ -359,10 +518,18 @@ 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() { toTreeSitter(result) = g.getBody() } + + final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } final override string getAPrimaryQlClass() { result = "UntilModifierExpr" } + + final override string toString() { result = "... until ..." } } /** @@ -373,16 +540,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() { toTreeSitter(result) = g.getBody() } /** Gets the pattern representing the iteration argument. */ - final Pattern getPattern() { result = range.getPattern() } + final Pattern getPattern() { toTreeSitter(result) = g.getPattern() } /** * Gets the value being iterated over. In the following example, the result @@ -393,5 +562,15 @@ class ForExpr extends Loop, @for { * end * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toTreeSitter(result) = g.getValue().getChild() } + + final override string toString() { result = "for ... in ..." } + + override predicate child(string label, AstNode child) { + Loop.super.child(label, child) + or + label = "getPattern" and child = this.getPattern() + or + label = "getValue" and child = this.getValue() + } } diff --git a/ql/src/codeql_ruby/ast/Expr.qll b/ql/src/codeql_ruby/ast/Expr.qll index dedcf04de4e..2d14acaa28d 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,20 +32,43 @@ 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) { + toTreeSitter(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 predicate child(string label, AstNode child) { + label = "getElement" and child = 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(_) } @@ -61,6 +81,44 @@ class StmtSequence extends Expr { /** Holds if this sequence has no statements. */ final predicate isEmpty() { this.getNumberOfStatements() = 0 } + + override predicate child(string label, AstNode child) { + label = "getStmt" and child = this.getStmt(_) + } +} + +private class Then extends StmtSequence, TThen { + private Generated::Then g; + + Then() { this = TThen(g) } + + override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } +} + +private class Else extends StmtSequence, TElse { + private Generated::Else g; + + Else() { this = TElse(g) } + + override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } +} + +private class Do extends StmtSequence, TDo { + private Generated::Do g; + + Do() { this = TDo(g) } + + override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } +} + +private class Ensure extends StmtSequence, TEnsure { + private Generated::Ensure g; + + Ensure() { this = TEnsure(g) } + + override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + + final override string toString() { result = "ensure ..." } } /** @@ -68,22 +126,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 | + toTreeSitter(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 | toTreeSitter(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 | toTreeSitter(s) = getChild(_)) } /** Gets the `ensure` clause in this block, if any. */ - final StmtSequence getEnsure() { result = range.getEnsure() } + final StmtSequence getEnsure() { result = unique(Ensure s | toTreeSitter(s) = getChild(_)) } final predicate hasEnsure() { exists(this.getEnsure()) } + + override predicate child(string label, AstNode child) { + StmtSequence.super.child(label, child) + or + label = "getRescue" and child = this.getRescue(_) + or + label = "getElse" and child = this.getElse() + or + label = "getEnsure" and child = this.getEnsure() + } } /** @@ -101,10 +207,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) { toTreeSitter(result) = g.getChild(n) } } /** @@ -117,8 +235,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 +253,7 @@ class Pair extends Expr, @pair { * { 'foo' => 123 } * ``` */ - final Expr getKey() { result = range.getKey() } + final Expr getKey() { toTreeSitter(result) = g.getKey() } /** * Gets the value expression of this pair. For example, the `InteralLiteral` @@ -142,7 +262,15 @@ class Pair extends Expr, @pair { * { 'foo' => 123 } * ``` */ - final Expr getValue() { result = range.getValue() } + final Expr getValue() { toTreeSitter(result) = g.getValue() } + + final override string toString() { result = "Pair" } + + override predicate child(string label, AstNode child) { + label = "getKey" and child = this.getKey() + or + label = "getValue" and child = this.getValue() + } } /** @@ -154,8 +282,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 +299,7 @@ class RescueClause extends Expr, @rescue { * end * ``` */ - final Expr getException(int n) { result = range.getException(n) } + final Expr getException(int n) { toTreeSitter(result) = g.getExceptions().getChild(n) } /** * Gets an exception to match, if any. For example `FirstError` or `SecondError` in: @@ -181,7 +311,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 +324,22 @@ class RescueClause extends Expr, @rescue { * end * ``` */ - final LhsExpr getVariableExpr() { result = range.getVariableExpr() } + final LhsExpr getVariableExpr() { toTreeSitter(result) = g.getVariable().getChild() } /** * Gets the exception handler body. */ - final StmtSequence getBody() { result = range.getBody() } + final StmtSequence getBody() { toTreeSitter(result) = g.getBody() } + + final override string toString() { result = "rescue ..." } + + override predicate child(string label, AstNode child) { + label = "getException" and child = this.getException(_) + or + label = "getVariableExpr" and child = this.getVariableExpr() + or + label = "getBody" and child = this.getBody() + } } /** @@ -208,8 +348,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 +361,7 @@ class RescueModifierExpr extends Expr, @rescue_modifier { * body rescue handler * ``` */ - final Stmt getBody() { result = range.getBody() } + final Stmt getBody() { toTreeSitter(result) = g.getBody() } /** * Gets the exception handler of this `RescueModifierExpr`. @@ -227,7 +369,15 @@ class RescueModifierExpr extends Expr, @rescue_modifier { * body rescue handler * ``` */ - final Stmt getHandler() { result = range.getHandler() } + final Stmt getHandler() { toTreeSitter(result) = g.getHandler() } + + final override string toString() { result = "... rescue ..." } + + override predicate child(string label, AstNode child) { + label = "getBody" and child = this.getBody() + or + label = "getHandler" and child = this.getHandler() + } } /** @@ -237,13 +387,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) { toTreeSitter(result) = g.getChild(n) } /** Gets a string literal in this concatenation. */ final StringLiteral getAString() { result = this.getString(_) } @@ -277,4 +429,10 @@ class StringConcatenation extends Expr, @chained_string { valueText order by i ) } + + final override string toString() { result = "\"...\" \"...\"" } + + override predicate child(string label, AstNode child) { + label = "getString" and child = this.getString(_) + } } diff --git a/ql/src/codeql_ruby/ast/Literal.qll b/ql/src/codeql_ruby/ast/Literal.qll index 00aa5359acb..cb3684e6f88 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) { toTreeSitter(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,51 @@ class StringlikeLiteral extends Literal { * ``` */ final int getNumberOfComponents() { result = count(this.getComponent(_)) } + + 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 + ) 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 = toTreeSitter(c).(Generated::Token).getValue() + or + not toTreeSitter(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 predicate child(string label, AstNode child) { + label = "getComponent" and child = this.getComponent(_) + } } /** @@ -216,12 +325,30 @@ class StringlikeLiteral extends Literal { * "hello, #{name}" * ``` */ -class StringLiteral extends StringlikeLiteral { - final override StringLiteral::Range range; +class StringLiteral extends StringlikeLiteral, TStringLiteral { + final override string getStartDelimiter() { result = "\"" } + + final override string getEndDelimiter() { result = "\"" } 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) { toTreeSitter(result) = g.getChild(n) } +} + +private class BareStringLiteral extends StringLiteral, TBareStringLiteral { + private Generated::BareString g; + + BareStringLiteral() { this = TBareStringLiteral(g) } + + final override StringComponent getComponent(int n) { toTreeSitter(result) = g.getChild(n) } +} + /** * A regular expression literal. * @@ -229,11 +356,19 @@ 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) { toTreeSitter(result) = g.getChild(i) } + + final override string getStartDelimiter() { result = "/" } + + final override string getEndDelimiter() { result = "/" } + /** * Gets the regex flags as a string. * @@ -242,7 +377,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 +408,55 @@ 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) } + + final override string getStartDelimiter() { result = ":" } + + // 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 { + final override string getStartDelimiter() { result = ":\"" } + + final override string getEndDelimiter() { result = "\"" } +} + +private class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral { + private Generated::DelimitedSymbol g; + + DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) } + + final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } +} + +private class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral { + private Generated::BareSymbol g; + + BareSymbolLiteral() { this = TBareSymbolLiteral(g) } + + final override StringComponent getComponent(int i) { toTreeSitter(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 +467,18 @@ 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) { toTreeSitter(result) = g.getChild(i) } + + final override string getStartDelimiter() { result = "`" } + + final override string getEndDelimiter() { result = "`" } } /** @@ -298,8 +489,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 +510,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 +535,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 +551,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) { + toTreeSitter(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = "-> { ... }" } + + final override predicate child(string label, AstNode child) { + Callable.super.child(label, child) + or + BodyStmt.super.child(label, child) + } } /** A block. */ -class Block extends Callable, StmtSequence, Scope { - override Block::Range range; +class Block extends Callable, StmtSequence, Scope, TBlock { + override predicate child(string label, AstNode child) { + Callable.super.child(label, child) + or + StmtSequence.super.child(label, child) + } } /** 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) { + toTreeSitter(result) = g.getParameters().getChild(n) + } + + final override string toString() { result = "do ... end" } + + final override predicate child(string label, AstNode child) { + Block.super.child(label, child) + or + BodyStmt.super.child(label, child) + } final override string getAPrimaryQlClass() { result = "DoBlock" } } @@ -84,8 +156,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) { + toTreeSitter(result) = g.getParameters().getChild(n) + } + + final override Stmt getStmt(int i) { toTreeSitter(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 5f3e150e79a..31bb892cd05 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -1,13 +1,12 @@ 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, Scope { - override ModuleBase::Range range; - +class ModuleBase extends BodyStmt, TModuleBase { /** Gets a method defined in this module/class. */ MethodBase getAMethod() { result = this.getAStmt() } @@ -37,20 +36,33 @@ class ModuleBase extends BodyStatement, Scope { * 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) { + toTreeSitter(result) = + rank[n](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i) + } /** * Gets a `BEGIN` block. */ final BeginBlock getABeginBlock() { result = getBeginBlock(_) } + + final override predicate child(string label, AstNode child) { + ModuleBase.super.child(label, child) + or + label = "getBeginBlock" and child = 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 predicate child(string label, AstNode child) { + ModuleBase.super.child(label, child) or + ConstantWriteAccess.super.child(label, child) + } + + 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() { toTreeSitter(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() { + toTreeSitter(result) = g.getName().(Generated::ScopeResolution).getScope() + } + + final override predicate hasGlobalScope() { + exists(Generated::ScopeResolution sr | + sr = g.getName() and + not exists(sr.getScope()) + ) + } + + final override predicate child(string label, AstNode child) { + Namespace.super.child(label, child) + or + label = "getSuperclassExpr" and child = 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() { toTreeSitter(result) = g.getValue() } + + final override string toString() { result = "class << ..." } + + final override predicate child(string label, AstNode child) { + ModuleBase.super.child(label, child) + or + label = "getValue" and child = 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() { + toTreeSitter(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..19e757689c1 100644 --- a/ql/src/codeql_ruby/ast/Operation.qll +++ b/ql/src/codeql_ruby/ast/Operation.qll @@ -1,33 +1,48 @@ 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 predicate child(string label, AstNode child) { + label = "getAnOperand" and child = 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 = toTreeSitter(this) } /** Gets the operand of this unary operation. */ - final Expr getOperand() { result = range.getOperand() } + final Expr getOperand() { toTreeSitter(result) = g.getOperand() } + + final override string getOperator() { result = g.getOperator() } + + final override Expr getAnOperand() { result = this.getOperand() } + + final override predicate child(string label, AstNode child) { + Operation.super.child(label, child) + or + label = "getOperand" and child = 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 +51,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 +64,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 +74,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 +87,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 +97,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 = toTreeSitter(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 predicate child(string label, AstNode child) { + Operation.super.child(label, child) + or + label = "getLeftOperand" and child = this.getLeftOperand() + or + label = "getRightOperand" and child = this.getRightOperand() + } /** Gets the left operand of this binary operation. */ - final Stmt getLeftOperand() { result = range.getLeftOperand() } + final Expr getLeftOperand() { toTreeSitter(result) = g.getLeft() } /** Gets the right operand of this binary operation. */ - final Stmt getRightOperand() { result = range.getRightOperand() } + final Expr getRightOperand() { toTreeSitter(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 +141,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 +151,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 +161,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 +171,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 +181,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 +191,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 +207,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 +218,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 +233,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 +243,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 +253,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 +263,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 +273,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 +281,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 +294,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 +304,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 +314,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 predicate child(string label, AstNode child) { + ComparisonOperation.super.child(label, child) + or + label = "getGreaterOperand" and child = this.getGreaterOperand() + or + label = "getLesserOperand" and child = this.getLesserOperand() + } } /** @@ -358,10 +343,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 +357,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 +371,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 +385,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 +399,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 +409,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 +419,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 +428,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 predicate child(string label, AstNode child) { + Operation.super.child(label, child) + or + label = "getLeftOperand" and child = getLeftOperand() + or + label = "getRightOperand" and child = getRightOperand() + } } /** @@ -457,8 +456,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() { toTreeSitter(result) = g.getLeft() } + + final override Expr getRightOperand() { toTreeSitter(result) = g.getRight() } + + final override string getOperator() { result = "=" } override string getAPrimaryQlClass() { result = "AssignExpr" } } @@ -466,16 +473,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 = toTreeSitter(this) } + + final override string getOperator() { result = g.getOperator() } + + final override LhsExpr getLeftOperand() { toTreeSitter(result) = g.getLeft() } + + final override Expr getRightOperand() { toTreeSitter(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 +496,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 +506,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 +516,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 +526,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 +536,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 +546,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 +561,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 +571,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 +586,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 +596,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 +606,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 +616,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 +626,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..022637dcf6c 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 predicate child(string label, AstNode child) { + PatternParameter.super.child(label, child) or + TuplePattern.super.child(label, child) + } } /** 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 predicate child(string label, AstNode child) { + Parameter.super.child(label, child) + or + label = "getDefiningAccess" and + child = 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) } + + 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() { toTreeSitter(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 predicate child(string label, AstNode child) { + NamedParameter.super.child(label, child) + or + label = "getDefaultValue" and child = 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() { toTreeSitter(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 predicate child(string label, AstNode child) { + NamedParameter.super.child(label, child) + or + label = "getDefaultValue" and child = 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..2ec168d66c2 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -1,18 +1,23 @@ 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(toTreeSitter(this), _) or + implicitAssignmentNode(toTreeSitter(this)) or + implicitParameterAssignmentNode(toTreeSitter(this), _) + } /** Gets a variable used in (or introduced by) this pattern. */ - Variable getAVariable() { result = range.getAVariable() } + Variable getAVariable() { none() } } +private class LhsExpr_ = TVariableAccess or TConstantAssignment 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 +34,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 +53,25 @@ 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 = toTreeSitter(this).(Generated::DestructuredParameter).getChild(i) + or + result = toTreeSitter(this).(Generated::DestructuredLeftAssignment).getChild(i) + or + result = toTreeSitter(this).(Generated::LeftAssignmentList).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) | + toTreeSitter(result) = c.(Generated::RestAssignment).getChild() + or + toTreeSitter(result) = c + ) + } /** Gets a sub pattern in this tuple pattern. */ final Pattern getAnElement() { result = this.getElement(_) } @@ -69,5 +83,15 @@ 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 predicate child(string label, AstNode child) { + label = "getElement" and child = getElement(_) + } } diff --git a/ql/src/codeql_ruby/ast/Scope.qll b/ql/src/codeql_ruby/ast/Scope.qll index c0484232a3d..79381b133c3 100644 --- a/ql/src/codeql_ruby/ast/Scope.qll +++ b/ql/src/codeql_ruby/ast/Scope.qll @@ -1,17 +1,21 @@ private import codeql_ruby.AST +private import internal.AST private import internal.Scope +private import internal.TreeSitter -class Scope extends AstNode, Scope::ScopeType { - override Scope::Range range; +class Scope extends AstNode, TScopeType { + private Scope::Range range; - AstNode getADescendant() { result = range.getADescendant() } + Scope() { range = toTreeSitter(this) } - ModuleBase getEnclosingModule() { result = range.getEnclosingModule() } + /** Gets the enclosing module, if any. */ + ModuleBase getEnclosingModule() { toTreeSitter(result) = range.getEnclosingModule() } - MethodBase getEnclosingMethod() { result = range.getEnclosingMethod() } + /** Gets the enclosing method, if any. */ + MethodBase getEnclosingMethod() { toTreeSitter(result) = range.getEnclosingMethod() } /** Gets the scope in which this scope is nested, if any. */ - Scope getOuterScope() { result = range.getOuterScope() } + Scope getOuterScope() { toTreeSitter(result) = range.getOuterScope() } /** Gets a variable that is declared in this scope. */ final Variable getAVariable() { result.getDeclaringScope() = this } diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index 2842ba23661..fad6855f5aa 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -1,7 +1,8 @@ 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 /** @@ -9,14 +10,12 @@ 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) } + CfgScope getCfgScope() { result = getCfgScope(toTreeSitter(this)) } /** Gets the enclosing callable, if any. */ Callable getEnclosingCallable() { result = this.getCfgScope() } @@ -25,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 = ";" } } /** @@ -39,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 ... " } } /** @@ -51,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) { toTreeSitter(result) = g.getChild(n) } } /** @@ -63,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) { toTreeSitter(result) = g.getChild(n) } } /** @@ -77,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) { toTreeSitter(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 predicate child(string label, AstNode child) { + label = "getMethodName" and child = this.getMethodName(_) + } } /** @@ -97,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() { toTreeSitter(result) = g.getName() } /** Gets the original method name. */ - final MethodName getOldName() { result = range.getOldName() } + final MethodName getOldName() { toTreeSitter(result) = g.getAlias() } final override string getAPrimaryQlClass() { result = "AliasStmt" } + + final override string toString() { result = "alias ..." } + + final override predicate child(string label, AstNode child) { + label = "getNewName" and child = this.getNewName() + or + label = "getOldName" and child = this.getOldName() + } } /** @@ -121,11 +150,32 @@ 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() { + toTreeSitter(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 predicate child(string label, AstNode child) { + label = "getValue" and child = this.getValue() + } } /** @@ -135,10 +185,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" } } /** @@ -148,10 +198,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" } } /** @@ -161,10 +211,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" } } /** @@ -173,10 +223,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" } } /** @@ -185,8 +235,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 955723399dd..38701154ab1 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -2,6 +2,8 @@ private import codeql_ruby.AST private import codeql.Locations +private import internal.AST +private import internal.TreeSitter private import internal.Variable /** A variable declared in a scope. */ @@ -20,7 +22,7 @@ class Variable extends TVariable { final Location getLocation() { result = range.getLocation() } /** Gets the scope this variable is declared in. */ - final Scope getDeclaringScope() { result = range.getDeclaringScope() } + final Scope getDeclaringScope() { toTreeSitter(result) = range.getDeclaringScope() } /** Gets an access to this variable. */ VariableAccess getAnAccess() { result.getVariable() = this } @@ -77,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 @@ -93,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(toTreeSitter(this), toTreeSitter(assignment)) + } /** * Holds if this access is a write access belonging to an implicit assignment. @@ -110,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(toTreeSitter(this)) } } /** An access to a variable where the value is updated. */ @@ -132,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 @@ -157,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) } + + override string toString() { result = g.getValue() } } /** An access to a local variable where the value is updated. */ @@ -166,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" } + + override string toString() { result = g.getValue() } } /** An access to a global variable where the value is updated. */ @@ -181,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" } + + 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" } + + 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..b3e73738845 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -1,17 +1,506 @@ 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) 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 + TScopeResolutionConstantReadAccess(Generated::ScopeResolution g, 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. + constant = g.getName() and + vcall(g) + } or + TScopeResolutionConstantWriteAccess(Generated::ScopeResolution g, Generated::Constant constant) { + explicitAssignmentNode(g, _) and constant = g.getName() + } 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 + TTokenConstantReadAccess(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 + TTokenConstantWriteAccess(Generated::Constant g) { 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 toTreeSitter(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 = TScopeResolutionConstantReadAccess(result, _) or + n = TScopeResolutionConstantWriteAccess(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 = TTokenConstantReadAccess(result) or + n = TTokenConstantWriteAccess(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 fromTreeSitter(Generated::AstNode n) { n = toTreeSitter(result) } + +class TCall = TMethodCall or TYieldCall; + +class TMethodCall = + TIdentifierMethodCall or TScopeResolutionMethodCall or TRegularMethodCall or TElementReference or + TSuperCall; + +class TSuperCall = TTokenSuperCall or TRegularSuperCall; + +class TConstantAccess = TConstantReadAccess or TConstantWriteAccess; + +class TConstantReadAccess = TTokenConstantReadAccess or TScopeResolutionConstantReadAccess; + +class TConstantWriteAccess = TConstantAssignment or TNamespace; + +class TConstantAssignment = TTokenConstantWriteAccess or TScopeResolutionConstantWriteAccess; + +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 0b31ca2d22d..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Method.qll +++ /dev/null @@ -1,144 +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 codeql_ruby.ast.internal.Scope -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 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 MethodBase::Range, @method { - final override Generated::Method generated; - - override Parameter::Range getParameter(int n) { result = generated.getParameters().getChild(n) } - - override 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() } - } -} - -module SingletonMethod { - class Range extends MethodBase::Range, @singleton_method { - final override Generated::SingletonMethod generated; - - override Parameter::Range getParameter(int n) { result = generated.getParameters().getChild(n) } - - override 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) { - MethodBase::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, Scope::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) - } - - override string toString() { result = StmtSequence::Range.super.toString() } - } -} - -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 349e6ced59a..00000000000 --- a/ql/src/codeql_ruby/ast/internal/Module.qll +++ /dev/null @@ -1,132 +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.Scope -private import codeql_ruby.ast.internal.TreeSitter - -module ModuleBase { - abstract class Range extends BodyStatement::Range, Scope::Range { - override string toString() { result = BodyStatement::Range.super.toString() } - } -} - -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 index ab057c2d921..7c76ef8affa 100644 --- a/ql/src/codeql_ruby/ast/internal/Scope.qll +++ b/ql/src/codeql_ruby/ast/internal/Scope.qll @@ -1,22 +1,19 @@ private import TreeSitter +private import codeql_ruby.ast.Scope 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 +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 ScopeType = MethodLike or ModuleLike or BlockLike; + class TypeRange = Callable::TypeRange or ModuleBase::TypeRange or @end_block; - 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) } + class Range extends Generated::AstNode, TypeRange { + Range() { not this = any(Generated::Lambda l).getBody() } ModuleBase::Range getEnclosingModule() { result = this @@ -32,19 +29,34 @@ module Scope { result = this.getOuterScope().getEnclosingMethod() } - Scope::Range getOuterScope() { result = scopeOf(this) } - - override string toString() { none() } + Range getOuterScope() { result = scopeOf(this) } } } -/** 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) - ) +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) { @@ -61,3 +73,13 @@ private Generated::AstNode parentOf(Generated::AstNode n) { 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 d65c7f0304b..ab9baf83cab 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -2,13 +2,45 @@ 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 +/** + * 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) +} + private predicate instanceVariableAccess( Generated::InstanceVariable var, string name, Scope::Range scope, boolean instance ) { @@ -23,11 +55,11 @@ private predicate classVariableAccess(Generated::ClassVariable var, string name, } private predicate hasEnclosingMethod(Generated::AstNode node) { - exists(Scope::Range s | node = s.getADescendant() and exists(s.getEnclosingMethod())) + exists(Scope::Range s | scopeOf(node) = s and exists(s.getEnclosingMethod())) } private ModuleBase::Range enclosingModuleOrClass(Generated::AstNode node) { - exists(Scope::Range s | node = s.getADescendant() and result = s.getEnclosingModule()) + exists(Scope::Range s | scopeOf(node) = s and result = s.getEnclosingModule()) } private predicate parameterAssignment(Callable::Range scope, string name, Generated::Identifier i) { @@ -49,8 +81,8 @@ private predicate scopeDefinesParameterVariable( ) or exists(Parameter::Range p | - p = scope.(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 @@ -64,7 +96,7 @@ private predicate scopeDefinesParameterVariable( 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` */ @@ -75,39 +107,8 @@ 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 - Scope::Range enclosingScope(Generated::AstNode node) { result.getADescendant() = 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 @@ -292,7 +293,7 @@ private module Cached { variable.getName() = name and name = access.getValue() | - variable.getDeclaringScope() = enclosingScope(access) and + 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 @@ -302,7 +303,7 @@ private module Cached { or exists(Scope::Range declScope | variable.getDeclaringScope() = declScope and - inherits(enclosingScope(access), name, declScope) + inherits(scopeOf(access), name, declScope) ) ) } @@ -329,8 +330,8 @@ private module Cached { } cached - predicate isCapturedAccess(LocalVariableAccess::Range access) { - access.getVariable().getDeclaringScope() != enclosingScope(access) + predicate isCapturedAccess(LocalVariableAccess access) { + toTreeSitter(access.getVariable().getDeclaringScope()) != scopeOf(toTreeSitter(access)) } cached @@ -353,7 +354,8 @@ private module Cached { import Cached /** Holds if this scope inherits `name` from an outer scope `outer`. */ -private predicate inherits(Block::Range scope, string name, Scope::Range 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 @@ -362,7 +364,7 @@ private predicate inherits(Block::Range scope, string name, Scope::Range outer) or exists(Generated::Identifier i | scopeAssigns(outer, name, i) and - strictlyBefore(i.getLocation(), scope.(Generated::AstNode).getLocation()) + strictlyBefore(i.getLocation(), scope.getLocation()) ) ) or @@ -396,7 +398,7 @@ module LocalVariable { final override Scope::Range getDeclaringScope() { result = scope } - final VariableAccess getDefiningAccess() { result = getNodeForIdentifier(i) } + final VariableAccess getDefiningAccess() { toTreeSitter(result) = i } } } @@ -414,8 +416,6 @@ module GlobalVariable { } } -private class ModuleOrClassScope = TClassScope or TModuleScope or TTopLevelScope; - module InstanceVariable { class Range extends Variable::Range, TInstanceVariable { private ModuleBase::Range scope; @@ -451,86 +451,29 @@ module ClassVariable { } } -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) - } - } -} - 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..4356f167a1c 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(toTreeSitter(jbp.getFirstNode().(AstCfgNode).getNode()), result) or - idOf(jbp.(EntryBasicBlock).getScope(), result) + idOf(toTreeSitter(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..49f286b6951 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -1,6 +1,7 @@ /** Provides classes representing nodes in a control flow graph. */ private import codeql_ruby.AST +private import codeql_ruby.ast.internal.AST private import codeql_ruby.controlflow.BasicBlocks private import ControlFlowGraph private import internal.ControlFlowGraphImpl @@ -62,11 +63,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(toTreeSitter(n), splits) } final override AstNode getNode() { result = n } @@ -131,7 +132,7 @@ abstract private class ExprChildMapping extends Expr { pragma[noinline] private BasicBlock getABasicBlockInScope() { - result.getANode() = TAstNode(this.getAChildStar(), _) + result.getANode() = TAstCfgNode(toTreeSitter(this.getAChildStar()), _) } pragma[nomagic] diff --git a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll index 5465e03e247..b5b54e6ba0d 100644 --- a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll +++ b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll @@ -2,6 +2,7 @@ private import codeql.Locations private import codeql_ruby.AST as AST +private import codeql_ruby.ast.internal.AST as ASTInternal private import codeql_ruby.controlflow.BasicBlocks private import SuccessorTypes private import internal.ControlFlowGraphImpl @@ -10,13 +11,13 @@ private import internal.Completion /** An AST node with an associated control-flow graph. */ class CfgScope extends AST::AstNode { - CfgScope() { this instanceof CfgScope::Range_ } + CfgScope() { ASTInternal::toTreeSitter(this) instanceof CfgScope::Range_ } /** Gets the CFG scope that this scope is nested under, if any. */ final CfgScope getOuterCfgScope() { exists(AST::AstNode parent | parent = this.getParent() and - result = getCfgScope(parent) + result = getCfgScope(ASTInternal::toTreeSitter(parent)) ) } } diff --git a/ql/src/codeql_ruby/controlflow/internal/Completion.qll b/ql/src/codeql_ruby/controlflow/internal/Completion.qll index c1148d430e3..e0f7348e770 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 @@ -53,7 +54,7 @@ private predicate nestedEnsureCompletion(Completion outer, int nestLevel) { pragma[noinline] private predicate completionIsValidForStmt(AstNode n, Completion c) { - n instanceof InRange and + n = TForIn(_) and c instanceof EmptinessCompletion or n instanceof BreakStmt and @@ -205,7 +206,7 @@ private predicate inMatchingContext(AstNode n) { w.getPattern(_) = n ) or - n = any(Trees::DefaultValueParameterTree t | t.hasDefaultValue()) + toTreeSitter(n).(Trees::DefaultValueParameterTree).hasDefaultValue() } /** diff --git a/ql/src/codeql_ruby/controlflow/internal/Consistency.qll b/ql/src/codeql_ruby/controlflow/internal/Consistency.qll index 0b8e07dd77c..ccec52fd56e 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Consistency.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Consistency.qll @@ -1,4 +1,4 @@ -private import codeql_ruby.AST +private import codeql_ruby.ast.internal.TreeSitter::Generated private import codeql_ruby.CFG private import Completion private import Splitting diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index f6d63161d94..2fbc47970c7 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -31,9 +31,10 @@ * caught up by its surrounding loop and turned into a `NormalCompletion`. */ +private import codeql_ruby.AST as 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.Scope +private import codeql_ruby.ast.Scope private import codeql_ruby.ast.internal.TreeSitter::Generated private import AstNodes private import codeql_ruby.ast.internal.Variable @@ -202,7 +203,7 @@ private predicate succImpl(AstNode pred, AstNode succ, Completion c) { } private predicate isHidden(ControlFlowTree t) { - not t instanceof ASTInternal::AstNode::Range + not t = ASTInternal::toTreeSitter(_) or t.isHidden() } @@ -290,16 +291,17 @@ 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 AST::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 AST::ForExpr { + override predicate child(string label, AST::AstNode child) { + AST::ForExpr.super.child(label, child) or - label = "" and this.(AstNode).getAFieldOrChild().(In) = child + label = "" and + child = ASTInternal::TForIn(ASTInternal::toTreeSitter(this).(For).getValue()) } } @@ -307,7 +309,7 @@ class ForRange extends Control::ForExpr::Range, @for { predicate isValidFor(Completion c, ControlFlowTree node) { c instanceof SimpleCompletion and isHidden(node) or - c.isValidFor(node) + c.isValidFor(ASTInternal::fromTreeSitter(node)) } abstract private class StandardPreOrderTree extends StandardNode, PreOrderTree { @@ -343,7 +345,8 @@ private class LeftToRightPostOrderNodes = @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; + @superclass or @symbol_array or @token_hash_key_symbol or @unary or @splat_parameter or + @hash_splat_parameter or @block_parameter; private class LeftToRightPostOrderTree extends StandardPostOrderTree, LeftToRightPostOrderNodes { LeftToRightPostOrderTree() { @@ -357,7 +360,10 @@ private class LeftToRightPostOrderTree extends StandardPostOrderTree, LeftToRigh this instanceof ChainedString or this instanceof ExceptionVariable or this instanceof LeftAssignmentList or - this instanceof RightAssignmentList + this instanceof RightAssignmentList or + this instanceof SplatParameter or + this instanceof HashSplatParameter or + this instanceof BlockParameter } } @@ -441,8 +447,6 @@ module Trees { } } - 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(_) @@ -505,18 +509,24 @@ module Trees { private class ConstantTree extends LeafTree, Constant { } /** A parameter that may have a default value. */ - abstract class DefaultValueParameterTree extends PreOrderTree { + abstract class DefaultValueParameterTree extends ControlFlowTree { abstract AstNode getDefaultValue(); + abstract AstNode getAccessNode(); + predicate hasDefaultValue() { exists(this.getDefaultValue()) } - final override predicate propagatesAbnormal(AstNode child) { child = this.getDefaultValue() } + final override predicate propagatesAbnormal(AstNode child) { + child = this.getDefaultValue() or child = this.getAccessNode() + } + + final override predicate first(AstNode first) { first = this.getAccessNode() } final override predicate last(AstNode last, Completion c) { last(this.getDefaultValue(), last, c) and c instanceof NormalCompletion or - last = this and + last = this.getAccessNode() and ( not this.hasDefaultValue() and c instanceof SimpleCompletion @@ -527,7 +537,7 @@ module Trees { } final override predicate succ(AstNode pred, AstNode succ, Completion c) { - pred = this and + pred = this.getAccessNode() and first(this.getDefaultValue(), succ) and c.(MatchingCompletion).getValue() = false } @@ -668,8 +678,6 @@ module Trees { private class GlobalVariableTree extends LeafTree, GlobalVariable { } - private class HashSplatParameterTree extends LeafTree, HashSplatParameter { } - private HeredocBody heredoc(HeredocBeginning start) { exists(int i, File f | start = @@ -725,6 +733,8 @@ module Trees { private class KeywordParameterTree extends DefaultValueParameterTree, KeywordParameter { final override AstNode getDefaultValue() { result = this.getValue() } + + final override AstNode getAccessNode() { result = this.getName() } } class LambdaTree extends LeafTree, Lambda { @@ -823,6 +833,8 @@ module Trees { private class OptionalParameterTree extends DefaultValueParameterTree, OptionalParameter { final override AstNode getDefaultValue() { result = this.getValue() } + + final override AstNode getAccessNode() { result = this.getName() } } private class RationalTree extends LeafTree, Rational { } @@ -1183,8 +1195,6 @@ module Trees { } } - private class SplatParameterTree extends LeafTree, SplatParameter { } - private class SuperTree extends LeafTree, Super { } private class TrueTree extends LeafTree, True { } @@ -1261,14 +1271,14 @@ module Trees { private Scope::Range parent(Scope::Range n) { result = n.getOuterScope() and - not n instanceof CfgScope + not n instanceof CfgScope::Range_ } cached private module Cached { /** Gets the CFG scope of node `n`. */ cached - CfgScope getCfgScope(AstNode n) { result = parent*(any(Scope::Range x | x.getADescendant() = n)) } + CfgScope getCfgScope(AstNode n) { ASTInternal::toTreeSitter(result) = parent*(scopeOf(n)) } private predicate isAbnormalExitType(SuccessorType t) { t instanceof RaiseSuccessor or t instanceof ExitSuccessor @@ -1288,7 +1298,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()) } @@ -1312,10 +1322,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 @@ -1325,7 +1335,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 @@ -1349,5 +1359,5 @@ import Cached /** An AST node that is split into multiple control flow nodes. */ class SplitAstNode extends AstNode { - SplitAstNode() { strictcount(CfgNode n | n.getNode() = this) > 1 } + SplitAstNode() { strictcount(CfgNode n | ASTInternal::toTreeSitter(n.getNode()) = this) > 1 } } diff --git a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll index c54cbc27b65..ea7cd31ed24 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll @@ -2,7 +2,9 @@ * Provides classes and predicates relevant for splitting the control flow graph. */ -private import codeql_ruby.AST +private import codeql_ruby.ast.internal.TreeSitter::Generated +private import codeql_ruby.ast.internal.AST as ASTInternal +private import codeql_ruby.AST as AST private import Completion private import ControlFlowGraphImpl private import SuccessorTypes @@ -217,19 +219,28 @@ private module ConditionalCompletionSplitting { succ(pred, succ, c) and last(succ, _, completion) and ( - last(succ.(NotExpr).getOperand(), pred, c) and + last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ).(AST::NotExpr).getOperand()), + pred, c) and completion.(BooleanCompletion).getDual() = c or - last(succ.(LogicalAndExpr).getAnOperand(), pred, c) and + last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + .(AST::LogicalAndExpr) + .getAnOperand()), pred, c) and completion = c or - last(succ.(LogicalOrExpr).getAnOperand(), pred, c) and + last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + .(AST::LogicalOrExpr) + .getAnOperand()), pred, c) and completion = c or - last(succ.(ParenthesizedExpr).getLastExpr(), pred, c) and + last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + .(AST::ParenthesizedExpr) + .getLastExpr()), pred, c) and completion = c or - last(succ.(ConditionalExpr).getBranch(_), pred, c) and + last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + .(AST::ConditionalExpr) + .getBranch(_)), pred, c) and completion = c ) } @@ -244,7 +255,7 @@ private module ConditionalCompletionSplitting { override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesTo(last) and - succExit(scope, last, c) and + succExit(ASTInternal::toTreeSitter(scope), last, c) and if c instanceof ConditionalCompletion then completion = c else any() } @@ -461,7 +472,7 @@ module EnsureSplitting { } override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { - succExit(scope, last, c) and + succExit(ASTInternal::toTreeSitter(scope), last, c) and ( exit(_, last, c, _) or @@ -506,7 +517,7 @@ class Splits extends TSplits { private predicate succEntrySplitsFromRank(CfgScope pred, AstNode succ, Splits splits, int rnk) { splits = TSplitsNil() and - succEntry(pred, succ) and + succEntry(ASTInternal::toTreeSitter(pred), succ) and rnk = 0 or exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | @@ -529,7 +540,7 @@ private predicate succEntrySplitsCons( pragma[noinline] predicate succEntrySplits(CfgScope pred, AstNode succ, Splits succSplits, SuccessorType t) { exists(int rnk | - succEntry(pred, succ) and + succEntry(ASTInternal::toTreeSitter(pred), succ) and t instanceof NormalSuccessor and succEntrySplitsFromRank(pred, succ, succSplits, rnk) | @@ -548,7 +559,7 @@ predicate succExitSplits(AstNode last, Splits predSplits, CfgScope scope, Succes exists(Reachability::SameSplitsBlock b, Completion c | last = b.getANode() | b.isReachable(predSplits) and t = c.getAMatchingSuccessorType() and - succExit(scope, last, c) and + succExit(ASTInternal::toTreeSitter(scope), last, c) and forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | predSplit.hasExitScope(scope, last, c) ) diff --git a/ql/src/codeql_ruby/printAst.qll b/ql/src/codeql_ruby/printAst.qll index 07cb40e180d..45544f91101 100644 --- a/ql/src/codeql_ruby/printAst.qll +++ b/ql/src/codeql_ruby/printAst.qll @@ -52,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) { this.child(edgeName, result) } } private predicate shouldPrintNode(AstNode n) { diff --git a/ql/test/library-tests/ast/Ast.expected b/ql/test/library-tests/ast/Ast.expected index 5df02154eb5..246512d1ec6 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] #{...} @@ -1528,8 +1540,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 +1552,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 +1566,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 +1608,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 +1646,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 +1659,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 +1670,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 +1683,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 +1700,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 +1716,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/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index d6f687ecb90..f106b06d2d8 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1,26 +1,26 @@ 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 break_ensure.rb - # 1| exit m1 -# 1| exit break_ensure.rb (normal) -#-----| -> exit break_ensure.rb +# 1| exit break_ensure.rb # 1| exit m1 (normal) #-----| -> exit m1 +# 1| exit break_ensure.rb (normal) +#-----| -> exit break_ensure.rb + # 2| for ... in ... #-----| -> ensure ... @@ -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 @@ -544,7 +544,7 @@ cfg.rb: #-----| -> &... # 29| enter { ... } -#-----| -> &x +#-----| -> x # 29| call to new #-----| -> true @@ -555,7 +555,7 @@ cfg.rb: # 29| { ... } #-----| -> call to new -# 29| &x +# 29| x #-----| -> x # 29| call to call @@ -1094,9 +1094,9 @@ cfg.rb: #-----| -> key # 101| key -#-----| -> **kwargs +#-----| -> kwargs -# 101| **kwargs +# 101| kwargs #-----| -> value # 101| exit parameters @@ -1456,7 +1456,7 @@ cfg.rb: #-----| -> call to new # 149| enter method -#-----| -> *x +#-----| -> x # 149| method #-----| -> two_parameters @@ -1464,7 +1464,7 @@ cfg.rb: # 149| silly #-----| -> method -# 149| *x +# 149| x #-----| -> x # 149| exit method @@ -1825,24 +1825,21 @@ cfg.rb: #-----| -> exit { ... } exit.rb: -# 1| enter exit.rb -#-----| -> m1 - # 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,6 +1847,9 @@ exit.rb: # 1| exit m1 (normal) #-----| -> exit m1 +# 1| exit exit.rb (normal) +#-----| -> exit exit.rb + # 2| if ... #-----| -> "x <= 2" @@ -1918,25 +1918,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,28 +1947,28 @@ 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 ifs.rb - # 1| exit m1 -# 1| exit ifs.rb (normal) -#-----| -> exit ifs.rb +# 1| exit ifs.rb # 1| exit m1 (normal) #-----| -> exit m1 +# 1| exit ifs.rb (normal) +#-----| -> exit ifs.rb + # 2| if ... #-----| -> exit m1 (normal) @@ -2343,28 +2343,28 @@ 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 loops.rb - # 1| exit m1 -# 1| exit loops.rb (normal) -#-----| -> exit loops.rb +# 1| exit loops.rb # 1| exit m1 (normal) #-----| -> exit m1 +# 1| exit loops.rb (normal) +#-----| -> exit loops.rb + # 2| while ... #-----| -> exit m1 (normal) 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 783b40a3b2c..55ce10e6324 100644 --- a/ql/test/library-tests/variables/varaccess.expected +++ b/ql/test/library-tests/variables/varaccess.expected @@ -51,18 +51,18 @@ variableAccess | 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:25:7:31 | *pizzas | parameters.rb:7:26:7:31 | pizzas | 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:15:15:19 | **map | parameters.rb:15:17:15:19 | map | parameters.rb:15:1:19:3 | print_map | +| 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:16:21:21 | &block | parameters.rb:21:17:21:21 | block | parameters.rb:21:1:23:3 | call_block | +| 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 | @@ -239,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 | From 5724112513af697dcc3c1a4f985a20c4ef92beaf Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 16 Mar 2021 20:39:49 +0100 Subject: [PATCH 17/39] Address review comments --- ql/src/codeql_ruby/AST.qll | 8 +- ql/src/codeql_ruby/ast/Call.qll | 25 ++---- ql/src/codeql_ruby/ast/Constant.qll | 8 +- ql/src/codeql_ruby/ast/Control.qll | 72 ++++++++-------- ql/src/codeql_ruby/ast/Expr.qll | 42 ++++----- ql/src/codeql_ruby/ast/Literal.qll | 86 ++++++++++++------- ql/src/codeql_ruby/ast/Method.qll | 35 ++++---- ql/src/codeql_ruby/ast/Module.qll | 24 +++--- ql/src/codeql_ruby/ast/Operation.qll | 38 ++++---- ql/src/codeql_ruby/ast/Parameter.qll | 28 +++--- ql/src/codeql_ruby/ast/Pattern.qll | 4 +- ql/src/codeql_ruby/ast/Statement.qll | 14 ++- ql/src/codeql_ruby/ast/Variable.qll | 8 +- .../internal/ControlFlowGraphImpl.qll | 8 +- ql/src/codeql_ruby/printAst.qll | 2 +- 15 files changed, 200 insertions(+), 202 deletions(-) diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index ce383d239fe..375ab3d806b 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -36,15 +36,15 @@ class AstNode extends TAstNode { Location getLocation() { result = toTreeSitter(this).getLocation() } /** Gets a child node of this `AstNode`. */ - final AstNode getAChild() { this.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 } /** - * Holds if `child` is a child of this node, and `child` can be retrieved using - * a predicate named `label`. + * Gets a child of this node, which can also be retrieved using a predicate + * named `pred`. */ cached - predicate child(string label, AstNode child) { none() } + AstNode getAChild(string pred) { none() } } diff --git a/ql/src/codeql_ruby/ast/Call.qll b/ql/src/codeql_ruby/ast/Call.qll index a2bf4f6e24a..2bbb5885f58 100644 --- a/ql/src/codeql_ruby/ast/Call.qll +++ b/ql/src/codeql_ruby/ast/Call.qll @@ -48,9 +48,7 @@ class Call extends Expr, TCall { */ final int getNumberOfArguments() { result = count(this.getAnArgument()) } - override predicate child(string label, AstNode child) { - label = "getArgument" and child = getArgument(_) - } + override AstNode getAChild(string pred) { pred = "getArgument" and result = this.getArgument(_) } } bindingset[s] @@ -108,12 +106,12 @@ class MethodCall extends Call, TMethodCall { override string toString() { result = "call to " + concat(this.getMethodName(), "/") } - final override predicate child(string label, AstNode child) { - Call.super.child(label, child) + final override AstNode getAChild(string pred) { + result = Call.super.getAChild(pred) or - label = "getReceiver" and child = getReceiver() + pred = "getReceiver" and result = this.getReceiver() or - label = "getBlock" and child = getBlock() + pred = "getBlock" and result = this.getBlock() } } @@ -161,7 +159,6 @@ private class RegularMethodCall extends MethodCall, TRegularMethodCall { final override Expr getArgument(int n) { toTreeSitter(result) = g.getArguments().getChild(n) or - not exists(g.getArguments()) and toTreeSitter(result) = g.getMethod().(Generated::ArgumentList).getChild(n) } @@ -279,9 +276,7 @@ class BlockArgument extends Expr, TBlockArgument { final override string toString() { result = "&..." } - final override predicate child(string label, AstNode child) { - label = "getValue" and child = getValue() - } + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } /** @@ -308,9 +303,7 @@ class SplatArgument extends Expr, TSplatArgument { final override string toString() { result = "*..." } - final override predicate child(string label, AstNode child) { - label = "getValue" and child = getValue() - } + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } /** @@ -337,7 +330,5 @@ class HashSplatArgument extends Expr, THashSplatArgument { final override string toString() { result = "**..." } - final override predicate child(string label, AstNode child) { - label = "getValue" and child = getValue() - } + 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 f3749d1b42b..423ff6afb88 100644 --- a/ql/src/codeql_ruby/ast/Constant.qll +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -37,9 +37,7 @@ class ConstantAccess extends Expr, TConstantAccess { override string toString() { result = this.getName() } - override predicate child(string label, AstNode child) { - label = "getScopeExpr" and child = getScopeExpr() - } + override AstNode getAChild(string pred) { pred = "getScopeExpr" and result = this.getScopeExpr() } } /** @@ -85,7 +83,7 @@ private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, final override Expr getScopeExpr() { toTreeSitter(result) = g.getScope() } - final override predicate hasGlobalScope() { not exists(this.getScopeExpr()) } + final override predicate hasGlobalScope() { not exists(g.getScope()) } } /** @@ -127,7 +125,7 @@ private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, final override Expr getScopeExpr() { toTreeSitter(result) = g.getScope() } - final override predicate hasGlobalScope() { not exists(this.getScopeExpr()) } + final override predicate hasGlobalScope() { not exists(g.getScope()) } } /** diff --git a/ql/src/codeql_ruby/ast/Control.qll b/ql/src/codeql_ruby/ast/Control.qll index 688726a328e..64977515a25 100644 --- a/ql/src/codeql_ruby/ast/Control.qll +++ b/ql/src/codeql_ruby/ast/Control.qll @@ -34,10 +34,10 @@ class ConditionalExpr extends ControlExpr, TConditionalExpr { */ Stmt getBranch(boolean cond) { none() } - override predicate child(string label, AstNode child) { - label = "getCondition" and child = this.getCondition() + override AstNode getAChild(string pred) { + pred = "getCondition" and result = this.getCondition() or - label = "getBranch" and child = this.getBranch(_) + pred = "getBranch" and result = this.getBranch(_) } } @@ -99,12 +99,12 @@ class IfExpr extends ConditionalExpr, TIfExpr { cond = false and result = this.getElse() } - override predicate child(string label, AstNode child) { - ConditionalExpr.super.child(label, child) + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) or - label = "getThen" and child = this.getThen() + pred = "getThen" and result = this.getThen() or - label = "getElse" and child = this.getElse() + pred = "getElse" and result = this.getElse() } } @@ -189,12 +189,12 @@ class UnlessExpr extends ConditionalExpr, TUnlessExpr { final override string toString() { result = "unless ..." } - override predicate child(string label, AstNode child) { - ConditionalExpr.super.child(label, child) + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) or - label = "getThen" and child = this.getThen() + pred = "getThen" and result = this.getThen() or - label = "getElse" and child = this.getElse() + pred = "getElse" and result = this.getElse() } } @@ -226,10 +226,10 @@ class IfModifierExpr extends ConditionalExpr, TIfModifierExpr { final override string toString() { result = "... if ..." } - override predicate child(string label, AstNode child) { - ConditionalExpr.super.child(label, child) + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) or - label = "getBody" and child = this.getBody() + pred = "getBody" and result = this.getBody() } } @@ -261,10 +261,10 @@ class UnlessModifierExpr extends ConditionalExpr, TUnlessModifierExpr { final override string toString() { result = "... unless ..." } - override predicate child(string label, AstNode child) { - ConditionalExpr.super.child(label, child) + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) or - label = "getBody" and child = this.getBody() + pred = "getBody" and result = this.getBody() } } @@ -297,12 +297,12 @@ class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr { final override string toString() { result = "... ? ... : ..." } - override predicate child(string label, AstNode child) { - ConditionalExpr.super.child(label, child) + override AstNode getAChild(string pred) { + result = ConditionalExpr.super.getAChild(pred) or - label = "getThen" and child = this.getThen() + pred = "getThen" and result = this.getThen() or - label = "getElse" and child = this.getElse() + pred = "getElse" and result = this.getElse() } } @@ -359,10 +359,10 @@ class CaseExpr extends ControlExpr, TCaseExpr { final override string toString() { result = "case ..." } - override predicate child(string label, AstNode child) { - label = "getValue" and child = this.getValue() + override AstNode getAChild(string pred) { + pred = "getValue" and result = this.getValue() or - label = "getBranch" and child = this.getBranch(_) + pred = "getBranch" and result = this.getBranch(_) } } @@ -409,10 +409,10 @@ class WhenExpr extends Expr, TWhenExpr { final override string toString() { result = "when ..." } - override predicate child(string label, AstNode child) { - label = "getBody" and child = this.getBody() + override AstNode getAChild(string pred) { + pred = "getBody" and result = this.getBody() or - label = "getPattern" and child = this.getPattern(_) + pred = "getPattern" and result = this.getPattern(_) } } @@ -424,9 +424,7 @@ class Loop extends ControlExpr, TLoop { /** Gets the body of this loop. */ Stmt getBody() { none() } - override predicate child(string label, AstNode child) { - label = "getBody" and child = this.getBody() - } + override AstNode getAChild(string pred) { pred = "getBody" and result = this.getBody() } } /** @@ -437,10 +435,10 @@ class ConditionalLoop extends Loop, TConditionalLoop { /** Gets the condition expression of this loop. */ Expr getCondition() { none() } - override predicate child(string label, AstNode child) { - Loop.super.child(label, child) + override AstNode getAChild(string pred) { + result = Loop.super.getAChild(pred) or - label = "getCondition" and child = this.getCondition() + pred = "getCondition" and result = this.getCondition() } } @@ -566,11 +564,11 @@ class ForExpr extends Loop, TForExpr { final override string toString() { result = "for ... in ..." } - override predicate child(string label, AstNode child) { - Loop.super.child(label, child) + override AstNode getAChild(string pred) { + result = Loop.super.getAChild(pred) or - label = "getPattern" and child = this.getPattern() + pred = "getPattern" and result = this.getPattern() or - label = "getValue" and child = this.getValue() + 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 2d14acaa28d..f71d111c5d8 100644 --- a/ql/src/codeql_ruby/ast/Expr.qll +++ b/ql/src/codeql_ruby/ast/Expr.qll @@ -48,8 +48,8 @@ class ArgumentList extends Expr, TArgumentList { final override string toString() { result = "..., ..." } - final override predicate child(string label, AstNode child) { - label = "getElement" and child = this.getElement(_) + final override AstNode getAChild(string pred) { + pred = "getElement" and result = this.getElement(_) } } @@ -82,9 +82,7 @@ class StmtSequence extends Expr, TStmtSequence { /** Holds if this sequence has no statements. */ final predicate isEmpty() { this.getNumberOfStatements() = 0 } - override predicate child(string label, AstNode child) { - label = "getStmt" and child = this.getStmt(_) - } + override AstNode getAChild(string pred) { pred = "getStmt" and result = this.getStmt(_) } } private class Then extends StmtSequence, TThen { @@ -181,14 +179,14 @@ class BodyStmt extends StmtSequence, TBodyStmt { final predicate hasEnsure() { exists(this.getEnsure()) } - override predicate child(string label, AstNode child) { - StmtSequence.super.child(label, child) + override AstNode getAChild(string pred) { + result = StmtSequence.super.getAChild(pred) or - label = "getRescue" and child = this.getRescue(_) + pred = "getRescue" and result = this.getRescue(_) or - label = "getElse" and child = this.getElse() + pred = "getElse" and result = this.getElse() or - label = "getEnsure" and child = this.getEnsure() + pred = "getEnsure" and result = this.getEnsure() } } @@ -266,10 +264,10 @@ class Pair extends Expr, TPair { final override string toString() { result = "Pair" } - override predicate child(string label, AstNode child) { - label = "getKey" and child = this.getKey() + override AstNode getAChild(string pred) { + pred = "getKey" and result = this.getKey() or - label = "getValue" and child = this.getValue() + pred = "getValue" and result = this.getValue() } } @@ -333,12 +331,12 @@ class RescueClause extends Expr, TRescueClause { final override string toString() { result = "rescue ..." } - override predicate child(string label, AstNode child) { - label = "getException" and child = this.getException(_) + override AstNode getAChild(string pred) { + pred = "getException" and result = this.getException(_) or - label = "getVariableExpr" and child = this.getVariableExpr() + pred = "getVariableExpr" and result = this.getVariableExpr() or - label = "getBody" and child = this.getBody() + pred = "getBody" and result = this.getBody() } } @@ -373,10 +371,10 @@ class RescueModifierExpr extends Expr, TRescueModifierExpr { final override string toString() { result = "... rescue ..." } - override predicate child(string label, AstNode child) { - label = "getBody" and child = this.getBody() + override AstNode getAChild(string pred) { + pred = "getBody" and result = this.getBody() or - label = "getHandler" and child = this.getHandler() + pred = "getHandler" and result = this.getHandler() } } @@ -432,7 +430,5 @@ class StringConcatenation extends Expr, TStringConcatenation { final override string toString() { result = "\"...\" \"...\"" } - override predicate child(string label, AstNode child) { - label = "getString" and child = this.getString(_) - } + 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 cb3684e6f88..57bc28dbb99 100644 --- a/ql/src/codeql_ruby/ast/Literal.qll +++ b/ql/src/codeql_ruby/ast/Literal.qll @@ -271,11 +271,51 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral { */ final int getNumberOfComponents() { result = count(this.getComponent(_)) } - string getStartDelimiter() { result = "" } + 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 = "" + } - string getEndDelimiter() { result = "" } - - final predicate isSimple() { count(this.getComponent(_)) <= 1 } + 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 @@ -312,8 +352,8 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral { ) } - final override predicate child(string label, AstNode child) { - label = "getComponent" and child = this.getComponent(_) + final override AstNode getAChild(string pred) { + pred = "getComponent" and result = this.getComponent(_) } } @@ -326,10 +366,6 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral { * ``` */ class StringLiteral extends StringlikeLiteral, TStringLiteral { - final override string getStartDelimiter() { result = "\"" } - - final override string getEndDelimiter() { result = "\"" } - final override string getAPrimaryQlClass() { result = "StringLiteral" } } @@ -365,10 +401,6 @@ class RegexLiteral extends StringlikeLiteral, TRegexLiteral { final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } - final override string getStartDelimiter() { result = "/" } - - final override string getEndDelimiter() { result = "/" } - /** * Gets the regex flags as a string. * @@ -419,19 +451,13 @@ private class SimpleSymbolLiteral extends SymbolLiteral, TSimpleSymbolLiteral { SimpleSymbolLiteral() { this = TSimpleSymbolLiteral(g) } - final override string getStartDelimiter() { result = ":" } - // 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 { - final override string getStartDelimiter() { result = ":\"" } - - final override string getEndDelimiter() { result = "\"" } -} +private class ComplexSymbolLiteral extends SymbolLiteral, TComplexSymbolLiteral { } private class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSymbolLiteral { private Generated::DelimitedSymbol g; @@ -475,10 +501,6 @@ class SubshellLiteral extends StringlikeLiteral, TSubshellLiteral { final override string getAPrimaryQlClass() { result = "SubshellLiteral" } final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } - - final override string getStartDelimiter() { result = "`" } - - final override string getEndDelimiter() { result = "`" } } /** @@ -604,8 +626,8 @@ class ArrayLiteral extends Literal, TArrayLiteral { /** Gets the number of elements in this array literal. */ final int getNumberOfElements() { result = count(this.getAnElement()) } - final override predicate child(string label, AstNode child) { - label = "getElement" and child = this.getElement(_) + final override AstNode getAChild(string pred) { + pred = "getElement" and result = this.getElement(_) } } @@ -676,8 +698,8 @@ class HashLiteral extends Literal, THashLiteral { final override string toString() { result = "{...}" } - final override predicate child(string label, AstNode child) { - label = "getElement" and child = this.getElement(_) + final override AstNode getAChild(string pred) { + pred = "getElement" and result = this.getElement(_) } } @@ -716,10 +738,10 @@ class RangeLiteral extends Literal, TRangeLiteral { final override string toString() { result = "_ " + g.getOperator() + " _" } - final override predicate child(string label, AstNode child) { - label = "getBegin" and child = this.getBegin() + final override AstNode getAChild(string pred) { + pred = "getBegin" and result = this.getBegin() or - label = "getEnd" and child = this.getEnd() + pred = "getEnd" and result = this.getEnd() } } diff --git a/ql/src/codeql_ruby/ast/Method.qll b/ql/src/codeql_ruby/ast/Method.qll index 3d50bf7e49c..3e0ad6c2b87 100644 --- a/ql/src/codeql_ruby/ast/Method.qll +++ b/ql/src/codeql_ruby/ast/Method.qll @@ -14,8 +14,8 @@ class Callable extends Expr, CfgScope, TCallable { /** Gets the `n`th parameter of this callable. */ Parameter getParameter(int n) { none() } - override predicate child(string label, AstNode child) { - label = "getParameter" and child = this.getParameter(_) + override AstNode getAChild(string pred) { + pred = "getParameter" and result = this.getParameter(_) } } @@ -24,10 +24,10 @@ class MethodBase extends Callable, BodyStmt, Scope, TMethodBase { /** Gets the name of this method. */ string getName() { none() } - override predicate child(string label, AstNode child) { - Callable.super.child(label, child) + override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) or - BodyStmt.super.child(label, child) + result = BodyStmt.super.getAChild(pred) } } @@ -77,7 +77,6 @@ class SingletonMethod extends MethodBase, TSingletonMethod { final override string getName() { result = g.getName().(Generated::Token).getValue() or - // result = g.getName().(SymbolLiteral).getValueText() or result = g.getName().(Generated::Setter).getName().getValue() + "=" } @@ -87,10 +86,10 @@ class SingletonMethod extends MethodBase, TSingletonMethod { final override string toString() { result = this.getName() } - final override predicate child(string label, AstNode child) { - MethodBase.super.child(label, child) + final override AstNode getAChild(string pred) { + result = MethodBase.super.getAChild(pred) or - label = "getObject" and child = this.getObject() + pred = "getObject" and result = this.getObject() } } @@ -113,19 +112,19 @@ class Lambda extends Callable, BodyStmt, TLambda { final override string toString() { result = "-> { ... }" } - final override predicate child(string label, AstNode child) { - Callable.super.child(label, child) + final override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) or - BodyStmt.super.child(label, child) + result = BodyStmt.super.getAChild(pred) } } /** A block. */ class Block extends Callable, StmtSequence, Scope, TBlock { - override predicate child(string label, AstNode child) { - Callable.super.child(label, child) + override AstNode getAChild(string pred) { + result = Callable.super.getAChild(pred) or - StmtSequence.super.child(label, child) + result = StmtSequence.super.getAChild(pred) } } @@ -141,10 +140,10 @@ class DoBlock extends Block, BodyStmt, TDoBlock { final override string toString() { result = "do ... end" } - final override predicate child(string label, AstNode child) { - Block.super.child(label, child) + final override AstNode getAChild(string pred) { + result = Block.super.getAChild(pred) or - BodyStmt.super.child(label, child) + result = BodyStmt.super.getAChild(pred) } final override string getAPrimaryQlClass() { result = "DoBlock" } diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index 31bb892cd05..ba23d8327a5 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -56,10 +56,10 @@ class Toplevel extends ModuleBase, TToplevel { */ final BeginBlock getABeginBlock() { result = getBeginBlock(_) } - final override predicate child(string label, AstNode child) { - ModuleBase.super.child(label, child) + final override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) or - label = "getBeginBlock" and child = this.getBeginBlock(_) + pred = "getBeginBlock" and result = this.getBeginBlock(_) } final override string toString() { result = g.getLocation().getFile().getBaseName() } @@ -132,9 +132,9 @@ class Namespace extends ModuleBase, ConstantWriteAccess, TNamespace { */ override predicate hasGlobalScope() { none() } - override predicate child(string label, AstNode child) { - ModuleBase.super.child(label, child) or - ConstantWriteAccess.super.child(label, child) + override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) or + result = ConstantWriteAccess.super.getAChild(pred) } final override string toString() { result = ConstantWriteAccess.super.toString() } @@ -191,10 +191,10 @@ class Class extends Namespace, TClass { ) } - final override predicate child(string label, AstNode child) { - Namespace.super.child(label, child) + final override AstNode getAChild(string pred) { + result = Namespace.super.getAChild(pred) or - label = "getSuperclassExpr" and child = this.getSuperclassExpr() + pred = "getSuperclassExpr" and result = this.getSuperclassExpr() } } @@ -229,10 +229,10 @@ class SingletonClass extends ModuleBase, TSingletonClass { final override string toString() { result = "class << ..." } - final override predicate child(string label, AstNode child) { - ModuleBase.super.child(label, child) + final override AstNode getAChild(string pred) { + result = ModuleBase.super.getAChild(pred) or - label = "getValue" and child = this.getValue() + pred = "getValue" and result = this.getValue() } } diff --git a/ql/src/codeql_ruby/ast/Operation.qll b/ql/src/codeql_ruby/ast/Operation.qll index 19e757689c1..8d13367f2c3 100644 --- a/ql/src/codeql_ruby/ast/Operation.qll +++ b/ql/src/codeql_ruby/ast/Operation.qll @@ -14,9 +14,7 @@ class Operation extends Expr, TOperation { /** Gets an operand of this operation. */ Expr getAnOperand() { none() } - override predicate child(string label, AstNode child) { - label = "getAnOperand" and child = this.getAnOperand() - } + override AstNode getAChild(string pred) { pred = "getAnOperand" and result = this.getAnOperand() } } /** A unary operation. */ @@ -32,10 +30,10 @@ class UnaryOperation extends Operation, TUnaryOperation { final override Expr getAnOperand() { result = this.getOperand() } - final override predicate child(string label, AstNode child) { - Operation.super.child(label, child) + final override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) or - label = "getOperand" and child = this.getOperand() + pred = "getOperand" and result = this.getOperand() } final override string toString() { result = this.getOperator() + " ..." } @@ -115,19 +113,19 @@ class BinaryOperation extends Operation, TBinaryOperation { final override string toString() { result = "... " + this.getOperator() + " ..." } - override predicate child(string label, AstNode child) { - Operation.super.child(label, child) + override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) or - label = "getLeftOperand" and child = this.getLeftOperand() + pred = "getLeftOperand" and result = this.getLeftOperand() or - label = "getRightOperand" and child = this.getRightOperand() + pred = "getRightOperand" and result = this.getRightOperand() } /** Gets the left operand of this binary operation. */ - final Expr getLeftOperand() { toTreeSitter(result) = g.getLeft() } + final Stmt getLeftOperand() { toTreeSitter(result) = g.getLeft() } /** Gets the right operand of this binary operation. */ - final Expr getRightOperand() { toTreeSitter(result) = g.getRight() } + final Stmt getRightOperand() { toTreeSitter(result) = g.getRight() } } /** @@ -328,12 +326,12 @@ class RelationalOperation extends ComparisonOperation, TRelationalOperation { /** Gets the lesser operand. */ Expr getLesserOperand() { none() } - final override predicate child(string label, AstNode child) { - ComparisonOperation.super.child(label, child) + final override AstNode getAChild(string pred) { + result = ComparisonOperation.super.getAChild(pred) or - label = "getGreaterOperand" and child = this.getGreaterOperand() + pred = "getGreaterOperand" and result = this.getGreaterOperand() or - label = "getLesserOperand" and child = this.getLesserOperand() + pred = "getLesserOperand" and result = this.getLesserOperand() } } @@ -441,12 +439,12 @@ class Assignment extends Operation, TAssignment { final override string toString() { result = "... " + this.getOperator() + " ..." } - override predicate child(string label, AstNode child) { - Operation.super.child(label, child) + override AstNode getAChild(string pred) { + result = Operation.super.getAChild(pred) or - label = "getLeftOperand" and child = getLeftOperand() + pred = "getLeftOperand" and result = getLeftOperand() or - label = "getRightOperand" and child = getRightOperand() + pred = "getRightOperand" and result = getRightOperand() } } diff --git a/ql/src/codeql_ruby/ast/Parameter.qll b/ql/src/codeql_ruby/ast/Parameter.qll index 022637dcf6c..1d207d1eb74 100644 --- a/ql/src/codeql_ruby/ast/Parameter.qll +++ b/ql/src/codeql_ruby/ast/Parameter.qll @@ -37,9 +37,9 @@ class TuplePatternParameter extends PatternParameter, TuplePattern, TTuplePatter final override string getAPrimaryQlClass() { result = "TuplePatternParameter" } - override predicate child(string label, AstNode child) { - PatternParameter.super.child(label, child) or - TuplePattern.super.child(label, child) + override AstNode getAChild(string pred) { + result = PatternParameter.super.getAChild(pred) or + result = TuplePattern.super.getAChild(pred) } } @@ -59,11 +59,11 @@ class NamedParameter extends Parameter, TNamedParameter { /** Gets the access that defines the underlying local variable. */ final VariableAccess getDefiningAccess() { result = this.getVariable().getDefiningAccess() } - override predicate child(string label, AstNode child) { - Parameter.super.child(label, child) + override AstNode getAChild(string pred) { + result = Parameter.super.getAChild(pred) or - label = "getDefiningAccess" and - child = this.getDefiningAccess() + pred = "getDefiningAccess" and + result = this.getDefiningAccess() } } @@ -77,7 +77,7 @@ class SimpleParameter extends NamedParameter, PatternParameter, VariablePattern, final override LocalVariable getVariable() { result = TLocalVariable(_, _, g) } - override LocalVariable getAVariable() { result = this.getVariable() } + final override LocalVariable getAVariable() { result = this.getVariable() } final override string getAPrimaryQlClass() { result = "SimpleParameter" } @@ -168,10 +168,10 @@ class KeywordParameter extends NamedParameter, TKeywordParameter { final override Location getLocation() { result = g.getName().getLocation() } - final override predicate child(string label, AstNode child) { - NamedParameter.super.child(label, child) + final override AstNode getAChild(string pred) { + result = NamedParameter.super.getAChild(pred) or - label = "getDefaultValue" and child = this.getDefaultValue() + pred = "getDefaultValue" and result = this.getDefaultValue() } } @@ -205,10 +205,10 @@ class OptionalParameter extends NamedParameter, TOptionalParameter { final override Location getLocation() { result = g.getName().getLocation() } - final override predicate child(string label, AstNode child) { - NamedParameter.super.child(label, child) + final override AstNode getAChild(string pred) { + result = NamedParameter.super.getAChild(pred) or - label = "getDefaultValue" and child = this.getDefaultValue() + pred = "getDefaultValue" and result = this.getDefaultValue() } } diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 2ec168d66c2..4018fbbe68f 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -91,7 +91,5 @@ class TuplePattern extends Pattern, TTuplePattern { override string toString() { result = "(..., ...)" } - override predicate child(string label, AstNode child) { - label = "getElement" and child = getElement(_) - } + override AstNode getAChild(string pred) { pred = "getElement" and result = getElement(_) } } diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index fad6855f5aa..94f1b49ec47 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -103,8 +103,8 @@ class UndefStmt extends Stmt, TUndefStmt { final override string toString() { result = "undef ..." } - final override predicate child(string label, AstNode child) { - label = "getMethodName" and child = this.getMethodName(_) + final override AstNode getAChild(string pred) { + pred = "getMethodName" and result = this.getMethodName(_) } } @@ -131,10 +131,10 @@ class AliasStmt extends Stmt, TAliasStmt { final override string toString() { result = "alias ..." } - final override predicate child(string label, AstNode child) { - label = "getNewName" and child = this.getNewName() + final override AstNode getAChild(string pred) { + pred = "getNewName" and result = this.getNewName() or - label = "getOldName" and child = this.getOldName() + pred = "getOldName" and result = this.getOldName() } } @@ -173,9 +173,7 @@ class ReturningStmt extends Stmt, TReturningStmt { ) } - final override predicate child(string label, AstNode child) { - label = "getValue" and child = this.getValue() - } + final override AstNode getAChild(string pred) { pred = "getValue" and result = this.getValue() } } /** diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index 38701154ab1..de1b0c73941 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -161,7 +161,7 @@ class LocalVariableAccess extends VariableAccess, TLocalVariableAccess { */ final predicate isCapturedAccess() { isCapturedAccess(this) } - override string toString() { result = g.getValue() } + final override string toString() { result = g.getValue() } } /** An access to a local variable where the value is updated. */ @@ -181,7 +181,7 @@ class GlobalVariableAccess extends VariableAccess, TGlobalVariableAccess { final override string getAPrimaryQlClass() { result = "GlobalVariableAccess" } - override string toString() { result = g.getValue() } + final override string toString() { result = g.getValue() } } /** An access to a global variable where the value is updated. */ @@ -201,7 +201,7 @@ class InstanceVariableAccess extends VariableAccess, TInstanceVariableAccess { final override string getAPrimaryQlClass() { result = "InstanceVariableAccess" } - override string toString() { result = g.getValue() } + final override string toString() { result = g.getValue() } } /** An access to a class variable. */ @@ -215,5 +215,5 @@ class ClassVariableAccess extends VariableAccess, TClassVariableAccess { final override string getAPrimaryQlClass() { result = "ClassVariableAccess" } - override string toString() { result = g.getValue() } + final override string toString() { result = g.getValue() } } diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 2fbc47970c7..ea51daf0b06 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -297,11 +297,11 @@ private class ForIn extends AST::AstNode, ASTInternal::TForIn { // TODO: remove this class; it should be replaced with an implicit non AST node private class ForRange extends AST::ForExpr { - override predicate child(string label, AST::AstNode child) { - AST::ForExpr.super.child(label, child) + override AST::AstNode getAChild(string pred) { + result = AST::ForExpr.super.getAChild(pred) or - label = "" and - child = ASTInternal::TForIn(ASTInternal::toTreeSitter(this).(For).getValue()) + pred = "" and + result = ASTInternal::TForIn(ASTInternal::toTreeSitter(this).(For).getValue()) } } diff --git a/ql/src/codeql_ruby/printAst.qll b/ql/src/codeql_ruby/printAst.qll index 45544f91101..2737e456251 100644 --- a/ql/src/codeql_ruby/printAst.qll +++ b/ql/src/codeql_ruby/printAst.qll @@ -52,7 +52,7 @@ class PrintAstNode extends AstNode { /** * Gets the child node that is accessed using the predicate `edgeName`. */ - PrintAstNode getChild(string edgeName) { this.child(edgeName, result) } + PrintAstNode getChild(string edgeName) { result = this.getAChild(edgeName) } } private predicate shouldPrintNode(AstNode n) { From eb7610c55fd12a89fda4805fd839bf7e6fe08f7c Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 17 Mar 2021 09:28:23 +0100 Subject: [PATCH 18/39] Rename `(to|from)TreeSitter` to `(to|from)Generated` --- ql/src/codeql_ruby/AST.qll | 2 +- ql/src/codeql_ruby/ast/Call.qll | 28 ++++----- ql/src/codeql_ruby/ast/Constant.qll | 4 +- ql/src/codeql_ruby/ast/Control.qll | 62 +++++++++---------- ql/src/codeql_ruby/ast/Expr.qll | 36 +++++------ ql/src/codeql_ruby/ast/Literal.qll | 34 +++++----- ql/src/codeql_ruby/ast/Method.qll | 14 ++--- ql/src/codeql_ruby/ast/Module.qll | 11 ++-- ql/src/codeql_ruby/ast/Operation.qll | 20 +++--- ql/src/codeql_ruby/ast/Parameter.qll | 4 +- ql/src/codeql_ruby/ast/Pattern.qll | 16 ++--- ql/src/codeql_ruby/ast/Scope.qll | 8 +-- ql/src/codeql_ruby/ast/Statement.qll | 14 ++--- ql/src/codeql_ruby/ast/Variable.qll | 6 +- ql/src/codeql_ruby/ast/internal/AST.qll | 4 +- ql/src/codeql_ruby/ast/internal/Variable.qll | 4 +- .../codeql_ruby/controlflow/BasicBlocks.qll | 4 +- ql/src/codeql_ruby/controlflow/CfgNodes.qll | 4 +- .../controlflow/ControlFlowGraph.qll | 4 +- .../controlflow/internal/Completion.qll | 2 +- .../internal/ControlFlowGraphImpl.qll | 10 +-- .../controlflow/internal/Splitting.qll | 20 +++--- 22 files changed, 155 insertions(+), 156 deletions(-) diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index 375ab3d806b..11f201ac6dd 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -33,7 +33,7 @@ class AstNode extends TAstNode { string toString() { none() } /** Gets the location of this node. */ - Location getLocation() { result = toTreeSitter(this).getLocation() } + Location getLocation() { result = toGenerated(this).getLocation() } /** Gets a child node of this `AstNode`. */ final AstNode getAChild() { result = this.getAChild(_) } diff --git a/ql/src/codeql_ruby/ast/Call.qll b/ql/src/codeql_ruby/ast/Call.qll index 2bbb5885f58..b38b2b37263 100644 --- a/ql/src/codeql_ruby/ast/Call.qll +++ b/ql/src/codeql_ruby/ast/Call.qll @@ -129,7 +129,7 @@ private class ScopeResolutionMethodCall extends MethodCall, TScopeResolutionMeth ScopeResolutionMethodCall() { this = TScopeResolutionMethodCall(g, i) } - final override Expr getReceiver() { toTreeSitter(result) = g.getScope() } + final override Expr getReceiver() { toGenerated(result) = g.getScope() } final override string getMethodName() { result = getMethodName(this, i.getValue()) } } @@ -140,10 +140,10 @@ private class RegularMethodCall extends MethodCall, TRegularMethodCall { RegularMethodCall() { this = TRegularMethodCall(g) } final override Expr getReceiver() { - toTreeSitter(result) = g.getReceiver() + toGenerated(result) = g.getReceiver() or not exists(g.getReceiver()) and - toTreeSitter(result) = g.getMethod().(Generated::ScopeResolution).getScope() + toGenerated(result) = g.getMethod().(Generated::ScopeResolution).getScope() } final override string getMethodName() { @@ -157,12 +157,12 @@ private class RegularMethodCall extends MethodCall, TRegularMethodCall { } final override Expr getArgument(int n) { - toTreeSitter(result) = g.getArguments().getChild(n) + toGenerated(result) = g.getArguments().getChild(n) or - toTreeSitter(result) = g.getMethod().(Generated::ArgumentList).getChild(n) + toGenerated(result) = g.getMethod().(Generated::ArgumentList).getChild(n) } - final override Block getBlock() { toTreeSitter(result) = g.getBlock() } + final override Block getBlock() { toGenerated(result) = g.getBlock() } } /** @@ -189,11 +189,11 @@ class ElementReference extends MethodCall, TElementReference { final override string getAPrimaryQlClass() { result = "ElementReference" } - final override Expr getReceiver() { toTreeSitter(result) = g.getObject() } + final override Expr getReceiver() { toGenerated(result) = g.getObject() } final override string getMethodName() { result = getMethodName(this, "[]") } - final override Expr getArgument(int n) { toTreeSitter(result) = g.getChild(n) } + final override Expr getArgument(int n) { toGenerated(result) = g.getChild(n) } final override string toString() { result = "...[...]" } } @@ -211,7 +211,7 @@ class YieldCall extends Call, TYieldCall { final override string getAPrimaryQlClass() { result = "YieldCall" } - final override Expr getArgument(int n) { toTreeSitter(result) = g.getChild().getChild(n) } + final override Expr getArgument(int n) { toGenerated(result) = g.getChild().getChild(n) } final override string toString() { result = "yield ..." } } @@ -247,9 +247,9 @@ private class RegularSuperCall extends SuperCall, TRegularSuperCall { result = getMethodName(this, g.getMethod().(Generated::Super).getValue()) } - final override Expr getArgument(int n) { toTreeSitter(result) = g.getArguments().getChild(n) } + final override Expr getArgument(int n) { toGenerated(result) = g.getArguments().getChild(n) } - final override Block getBlock() { toTreeSitter(result) = g.getBlock() } + final override Block getBlock() { toGenerated(result) = g.getBlock() } } /** @@ -272,7 +272,7 @@ class BlockArgument extends Expr, TBlockArgument { * foo(&bar) * ``` */ - final Expr getValue() { toTreeSitter(result) = g.getChild() } + final Expr getValue() { toGenerated(result) = g.getChild() } final override string toString() { result = "&..." } @@ -299,7 +299,7 @@ class SplatArgument extends Expr, TSplatArgument { * foo(*bar) * ``` */ - final Expr getValue() { toTreeSitter(result) = g.getChild() } + final Expr getValue() { toGenerated(result) = g.getChild() } final override string toString() { result = "*..." } @@ -326,7 +326,7 @@ class HashSplatArgument extends Expr, THashSplatArgument { * foo(**bar) * ``` */ - final Expr getValue() { toTreeSitter(result) = g.getChild() } + final Expr getValue() { toGenerated(result) = g.getChild() } final override string toString() { result = "**..." } diff --git a/ql/src/codeql_ruby/ast/Constant.qll b/ql/src/codeql_ruby/ast/Constant.qll index 423ff6afb88..3ae3c1207b2 100644 --- a/ql/src/codeql_ruby/ast/Constant.qll +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -81,7 +81,7 @@ private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, final override string getName() { result = constant.getValue() } - final override Expr getScopeExpr() { toTreeSitter(result) = g.getScope() } + final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } final override predicate hasGlobalScope() { not exists(g.getScope()) } } @@ -123,7 +123,7 @@ private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, final override string getName() { result = constant.getValue() } - final override Expr getScopeExpr() { toTreeSitter(result) = g.getScope() } + final override Expr getScopeExpr() { toGenerated(result) = g.getScope() } final override predicate hasGlobalScope() { not exists(g.getScope()) } } diff --git a/ql/src/codeql_ruby/ast/Control.qll b/ql/src/codeql_ruby/ast/Control.qll index 64977515a25..69588659002 100644 --- a/ql/src/codeql_ruby/ast/Control.qll +++ b/ql/src/codeql_ruby/ast/Control.qll @@ -113,11 +113,11 @@ private class If extends IfExpr, TIf { If() { this = TIf(g) } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } - final override Stmt getThen() { toTreeSitter(result) = g.getConsequence() } + final override Stmt getThen() { toGenerated(result) = g.getConsequence() } - final override Stmt getElse() { toTreeSitter(result) = g.getAlternative() } + final override Stmt getElse() { toGenerated(result) = g.getAlternative() } final override string toString() { result = "if ..." } } @@ -129,11 +129,11 @@ private class Elsif extends IfExpr, TElsif { final override predicate isElsif() { any() } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } - final override Stmt getThen() { toTreeSitter(result) = g.getConsequence() } + final override Stmt getThen() { toGenerated(result) = g.getConsequence() } - final override Stmt getElse() { toTreeSitter(result) = g.getAlternative() } + final override Stmt getElse() { toGenerated(result) = g.getAlternative() } final override string toString() { result = "elsif ..." } } @@ -153,7 +153,7 @@ class UnlessExpr extends ConditionalExpr, TUnlessExpr { final override string getAPrimaryQlClass() { result = "UnlessExpr" } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } /** * Gets the 'then' branch of this `unless` expression. In the following @@ -166,7 +166,7 @@ class UnlessExpr extends ConditionalExpr, TUnlessExpr { * end * ``` */ - final Stmt getThen() { toTreeSitter(result) = g.getConsequence() } + final Stmt getThen() { toGenerated(result) = g.getConsequence() } /** * Gets the 'else' branch of this `unless` expression. In the following @@ -179,7 +179,7 @@ class UnlessExpr extends ConditionalExpr, TUnlessExpr { * end * ``` */ - final Stmt getElse() { toTreeSitter(result) = g.getAlternative() } + final Stmt getElse() { toGenerated(result) = g.getAlternative() } final override Expr getBranch(boolean cond) { cond = false and result = getThen() @@ -211,7 +211,7 @@ class IfModifierExpr extends ConditionalExpr, TIfModifierExpr { final override string getAPrimaryQlClass() { result = "IfModifierExpr" } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } final override Stmt getBranch(boolean cond) { cond = true and result = this.getBody() } @@ -222,7 +222,7 @@ class IfModifierExpr extends ConditionalExpr, TIfModifierExpr { * foo if bar * ``` */ - final Stmt getBody() { toTreeSitter(result) = g.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } final override string toString() { result = "... if ..." } @@ -246,7 +246,7 @@ class UnlessModifierExpr extends ConditionalExpr, TUnlessModifierExpr { final override string getAPrimaryQlClass() { result = "UnlessModifierExpr" } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } final override Stmt getBranch(boolean cond) { cond = false and result = this.getBody() } @@ -257,7 +257,7 @@ class UnlessModifierExpr extends ConditionalExpr, TUnlessModifierExpr { * foo unless bar * ``` */ - final Stmt getBody() { toTreeSitter(result) = g.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } final override string toString() { result = "... unless ..." } @@ -281,13 +281,13 @@ class TernaryIfExpr extends ConditionalExpr, TTernaryIfExpr { final override string getAPrimaryQlClass() { result = "TernaryIfExpr" } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } /** Gets the 'then' branch of this ternary if expression. */ - final Stmt getThen() { toTreeSitter(result) = g.getConsequence() } + final Stmt getThen() { toGenerated(result) = g.getConsequence() } /** Gets the 'else' branch of this ternary if expression. */ - final Stmt getElse() { toTreeSitter(result) = g.getAlternative() } + final Stmt getElse() { toGenerated(result) = g.getAlternative() } final override Stmt getBranch(boolean cond) { cond = true and result = getThen() @@ -332,13 +332,13 @@ class CaseExpr extends ControlExpr, TCaseExpr { * end * ``` */ - final Expr getValue() { toTreeSitter(result) = g.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) { toTreeSitter(result) = g.getChild(n) } + final Expr getBranch(int n) { toGenerated(result) = g.getChild(n) } /** * Gets a branch of this case expression, either a `WhenExpr` or an @@ -382,7 +382,7 @@ class WhenExpr extends Expr, TWhenExpr { final override string getAPrimaryQlClass() { result = "WhenExpr" } /** Gets the body of this case-when expression. */ - final Stmt getBody() { toTreeSitter(result) = g.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } /** * Gets the `n`th pattern (or condition) in this case-when expression. In the @@ -395,7 +395,7 @@ class WhenExpr extends Expr, TWhenExpr { * end * ``` */ - final Expr getPattern(int n) { toTreeSitter(result) = g.getPattern(n).getChild() } + final Expr getPattern(int n) { toGenerated(result) = g.getPattern(n).getChild() } /** * Gets a pattern (or condition) in this case-when expression. @@ -459,9 +459,9 @@ class WhileExpr extends ConditionalLoop, TWhileExpr { final override string getAPrimaryQlClass() { result = "WhileExpr" } /** Gets the body of this `while` loop. */ - final override Stmt getBody() { toTreeSitter(result) = g.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } final override string toString() { result = "while ..." } } @@ -483,9 +483,9 @@ class UntilExpr extends ConditionalLoop, TUntilExpr { final override string getAPrimaryQlClass() { result = "UntilExpr" } /** Gets the body of this `until` loop. */ - final override Stmt getBody() { toTreeSitter(result) = g.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } final override string toString() { result = "until ..." } } @@ -501,9 +501,9 @@ class WhileModifierExpr extends ConditionalLoop, TWhileModifierExpr { WhileModifierExpr() { this = TWhileModifierExpr(g) } - final override Stmt getBody() { toTreeSitter(result) = g.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } final override string getAPrimaryQlClass() { result = "WhileModifierExpr" } @@ -521,9 +521,9 @@ class UntilModifierExpr extends ConditionalLoop, TUntilModifierExpr { UntilModifierExpr() { this = TUntilModifierExpr(g) } - final override Stmt getBody() { toTreeSitter(result) = g.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } - final override Expr getCondition() { toTreeSitter(result) = g.getCondition() } + final override Expr getCondition() { toGenerated(result) = g.getCondition() } final override string getAPrimaryQlClass() { result = "UntilModifierExpr" } @@ -546,10 +546,10 @@ class ForExpr extends Loop, TForExpr { final override string getAPrimaryQlClass() { result = "ForExpr" } /** Gets the body of this `for` loop. */ - final override Stmt getBody() { toTreeSitter(result) = g.getBody() } + final override Stmt getBody() { toGenerated(result) = g.getBody() } /** Gets the pattern representing the iteration argument. */ - final Pattern getPattern() { toTreeSitter(result) = g.getPattern() } + final Pattern getPattern() { toGenerated(result) = g.getPattern() } /** * Gets the value being iterated over. In the following example, the result @@ -560,7 +560,7 @@ class ForExpr extends Loop, TForExpr { * end * ``` */ - final Expr getValue() { toTreeSitter(result) = g.getValue().getChild() } + final Expr getValue() { toGenerated(result) = g.getValue().getChild() } final override string toString() { result = "for ... in ..." } diff --git a/ql/src/codeql_ruby/ast/Expr.qll b/ql/src/codeql_ruby/ast/Expr.qll index f71d111c5d8..f584b7f96f7 100644 --- a/ql/src/codeql_ruby/ast/Expr.qll +++ b/ql/src/codeql_ruby/ast/Expr.qll @@ -39,7 +39,7 @@ class ArgumentList extends Expr, TArgumentList { /** Gets the `i`th element in this argument list. */ Expr getElement(int i) { - toTreeSitter(result) in [ + toGenerated(result) in [ g.(Generated::ArgumentList).getChild(i), g.(Generated::RightAssignmentList).getChild(i) ] } @@ -90,7 +90,7 @@ private class Then extends StmtSequence, TThen { Then() { this = TThen(g) } - override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } private class Else extends StmtSequence, TElse { @@ -98,7 +98,7 @@ private class Else extends StmtSequence, TElse { Else() { this = TElse(g) } - override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } private class Do extends StmtSequence, TDo { @@ -106,7 +106,7 @@ private class Do extends StmtSequence, TDo { Do() { this = TDo(g) } - override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } private class Ensure extends StmtSequence, TEnsure { @@ -114,7 +114,7 @@ private class Ensure extends StmtSequence, TEnsure { Ensure() { this = TEnsure(g) } - override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } final override string toString() { result = "ensure ..." } } @@ -153,7 +153,7 @@ class BodyStmt extends StmtSequence, TBodyStmt { final override Stmt getStmt(int n) { result = rank[n + 1](AstNode node, int i | - toTreeSitter(node) = this.getChild(i) and + toGenerated(node) = this.getChild(i) and not node instanceof Else and not node instanceof RescueClause and not node instanceof Ensure @@ -165,17 +165,17 @@ class BodyStmt extends StmtSequence, TBodyStmt { /** Gets the `n`th rescue clause in this block. */ final RescueClause getRescue(int n) { result = - rank[n + 1](RescueClause node, int i | toTreeSitter(node) = getChild(i) | node order by i) + 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 = unique(Else s | toTreeSitter(s) = getChild(_)) } + final StmtSequence getElse() { result = unique(Else s | toGenerated(s) = getChild(_)) } /** Gets the `ensure` clause in this block, if any. */ - final StmtSequence getEnsure() { result = unique(Ensure s | toTreeSitter(s) = getChild(_)) } + final StmtSequence getEnsure() { result = unique(Ensure s | toGenerated(s) = getChild(_)) } final predicate hasEnsure() { exists(this.getEnsure()) } @@ -220,7 +220,7 @@ class ParenthesizedExpr extends StmtSequence, TParenthesizedExpr { ) } - final override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -251,7 +251,7 @@ class Pair extends Expr, TPair { * { 'foo' => 123 } * ``` */ - final Expr getKey() { toTreeSitter(result) = g.getKey() } + final Expr getKey() { toGenerated(result) = g.getKey() } /** * Gets the value expression of this pair. For example, the `InteralLiteral` @@ -260,7 +260,7 @@ class Pair extends Expr, TPair { * { 'foo' => 123 } * ``` */ - final Expr getValue() { toTreeSitter(result) = g.getValue() } + final Expr getValue() { toGenerated(result) = g.getValue() } final override string toString() { result = "Pair" } @@ -297,7 +297,7 @@ class RescueClause extends Expr, TRescueClause { * end * ``` */ - final Expr getException(int n) { toTreeSitter(result) = g.getExceptions().getChild(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: @@ -322,12 +322,12 @@ class RescueClause extends Expr, TRescueClause { * end * ``` */ - final LhsExpr getVariableExpr() { toTreeSitter(result) = g.getVariable().getChild() } + final LhsExpr getVariableExpr() { toGenerated(result) = g.getVariable().getChild() } /** * Gets the exception handler body. */ - final StmtSequence getBody() { toTreeSitter(result) = g.getBody() } + final StmtSequence getBody() { toGenerated(result) = g.getBody() } final override string toString() { result = "rescue ..." } @@ -359,7 +359,7 @@ class RescueModifierExpr extends Expr, TRescueModifierExpr { * body rescue handler * ``` */ - final Stmt getBody() { toTreeSitter(result) = g.getBody() } + final Stmt getBody() { toGenerated(result) = g.getBody() } /** * Gets the exception handler of this `RescueModifierExpr`. @@ -367,7 +367,7 @@ class RescueModifierExpr extends Expr, TRescueModifierExpr { * body rescue handler * ``` */ - final Stmt getHandler() { toTreeSitter(result) = g.getHandler() } + final Stmt getHandler() { toGenerated(result) = g.getHandler() } final override string toString() { result = "... rescue ..." } @@ -393,7 +393,7 @@ class StringConcatenation extends Expr, TStringConcatenation { final override string getAPrimaryQlClass() { result = "StringConcatenation" } /** Gets the `n`th string literal in this concatenation. */ - final StringLiteral getString(int n) { toTreeSitter(result) = g.getChild(n) } + final StringLiteral getString(int n) { toGenerated(result) = g.getChild(n) } /** Gets a string literal in this concatenation. */ final StringLiteral getAString() { result = this.getString(_) } diff --git a/ql/src/codeql_ruby/ast/Literal.qll b/ql/src/codeql_ruby/ast/Literal.qll index 57bc28dbb99..2354084910a 100644 --- a/ql/src/codeql_ruby/ast/Literal.qll +++ b/ql/src/codeql_ruby/ast/Literal.qll @@ -226,7 +226,7 @@ class StringInterpolationComponent extends StringComponent, StmtSequence, final override string toString() { result = "#{...}" } - final override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } final override string getValueText() { none() } @@ -334,9 +334,9 @@ class StringlikeLiteral extends Literal, TStringlikeLiteral { concat(StringComponent c, int i, string s | c = this.getComponent(i) and ( - s = toTreeSitter(c).(Generated::Token).getValue() + s = toGenerated(c).(Generated::Token).getValue() or - not toTreeSitter(c) instanceof Generated::Token and + not toGenerated(c) instanceof Generated::Token and s = "#{...}" ) | @@ -374,7 +374,7 @@ private class RegularStringLiteral extends StringLiteral, TRegularStringLiteral RegularStringLiteral() { this = TRegularStringLiteral(g) } - final override StringComponent getComponent(int n) { toTreeSitter(result) = g.getChild(n) } + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } } private class BareStringLiteral extends StringLiteral, TBareStringLiteral { @@ -382,7 +382,7 @@ private class BareStringLiteral extends StringLiteral, TBareStringLiteral { BareStringLiteral() { this = TBareStringLiteral(g) } - final override StringComponent getComponent(int n) { toTreeSitter(result) = g.getChild(n) } + final override StringComponent getComponent(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -399,7 +399,7 @@ class RegexLiteral extends StringlikeLiteral, TRegexLiteral { final override string getAPrimaryQlClass() { result = "RegexLiteral" } - final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } /** * Gets the regex flags as a string. @@ -464,7 +464,7 @@ private class DelimitedSymbolLiteral extends ComplexSymbolLiteral, TDelimitedSym DelimitedSymbolLiteral() { this = TDelimitedSymbolLiteral(g) } - final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } } private class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral { @@ -472,7 +472,7 @@ private class BareSymbolLiteral extends ComplexSymbolLiteral, TBareSymbolLiteral BareSymbolLiteral() { this = TBareSymbolLiteral(g) } - final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } } private class HashKeySymbolLiteral extends SymbolLiteral, THashKeySymbolLiteral { @@ -500,7 +500,7 @@ class SubshellLiteral extends StringlikeLiteral, TSubshellLiteral { final override string getAPrimaryQlClass() { result = "SubshellLiteral" } - final override StringComponent getComponent(int i) { toTreeSitter(result) = g.getChild(i) } + final override StringComponent getComponent(int i) { toGenerated(result) = g.getChild(i) } } /** @@ -599,7 +599,7 @@ class HereDoc extends StringlikeLiteral, THereDoc { } final override StringComponent getComponent(int n) { - toTreeSitter(result) = this.getBody().getChild(n) + toGenerated(result) = this.getBody().getChild(n) } final override string toString() { result = g.getValue() } @@ -636,7 +636,7 @@ private class RegularArrayLiteral extends ArrayLiteral, TRegularArrayLiteral { RegularArrayLiteral() { this = TRegularArrayLiteral(g) } - final override Expr getElement(int i) { toTreeSitter(result) = g.getChild(i) } + final override Expr getElement(int i) { toGenerated(result) = g.getChild(i) } final override string toString() { result = "[...]" } } @@ -646,7 +646,7 @@ private class StringArrayLiteral extends ArrayLiteral, TStringArrayLiteral { StringArrayLiteral() { this = TStringArrayLiteral(g) } - final override Expr getElement(int i) { toTreeSitter(result) = g.getChild(i) } + final override Expr getElement(int i) { toGenerated(result) = g.getChild(i) } final override string toString() { result = "%w(...)" } } @@ -656,7 +656,7 @@ private class SymbolArrayLiteral extends ArrayLiteral, TSymbolArrayLiteral { SymbolArrayLiteral() { this = TSymbolArrayLiteral(g) } - final override Expr getElement(int i) { toTreeSitter(result) = g.getChild(i) } + final override Expr getElement(int i) { toGenerated(result) = g.getChild(i) } final override string toString() { result = "%i(...)" } } @@ -685,7 +685,7 @@ class HashLiteral extends Literal, THashLiteral { * { foo: 123, **bar } * ``` */ - final Expr getElement(int n) { toTreeSitter(result) = g.getChild(n) } + final Expr getElement(int n) { toGenerated(result) = g.getChild(n) } /** Gets an element in this array literal. */ final Expr getAnElement() { result = this.getElement(_) } @@ -719,10 +719,10 @@ class RangeLiteral extends Literal, TRangeLiteral { final override string getAPrimaryQlClass() { result = "RangeLiteral" } /** Gets the begin expression of this range, if any. */ - final Expr getBegin() { toTreeSitter(result) = g.getBegin() } + final Expr getBegin() { toGenerated(result) = g.getBegin() } /** Gets the end expression of this range, if any. */ - final Expr getEnd() { toTreeSitter(result) = g.getEnd() } + final Expr getEnd() { toGenerated(result) = g.getEnd() } /** * Holds if the range is inclusive of the end value, i.e. uses the `..` @@ -755,7 +755,7 @@ class RangeLiteral extends Literal, TRangeLiteral { * ``` */ class MethodName extends Literal { - MethodName() { MethodName::range(toTreeSitter(this)) } + MethodName() { MethodName::range(toGenerated(this)) } final override string getAPrimaryQlClass() { result = "MethodName" } } diff --git a/ql/src/codeql_ruby/ast/Method.qll b/ql/src/codeql_ruby/ast/Method.qll index 3e0ad6c2b87..3836bee596b 100644 --- a/ql/src/codeql_ruby/ast/Method.qll +++ b/ql/src/codeql_ruby/ast/Method.qll @@ -57,7 +57,7 @@ class Method extends MethodBase, TMethod { final predicate isSetter() { g.getName() instanceof Generated::Setter } final override Parameter getParameter(int n) { - toTreeSitter(result) = g.getParameters().getChild(n) + toGenerated(result) = g.getParameters().getChild(n) } final override string toString() { result = this.getName() } @@ -72,7 +72,7 @@ class SingletonMethod extends MethodBase, TSingletonMethod { final override string getAPrimaryQlClass() { result = "SingletonMethod" } /** Gets the object of this singleton method. */ - final Expr getObject() { toTreeSitter(result) = g.getObject() } + final Expr getObject() { toGenerated(result) = g.getObject() } final override string getName() { result = g.getName().(Generated::Token).getValue() @@ -81,7 +81,7 @@ class SingletonMethod extends MethodBase, TSingletonMethod { } final override Parameter getParameter(int n) { - toTreeSitter(result) = g.getParameters().getChild(n) + toGenerated(result) = g.getParameters().getChild(n) } final override string toString() { result = this.getName() } @@ -107,7 +107,7 @@ class Lambda extends Callable, BodyStmt, TLambda { final override string getAPrimaryQlClass() { result = "Lambda" } final override Parameter getParameter(int n) { - toTreeSitter(result) = g.getParameters().getChild(n) + toGenerated(result) = g.getParameters().getChild(n) } final override string toString() { result = "-> { ... }" } @@ -135,7 +135,7 @@ class DoBlock extends Block, BodyStmt, TDoBlock { DoBlock() { this = TDoBlock(g) } final override Parameter getParameter(int n) { - toTreeSitter(result) = g.getParameters().getChild(n) + toGenerated(result) = g.getParameters().getChild(n) } final override string toString() { result = "do ... end" } @@ -161,10 +161,10 @@ class BraceBlock extends Block, TBraceBlock { BraceBlock() { this = TBraceBlock(g) } final override Parameter getParameter(int n) { - toTreeSitter(result) = g.getParameters().getChild(n) + toGenerated(result) = g.getParameters().getChild(n) } - final override Stmt getStmt(int i) { toTreeSitter(result) = g.getChild(i) } + final override Stmt getStmt(int i) { toGenerated(result) = g.getChild(i) } final override string toString() { result = "{ ... }" } diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index ba23d8327a5..568a5d64cac 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -47,8 +47,7 @@ class Toplevel extends ModuleBase, TToplevel { * Gets the `n`th `BEGIN` block. */ final BeginBlock getBeginBlock(int n) { - toTreeSitter(result) = - rank[n](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i) + toGenerated(result) = rank[n](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i) } /** @@ -173,7 +172,7 @@ class Class extends Namespace, TClass { * end * ``` */ - final Expr getSuperclassExpr() { toTreeSitter(result) = g.getSuperclass().getChild() } + final Expr getSuperclassExpr() { toGenerated(result) = g.getSuperclass().getChild() } final override string getName() { result = g.getName().(Generated::Token).getValue() or @@ -181,7 +180,7 @@ class Class extends Namespace, TClass { } final override Expr getScopeExpr() { - toTreeSitter(result) = g.getName().(Generated::ScopeResolution).getScope() + toGenerated(result) = g.getName().(Generated::ScopeResolution).getScope() } final override predicate hasGlobalScope() { @@ -225,7 +224,7 @@ class SingletonClass extends ModuleBase, TSingletonClass { * end * ``` */ - final Expr getValue() { toTreeSitter(result) = g.getValue() } + final Expr getValue() { toGenerated(result) = g.getValue() } final override string toString() { result = "class << ..." } @@ -274,7 +273,7 @@ class Module extends Namespace, TModule { } final override Expr getScopeExpr() { - toTreeSitter(result) = g.getName().(Generated::ScopeResolution).getScope() + toGenerated(result) = g.getName().(Generated::ScopeResolution).getScope() } final override predicate hasGlobalScope() { diff --git a/ql/src/codeql_ruby/ast/Operation.qll b/ql/src/codeql_ruby/ast/Operation.qll index 8d13367f2c3..b5acf8c8116 100644 --- a/ql/src/codeql_ruby/ast/Operation.qll +++ b/ql/src/codeql_ruby/ast/Operation.qll @@ -21,10 +21,10 @@ class Operation extends Expr, TOperation { class UnaryOperation extends Operation, TUnaryOperation { private Generated::Unary g; - UnaryOperation() { g = toTreeSitter(this) } + UnaryOperation() { g = toGenerated(this) } /** Gets the operand of this unary operation. */ - final Expr getOperand() { toTreeSitter(result) = g.getOperand() } + final Expr getOperand() { toGenerated(result) = g.getOperand() } final override string getOperator() { result = g.getOperator() } @@ -103,7 +103,7 @@ class DefinedExpr extends UnaryOperation, TDefinedExpr { class BinaryOperation extends Operation, TBinaryOperation { private Generated::Binary g; - BinaryOperation() { g = toTreeSitter(this) } + BinaryOperation() { g = toGenerated(this) } final override string getOperator() { result = g.getOperator() } @@ -122,10 +122,10 @@ class BinaryOperation extends Operation, TBinaryOperation { } /** Gets the left operand of this binary operation. */ - final Stmt getLeftOperand() { toTreeSitter(result) = g.getLeft() } + final Stmt getLeftOperand() { toGenerated(result) = g.getLeft() } /** Gets the right operand of this binary operation. */ - final Stmt getRightOperand() { toTreeSitter(result) = g.getRight() } + final Stmt getRightOperand() { toGenerated(result) = g.getRight() } } /** @@ -459,9 +459,9 @@ class AssignExpr extends Assignment, TAssignExpr { AssignExpr() { this = TAssignExpr(g) } - final override Pattern getLeftOperand() { toTreeSitter(result) = g.getLeft() } + final override Pattern getLeftOperand() { toGenerated(result) = g.getLeft() } - final override Expr getRightOperand() { toTreeSitter(result) = g.getRight() } + final override Expr getRightOperand() { toGenerated(result) = g.getRight() } final override string getOperator() { result = "=" } @@ -474,13 +474,13 @@ class AssignExpr extends Assignment, TAssignExpr { class AssignOperation extends Assignment, TAssignOperation { private Generated::OperatorAssignment g; - AssignOperation() { g = toTreeSitter(this) } + AssignOperation() { g = toGenerated(this) } final override string getOperator() { result = g.getOperator() } - final override LhsExpr getLeftOperand() { toTreeSitter(result) = g.getLeft() } + final override LhsExpr getLeftOperand() { toGenerated(result) = g.getLeft() } - final override Expr getRightOperand() { toTreeSitter(result) = g.getRight() } + final override Expr getRightOperand() { toGenerated(result) = g.getRight() } } /** diff --git a/ql/src/codeql_ruby/ast/Parameter.qll b/ql/src/codeql_ruby/ast/Parameter.qll index 1d207d1eb74..7f9c11b417b 100644 --- a/ql/src/codeql_ruby/ast/Parameter.qll +++ b/ql/src/codeql_ruby/ast/Parameter.qll @@ -154,7 +154,7 @@ class KeywordParameter extends NamedParameter, TKeywordParameter { * 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() { toTreeSitter(result) = g.getValue() } + final Expr getDefaultValue() { toGenerated(result) = g.getValue() } /** * Holds if the parameter is optional. That is, there is a default value that @@ -195,7 +195,7 @@ class OptionalParameter extends NamedParameter, TOptionalParameter { * Gets the default value, i.e. the value assigned to the parameter when one * is not provided by the caller. */ - final Expr getDefaultValue() { toTreeSitter(result) = g.getValue() } + final Expr getDefaultValue() { toGenerated(result) = g.getValue() } final override LocalVariable getVariable() { result = TLocalVariable(_, _, g.getName()) } diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 4018fbbe68f..39ddfbab383 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -7,9 +7,9 @@ private import internal.Variable /** A pattern. */ class Pattern extends AstNode { Pattern() { - explicitAssignmentNode(toTreeSitter(this), _) or - implicitAssignmentNode(toTreeSitter(this)) or - implicitParameterAssignmentNode(toTreeSitter(this), _) + explicitAssignmentNode(toGenerated(this), _) or + implicitAssignmentNode(toGenerated(this)) or + implicitParameterAssignmentNode(toGenerated(this), _) } /** Gets a variable used in (or introduced by) this pattern. */ @@ -57,19 +57,19 @@ class TuplePattern extends Pattern, TTuplePattern { override string getAPrimaryQlClass() { result = "TuplePattern" } private Generated::AstNode getChild(int i) { - result = toTreeSitter(this).(Generated::DestructuredParameter).getChild(i) + result = toGenerated(this).(Generated::DestructuredParameter).getChild(i) or - result = toTreeSitter(this).(Generated::DestructuredLeftAssignment).getChild(i) + result = toGenerated(this).(Generated::DestructuredLeftAssignment).getChild(i) or - result = toTreeSitter(this).(Generated::LeftAssignmentList).getChild(i) + result = toGenerated(this).(Generated::LeftAssignmentList).getChild(i) } /** Gets the `i`th pattern in this tuple pattern. */ final Pattern getElement(int i) { exists(Generated::AstNode c | c = this.getChild(i) | - toTreeSitter(result) = c.(Generated::RestAssignment).getChild() + toGenerated(result) = c.(Generated::RestAssignment).getChild() or - toTreeSitter(result) = c + toGenerated(result) = c ) } diff --git a/ql/src/codeql_ruby/ast/Scope.qll b/ql/src/codeql_ruby/ast/Scope.qll index 79381b133c3..36b23e880e5 100644 --- a/ql/src/codeql_ruby/ast/Scope.qll +++ b/ql/src/codeql_ruby/ast/Scope.qll @@ -6,16 +6,16 @@ private import internal.TreeSitter class Scope extends AstNode, TScopeType { private Scope::Range range; - Scope() { range = toTreeSitter(this) } + Scope() { range = toGenerated(this) } /** Gets the enclosing module, if any. */ - ModuleBase getEnclosingModule() { toTreeSitter(result) = range.getEnclosingModule() } + ModuleBase getEnclosingModule() { toGenerated(result) = range.getEnclosingModule() } /** Gets the enclosing method, if any. */ - MethodBase getEnclosingMethod() { toTreeSitter(result) = range.getEnclosingMethod() } + MethodBase getEnclosingMethod() { toGenerated(result) = range.getEnclosingMethod() } /** Gets the scope in which this scope is nested, if any. */ - Scope getOuterScope() { toTreeSitter(result) = range.getOuterScope() } + Scope getOuterScope() { toGenerated(result) = range.getOuterScope() } /** Gets a variable that is declared in this scope. */ final Variable getAVariable() { result.getDeclaringScope() = this } diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index 94f1b49ec47..fcc2ac8c09f 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -15,7 +15,7 @@ class Stmt extends AstNode, TStmt { CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } /** Gets the control-flow scope of this statement, if any. */ - CfgScope getCfgScope() { result = getCfgScope(toTreeSitter(this)) } + CfgScope getCfgScope() { result = getCfgScope(toGenerated(this)) } /** Gets the enclosing callable, if any. */ Callable getEnclosingCallable() { result = this.getCfgScope() } @@ -59,7 +59,7 @@ class BeginBlock extends StmtSequence, TBeginBlock { final override string toString() { result = "BEGIN { ... }" } - final override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -77,7 +77,7 @@ class EndBlock extends StmtSequence, TEndBlock { final override string toString() { result = "END { ... }" } - final override Stmt getStmt(int n) { toTreeSitter(result) = g.getChild(n) } + final override Stmt getStmt(int n) { toGenerated(result) = g.getChild(n) } } /** @@ -94,7 +94,7 @@ class UndefStmt extends Stmt, TUndefStmt { UndefStmt() { this = TUndefStmt(g) } /** Gets the `n`th method name to undefine. */ - final MethodName getMethodName(int n) { toTreeSitter(result) = g.getChild(n) } + final MethodName getMethodName(int n) { toGenerated(result) = g.getChild(n) } /** Gets a method name to undefine. */ final MethodName getAMethodName() { result = getMethodName(_) } @@ -122,10 +122,10 @@ class AliasStmt extends Stmt, TAliasStmt { AliasStmt() { this = TAliasStmt(g) } /** Gets the new method name. */ - final MethodName getNewName() { toTreeSitter(result) = g.getName() } + final MethodName getNewName() { toGenerated(result) = g.getName() } /** Gets the original method name. */ - final MethodName getOldName() { toTreeSitter(result) = g.getAlias() } + final MethodName getOldName() { toGenerated(result) = g.getAlias() } final override string getAPrimaryQlClass() { result = "AliasStmt" } @@ -161,7 +161,7 @@ class ReturningStmt extends Stmt, TReturningStmt { /** Gets the returned value, if any. */ final Expr getValue() { - toTreeSitter(result) = + toGenerated(result) = any(Generated::AstNode res | exists(Generated::ArgumentList a, int c | a = this.getArgumentList() and c = count(a.getChild(_)) diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index de1b0c73941..bc2ab278e6c 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -22,7 +22,7 @@ class Variable extends TVariable { final Location getLocation() { result = range.getLocation() } /** Gets the scope this variable is declared in. */ - final Scope getDeclaringScope() { toTreeSitter(result) = range.getDeclaringScope() } + final Scope getDeclaringScope() { toGenerated(result) = range.getDeclaringScope() } /** Gets an access to this variable. */ VariableAccess getAnAccess() { result.getVariable() = this } @@ -94,7 +94,7 @@ class VariableAccess extends Expr, TVariableAccess { * both `a` and `b` are write accesses belonging to the same assignment. */ predicate isExplicitWrite(AstNode assignment) { - explicitWriteAccess(toTreeSitter(this), toTreeSitter(assignment)) + explicitWriteAccess(toGenerated(this), toGenerated(assignment)) } /** @@ -112,7 +112,7 @@ class VariableAccess extends Expr, TVariableAccess { * the access to `elements` in the parameter list is an implicit assignment, * as is the first access to `e`. */ - predicate isImplicitWrite() { implicitWriteAccess(toTreeSitter(this)) } + predicate isImplicitWrite() { implicitWriteAccess(toGenerated(this)) } } /** An access to a variable where the value is updated. */ diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index b3e73738845..23cabfa08ab 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -221,7 +221,7 @@ private module Cached { /** Gets the underlying TreeSitter entity for a given AST node. */ cached - Generated::AstNode toTreeSitter(AST::AstNode n) { + Generated::AstNode toGenerated(AST::AstNode n) { n = TAddExpr(result) or n = TAliasStmt(result) or n = TArgumentList(result) or @@ -367,7 +367,7 @@ private module Cached { import Cached -TAstNode fromTreeSitter(Generated::AstNode n) { n = toTreeSitter(result) } +TAstNode fromGenerated(Generated::AstNode n) { n = toGenerated(result) } class TCall = TMethodCall or TYieldCall; diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index ab9baf83cab..fcd1e428862 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -331,7 +331,7 @@ private module Cached { cached predicate isCapturedAccess(LocalVariableAccess access) { - toTreeSitter(access.getVariable().getDeclaringScope()) != scopeOf(toTreeSitter(access)) + toGenerated(access.getVariable().getDeclaringScope()) != scopeOf(toGenerated(access)) } cached @@ -398,7 +398,7 @@ module LocalVariable { final override Scope::Range getDeclaringScope() { result = scope } - final VariableAccess getDefiningAccess() { toTreeSitter(result) = i } + final VariableAccess getDefiningAccess() { toGenerated(result) = i } } } diff --git a/ql/src/codeql_ruby/controlflow/BasicBlocks.qll b/ql/src/codeql_ruby/controlflow/BasicBlocks.qll index 4356f167a1c..2926d00fe18 100644 --- a/ql/src/codeql_ruby/controlflow/BasicBlocks.qll +++ b/ql/src/codeql_ruby/controlflow/BasicBlocks.qll @@ -358,9 +358,9 @@ private module JoinBlockPredecessors { private predicate idOf(Generated::AstNode x, int y) = equivalenceRelation(id/2)(x, y) int getId(JoinBlockPredecessor jbp) { - idOf(toTreeSitter(jbp.getFirstNode().(AstCfgNode).getNode()), result) + idOf(toGenerated(jbp.getFirstNode().(AstCfgNode).getNode()), result) or - idOf(toTreeSitter(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 49f286b6951..e2a4badff91 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -67,7 +67,7 @@ class AstCfgNode extends CfgNode, TAstCfgNode { private Splits splits; private AstNode n; - AstCfgNode() { this = TAstCfgNode(toTreeSitter(n), splits) } + AstCfgNode() { this = TAstCfgNode(toGenerated(n), splits) } final override AstNode getNode() { result = n } @@ -132,7 +132,7 @@ abstract private class ExprChildMapping extends Expr { pragma[noinline] private BasicBlock getABasicBlockInScope() { - result.getANode() = TAstCfgNode(toTreeSitter(this.getAChildStar()), _) + result.getANode() = TAstCfgNode(toGenerated(this.getAChildStar()), _) } pragma[nomagic] diff --git a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll index b5b54e6ba0d..d1aca73b49f 100644 --- a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll +++ b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll @@ -11,13 +11,13 @@ private import internal.Completion /** An AST node with an associated control-flow graph. */ class CfgScope extends AST::AstNode { - CfgScope() { ASTInternal::toTreeSitter(this) instanceof CfgScope::Range_ } + CfgScope() { ASTInternal::toGenerated(this) instanceof CfgScope::Range_ } /** Gets the CFG scope that this scope is nested under, if any. */ final CfgScope getOuterCfgScope() { exists(AST::AstNode parent | parent = this.getParent() and - result = getCfgScope(ASTInternal::toTreeSitter(parent)) + result = getCfgScope(ASTInternal::toGenerated(parent)) ) } } diff --git a/ql/src/codeql_ruby/controlflow/internal/Completion.qll b/ql/src/codeql_ruby/controlflow/internal/Completion.qll index e0f7348e770..ed17cb5f125 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Completion.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Completion.qll @@ -206,7 +206,7 @@ private predicate inMatchingContext(AstNode n) { w.getPattern(_) = n ) or - toTreeSitter(n).(Trees::DefaultValueParameterTree).hasDefaultValue() + toGenerated(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 ea51daf0b06..89e3a604173 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -203,7 +203,7 @@ private predicate succImpl(AstNode pred, AstNode succ, Completion c) { } private predicate isHidden(ControlFlowTree t) { - not t = ASTInternal::toTreeSitter(_) + not t = ASTInternal::toGenerated(_) or t.isHidden() } @@ -301,7 +301,7 @@ private class ForRange extends AST::ForExpr { result = AST::ForExpr.super.getAChild(pred) or pred = "" and - result = ASTInternal::TForIn(ASTInternal::toTreeSitter(this).(For).getValue()) + result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(For).getValue()) } } @@ -309,7 +309,7 @@ private class ForRange extends AST::ForExpr { predicate isValidFor(Completion c, ControlFlowTree node) { c instanceof SimpleCompletion and isHidden(node) or - c.isValidFor(ASTInternal::fromTreeSitter(node)) + c.isValidFor(ASTInternal::fromGenerated(node)) } abstract private class StandardPreOrderTree extends StandardNode, PreOrderTree { @@ -1278,7 +1278,7 @@ cached private module Cached { /** Gets the CFG scope of node `n`. */ cached - CfgScope getCfgScope(AstNode n) { ASTInternal::toTreeSitter(result) = parent*(scopeOf(n)) } + CfgScope getCfgScope(AstNode n) { ASTInternal::toGenerated(result) = parent*(scopeOf(n)) } private predicate isAbnormalExitType(SuccessorType t) { t instanceof RaiseSuccessor or t instanceof ExitSuccessor @@ -1359,5 +1359,5 @@ import Cached /** An AST node that is split into multiple control flow nodes. */ class SplitAstNode extends AstNode { - SplitAstNode() { strictcount(CfgNode n | ASTInternal::toTreeSitter(n.getNode()) = this) > 1 } + SplitAstNode() { strictcount(CfgNode n | ASTInternal::toGenerated(n.getNode()) = this) > 1 } } diff --git a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll index ea7cd31ed24..e1cf24a2815 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll @@ -219,26 +219,26 @@ private module ConditionalCompletionSplitting { succ(pred, succ, c) and last(succ, _, completion) and ( - last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ).(AST::NotExpr).getOperand()), + last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ).(AST::NotExpr).getOperand()), pred, c) and completion.(BooleanCompletion).getDual() = c or - last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) .(AST::LogicalAndExpr) .getAnOperand()), pred, c) and completion = c or - last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) .(AST::LogicalOrExpr) .getAnOperand()), pred, c) and completion = c or - last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) .(AST::ParenthesizedExpr) .getLastExpr()), pred, c) and completion = c or - last(ASTInternal::toTreeSitter(ASTInternal::fromTreeSitter(succ) + last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) .(AST::ConditionalExpr) .getBranch(_)), pred, c) and completion = c @@ -255,7 +255,7 @@ private module ConditionalCompletionSplitting { override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesTo(last) and - succExit(ASTInternal::toTreeSitter(scope), last, c) and + succExit(ASTInternal::toGenerated(scope), last, c) and if c instanceof ConditionalCompletion then completion = c else any() } @@ -472,7 +472,7 @@ module EnsureSplitting { } override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { - succExit(ASTInternal::toTreeSitter(scope), last, c) and + succExit(ASTInternal::toGenerated(scope), last, c) and ( exit(_, last, c, _) or @@ -517,7 +517,7 @@ class Splits extends TSplits { private predicate succEntrySplitsFromRank(CfgScope pred, AstNode succ, Splits splits, int rnk) { splits = TSplitsNil() and - succEntry(ASTInternal::toTreeSitter(pred), succ) and + succEntry(ASTInternal::toGenerated(pred), succ) and rnk = 0 or exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | @@ -540,7 +540,7 @@ private predicate succEntrySplitsCons( pragma[noinline] predicate succEntrySplits(CfgScope pred, AstNode succ, Splits succSplits, SuccessorType t) { exists(int rnk | - succEntry(ASTInternal::toTreeSitter(pred), succ) and + succEntry(ASTInternal::toGenerated(pred), succ) and t instanceof NormalSuccessor and succEntrySplitsFromRank(pred, succ, succSplits, rnk) | @@ -559,7 +559,7 @@ predicate succExitSplits(AstNode last, Splits predSplits, CfgScope scope, Succes exists(Reachability::SameSplitsBlock b, Completion c | last = b.getANode() | b.isReachable(predSplits) and t = c.getAMatchingSuccessorType() and - succExit(ASTInternal::toTreeSitter(scope), last, c) and + succExit(ASTInternal::toGenerated(scope), last, c) and forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | predSplit.hasExitScope(scope, last, c) ) From 39aa2c6e5325b4c0c19b3cb905f1d47c9c3e0189 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 17 Mar 2021 14:27:21 +0100 Subject: [PATCH 19/39] Rework IPA injectors for constant accesses --- ql/src/codeql_ruby/ast/Constant.qll | 85 ++++++++++--------------- ql/src/codeql_ruby/ast/Pattern.qll | 4 +- ql/src/codeql_ruby/ast/internal/AST.qll | 36 +++++------ 3 files changed, 53 insertions(+), 72 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Constant.qll b/ql/src/codeql_ruby/ast/Constant.qll index 3ae3c1207b2..d8a5b9030a1 100644 --- a/ql/src/codeql_ruby/ast/Constant.qll +++ b/ql/src/codeql_ruby/ast/Constant.qll @@ -1,5 +1,6 @@ private import codeql_ruby.AST private import internal.AST +private import internal.Variable private import internal.TreeSitter /** An access to a constant. */ @@ -40,6 +41,27 @@ class ConstantAccess extends Expr, TConstantAccess { 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()) } +} + /** * A use (read) of a constant. * @@ -56,36 +78,17 @@ class ConstantAccess extends Expr, TConstantAccess { * end * ``` */ -class ConstantReadAccess extends ConstantAccess, TConstantReadAccess { - override Expr getScopeExpr() { none() } - - override predicate hasGlobalScope() { none() } +class ConstantReadAccess extends ConstantAccess { + 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" } } -private class TokenConstantReadAccess extends ConstantReadAccess, TTokenConstantReadAccess { - private Generated::Constant g; - - TokenConstantReadAccess() { this = TTokenConstantReadAccess(g) } - - final override string getName() { result = g.getValue() } -} - -private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, - TScopeResolutionConstantReadAccess { - private Generated::ScopeResolution g; - private Generated::Constant constant; - - ScopeResolutionConstantReadAccess() { this = TScopeResolutionConstantReadAccess(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()) } -} - /** * A definition of a constant. * @@ -102,32 +105,14 @@ private class ScopeResolutionConstantReadAccess extends ConstantReadAccess, * module M::Baz; end # defines constant Baz as a module in module M * ``` */ -class ConstantWriteAccess extends ConstantAccess, TConstantWriteAccess { +class ConstantWriteAccess extends ConstantAccess { + ConstantWriteAccess() { + explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace + } + override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } } -private class TokenConstantWriteAccess extends ConstantWriteAccess, TTokenConstantWriteAccess { - private Generated::Constant g; - - TokenConstantWriteAccess() { this = TTokenConstantWriteAccess(g) } - - final override string getName() { result = g.getValue() } -} - -private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, - TScopeResolutionConstantWriteAccess { - private Generated::ScopeResolution g; - private Generated::Constant constant; - - ScopeResolutionConstantWriteAccess() { this = TScopeResolutionConstantWriteAccess(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()) } -} - /** * A definition of a constant via assignment. For example, the left-hand * operand in the following example: @@ -136,6 +121,6 @@ private class ScopeResolutionConstantWriteAccess extends ConstantWriteAccess, * MAX_SIZE = 100 * ``` */ -class ConstantAssignment extends ConstantWriteAccess, LhsExpr, TConstantAssignment { +class ConstantAssignment extends ConstantWriteAccess, LhsExpr { override string getAPrimaryQlClass() { result = "ConstantAssignment" } } diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 39ddfbab383..8cf30a3ea4b 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -16,7 +16,9 @@ class Pattern extends AstNode { Variable getAVariable() { none() } } -private class LhsExpr_ = TVariableAccess or TConstantAssignment or TMethodCall or TSimpleParameter; +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 diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index 23cabfa08ab..6cf9696c510 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -162,15 +162,16 @@ private module Cached { TRescueModifierExpr(Generated::RescueModifier g) or TRetryStmt(Generated::Retry g) or TReturnStmt(Generated::Return g) or - TScopeResolutionConstantReadAccess(Generated::ScopeResolution g, 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. + TScopeResolutionConstantAccess(Generated::ScopeResolution g, Generated::Constant constant) { constant = g.getName() and - vcall(g) - } or - TScopeResolutionConstantWriteAccess(Generated::ScopeResolution g, Generated::Constant constant) { - explicitAssignmentNode(g, _) and constant = g.getName() + ( + // 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 @@ -196,12 +197,13 @@ private module Cached { TSymbolArrayLiteral(Generated::SymbolArray g) or TTernaryIfExpr(Generated::Conditional g) or TThen(Generated::Then g) or - TTokenConstantReadAccess(Generated::Constant g) { + 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 - TTokenConstantWriteAccess(Generated::Constant g) { 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 @@ -323,8 +325,7 @@ private module Cached { n = TRescueModifierExpr(result) or n = TRetryStmt(result) or n = TReturnStmt(result) or - n = TScopeResolutionConstantReadAccess(result, _) or - n = TScopeResolutionConstantWriteAccess(result, _) or + n = TScopeResolutionConstantAccess(result, _) or n = TScopeResolutionMethodCall(result, _) or n = TSelf(result) or n = TSimpleParameter(result) or @@ -344,8 +345,7 @@ private module Cached { n = TSymbolArrayLiteral(result) or n = TTernaryIfExpr(result) or n = TThen(result) or - n = TTokenConstantReadAccess(result) or - n = TTokenConstantWriteAccess(result) or + n = TTokenConstantAccess(result) or n = TTokenMethodName(result) or n = TTokenSuperCall(result) or n = TToplevel(result) or @@ -377,13 +377,7 @@ class TMethodCall = class TSuperCall = TTokenSuperCall or TRegularSuperCall; -class TConstantAccess = TConstantReadAccess or TConstantWriteAccess; - -class TConstantReadAccess = TTokenConstantReadAccess or TScopeResolutionConstantReadAccess; - -class TConstantWriteAccess = TConstantAssignment or TNamespace; - -class TConstantAssignment = TTokenConstantWriteAccess or TScopeResolutionConstantWriteAccess; +class TConstantAccess = TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace; class TControlExpr = TConditionalExpr or TCaseExpr or TLoop; From 26c251f08060c6ac8eb8b3f7dc8cd954f6b88331 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Wed, 17 Mar 2021 19:05:41 +0000 Subject: [PATCH 20/39] Order CFG nodes by column as well --- .../codeql_ruby/controlflow/internal/Cfg.ql | 2 +- .../controlflow/graph/Cfg.expected | 326 +++++++++--------- 2 files changed, 164 insertions(+), 164 deletions(-) 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/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index f106b06d2d8..028f0d08b9f 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -8,9 +8,6 @@ break_ensure.rb: # 1| m1 #-----| -> m2 -# 1| elements -#-----| -> elements - # 1| exit m1 # 1| exit break_ensure.rb @@ -21,6 +18,9 @@ break_ensure.rb: # 1| exit break_ensure.rb (normal) #-----| -> exit break_ensure.rb +# 1| elements +#-----| -> elements + # 2| for ... in ... #-----| -> ensure ... @@ -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) @@ -156,14 +156,14 @@ break_ensure.rb: # 27| m3 #-----| -> m4 -# 27| elements -#-----| -> elements - # 27| exit m3 # 27| exit m3 (normal) #-----| -> exit m3 +# 27| elements +#-----| -> elements + # 29| if ... #-----| -> ensure ... @@ -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) @@ -508,18 +508,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 +534,6 @@ cfg.rb: # 25| x #-----| -> call to puts -# 25| exit { ... } - -# 25| exit { ... } (normal) -#-----| -> exit { ... } - # 27| call to puts #-----| -> Proc @@ -543,18 +543,23 @@ cfg.rb: # 27| :puts #-----| -> &... -# 29| enter { ... } -#-----| -> x - # 29| call to new #-----| -> true # 29| Proc #-----| -> { ... } +# 29| enter { ... } +#-----| -> x + # 29| { ... } #-----| -> call to new +# 29| exit { ... } + +# 29| exit { ... } (normal) +#-----| -> exit { ... } + # 29| x #-----| -> x @@ -564,11 +569,6 @@ cfg.rb: # 29| x #-----| -> call to call -# 29| exit { ... } - -# 29| exit { ... } (normal) -#-----| -> exit { ... } - # 31| while ... #-----| -> false @@ -811,6 +811,11 @@ cfg.rb: # 63| pattern #-----| -> 1 +# 63| exit pattern + +# 63| exit pattern (normal) +#-----| -> exit pattern + # 63| (..., ...) #-----| -> a @@ -820,11 +825,6 @@ cfg.rb: # 63| b #-----| -> (..., ...) -# 63| exit pattern - -# 63| exit pattern (normal) -#-----| -> exit pattern - # 64| call to puts #-----| -> b @@ -1086,6 +1086,11 @@ cfg.rb: # 101| parameters #-----| -> "healthy" +# 101| exit parameters + +# 101| exit parameters (normal) +#-----| -> exit parameters + # 101| value #-----| no-match -> 42 #-----| match -> key @@ -1099,11 +1104,6 @@ cfg.rb: # 101| kwargs #-----| -> value -# 101| exit parameters - -# 101| exit parameters (normal) -#-----| -> exit parameters - # 102| call to puts #-----| -> kwargs @@ -1201,18 +1201,23 @@ cfg.rb: # 117| 10 #-----| -> @@static_field -# 120| enter -> { ... } -#-----| -> x - # 120| ... = ... #-----| -> M # 120| swap #-----| -> ... = ... +# 120| enter -> { ... } +#-----| -> x + # 120| -> { ... } #-----| -> swap +# 120| exit -> { ... } + +# 120| exit -> { ... } (normal) +#-----| -> exit -> { ... } + # 120| (..., ...) #-----| -> y @@ -1231,11 +1236,6 @@ cfg.rb: # 120| x #-----| -> [...] -# 120| exit -> { ... } - -# 120| exit -> { ... } (normal) -#-----| -> exit -> { ... } - # 122| M #-----| -> nil @@ -1461,17 +1461,17 @@ cfg.rb: # 149| method #-----| -> two_parameters +# 149| exit method + +# 149| exit method (normal) +#-----| -> exit method + # 149| silly #-----| -> method # 149| x #-----| -> x -# 149| exit method - -# 149| exit method (normal) -#-----| -> exit method - # 150| call to puts #-----| -> exit method (normal) @@ -1484,17 +1484,17 @@ cfg.rb: # 153| two_parameters #-----| -> 1 +# 153| exit two_parameters + +# 153| exit two_parameters (normal) +#-----| -> exit two_parameters + # 153| a #-----| -> b # 153| b #-----| -> exit two_parameters (normal) -# 153| exit two_parameters - -# 153| exit two_parameters (normal) -#-----| -> exit two_parameters - # 155| call to two_parameters #-----| -> call to __FILE__ @@ -1801,15 +1801,20 @@ cfg.rb: # 188| 42 -# 191| enter { ... } -#-----| -> x - # 191| call to run_block #-----| -> exit cfg.rb (normal) +# 191| enter { ... } +#-----| -> x + # 191| { ... } #-----| -> call to run_block +# 191| exit { ... } + +# 191| exit { ... } (normal) +#-----| -> exit { ... } + # 191| x #-----| -> x @@ -1819,11 +1824,6 @@ cfg.rb: # 191| x #-----| -> call to puts -# 191| exit { ... } - -# 191| exit { ... } (normal) -#-----| -> exit { ... } - exit.rb: # 1| enter m1 #-----| -> x @@ -1834,9 +1834,6 @@ exit.rb: # 1| m1 #-----| -> m2 -# 1| x -#-----| -> x - # 1| exit m1 # 1| exit exit.rb @@ -1850,6 +1847,9 @@ exit.rb: # 1| exit exit.rb (normal) #-----| -> exit exit.rb +# 1| x +#-----| -> x + # 2| if ... #-----| -> "x <= 2" @@ -1881,9 +1881,6 @@ exit.rb: # 8| m2 #-----| -> exit exit.rb (normal) -# 8| x -#-----| -> x - # 8| exit m2 # 8| exit m2 (abnormal) @@ -1892,6 +1889,9 @@ exit.rb: # 8| exit m2 (normal) #-----| -> exit m2 +# 8| x +#-----| -> x + # 9| if ... #-----| -> "x <= 2" @@ -1956,9 +1956,6 @@ ifs.rb: # 1| m1 #-----| -> m2 -# 1| x -#-----| -> x - # 1| exit m1 # 1| exit ifs.rb @@ -1969,6 +1966,9 @@ ifs.rb: # 1| exit ifs.rb (normal) #-----| -> exit ifs.rb +# 1| x +#-----| -> x + # 2| if ... #-----| -> exit m1 (normal) @@ -2063,14 +2063,14 @@ ifs.rb: # 11| m2 #-----| -> m3 -# 11| b -#-----| -> b - # 11| exit m2 # 11| exit m2 (normal) #-----| -> exit m2 +# 11| b +#-----| -> b + # 12| if ... #-----| -> 1 @@ -2096,14 +2096,14 @@ ifs.rb: # 18| m3 #-----| -> m4 -# 18| x -#-----| -> x - # 18| exit m3 # 18| exit m3 (normal) #-----| -> exit m3 +# 18| x +#-----| -> x + # 19| if ... #-----| -> x @@ -2169,6 +2169,11 @@ ifs.rb: # 28| m4 #-----| -> m5 +# 28| exit m4 + +# 28| exit m4 (normal) +#-----| -> exit m4 + # 28| b1 #-----| -> b2 @@ -2178,11 +2183,6 @@ ifs.rb: # 28| b3 #-----| -> b1 -# 28| exit m4 - -# 28| exit m4 (normal) -#-----| -> exit m4 - # 29| return #-----| return -> exit m4 (normal) @@ -2225,6 +2225,11 @@ ifs.rb: # 32| m5 #-----| -> 1 +# 32| exit m5 + +# 32| exit m5 (normal) +#-----| -> exit m5 + # 32| b1 #-----| -> b2 @@ -2240,11 +2245,6 @@ ifs.rb: # 32| b5 #-----| -> b1 -# 32| exit m5 - -# 32| exit m5 (normal) -#-----| -> exit m5 - # 33| if ... #-----| -> exit m5 (normal) @@ -2352,9 +2352,6 @@ loops.rb: # 1| m1 #-----| -> m2 -# 1| x -#-----| -> x - # 1| exit m1 # 1| exit loops.rb @@ -2365,6 +2362,9 @@ loops.rb: # 1| exit loops.rb (normal) #-----| -> exit loops.rb +# 1| x +#-----| -> x + # 2| while ... #-----| -> exit m1 (normal) @@ -2399,14 +2399,14 @@ loops.rb: # 8| m2 #-----| -> m3 -# 8| x -#-----| -> x - # 8| exit m2 # 8| exit m2 (normal) #-----| -> exit m2 +# 8| x +#-----| -> x + # 9| while ... #-----| -> "Done" @@ -2506,9 +2506,6 @@ loops.rb: # 24| exit m3 (normal) #-----| -> exit m3 -# 25| enter do ... end -#-----| -> x - # 25| call to each #-----| -> exit m3 (normal) @@ -2524,17 +2521,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 +2548,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 +2568,6 @@ raise.rb: # 7| m1 #-----| -> m2 -# 7| x -#-----| -> x - # 7| exit m1 # 7| exit m1 (abnormal) @@ -2579,6 +2576,9 @@ raise.rb: # 7| exit m1 (normal) #-----| -> exit m1 +# 7| x +#-----| -> x + # 8| if ... #-----| -> "x <= 2" @@ -2610,9 +2610,6 @@ raise.rb: # 14| m2 #-----| -> m3 -# 14| b -#-----| -> b - # 14| exit m2 # 14| exit m2 (abnormal) @@ -2621,6 +2618,9 @@ raise.rb: # 14| exit m2 (normal) #-----| -> exit m2 +# 14| b +#-----| -> b + # 16| if ... #-----| -> "End m2" @@ -2659,14 +2659,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 +2701,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 +2746,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 +2785,6 @@ raise.rb: # 57| m6 #-----| -> m7 -# 57| b -#-----| -> b - # 57| exit m6 # 57| exit m6 (abnormal) @@ -2796,6 +2793,9 @@ raise.rb: # 57| exit m6 (normal) #-----| -> exit m6 +# 57| b +#-----| -> b + # 59| if ... #-----| -> "End m6" @@ -2841,9 +2841,6 @@ raise.rb: # 68| m7 #-----| -> m8 -# 68| x -#-----| -> x - # 68| exit m7 # 68| exit m7 (abnormal) @@ -2852,6 +2849,9 @@ raise.rb: # 68| exit m7 (normal) #-----| -> exit m7 +# 68| x +#-----| -> x + # 69| if ... #-----| -> "0 <= x <= 2" @@ -2929,9 +2929,6 @@ raise.rb: # 79| m8 #-----| -> m9 -# 79| x -#-----| -> "Begin m8" - # 79| exit m8 # 79| exit m8 (abnormal) @@ -2940,6 +2937,9 @@ raise.rb: # 79| exit m8 (normal) #-----| -> exit m8 +# 79| x +#-----| -> "Begin m8" + # 80| call to puts #-----| -> x @@ -3029,6 +3029,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 +3046,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 @@ -3294,6 +3294,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 +3312,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 +3327,6 @@ raise.rb: # 128| m11 #-----| -> m12 -# 128| b -#-----| -> b - # 128| exit m11 # 128| exit m11 (abnormal) @@ -3338,6 +3335,9 @@ raise.rb: # 128| exit m11 (normal) #-----| -> exit m11 +# 128| b +#-----| -> b + # 130| if ... #-----| -> ensure ... @@ -3401,14 +3401,14 @@ raise.rb: # 142| m12 #-----| -> m13 -# 142| b -#-----| -> b - # 142| exit m12 # 142| exit m12 (normal) #-----| -> exit m12 +# 142| b +#-----| -> b + # 143| if ... #-----| -> ensure ... @@ -3460,16 +3460,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,9 +3474,20 @@ raise.rb: # 155| element #-----| -> { ... } +# 155| enter { ... } +#-----| -> elem + # 155| { ... } #-----| -> call to each +# 155| exit { ... } + +# 155| exit { ... } (abnormal) +#-----| -> exit { ... } + +# 155| exit { ... } (normal) +#-----| -> exit { ... } + # 155| elem #-----| -> element @@ -3498,11 +3506,3 @@ raise.rb: # 155| element #-----| -> call to nil? - -# 155| exit { ... } - -# 155| exit { ... } (abnormal) -#-----| -> exit { ... } - -# 155| exit { ... } (normal) -#-----| -> exit { ... } From 32e2b257bff417a82e27654d11a2982f94253d8b Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Mon, 15 Mar 2021 18:43:41 +0000 Subject: [PATCH 21/39] Port CFG implementation to public AST interface --- ql/src/codeql_ruby/ast/Control.qll | 27 + ql/src/codeql_ruby/ast/Method.qll | 2 +- ql/src/codeql_ruby/ast/Module.qll | 3 +- ql/src/codeql_ruby/ast/Statement.qll | 2 +- ql/src/codeql_ruby/controlflow/CfgNodes.qll | 4 +- .../controlflow/ControlFlowGraph.qll | 13 +- .../controlflow/internal/AstNodes.qll | 149 -- .../controlflow/internal/Completion.qll | 4 +- .../internal/ControlFlowGraphImpl.qll | 1669 +++++++++-------- .../controlflow/internal/Splitting.qll | 58 +- .../codeql_ruby/dataflow/internal/SsaImpl.qll | 2 +- ql/test/library-tests/ast/calls/calls.ql | 1 - .../ast/modules/toplevel.expected | 2 +- .../controlflow/graph/Cfg.expected | 205 +- 14 files changed, 1052 insertions(+), 1089 deletions(-) delete mode 100644 ql/src/codeql_ruby/controlflow/internal/AstNodes.qll diff --git a/ql/src/codeql_ruby/ast/Control.qll b/ql/src/codeql_ruby/ast/Control.qll index 69588659002..de63484c38d 100644 --- a/ql/src/codeql_ruby/ast/Control.qll +++ b/ql/src/codeql_ruby/ast/Control.qll @@ -440,6 +440,9 @@ class ConditionalLoop extends Loop, TConditionalLoop { or pred = "getCondition" and result = this.getCondition() } + + /** Holds if the loop body is entered when the condition is `condValue`. */ + predicate entersLoopWhenConditionIs(boolean condValue) { none() } } /** @@ -463,6 +466,12 @@ class WhileExpr extends ConditionalLoop, TWhileExpr { 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 ..." } } @@ -487,6 +496,12 @@ class UntilExpr extends ConditionalLoop, TUntilExpr { 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 ..." } } @@ -505,6 +520,12 @@ class WhileModifierExpr extends ConditionalLoop, TWhileModifierExpr { 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 ..." } @@ -525,6 +546,12 @@ class UntilModifierExpr extends ConditionalLoop, TUntilModifierExpr { 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 ..." } diff --git a/ql/src/codeql_ruby/ast/Method.qll b/ql/src/codeql_ruby/ast/Method.qll index 3836bee596b..7c205f174ab 100644 --- a/ql/src/codeql_ruby/ast/Method.qll +++ b/ql/src/codeql_ruby/ast/Method.qll @@ -4,7 +4,7 @@ private import internal.AST private import internal.TreeSitter /** A callable. */ -class Callable extends Expr, CfgScope, TCallable { +class Callable extends Expr, TCallable { /** Gets the number of parameters of this callable. */ final int getNumberOfParameters() { result = count(this.getAParameter()) } diff --git a/ql/src/codeql_ruby/ast/Module.qll b/ql/src/codeql_ruby/ast/Module.qll index 568a5d64cac..0eff778dbb8 100644 --- a/ql/src/codeql_ruby/ast/Module.qll +++ b/ql/src/codeql_ruby/ast/Module.qll @@ -47,7 +47,8 @@ class Toplevel extends ModuleBase, TToplevel { * Gets the `n`th `BEGIN` block. */ final BeginBlock getBeginBlock(int n) { - toGenerated(result) = rank[n](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i) + toGenerated(result) = + rank[n + 1](int i, Generated::BeginBlock b | b = g.getChild(i) | b order by i) } /** diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index fcc2ac8c09f..c75d4e07221 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -15,7 +15,7 @@ class Stmt extends AstNode, TStmt { CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } /** Gets the control-flow scope of this statement, if any. */ - CfgScope getCfgScope() { result = getCfgScope(toGenerated(this)) } + CfgScope getCfgScope() { result = getCfgScope(this) } /** Gets the enclosing callable, if any. */ Callable getEnclosingCallable() { result = this.getCfgScope() } diff --git a/ql/src/codeql_ruby/controlflow/CfgNodes.qll b/ql/src/codeql_ruby/controlflow/CfgNodes.qll index e2a4badff91..d67849934fc 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -67,7 +67,7 @@ class AstCfgNode extends CfgNode, TAstCfgNode { private Splits splits; private AstNode n; - AstCfgNode() { this = TAstCfgNode(toGenerated(n), splits) } + AstCfgNode() { this = TAstCfgNode(n, splits) } final override AstNode getNode() { result = n } @@ -132,7 +132,7 @@ abstract private class ExprChildMapping extends Expr { pragma[noinline] private BasicBlock getABasicBlockInScope() { - result.getANode() = TAstCfgNode(toGenerated(this.getAChildStar()), _) + result.getANode() = TAstCfgNode(this.getAChildStar(), _) } pragma[nomagic] diff --git a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll index d1aca73b49f..30e75576397 100644 --- a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll +++ b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll @@ -1,8 +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.internal.AST as ASTInternal +private import codeql_ruby.AST private import codeql_ruby.controlflow.BasicBlocks private import SuccessorTypes private import internal.ControlFlowGraphImpl @@ -10,14 +9,14 @@ private import internal.Splitting private import internal.Completion /** An AST node with an associated control-flow graph. */ -class CfgScope extends AST::AstNode { - CfgScope() { ASTInternal::toGenerated(this) instanceof CfgScope::Range_ } +class CfgScope extends AstNode { + 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(ASTInternal::toGenerated(parent)) + result = getCfgScope(parent) ) } } @@ -35,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/Completion.qll b/ql/src/codeql_ruby/controlflow/internal/Completion.qll index ed17cb5f125..b25098b2a06 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Completion.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Completion.qll @@ -49,7 +49,7 @@ 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] @@ -206,7 +206,7 @@ private predicate inMatchingContext(AstNode n) { w.getPattern(_) = n ) or - toGenerated(n).(Trees::DefaultValueParameterTree).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 89e3a604173..c864d1aee40 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -31,18 +31,16 @@ * caught up by its surrounding loop and turned into a `NormalCompletion`. */ -private import codeql_ruby.AST as AST +private import codeql_ruby.AST private import codeql_ruby.ast.internal.AST as ASTInternal private import codeql_ruby.ast.internal.Scope private import codeql_ruby.ast.Scope -private import codeql_ruby.ast.internal.TreeSitter::Generated -private import AstNodes +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 { @@ -51,22 +49,12 @@ module CfgScope { abstract predicate exit(AstNode last, Completion c); } - private class ProgramScope extends Range_, Program { + private class ProgramScope 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) @@ -77,80 +65,50 @@ 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 MethodScope extends Range_, Method { + 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 } - - final override predicate entry(AstNode first) { - this.(Trees::RescueEnsureBlockTree).firstInner(first) - } + private class SingletonMethodScope extends Range_, SingletonMethod { + 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 DoBlockScope extends Range_, DoBlock { - DoBlockScope() { not this.getParent() instanceof Lambda } - - final override predicate entry(AstNode first) { - this.(Trees::RescueEnsureBlockTree).firstInner(first) - } + 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 BlockScope extends Range_, Block { - BlockScope() { not this.getParent() instanceof Lambda } - + private class BraceBlockScope extends Range_, BraceBlock { final override predicate entry(AstNode first) { - first(this.(Trees::BlockTree).getFirstChildNode(), first) + first(this.(Trees::BraceBlockTree).getFirstChildNode(), first) } final override predicate exit(AstNode last, Completion c) { - last(this.(Trees::BlockTree).getLastChildNode(), last, c) + last(this.(Trees::BraceBlockTree).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 entry(AstNode first) { this.(Trees::BodyStmtTree).firstInner(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) + this.(Trees::BodyStmtTree).lastInner(last, c) } } } -abstract private class ControlFlowTree extends AstNode { +abstract /*private*/ class ControlFlowTree extends AstNode { /** * Holds if `first` is the first element executed within this AST node. */ @@ -202,11 +160,7 @@ private predicate succImpl(AstNode pred, AstNode succ, Completion c) { any(ControlFlowTree cft).succ(pred, succ, c) } -private predicate isHidden(ControlFlowTree t) { - not t = ASTInternal::toGenerated(_) - or - t.isHidden() -} +private predicate isHidden(ControlFlowTree t) { t.isHidden() } private predicate succImplIfHidden(AstNode pred, AstNode succ) { isHidden(pred) and @@ -254,10 +208,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) @@ -291,17 +242,21 @@ abstract private class PreOrderTree extends ControlFlowTree { } // TODO: remove this class; it should be replaced with an implicit non AST node -private class ForIn extends AST::AstNode, ASTInternal::TForIn { +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 -private class ForRange extends AST::ForExpr { - override AST::AstNode getAChild(string pred) { - result = AST::ForExpr.super.getAChild(pred) +private class ForRange extends ForExpr { + override AstNode getAChild(string pred) { + result = ForExpr.super.getAChild(pred) or pred = "" and - result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(For).getValue()) + result = this.getIn() + } + + ForIn getIn() { + result = ASTInternal::TForIn(ASTInternal::toGenerated(this).(Generated::For).getValue()) } } @@ -309,7 +264,7 @@ private class ForRange extends AST::ForExpr { predicate isValidFor(Completion c, ControlFlowTree node) { c instanceof SimpleCompletion and isHidden(node) or - c.isValidFor(ASTInternal::fromGenerated(node)) + c.isValidFor(node) } abstract private class StandardPreOrderTree extends StandardNode, PreOrderTree { @@ -337,53 +292,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 or @splat_parameter or - @hash_splat_parameter or @block_parameter; - -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 or - this instanceof SplatParameter or - this instanceof HashSplatParameter or - this instanceof BlockParameter - } -} - -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) @@ -417,590 +325,142 @@ 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 - } + private class ArgumentListTree extends StandardPostOrderTree, ArgumentList { + final override ControlFlowTree getChildNode(int i) { result = this.getElement(i) } + override predicate isHidden() { any() } + } + + private class ArrayLiteralTree extends StandardPostOrderTree, ArrayLiteral { + final override ControlFlowTree getChildNode(int i) { result = this.getElement(i) } + } + + private class AssignOperationTree extends StandardPostOrderTree, AssignOperation { + final override ControlFlowTree getChildNode(int i) { + result = this.getLeftOperand() and i = 0 + or + result = this.getRightOperand() and i = 1 + } + } + + private class AssignmentTree extends StandardPostOrderTree, AssignExpr { + final override ControlFlowTree getChildNode(int i) { + result = this.getRightOperand() and i = 0 + or + result = this.getLeftOperand() and i = 1 + } + } + + private class BeginTree extends BodyStmtTree, PreOrderTree, BeginExpr { final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } override predicate isHidden() { any() } } - class BeginBlockTree extends ScopeTree, BeginBlock { - final override ControlFlowTree getChildNode(int i) { result = this.getChild(i) } - } + private class BinaryOperationTree extends StandardPostOrderTree, BinaryOperation { + // Logical AND and OR are handled separately + BinaryOperationTree() { not this instanceof BinaryLogicalOperation } - class BlockTree extends ScopeTree, Block { 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 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(_)) - 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 - ) - } + private class BlockArgumentTree extends StandardPostOrderTree, BlockArgument { + final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - 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 ControlFlowTree { - abstract AstNode getDefaultValue(); - - abstract AstNode getAccessNode(); - - predicate hasDefaultValue() { exists(this.getDefaultValue()) } - - final override predicate propagatesAbnormal(AstNode child) { - child = this.getDefaultValue() or child = this.getAccessNode() - } - - final override predicate first(AstNode first) { first = this.getAccessNode() } - - final override predicate last(AstNode last, Completion c) { - last(this.getDefaultValue(), 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.getDefaultValue(), succ) and - c.(MatchingCompletion).getValue() = false + private class BlockParameterTree extends StandardPostOrderTree, BlockParameter { + final override ControlFlowTree getChildNode(int i) { + result = this.getVariable().getDefiningAccess() and i = 0 } } - 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) - ) - } - - override predicate isHidden() { any() } - } - - private class FalseTree extends LeafTree, False { } - - private class FloatTree extends LeafTree, Float { } - /** - * 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 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() } - - final override AstNode getAccessNode() { result = this.getName() } - } - - 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().(AstNode), 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() } - - final override AstNode getAccessNode() { result = this.getName() } - } - - 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) } /** @@ -1048,6 +508,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 @@ -1056,83 +550,472 @@ 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 { + 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 BodyStmtTree, PreOrderTree, 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 + } + + final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + } + + 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 BodyStmtTree, PostOrderTree, DoBlock { + final override predicate first(AstNode first) { first = this } + + /** 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 = BodyStmtTree.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 StandardPostOrderTree, HashSplatParameter { + final override ControlFlowTree getChildNode(int i) { + result = this.getVariable().getDefiningAccess() and i = 0 + } + } + + 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 BodyStmtTree, PostOrderTree, Lambda { + final override predicate first(AstNode first) { first = this } + + /** 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 = BodyStmtTree.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 { } + + private class MethodTree extends BodyStmtTree, PostOrderTree, Method { + final override predicate first(AstNode first) { first = this } + + /** 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 = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + } + } + + private class ModuleTree extends BodyStmtTree, PreOrderTree, 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 = BodyStmtTree.super.getBodyChild(i - count(this.getScopeExpr()), rescuable) + } + + final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + } + + 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() } + } + + /** Contains partridges */ + 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 { } + + /** A block that may contain `rescue`/`ensure`. */ + private class RescueModifierTree extends PreOrderTree, RescueModifierExpr { final override predicate propagatesAbnormal(AstNode child) { child = this.getHandler() } final override predicate last(AstNode last, Completion c) { @@ -1153,56 +1036,277 @@ module Trees { } } + private class RescueTree extends PreOrderTree, RescueClause { + final override predicate propagatesAbnormal(AstNode child) { child = this.getAnException() } + + private Expr getLastException() { + exists(int i | result = this.getException(i) and not exists(this.getException(i + 1))) + } + + predicate lastMatch(AstNode last, Completion c) { + last(this.getBody(), last, c) + or + not exists(this.getBody()) and + ( + last(this.getVariableExpr(), last, c) + or + 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) + ) + ) + } + + predicate lastNoMatch(AstNode last, Completion c) { + last(this.getLastException(), 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.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 SetterTree extends LeafTree, Setter { } + private class SimpleParameterTree extends LeafTree, SimpleParameter { } - private class SingletonClassTree extends RescueEnsureBlockTree, PreOrderTree, SingletonClass { - final override AstNode getChildNode(int i, boolean rescuable) { - rescuable = true and + /** + * 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() { + // TODO: unhide, or avoid using Generated types + ASTInternal::toGenerated(this) instanceof Generated::Else or + ASTInternal::toGenerated(this) instanceof Generated::Then or + ASTInternal::toGenerated(this) instanceof Generated::Do + } + + 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) { + StmtSequenceTree.super.succ(pred, succ, c) + } + } + + private class SingletonClassTree extends BodyStmtTree, PreOrderTree, 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 + result = this.getValue() and i = 0 and rescuable = false or - result = this.getChild(i - 1) + result = BodyStmtTree.super.getBodyChild(i - 1, rescuable) ) } 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 + private class SingletonMethodTree extends BodyStmtTree, PostOrderTree, 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 = this.getChild(i - 1) and - rescuable = true + result = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) } final override predicate first(AstNode first) { first(this.getObject(), first) } override predicate succ(AstNode pred, AstNode succ, Completion c) { - RescueEnsureBlockTree.super.succ(pred, succ, c) + BodyStmtTree.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 SuperTree extends LeafTree, Super { } + private class SplatArgumentTree extends StandardPostOrderTree, SplatArgument { + final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } + } - private class TrueTree extends LeafTree, True { } + private class SplatParameterTree extends StandardPostOrderTree, SplatParameter { + final override ControlFlowTree getChildNode(int i) { + result = this.getVariable().getDefiningAccess() and i = 0 + } + } - private class WhenTree extends PreOrderTree, When { - final override predicate propagatesAbnormal(AstNode child) { child = this.getPattern(_) } + abstract class StmtSequenceTree extends ControlFlowTree, StmtSequence { + override predicate propagatesAbnormal(AstNode child) { none() } - final Pattern getLastPattern() { + /** 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 + } + + 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) + 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 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 BodyStmtTree, PreOrderTree, Toplevel { + final override AstNode getBodyChild(int i, boolean rescuable) { + result = this.getBeginBlock(i) and rescuable = true + or + result = BodyStmtTree.super.getBodyChild(i - count(this.getABeginBlock()), rescuable) + } + + final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } + + 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)) @@ -1221,7 +1325,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() @@ -1235,41 +1339,20 @@ module Trees { } } - private class ConditionalLoopTree extends PostOrderTree, ConditionalLoopAstNode { - final override predicate propagatesAbnormal(AstNode child) { child = this.getConditionNode() } + // TODO: make post-order + private class YieldCallTree extends StandardPreOrderTree, YieldCall { + final override ControlFlowTree getChildNode(int i) { result = this.getArgument(i) } + } - 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()) - ) - } + /** 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::Range parent(Scope::Range n) { +private Scope parent(Scope n) { result = n.getOuterScope() and not n instanceof CfgScope::Range_ } @@ -1278,7 +1361,9 @@ cached private module Cached { /** Gets the CFG scope of node `n`. */ cached - CfgScope getCfgScope(AstNode n) { ASTInternal::toGenerated(result) = parent*(scopeOf(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 @@ -1359,5 +1444,5 @@ import Cached /** An AST node that is split into multiple control flow nodes. */ class SplitAstNode extends AstNode { - SplitAstNode() { strictcount(CfgNode n | ASTInternal::toGenerated(n.getNode()) = this) > 1 } + SplitAstNode() { strictcount(CfgNode n | n.getNode() = this) > 1 } } diff --git a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll index e1cf24a2815..7a81548ed5d 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll @@ -2,9 +2,7 @@ * Provides classes and predicates relevant for splitting the control flow graph. */ -private import codeql_ruby.ast.internal.TreeSitter::Generated -private import codeql_ruby.ast.internal.AST as ASTInternal -private import codeql_ruby.AST as AST +private import codeql_ruby.AST private import Completion private import ControlFlowGraphImpl private import SuccessorTypes @@ -18,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 @@ -219,28 +217,19 @@ private module ConditionalCompletionSplitting { succ(pred, succ, c) and last(succ, _, completion) and ( - last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ).(AST::NotExpr).getOperand()), - pred, c) and + last(succ.(NotExpr).getOperand(), pred, c) and completion.(BooleanCompletion).getDual() = c or - last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) - .(AST::LogicalAndExpr) - .getAnOperand()), pred, c) and + last(succ.(LogicalAndExpr).getAnOperand(), pred, c) and completion = c or - last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) - .(AST::LogicalOrExpr) - .getAnOperand()), pred, c) and + last(succ.(LogicalOrExpr).getAnOperand(), pred, c) and completion = c or - last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) - .(AST::ParenthesizedExpr) - .getLastExpr()), pred, c) and + last(succ.(ParenthesizedExpr).getLastExpr(), pred, c) and completion = c or - last(ASTInternal::toGenerated(ASTInternal::fromGenerated(succ) - .(AST::ConditionalExpr) - .getBranch(_)), pred, c) and + last(succ.(ConditionalExpr).getBranch(_), pred, c) and completion = c ) } @@ -255,7 +244,7 @@ private module ConditionalCompletionSplitting { override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesTo(last) and - succExit(ASTInternal::toGenerated(scope), last, c) and + succExit(scope, last, c) and if c instanceof ConditionalCompletion then completion = c else any() } @@ -288,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) } @@ -366,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) } @@ -389,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) } @@ -402,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() @@ -472,7 +456,7 @@ module EnsureSplitting { } override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { - succExit(ASTInternal::toGenerated(scope), last, c) and + succExit(scope, last, c) and ( exit(_, last, c, _) or @@ -488,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() ) } } @@ -517,7 +501,7 @@ class Splits extends TSplits { private predicate succEntrySplitsFromRank(CfgScope pred, AstNode succ, Splits splits, int rnk) { splits = TSplitsNil() and - succEntry(ASTInternal::toGenerated(pred), succ) and + succEntry(pred, succ) and rnk = 0 or exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | @@ -540,7 +524,7 @@ private predicate succEntrySplitsCons( pragma[noinline] predicate succEntrySplits(CfgScope pred, AstNode succ, Splits succSplits, SuccessorType t) { exists(int rnk | - succEntry(ASTInternal::toGenerated(pred), succ) and + succEntry(pred, succ) and t instanceof NormalSuccessor and succEntrySplitsFromRank(pred, succ, succSplits, rnk) | @@ -559,7 +543,7 @@ predicate succExitSplits(AstNode last, Splits predSplits, CfgScope scope, Succes exists(Reachability::SameSplitsBlock b, Completion c | last = b.getANode() | b.isReachable(predSplits) and t = c.getAMatchingSuccessorType() and - succExit(ASTInternal::toGenerated(scope), last, c) and + succExit(scope, last, c) and forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | predSplit.hasExitScope(scope, last, c) ) diff --git a/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll b/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll index bdc3c813ca0..ad2d2932044 100644 --- a/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll +++ b/ql/src/codeql_ruby/dataflow/internal/SsaImpl.qll @@ -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/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/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 028f0d08b9f..5481f1a2439 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -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? @@ -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? @@ -168,8 +168,8 @@ break_ensure.rb: #-----| -> 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 @@ -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 @@ -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 ... @@ -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 @@ -560,9 +552,12 @@ cfg.rb: # 29| exit { ... } (normal) #-----| -> exit { ... } -# 29| x +# 29| &x #-----| -> x +# 29| x +#-----| -> &x + # 29| call to call #-----| -> exit { ... } (normal) @@ -587,12 +582,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 +614,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" @@ -668,8 +663,8 @@ cfg.rb: #-----| -> b # 49| ... == ... -#-----| false -> b #-----| true -> "some" +#-----| false -> b # 49| b #-----| -> 0 @@ -775,6 +770,9 @@ cfg.rb: # 62| ... = ... #-----| -> pattern +# 62| (..., ...) +#-----| -> (..., ...) + # 62| (..., ...) #-----| -> ... = ... @@ -987,8 +985,8 @@ cfg.rb: #-----| -> x # 91| ... > ... -#-----| false -> if ... #-----| true -> next +#-----| false -> if ... # 91| x #-----| -> 3 @@ -1023,21 +1021,21 @@ cfg.rb: # 97| {...} #-----| -> map1 -# 97| Pair -#-----| -> "c" - # 97| "a" #-----| -> "b" +# 97| Pair +#-----| -> "c" + # 97| "b" #-----| -> Pair -# 97| Pair -#-----| -> :e - # 97| "c" #-----| -> "d" +# 97| Pair +#-----| -> :e + # 97| "d" #-----| -> Pair @@ -1065,12 +1063,12 @@ cfg.rb: # 98| map1 #-----| -> **... -# 98| Pair -#-----| -> map1 - # 98| "x" #-----| -> "y" +# 98| Pair +#-----| -> map1 + # 98| "y" #-----| -> Pair @@ -1101,9 +1099,12 @@ cfg.rb: # 101| key #-----| -> kwargs -# 101| kwargs +# 101| **kwargs #-----| -> value +# 101| kwargs +#-----| -> **kwargs + # 102| call to puts #-----| -> kwargs @@ -1161,18 +1162,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 @@ -1372,6 +1373,9 @@ cfg.rb: # 135| ... = ... #-----| -> M +# 135| (..., ...) +#-----| -> (..., ...) + # 135| (..., ...) #-----| -> ... = ... @@ -1390,14 +1394,11 @@ cfg.rb: # 135| 3 #-----| -> init -# 137| Constant -#-----| -> M - # 137| M #-----| -> Constant -# 138| Constant -#-----| -> class << ... +# 137| Constant +#-----| -> M # 138| call to itself #-----| -> Constant @@ -1405,6 +1406,9 @@ cfg.rb: # 138| M #-----| -> call to itself +# 138| Constant +#-----| -> class << ... + # 140| class << ... #-----| -> Silly @@ -1469,9 +1473,12 @@ cfg.rb: # 149| silly #-----| -> method -# 149| x +# 149| *x #-----| -> x +# 149| x +#-----| -> *x + # 150| call to puts #-----| -> exit method (normal) @@ -1619,18 +1626,18 @@ cfg.rb: # 169| "bye" #-----| -> call to puts -# 171| ... unless ... -#-----| -> x - # 171| call to puts #-----| -> ... unless ... +# 171| ... unless ... +#-----| -> x + # 171| "hi" #-----| -> call to puts # 171| ... == ... -#-----| true -> ... unless ... #-----| false -> "hi" +#-----| true -> ... unless ... # 171| x #-----| -> 0 @@ -1675,12 +1682,12 @@ cfg.rb: # 175| 0 #-----| -> i -# 176| ... until ... -#-----| -> 0 - # 176| (...; ...) #-----| -> i +# 176| ... until ... +#-----| -> 0 + # 176| call to puts #-----| -> i @@ -1697,8 +1704,8 @@ cfg.rb: #-----| -> ... += ... # 176| ... == ... -#-----| true -> ... until ... #-----| false -> "hello" +#-----| true -> ... until ... # 176| i #-----| -> 10 @@ -1719,8 +1726,8 @@ cfg.rb: #-----| -> i # 179| ... < ... -#-----| false -> while ... #-----| true -> x +#-----| false -> while ... # 179| x #-----| -> 10 @@ -1741,8 +1748,8 @@ cfg.rb: #-----| -> x # 181| ... == ... -#-----| false -> if ... #-----| true -> redo +#-----| false -> if ... # 181| x #-----| -> 5 @@ -1759,12 +1766,12 @@ cfg.rb: # 182| x #-----| -> call to puts -# 185| ... while ... -#-----| -> run_block - # 185| (...; ...) #-----| -> i +# 185| ... while ... +#-----| -> run_block + # 185| call to puts #-----| -> i @@ -1781,8 +1788,8 @@ cfg.rb: #-----| -> ... -= ... # 185| ... != ... -#-----| false -> ... while ... #-----| true -> "hello" +#-----| false -> ... while ... # 185| i #-----| -> 0 @@ -1796,10 +1803,16 @@ cfg.rb: # 187| run_block #-----| -> { ... } +# 187| exit run_block + +# 187| exit run_block (normal) +#-----| -> exit run_block + # 188| yield ... #-----| -> 42 # 188| 42 +#-----| -> exit run_block (normal) # 191| call to run_block #-----| -> exit cfg.rb (normal) @@ -1854,8 +1867,8 @@ exit.rb: #-----| -> "x <= 2" # 2| ... > ... -#-----| false -> if ... #-----| true -> 1 +#-----| false -> if ... # 2| x #-----| -> 2 @@ -1896,8 +1909,8 @@ exit.rb: #-----| -> "x <= 2" # 9| ... > ... -#-----| false -> if ... #-----| true -> "abort!" +#-----| false -> if ... # 9| x #-----| -> 2 @@ -2075,8 +2088,8 @@ ifs.rb: #-----| -> 1 # 12| b -#-----| false -> if ... #-----| true -> 0 +#-----| false -> if ... # 13| return #-----| return -> exit m2 (normal) @@ -2295,12 +2308,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 +2326,8 @@ ifs.rb: #-----| -> call to puts # 38| ... == ... -#-----| true -> ... unless ... #-----| false -> conditional_method_def +#-----| true -> ... unless ... # 38| 1 #-----| -> 2 @@ -2369,8 +2382,8 @@ loops.rb: #-----| -> exit m1 (normal) # 2| ... >= ... -#-----| false -> while ... #-----| true -> x +#-----| false -> while ... # 2| x #-----| -> 0 @@ -2411,8 +2424,8 @@ loops.rb: #-----| -> "Done" # 9| ... >= ... -#-----| false -> while ... #-----| true -> x +#-----| false -> while ... # 9| x #-----| -> 0 @@ -2471,8 +2484,8 @@ loops.rb: #-----| -> elsif ... # 16| ... > ... -#-----| false -> elsif ... #-----| true -> redo +#-----| false -> elsif ... # 16| x #-----| -> 10 @@ -2506,12 +2519,12 @@ loops.rb: # 24| exit m3 (normal) #-----| -> exit m3 -# 25| call to each -#-----| -> exit m3 (normal) - # 25| [...] #-----| -> do ... end +# 25| call to each +#-----| -> exit m3 (normal) + # 25| 1 #-----| -> 2 @@ -2583,8 +2596,8 @@ raise.rb: #-----| -> "x <= 2" # 8| ... > ... -#-----| false -> if ... #-----| true -> "x > 2" +#-----| false -> if ... # 8| x #-----| -> 2 @@ -2875,8 +2888,8 @@ raise.rb: #-----| -> if ... # 71| ... < ... -#-----| false -> elsif ... #-----| true -> "x < 0" +#-----| false -> elsif ... # 71| x #-----| -> 0 @@ -2969,8 +2982,8 @@ raise.rb: #-----| -> if ... # 84| ... < ... -#-----| false -> elsif ... #-----| true -> "x < 0" +#-----| false -> elsif ... # 84| x #-----| -> 0 @@ -3075,8 +3088,8 @@ raise.rb: #-----| -> if ... # 99| ... < ... -#-----| false -> elsif ... #-----| true -> "x < 0" +#-----| false -> elsif ... # 99| x #-----| -> 0 @@ -3133,16 +3146,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 +3272,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) @@ -3306,7 +3319,11 @@ raise.rb: #-----| no-match -> "Exception" #-----| match -> ensure ... +# 121| (call to raise) +#-----| -> ensure ... + # 121| call to raise +#-----| raise -> (call to raise) #-----| raise -> exit m10 (abnormal) # 121| "Exception" @@ -3413,8 +3430,8 @@ raise.rb: #-----| -> ensure ... # 143| b -#-----| false -> if ... #-----| true -> "" +#-----| false -> if ... # 144| call to raise #-----| raise -> [ensure: raise] ensure ... @@ -3491,18 +3508,18 @@ raise.rb: # 155| elem #-----| -> element -# 155| ... if ... -#-----| -> exit { ... } (normal) - # 155| call to raise #-----| raise -> exit { ... } (abnormal) +# 155| ... if ... +#-----| -> exit { ... } (normal) + # 155| "" #-----| -> call to raise # 155| call to nil? -#-----| false -> ... if ... #-----| true -> "" +#-----| false -> ... if ... # 155| element #-----| -> call to nil? From d4030c66d8372981b436aefc1a5bd257568b1f89 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Mar 2021 09:54:44 +0100 Subject: [PATCH 22/39] Update Consistency.qll --- ql/src/codeql_ruby/controlflow/internal/Consistency.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/Consistency.qll b/ql/src/codeql_ruby/controlflow/internal/Consistency.qll index ccec52fd56e..0b8e07dd77c 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Consistency.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Consistency.qll @@ -1,4 +1,4 @@ -private import codeql_ruby.ast.internal.TreeSitter::Generated +private import codeql_ruby.AST private import codeql_ruby.CFG private import Completion private import Splitting From 3bb2c529a59c116f8dafc3feba2e56a51812c4a9 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 18 Mar 2021 10:43:10 +0100 Subject: [PATCH 23/39] CFG: Revert change to mandatory parameters --- .../internal/ControlFlowGraphImpl.qll | 38 ++++++++++++------- .../controlflow/graph/Cfg.expected | 15 ++------ 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index c864d1aee40..119d2c4e7cf 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -380,12 +380,24 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - private class BlockParameterTree extends StandardPostOrderTree, BlockParameter { - final override ControlFlowTree getChildNode(int i) { - result = this.getVariable().getDefiningAccess() and i = 0 + abstract private class MandatoryParameterTree 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 MandatoryParameterTree, BlockParameter { } + /** * TODO: make all StmtSequence tree classes post-order, and simplify class * hierarchy. @@ -868,11 +880,7 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - private class HashSplatParameterTree extends StandardPostOrderTree, HashSplatParameter { - final override ControlFlowTree getChildNode(int i) { - result = this.getVariable().getDefiningAccess() and i = 0 - } - } + private class HashSplatParameterTree extends MandatoryParameterTree, HashSplatParameter { } private class HereDocTree extends StandardPreOrderTree, HereDoc { final override ControlFlowTree getChildNode(int i) { result = this.getComponent(i) } @@ -1121,7 +1129,13 @@ module Trees { private class SelfTree extends LeafTree, Self { } - private class SimpleParameterTree extends LeafTree, SimpleParameter { } + private class SimpleParameterTree extends MandatoryParameterTree, 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 @@ -1215,11 +1229,7 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - private class SplatParameterTree extends StandardPostOrderTree, SplatParameter { - final override ControlFlowTree getChildNode(int i) { - result = this.getVariable().getDefiningAccess() and i = 0 - } - } + private class SplatParameterTree extends MandatoryParameterTree, SplatParameter { } abstract class StmtSequenceTree extends ControlFlowTree, StmtSequence { override predicate propagatesAbnormal(AstNode child) { none() } diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index 5481f1a2439..40746ef4ce4 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -552,11 +552,8 @@ cfg.rb: # 29| exit { ... } (normal) #-----| -> exit { ... } -# 29| &x -#-----| -> x - # 29| x -#-----| -> &x +#-----| -> x # 29| call to call #-----| -> exit { ... } (normal) @@ -1099,11 +1096,8 @@ cfg.rb: # 101| key #-----| -> kwargs -# 101| **kwargs -#-----| -> value - # 101| kwargs -#-----| -> **kwargs +#-----| -> value # 102| call to puts #-----| -> kwargs @@ -1473,11 +1467,8 @@ cfg.rb: # 149| silly #-----| -> method -# 149| *x -#-----| -> x - # 149| x -#-----| -> *x +#-----| -> x # 150| call to puts #-----| -> exit method (normal) From c8eab42c1db8ff5c8d1902a751913791a6b370d2 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 11:09:21 +0000 Subject: [PATCH 24/39] Minor comment fixes --- .../codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 119d2c4e7cf..125c60296ef 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -108,7 +108,7 @@ module CfgScope { } } -abstract /*private*/ class ControlFlowTree extends AstNode { +abstract private class ControlFlowTree extends AstNode { /** * Holds if `first` is the first element executed within this AST node. */ @@ -1003,7 +1003,6 @@ module Trees { final override AstNode getAccessNode() { result = this.getDefiningAccess() } } - /** Contains partridges */ private class PairTree extends StandardPostOrderTree, Pair { final override ControlFlowTree getChildNode(int i) { result = this.getKey() and i = 0 From ceda7c8fd2a7795cb84876fcf75fa946512e6cc6 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 11:21:11 +0000 Subject: [PATCH 25/39] Generalise splitting of parenthesized exprs to all statement sequences --- ql/src/codeql_ruby/ast/Expr.qll | 4 ++-- ql/src/codeql_ruby/controlflow/CfgNodes.qll | 6 +++--- ql/src/codeql_ruby/controlflow/internal/Completion.qll | 2 +- ql/src/codeql_ruby/controlflow/internal/Splitting.qll | 2 +- ql/src/codeql_ruby/dataflow/internal/DataFlowPrivate.qll | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Expr.qll b/ql/src/codeql_ruby/ast/Expr.qll index f584b7f96f7..6f27b0462bc 100644 --- a/ql/src/codeql_ruby/ast/Expr.qll +++ b/ql/src/codeql_ruby/ast/Expr.qll @@ -73,8 +73,8 @@ class StmtSequence extends Expr, TStmtSequence { /** 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()) } diff --git a/ql/src/codeql_ruby/controlflow/CfgNodes.qll b/ql/src/codeql_ruby/controlflow/CfgNodes.qll index d67849934fc..3a80fffbae4 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -284,7 +284,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. */ @@ -293,8 +293,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/internal/Completion.qll b/ql/src/codeql_ruby/controlflow/internal/Completion.qll index b25098b2a06..dc80bbb98f2 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Completion.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Completion.qll @@ -176,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 diff --git a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll index 7a81548ed5d..8543c64f055 100644 --- a/ql/src/codeql_ruby/controlflow/internal/Splitting.qll +++ b/ql/src/codeql_ruby/controlflow/internal/Splitting.qll @@ -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 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 From 4ce7faf868bc0b755862493730d5362612c7e3e3 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 13:01:16 +0000 Subject: [PATCH 26/39] Fix erroneous flow from 'raise' call to StmtSequence --- .../codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll | 3 ++- ql/test/library-tests/controlflow/graph/Cfg.expected | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 125c60296ef..4a551dddd86 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -1254,7 +1254,8 @@ module Trees { or this instanceof PostOrderTree and succ = this and - last(this.getLastBodyChild(), pred, c) + last(this.getLastBodyChild(), pred, c) and + c instanceof NormalCompletion or // Normal left-to-right evaluation in the body exists(int i | diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index 40746ef4ce4..a3136e61ede 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -3310,11 +3310,7 @@ raise.rb: #-----| no-match -> "Exception" #-----| match -> ensure ... -# 121| (call to raise) -#-----| -> ensure ... - # 121| call to raise -#-----| raise -> (call to raise) #-----| raise -> exit m10 (abnormal) # 121| "Exception" From 434d9e54a18c011445cf01cfa929a6720f08471a Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 14:48:08 +0000 Subject: [PATCH 27/39] Fix complex symbols having multiple ControlFlowTree implementations --- .../controlflow/internal/ControlFlowGraphImpl.qll | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 4a551dddd86..613b8687596 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -969,7 +969,7 @@ module Trees { } } - private class MethodNameTree extends LeafTree, MethodName { } + private class MethodNameTree extends LeafTree, MethodName, ASTInternal::TTokenMethodName { } private class MethodTree extends BodyStmtTree, PostOrderTree, Method { final override predicate first(AstNode first) { first = this } @@ -1276,6 +1276,10 @@ module Trees { 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 } From 37435764a0cb41adeb0769c0be1a288937c4ba53 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 18:58:40 +0000 Subject: [PATCH 28/39] Fix control-flow for empty classes and modules --- .../internal/ControlFlowGraphImpl.qll | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 613b8687596..9ffbd14ab79 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -359,9 +359,7 @@ module Trees { } } - private class BeginTree extends BodyStmtTree, PreOrderTree, BeginExpr { - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } - + private class BeginTree extends BodyStmtPreOrderTree, BeginExpr { override predicate isHidden() { any() } } @@ -437,7 +435,7 @@ module Trees { not exists(this.getRescue(_)) and this.lastEnsure0(last, c) or - last([this.getEnsure().(AstNode), this.getBodyChild(_, false)], last, c) and + last([this.getEnsure(), this.getBodyChild(_, false)], last, c) and not c instanceof NormalCompletion } @@ -585,6 +583,20 @@ module Trees { } } + 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 { @@ -638,7 +650,7 @@ module Trees { private class CharacterTree extends LeafTree, CharacterLiteral { } - private class ClassTree extends BodyStmtTree, PreOrderTree, Class { + 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 @@ -650,8 +662,6 @@ module Trees { result = this.getStmt(i - count(this.getScopeExpr()) - count(this.getSuperclassExpr())) and rescuable = true } - - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } } private class ClassVariableTree extends LeafTree, ClassVariableAccess { } @@ -770,14 +780,12 @@ module Trees { } } - private class DoBlockTree extends BodyStmtTree, PostOrderTree, DoBlock { - final override predicate first(AstNode first) { first = this } - + 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 = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) } } @@ -894,14 +902,12 @@ module Trees { final override AstNode getAccessNode() { result = this.getDefiningAccess() } } - private class LambdaTree extends BodyStmtTree, PostOrderTree, Lambda { - final override predicate first(AstNode first) { first = this } - + 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 = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) } } @@ -971,26 +977,22 @@ module Trees { private class MethodNameTree extends LeafTree, MethodName, ASTInternal::TTokenMethodName { } - private class MethodTree extends BodyStmtTree, PostOrderTree, Method { - final override predicate first(AstNode first) { first = this } - + 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 = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) } } - private class ModuleTree extends BodyStmtTree, PreOrderTree, Module { + 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 = BodyStmtTree.super.getBodyChild(i - count(this.getScopeExpr()), rescuable) + result = BodyStmtPreOrderTree.super.getBodyChild(i - count(this.getScopeExpr()), rescuable) } - - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } } private class NilTree extends LeafTree, NilLiteral { } @@ -1192,31 +1194,29 @@ module Trees { } } - private class SingletonClassTree extends BodyStmtTree, PreOrderTree, SingletonClass { + 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 = BodyStmtTree.super.getBodyChild(i - 1, rescuable) + result = BodyStmtPreOrderTree.super.getBodyChild(i - 1, rescuable) ) } - - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } } - private class SingletonMethodTree extends BodyStmtTree, PostOrderTree, SingletonMethod { + 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 = BodyStmtTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) + result = BodyStmtPostOrderTree.super.getBodyChild(i - this.getNumberOfParameters(), rescuable) } - final override predicate first(AstNode first) { first(this.getObject(), first) } + override predicate first(AstNode first) { first(this.getObject(), first) } override predicate succ(AstNode pred, AstNode succ, Completion c) { - BodyStmtTree.super.succ(pred, succ, c) + BodyStmtPostOrderTree.super.succ(pred, succ, c) or last(this.getObject(), pred, c) and succ = this and @@ -1290,15 +1290,13 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getArgument(i) } } - private class ToplevelTree extends BodyStmtTree, PreOrderTree, Toplevel { + private class ToplevelTree extends BodyStmtPreOrderTree, Toplevel { final override AstNode getBodyChild(int i, boolean rescuable) { result = this.getBeginBlock(i) and rescuable = true or - result = BodyStmtTree.super.getBodyChild(i - count(this.getABeginBlock()), rescuable) + result = BodyStmtPreOrderTree.super.getBodyChild(i - count(this.getABeginBlock()), rescuable) } - final override predicate last(AstNode last, Completion c) { this.lastInner(last, c) } - override predicate isHidden() { any() } } From 9493997e9d00c8a812409c07c4ab1faa54033a83 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 19:01:11 +0000 Subject: [PATCH 29/39] Make space in CFG test for two new lines in the middle Commented out to make it easier to ignore the noise from line number changes. --- .../controlflow/graph/Cfg.expected | 376 +++++++++--------- .../library-tests/controlflow/graph/cfg.rb | 3 + 2 files changed, 191 insertions(+), 188 deletions(-) diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index a3136e61ede..b691caa8394 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1345,487 +1345,487 @@ cfg.rb: # 130| 5 #-----| -> Constant -# 133| ... rescue ... +# 136| ... rescue ... #-----| -> 1 -# 133| ... / ... +# 136| ... / ... #-----| raise -> "div by zero" #-----| -> 1 -# 133| 1 +# 136| 1 #-----| -> 0 -# 133| 0 +# 136| 0 #-----| -> ... / ... -# 133| call to puts +# 136| call to puts #-----| -> 1 -# 133| "div by zero" +# 136| "div by zero" #-----| -> call to puts -# 135| ... = ... +# 138| ... = ... #-----| -> M -# 135| (..., ...) +# 138| (..., ...) #-----| -> (..., ...) -# 135| (..., ...) +# 138| (..., ...) #-----| -> ... = ... -# 135| init +# 138| init #-----| -> last -# 135| last +# 138| last #-----| -> (..., ...) -# 135| 1 +# 138| 1 #-----| -> 2 -# 135| 2 +# 138| 2 #-----| -> 3 -# 135| 3 +# 138| 3 #-----| -> init -# 137| M +# 140| M #-----| -> Constant -# 137| Constant +# 140| Constant #-----| -> M -# 138| call to itself +# 141| call to itself #-----| -> Constant -# 138| M +# 141| M #-----| -> call to itself -# 138| Constant +# 141| Constant #-----| -> class << ... -# 140| class << ... +# 143| class << ... #-----| -> Silly -# 140| call to itself +# 143| call to itself #-----| -> setter= -# 140| Silly +# 143| Silly #-----| -> call to itself -# 141| setter= +# 144| setter= #-----| -> print -# 142| enter print +# 145| enter print #-----| -> "singleton" -# 142| print +# 145| print #-----| -> Silly -# 142| exit print +# 145| exit print -# 142| exit print (normal) +# 145| exit print (normal) #-----| -> exit print -# 143| call to puts +# 146| call to puts #-----| -> call to super -# 143| "singleton" +# 146| "singleton" #-----| -> call to puts -# 144| call to puts +# 147| call to puts #-----| -> exit print (normal) -# 144| call to print +# 147| call to print #-----| -> call to puts -# 144| call to super +# 147| call to super #-----| -> call to print -# 148| ... = ... +# 151| ... = ... #-----| -> silly -# 148| silly +# 151| silly #-----| -> ... = ... -# 148| call to new +# 151| call to new #-----| -> silly -# 148| Silly +# 151| Silly #-----| -> call to new -# 149| enter method +# 152| enter method #-----| -> x -# 149| method +# 152| method #-----| -> two_parameters -# 149| exit method +# 152| exit method -# 149| exit method (normal) +# 152| exit method (normal) #-----| -> exit method -# 149| silly +# 152| silly #-----| -> method -# 149| x +# 152| x #-----| -> x -# 150| call to puts +# 153| call to puts #-----| -> exit method (normal) -# 150| x +# 153| x #-----| -> call to puts -# 153| enter two_parameters +# 156| enter two_parameters #-----| -> a -# 153| two_parameters +# 156| two_parameters #-----| -> 1 -# 153| exit two_parameters +# 156| exit two_parameters -# 153| exit two_parameters (normal) +# 156| exit two_parameters (normal) #-----| -> exit two_parameters -# 153| a +# 156| a #-----| -> b -# 153| b +# 156| b #-----| -> exit two_parameters (normal) -# 155| call to two_parameters +# 158| call to two_parameters #-----| -> call to __FILE__ -# 155| *... +# 158| *... #-----| -> call to two_parameters -# 155| [...] +# 158| [...] #-----| -> *... -# 155| 1 +# 158| 1 #-----| -> 2 -# 155| 2 +# 158| 2 #-----| -> [...] -# 157| ... = ... +# 160| ... = ... #-----| -> :hello -# 157| scriptfile +# 160| scriptfile #-----| -> ... = ... -# 157| `cat "#{...}"` +# 160| `cat "#{...}"` #-----| -> scriptfile -# 157| #{...} +# 160| #{...} #-----| -> `cat "#{...}"` -# 157| call to __FILE__ +# 160| call to __FILE__ #-----| -> #{...} -# 159| ... = ... +# 162| ... = ... #-----| -> 12 -# 159| symbol +# 162| symbol #-----| -> ... = ... -# 159| :hello +# 162| :hello #-----| -> symbol -# 161| ... = ... +# 164| ... = ... #-----| -> true -# 161| delimited_symbol +# 164| delimited_symbol #-----| -> ... = ... -# 161| :"goodbye-#{...}" +# 164| :"goodbye-#{...}" #-----| -> delimited_symbol -# 161| #{...} +# 164| #{...} #-----| -> :"goodbye-#{...}" -# 161| ... + ... +# 164| ... + ... #-----| -> #{...} -# 161| 12 +# 164| 12 #-----| -> 13 -# 161| 13 +# 164| 13 #-----| -> ... + ... -# 163| ... = ... +# 166| ... = ... #-----| -> true -# 163| x +# 166| x #-----| -> ... = ... -# 163| true +# 166| true #-----| -> x -# 164| ... = ... +# 167| ... = ... #-----| -> 42 -# 164| x +# 167| x #-----| -> ... = ... -# 164| ! ... +# 167| ! ... #-----| -> x -# 164| true +# 167| true #-----| -> ! ... -# 165| ... = ... +# 168| ... = ... #-----| -> undef ... -# 165| x +# 168| x #-----| -> ... = ... -# 165| - ... +# 168| - ... #-----| -> x -# 165| 42 +# 168| 42 #-----| -> - ... -# 167| undef ... +# 170| undef ... #-----| -> two_parameters -# 167| two_parameters +# 170| two_parameters #-----| -> x -# 169| unless ... +# 172| unless ... #-----| -> x -# 169| ... == ... +# 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| call to puts +# 174| call to puts #-----| -> ... unless ... -# 171| ... unless ... +# 174| ... unless ... #-----| -> x -# 171| "hi" +# 174| "hi" #-----| -> call to puts -# 171| ... == ... +# 174| ... == ... #-----| false -> "hi" #-----| true -> ... unless ... -# 171| x +# 174| x #-----| -> 0 -# 171| 0 +# 174| 0 #-----| -> ... == ... -# 173| until ... +# 176| until ... #-----| -> 0 -# 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| (...; ...) -#-----| -> i - -# 176| ... until ... -#-----| -> 0 - # 176| call to puts -#-----| -> i +#-----| -> x # 176| "hello" #-----| -> call to puts -# 176| ... += ... -#-----| -> (...; ...) - -# 176| i -#-----| -> 1 - -# 176| 1 -#-----| -> ... += ... - -# 176| ... == ... -#-----| false -> "hello" -#-----| true -> ... until ... - -# 176| i -#-----| -> 10 - -# 176| 10 -#-----| -> ... == ... - # 178| ... = ... -#-----| -> x +#-----| -> i -# 178| x +# 178| i #-----| -> ... = ... # 178| 0 -#-----| -> x - -# 179| while ... #-----| -> i -# 179| ... < ... -#-----| true -> x -#-----| false -> while ... +# 179| (...; ...) +#-----| -> i -# 179| x +# 179| ... until ... +#-----| -> 0 + +# 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 +#-----| -> ... == ... + +# 181| ... = ... +#-----| -> x + +# 181| x +#-----| -> ... = ... + +# 181| 0 +#-----| -> x + +# 182| while ... +#-----| -> i + +# 182| ... < ... +#-----| true -> x +#-----| false -> while ... + +# 182| x +#-----| -> 10 + +# 182| 10 #-----| -> ... < ... -# 180| ... += ... +# 183| ... += ... #-----| -> x -# 180| x +# 183| x #-----| -> 1 -# 180| 1 +# 183| 1 #-----| -> ... += ... -# 181| if ... +# 184| if ... #-----| -> x -# 181| ... == ... +# 184| ... == ... #-----| true -> redo #-----| false -> if ... -# 181| x +# 184| x #-----| -> 5 -# 181| 5 +# 184| 5 #-----| -> ... == ... -# 181| redo +# 184| redo #-----| redo -> x -# 182| call to puts +# 185| call to puts #-----| -> x -# 182| x +# 185| x #-----| -> call to puts -# 185| (...; ...) +# 188| (...; ...) #-----| -> i -# 185| ... while ... +# 188| ... while ... #-----| -> run_block -# 185| call to puts +# 188| call to puts #-----| -> i -# 185| "hello" +# 188| "hello" #-----| -> call to puts -# 185| ... -= ... +# 188| ... -= ... #-----| -> (...; ...) -# 185| i +# 188| i #-----| -> 1 -# 185| 1 +# 188| 1 #-----| -> ... -= ... -# 185| ... != ... +# 188| ... != ... #-----| true -> "hello" #-----| false -> ... while ... -# 185| i +# 188| i #-----| -> 0 -# 185| 0 +# 188| 0 #-----| -> ... != ... -# 187| enter run_block +# 190| enter run_block #-----| -> yield ... -# 187| run_block +# 190| run_block #-----| -> { ... } -# 187| exit run_block +# 190| exit run_block -# 187| exit run_block (normal) +# 190| exit run_block (normal) #-----| -> exit run_block -# 188| yield ... +# 191| yield ... #-----| -> 42 -# 188| 42 +# 191| 42 #-----| -> exit run_block (normal) -# 191| call to run_block +# 194| call to run_block #-----| -> exit cfg.rb (normal) -# 191| enter { ... } +# 194| enter { ... } #-----| -> x -# 191| { ... } +# 194| { ... } #-----| -> call to run_block -# 191| exit { ... } +# 194| exit { ... } -# 191| exit { ... } (normal) +# 194| exit { ... } (normal) #-----| -> exit { ... } -# 191| x +# 194| x #-----| -> x -# 191| call to puts +# 194| call to puts #-----| -> exit { ... } (normal) -# 191| x +# 194| x #-----| -> call to puts exit.rb: diff --git a/ql/test/library-tests/controlflow/graph/cfg.rb b/ql/test/library-tests/controlflow/graph/cfg.rb index ff3305df60a..65f6a927b10 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 From 6bcc433af31331e9b3023464427a2470160584f4 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 19:02:32 +0000 Subject: [PATCH 30/39] Uncomment empty class and module in CFG test --- ql/test/library-tests/controlflow/graph/Cfg.expected | 8 +++++++- ql/test/library-tests/controlflow/graph/cfg.rb | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index b691caa8394..7140bb4c6be 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -1337,7 +1337,7 @@ cfg.rb: #-----| -> #{...} # 130| ... = ... -#-----| -> ... rescue ... +#-----| -> EmptyClass # 130| Constant #-----| -> ... = ... @@ -1345,6 +1345,12 @@ cfg.rb: # 130| 5 #-----| -> Constant +# 133| EmptyClass +#-----| -> EmptyModule + +# 134| EmptyModule +#-----| -> ... rescue ... + # 136| ... rescue ... #-----| -> 1 diff --git a/ql/test/library-tests/controlflow/graph/cfg.rb b/ql/test/library-tests/controlflow/graph/cfg.rb index 65f6a927b10..aa43ec6c9ae 100644 --- a/ql/test/library-tests/controlflow/graph/cfg.rb +++ b/ql/test/library-tests/controlflow/graph/cfg.rb @@ -130,8 +130,8 @@ module M Constant = 5 end -#class EmptyClass; end -#module EmptyModule; end +class EmptyClass; end +module EmptyModule; end 1/0 rescue puts "div by zero" From c0636bef29097b02928c0cad2b1da9dd24d2f458 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Thu, 18 Mar 2021 19:08:51 +0000 Subject: [PATCH 31/39] Make CfgScope extend Scope --- ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll index 30e75576397..519d2b30dad 100644 --- a/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll +++ b/ql/src/codeql_ruby/controlflow/ControlFlowGraph.qll @@ -9,7 +9,7 @@ private import internal.Splitting private import internal.Completion /** An AST node with an associated control-flow graph. */ -class CfgScope extends AstNode { +class CfgScope extends Scope { CfgScope() { this instanceof CfgScope::Range_ } /** Gets the CFG scope that this scope is nested under, if any. */ From e17551329341f4a0469132784fc4d91965d4650f Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 19 Mar 2021 10:23:31 +0100 Subject: [PATCH 32/39] Remove duplicate tuple patterns --- ql/src/codeql_ruby/ast/Pattern.qll | 9 ++++++++- ql/src/codeql_ruby/ast/internal/AST.qll | 4 +++- ql/test/library-tests/ast/Ast.expected | 5 ++--- ql/test/library-tests/ast/control/Loop.expected | 3 ++- ql/test/library-tests/controlflow/graph/Cfg.expected | 6 ------ 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 8cf30a3ea4b..3f5d335ec05 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -63,7 +63,14 @@ class TuplePattern extends Pattern, TTuplePattern { or result = toGenerated(this).(Generated::DestructuredLeftAssignment).getChild(i) or - result = toGenerated(this).(Generated::LeftAssignmentList).getChild(i) + 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. */ diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index 6cf9696c510..37ef071d507 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -90,7 +90,9 @@ private module Cached { TComplexLiteral(Generated::Complex g) or TDefinedExpr(Generated::Unary g) { g instanceof @unary_definedquestion } or TDelimitedSymbolLiteral(Generated::DelimitedSymbol g) or - TDestructuredLeftAssignment(Generated::DestructuredLeftAssignment 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 diff --git a/ql/test/library-tests/ast/Ast.expected b/ql/test/library-tests/ast/Ast.expected index 246512d1ec6..8b74d275522 100644 --- a/ql/test/library-tests/ast/Ast.expected +++ b/ql/test/library-tests/ast/Ast.expected @@ -1185,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 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/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index 7140bb4c6be..18ff3d01381 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -767,9 +767,6 @@ cfg.rb: # 62| ... = ... #-----| -> pattern -# 62| (..., ...) -#-----| -> (..., ...) - # 62| (..., ...) #-----| -> ... = ... @@ -1373,9 +1370,6 @@ cfg.rb: # 138| ... = ... #-----| -> M -# 138| (..., ...) -#-----| -> (..., ...) - # 138| (..., ...) #-----| -> ... = ... From 5cedf7ee86185f0ffb027bc7f9628a78db9c718a Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 13:59:02 +0000 Subject: [PATCH 33/39] Remove unused import --- ql/src/codeql_ruby/controlflow/CfgNodes.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/ql/src/codeql_ruby/controlflow/CfgNodes.qll b/ql/src/codeql_ruby/controlflow/CfgNodes.qll index 3a80fffbae4..10449ed7c91 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -1,7 +1,6 @@ /** Provides classes representing nodes in a control flow graph. */ private import codeql_ruby.AST -private import codeql_ruby.ast.internal.AST private import codeql_ruby.controlflow.BasicBlocks private import ControlFlowGraph private import internal.ControlFlowGraphImpl From f381f94bc2a2968f757c8c29a87f9312262e8442 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 14:02:54 +0000 Subject: [PATCH 34/39] Rename ProgramScope to ToplevelScope --- .../codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 9ffbd14ab79..c3e7d79d64d 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -49,7 +49,7 @@ module CfgScope { abstract predicate exit(AstNode last, Completion c); } - private class ProgramScope extends Range_, Toplevel { + 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) } From c6958f64e49409f6f8b9acb4f7d8136fefaa4e1d Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 14:25:38 +0000 Subject: [PATCH 35/39] Make CFG for AssignExpr visit left operand before right --- .../internal/ControlFlowGraphImpl.qll | 4 +- .../controlflow/graph/Cfg.expected | 266 +++++++++--------- 2 files changed, 135 insertions(+), 135 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index c3e7d79d64d..241fe949329 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -353,9 +353,9 @@ module Trees { private class AssignmentTree extends StandardPostOrderTree, AssignExpr { final override ControlFlowTree getChildNode(int i) { - result = this.getRightOperand() and i = 0 + result = this.getLeftOperand() and i = 0 or - result = this.getLeftOperand() and i = 1 + result = this.getRightOperand() and i = 1 } } diff --git a/ql/test/library-tests/controlflow/graph/Cfg.expected b/ql/test/library-tests/controlflow/graph/Cfg.expected index 18ff3d01381..b585b8ac4f0 100644 --- a/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -418,16 +418,16 @@ cfg.rb: #-----| -> bar # 3| bar -#-----| -> 42 +#-----| -> b # 5| ... = ... #-----| -> b # 5| b -#-----| -> ... = ... +#-----| -> 42 # 5| 42 -#-----| -> b +#-----| -> ... = ... # 7| %i(...) #-----| -> b @@ -651,7 +651,7 @@ cfg.rb: #-----| -> ... == ... # 48| call to puts -#-----| -> "a" +#-----| -> chained # 48| "one" #-----| -> call to puts @@ -671,7 +671,7 @@ cfg.rb: # 49| ... > ... #-----| true -> "some" -#-----| false -> "a" +#-----| false -> chained # 49| b #-----| -> 1 @@ -680,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 @@ -704,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" @@ -756,19 +756,19 @@ cfg.rb: #-----| -> ... ? ... : ... # 61| ... = ... -#-----| -> 1 +#-----| -> x # 61| C -#-----| -> ... = ... +#-----| -> "constant" # 61| "constant" -#-----| -> C +#-----| -> ... = ... # 62| ... = ... #-----| -> pattern # 62| (..., ...) -#-----| -> ... = ... +#-----| -> 1 # 62| x #-----| -> y @@ -783,7 +783,7 @@ cfg.rb: #-----| -> (..., ...) # 62| [...] -#-----| -> x +#-----| -> ... = ... # 62| 1 #-----| -> 2 @@ -801,7 +801,7 @@ cfg.rb: #-----| -> a # 63| pattern -#-----| -> 1 +#-----| -> items # 63| exit pattern @@ -833,10 +833,10 @@ cfg.rb: #-----| -> items # 67| items -#-----| -> ... = ... +#-----| -> 1 # 67| [...] -#-----| -> items +#-----| -> ... = ... # 67| 1 #-----| -> 2 @@ -863,7 +863,7 @@ cfg.rb: #-----| -> "silly" # 69| print -#-----| -> 42 +#-----| -> x # 69| exit print @@ -880,10 +880,10 @@ cfg.rb: #-----| -> x # 74| x -#-----| -> ... = ... +#-----| -> 42 # 74| 42 -#-----| -> x +#-----| -> ... = ... # 75| if ... #-----| -> ; @@ -933,7 +933,7 @@ cfg.rb: #-----| -> "end" # 85| call to puts -#-----| -> x +#-----| -> escape # 85| "end" #-----| -> call to puts @@ -942,10 +942,10 @@ cfg.rb: #-----| -> 1.4 # 88| escape -#-----| -> ... = ... +#-----| -> x # 88| "\u1234#{...}\n" -#-----| -> escape +#-----| -> ... = ... # 88| #{...} #-----| -> "\u1234#{...}\n" @@ -954,7 +954,7 @@ cfg.rb: #-----| -> #{...} # 90| for ... in ... -#-----| -> 42 +#-----| -> $global # 90| x #-----| -> x @@ -998,22 +998,22 @@ cfg.rb: #-----| -> call to puts # 95| ... = ... -#-----| -> "a" +#-----| -> map1 # 95| $global -#-----| -> ... = ... +#-----| -> 42 # 95| 42 -#-----| -> $global - -# 97| ... = ... -#-----| -> map1 - -# 97| map1 #-----| -> ... = ... +# 97| ... = ... +#-----| -> map2 + +# 97| map1 +#-----| -> "a" + # 97| {...} -#-----| -> map1 +#-----| -> ... = ... # 97| "a" #-----| -> "b" @@ -1046,10 +1046,10 @@ cfg.rb: #-----| -> parameters # 98| map2 -#-----| -> ... = ... +#-----| -> map1 # 98| {...} -#-----| -> map2 +#-----| -> ... = ... # 98| **... #-----| -> "x" @@ -1076,7 +1076,7 @@ cfg.rb: #-----| -> value # 101| parameters -#-----| -> "healthy" +#-----| -> type # 101| exit parameters @@ -1115,22 +1115,22 @@ cfg.rb: #-----| -> ...[...] # 106| ... = ... -#-----| -> "food" +#-----| -> table # 106| type -#-----| -> ... = ... +#-----| -> "healthy" # 106| "healthy" -#-----| -> type +#-----| -> ... = ... # 107| ... = ... #-----| -> < ... = ... +#-----| -> "food" # 107| "food" -#-----| -> table +#-----| -> ... = ... # 108| call to puts #-----| -> b @@ -1173,37 +1173,37 @@ cfg.rb: #-----| -> ... > ... # 115| C -#-----| -> 42 - -# 116| ... = ... -#-----| -> 10 - -# 116| @field -#-----| -> ... = ... - -# 116| 42 #-----| -> @field -# 117| ... = ... -#-----| -> -> { ... } +# 116| ... = ... +#-----| -> @@static_field -# 117| @@static_field +# 116| @field +#-----| -> 42 + +# 116| 42 #-----| -> ... = ... +# 117| ... = ... +#-----| -> swap + +# 117| @@static_field +#-----| -> 10 + # 117| 10 -#-----| -> @@static_field +#-----| -> ... = ... # 120| ... = ... #-----| -> M # 120| swap -#-----| -> ... = ... +#-----| -> -> { ... } # 120| enter -> { ... } #-----| -> x # 120| -> { ... } -#-----| -> swap +#-----| -> ... = ... # 120| exit -> { ... } @@ -1229,28 +1229,28 @@ cfg.rb: #-----| -> [...] # 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 @@ -1259,13 +1259,13 @@ cfg.rb: #-----| -> ... += ... # 126| ... = ... -#-----| -> 0 +#-----| -> range # 126| last -#-----| -> ... = ... +#-----| -> 2 # 126| (...; ...) -#-----| -> last +#-----| -> ... = ... # 126| 2 #-----| -> 4 @@ -1277,13 +1277,13 @@ cfg.rb: #-----| -> (...; ...) # 127| ... = ... -#-----| -> 1 +#-----| -> half # 127| range -#-----| -> ... = ... +#-----| -> 0 # 127| _ .. _ -#-----| -> range +#-----| -> ... = ... # 127| 0 #-----| -> 9 @@ -1292,13 +1292,13 @@ cfg.rb: #-----| -> _ .. _ # 128| ... = ... -#-----| -> range +#-----| -> regex # 128| half -#-----| -> ... = ... +#-----| -> 1 # 128| ... + ... -#-----| -> half +#-----| -> ... = ... # 128| ... / ... #-----| -> 1 @@ -1319,13 +1319,13 @@ cfg.rb: #-----| -> ... / ... # 129| ... = ... -#-----| -> 5 +#-----| -> Constant # 129| regex -#-----| -> ... = ... +#-----| -> range # 129| /hello\s+[#{...}]/ -#-----| -> regex +#-----| -> ... = ... # 129| #{...} #-----| -> /hello\s+[#{...}]/ @@ -1337,10 +1337,10 @@ cfg.rb: #-----| -> EmptyClass # 130| Constant -#-----| -> ... = ... +#-----| -> 5 # 130| 5 -#-----| -> Constant +#-----| -> ... = ... # 133| EmptyClass #-----| -> EmptyModule @@ -1353,7 +1353,7 @@ cfg.rb: # 136| ... / ... #-----| raise -> "div by zero" -#-----| -> 1 +#-----| -> init # 136| 1 #-----| -> 0 @@ -1362,7 +1362,7 @@ cfg.rb: #-----| -> ... / ... # 136| call to puts -#-----| -> 1 +#-----| -> init # 136| "div by zero" #-----| -> call to puts @@ -1371,7 +1371,7 @@ cfg.rb: #-----| -> M # 138| (..., ...) -#-----| -> ... = ... +#-----| -> 1 # 138| init #-----| -> last @@ -1386,7 +1386,7 @@ cfg.rb: #-----| -> 3 # 138| 3 -#-----| -> init +#-----| -> ... = ... # 140| M #-----| -> Constant @@ -1419,7 +1419,7 @@ cfg.rb: #-----| -> "singleton" # 145| print -#-----| -> Silly +#-----| -> silly # 145| exit print @@ -1445,10 +1445,10 @@ cfg.rb: #-----| -> silly # 151| silly -#-----| -> ... = ... +#-----| -> Silly # 151| call to new -#-----| -> silly +#-----| -> ... = ... # 151| Silly #-----| -> call to new @@ -1494,7 +1494,7 @@ cfg.rb: #-----| -> exit two_parameters (normal) # 158| call to two_parameters -#-----| -> call to __FILE__ +#-----| -> scriptfile # 158| *... #-----| -> call to two_parameters @@ -1509,13 +1509,13 @@ cfg.rb: #-----| -> [...] # 160| ... = ... -#-----| -> :hello +#-----| -> symbol # 160| scriptfile -#-----| -> ... = ... +#-----| -> call to __FILE__ # 160| `cat "#{...}"` -#-----| -> scriptfile +#-----| -> ... = ... # 160| #{...} #-----| -> `cat "#{...}"` @@ -1524,22 +1524,22 @@ cfg.rb: #-----| -> #{...} # 162| ... = ... -#-----| -> 12 +#-----| -> delimited_symbol # 162| symbol -#-----| -> ... = ... +#-----| -> :hello # 162| :hello -#-----| -> symbol - -# 164| ... = ... -#-----| -> true - -# 164| delimited_symbol #-----| -> ... = ... +# 164| ... = ... +#-----| -> x + +# 164| delimited_symbol +#-----| -> 12 + # 164| :"goodbye-#{...}" -#-----| -> delimited_symbol +#-----| -> ... = ... # 164| #{...} #-----| -> :"goodbye-#{...}" @@ -1554,23 +1554,23 @@ cfg.rb: #-----| -> ... + ... # 166| ... = ... -#-----| -> true +#-----| -> x # 166| x -#-----| -> ... = ... +#-----| -> true # 166| true -#-----| -> x - -# 167| ... = ... -#-----| -> 42 - -# 167| x #-----| -> ... = ... -# 167| ! ... +# 167| ... = ... #-----| -> x +# 167| x +#-----| -> true + +# 167| ! ... +#-----| -> ... = ... + # 167| true #-----| -> ! ... @@ -1578,10 +1578,10 @@ cfg.rb: #-----| -> undef ... # 168| x -#-----| -> ... = ... +#-----| -> 42 # 168| - ... -#-----| -> x +#-----| -> ... = ... # 168| 42 #-----| -> - ... @@ -1637,7 +1637,7 @@ cfg.rb: #-----| -> ... == ... # 176| until ... -#-----| -> 0 +#-----| -> i # 176| ... > ... #-----| true -> until ... @@ -1668,16 +1668,16 @@ cfg.rb: #-----| -> i # 178| i -#-----| -> ... = ... +#-----| -> 0 # 178| 0 -#-----| -> i +#-----| -> ... = ... # 179| (...; ...) #-----| -> i # 179| ... until ... -#-----| -> 0 +#-----| -> x # 179| call to puts #-----| -> i @@ -1708,10 +1708,10 @@ cfg.rb: #-----| -> x # 181| x -#-----| -> ... = ... +#-----| -> 0 # 181| 0 -#-----| -> x +#-----| -> ... = ... # 182| while ... #-----| -> i @@ -2125,10 +2125,10 @@ ifs.rb: #-----| -> x # 20| x -#-----| -> ... = ... +#-----| -> x # 20| - ... -#-----| -> x +#-----| -> ... = ... # 20| x #-----| -> - ... @@ -2150,10 +2150,10 @@ ifs.rb: #-----| -> if ... # 22| x -#-----| -> ... = ... +#-----| -> x # 22| ... - ... -#-----| -> x +#-----| -> ... = ... # 22| x #-----| -> 1 From f37c862c92b791747fa92892d37a2d567bcbd3d0 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 14:27:29 +0000 Subject: [PATCH 36/39] Rename MandatoryParameterTree to NonDefaultValueParameterTree --- .../controlflow/internal/ControlFlowGraphImpl.qll | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 241fe949329..5c6816e0287 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -378,7 +378,7 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - abstract private class MandatoryParameterTree extends ControlFlowTree, NamedParameter { + abstract private class NonDefaultValueParameterTree extends ControlFlowTree, NamedParameter { final override predicate first(AstNode first) { this.getDefiningAccess().(ControlFlowTree).first(first) } @@ -394,7 +394,7 @@ module Trees { final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } } - private class BlockParameterTree extends MandatoryParameterTree, BlockParameter { } + private class BlockParameterTree extends NonDefaultValueParameterTree, BlockParameter { } /** * TODO: make all StmtSequence tree classes post-order, and simplify class @@ -888,7 +888,7 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - private class HashSplatParameterTree extends MandatoryParameterTree, HashSplatParameter { } + private class HashSplatParameterTree extends NonDefaultValueParameterTree, HashSplatParameter { } private class HereDocTree extends StandardPreOrderTree, HereDoc { final override ControlFlowTree getChildNode(int i) { result = this.getComponent(i) } @@ -1130,7 +1130,7 @@ module Trees { private class SelfTree extends LeafTree, Self { } - private class SimpleParameterTree extends MandatoryParameterTree, SimpleParameter { } + 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 @@ -1228,7 +1228,7 @@ module Trees { final override ControlFlowTree getChildNode(int i) { result = this.getValue() and i = 0 } } - private class SplatParameterTree extends MandatoryParameterTree, SplatParameter { } + private class SplatParameterTree extends NonDefaultValueParameterTree, SplatParameter { } abstract class StmtSequenceTree extends ControlFlowTree, StmtSequence { override predicate propagatesAbnormal(AstNode child) { none() } From 21192bf43cf29df4f7e4cdeac387354f21f33654 Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 14:28:26 +0000 Subject: [PATCH 37/39] Remove outdated comment --- ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 5c6816e0287..822bd8ced4a 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -1023,7 +1023,6 @@ module Trees { private class RedoStmtTree extends LeafTree, RedoStmt { } - /** A block that may contain `rescue`/`ensure`. */ private class RescueModifierTree extends PreOrderTree, RescueModifierExpr { final override predicate propagatesAbnormal(AstNode child) { child = this.getHandler() } From 7667606b89474b33e992a2253250df787d75a49e Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 14:31:17 +0000 Subject: [PATCH 38/39] Replace some uses of Generated types --- .../controlflow/internal/ControlFlowGraphImpl.qll | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index 822bd8ced4a..bf1448acdc4 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -1172,10 +1172,9 @@ module Trees { override predicate propagatesAbnormal(AstNode child) { child = this.getAStmt() } override predicate isHidden() { - // TODO: unhide, or avoid using Generated types - ASTInternal::toGenerated(this) instanceof Generated::Else or - ASTInternal::toGenerated(this) instanceof Generated::Then or - ASTInternal::toGenerated(this) instanceof Generated::Do + this instanceof ASTInternal::TElse or + this instanceof ASTInternal::TThen or + this instanceof ASTInternal::TDo } final AstNode getLastChildNode() { result = this.getStmt(this.getNumberOfStatements() - 1) } From cf7ce911bccc4ed049216d07d80e57a24f5d69fc Mon Sep 17 00:00:00 2001 From: Nick Rolfe Date: Fri, 19 Mar 2021 16:08:43 +0000 Subject: [PATCH 39/39] =?UTF-8?q?Combine=20CfgScope=20classes=20for=20Body?= =?UTF-8?q?Stmt=20=E2=88=A9=20Callable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../internal/ControlFlowGraphImpl.qll | 26 +------------------ 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll index bf1448acdc4..83091c2f87b 100644 --- a/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll +++ b/ql/src/codeql_ruby/controlflow/internal/ControlFlowGraphImpl.qll @@ -65,23 +65,7 @@ module CfgScope { } } - private class MethodScope extends Range_, Method { - final override predicate entry(AstNode first) { this.(Trees::BodyStmtTree).firstInner(first) } - - final override predicate exit(AstNode last, Completion c) { - this.(Trees::BodyStmtTree).lastInner(last, c) - } - } - - private class SingletonMethodScope extends Range_, SingletonMethod { - final override predicate entry(AstNode first) { this.(Trees::BodyStmtTree).firstInner(first) } - - final override predicate exit(AstNode last, Completion c) { - this.(Trees::BodyStmtTree).lastInner(last, c) - } - } - - private class DoBlockScope extends Range_, DoBlock { + 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) { @@ -98,14 +82,6 @@ module CfgScope { last(this.(Trees::BraceBlockTree).getLastChildNode(), last, c) } } - - private class LambdaScope extends Range_, Lambda { - final override predicate entry(AstNode first) { this.(Trees::BodyStmtTree).firstInner(first) } - - final override predicate exit(AstNode last, Completion c) { - this.(Trees::BodyStmtTree).lastInner(last, c) - } - } } abstract private class ControlFlowTree extends AstNode {