From c991d550cdc73043170adfb88e151223b6bec84e Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Mon, 8 Feb 2021 18:58:09 +0100 Subject: [PATCH] AST: add Statement and ReturningStatement --- ql/src/codeql_ruby/AST.qll | 1 + ql/src/codeql_ruby/ast/Statement.qll | 97 +++++++++++++++++++ ql/src/codeql_ruby/ast/internal/Statement.qll | 44 +++++++++ 3 files changed, 142 insertions(+) create mode 100644 ql/src/codeql_ruby/ast/Statement.qll create 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 5131e6fd027..c914be66882 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -7,6 +7,7 @@ import ast.Module import ast.Parameter import ast.Operation import ast.Pattern +import ast.Statement import ast.Variable private import ast.internal.TreeSitter diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll new file mode 100644 index 00000000000..28ae2f933e3 --- /dev/null +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -0,0 +1,97 @@ +private import codeql_ruby.AST +private import codeql_ruby.CFG +private import internal.Statement +private import codeql_ruby.controlflow.internal.ControlFlowGraphImpl +private import codeql_ruby.ast.internal.TreeSitter + +/** + * A statement. + * + * This is the root QL class for all statements. + */ +class Statement extends AstNode { + Statement::Range range; + + Statement() { this = range } + + /** Gets a control-flow node for this statement, if any. */ + CfgNodes::AstCfgNode getAControlFlowNode() { result.getNode() = this } + + /** Gets the control-flow scope of this statement, if any. */ + CfgScope getCfgScope() { result = getCfgScope(this) } + + /** Gets the enclosing callable, if any. */ + Callable getEnclosingCallable() { result = this.getCfgScope() } +} + +/** + * A statement that may return a value: `return`, `break` and `next`. + * + * ```rb + * return + * return value + * break + * break value + * next + * next value + * ``` + */ +class ReturningStatement extends Statement { + override ReturningStatement::Range range; + + final override string toString() { + not exists(getValue()) and result = range.getStatementName() + or + result = range.getStatementName() + " " + getValue().toString() + } + + /** Gets the returned value, if any. */ + final Expr getValue() { + exists(Generated::ArgumentList a, int c | + a = range.getArgumentList() and c = count(a.getChild(_)) + | + result = a.getChild(0) and c = 1 + or + result = a and c > 1 + ) + } +} + +/** + * A `return` statement. + * ```rb + * return + * return value + * ``` + */ +class ReturnStmt extends ReturningStatement, @return { + final override ReturnStmt::Range range; + + final override string getAPrimaryQlClass() { result = "ReturnStmt" } +} + +/** + * A `break` statement. + * ```rb + * break + * break value + * ``` + */ +class BreakStmt extends ReturningStatement, @break { + final override BreakStmt::Range range; + + final override string getAPrimaryQlClass() { result = "BreakStmt" } +} + +/** + * A `next` statement. + * ```rb + * next + * next value + * ``` + */ +class NextStmt extends ReturningStatement, @next { + final override NextStmt::Range range; + + final override string getAPrimaryQlClass() { result = "NextStmt" } +} diff --git a/ql/src/codeql_ruby/ast/internal/Statement.qll b/ql/src/codeql_ruby/ast/internal/Statement.qll new file mode 100644 index 00000000000..72da586ebbb --- /dev/null +++ b/ql/src/codeql_ruby/ast/internal/Statement.qll @@ -0,0 +1,44 @@ +private import codeql_ruby.AST +private import codeql_ruby.ast.internal.TreeSitter + +module Statement { + abstract class Range extends AstNode { } +} + +module ReturningStatement { + abstract class Range extends Statement::Range { + abstract Generated::ArgumentList getArgumentList(); + + abstract string getStatementName(); + } +} + +module ReturnStmt { + class Range extends ReturningStatement::Range, @return { + final override Generated::Return generated; + + final override string getStatementName() { result = "return" } + + final override Generated::ArgumentList getArgumentList() { result = generated.getChild() } + } +} + +module BreakStmt { + class Range extends ReturningStatement::Range, @break { + final override Generated::Break generated; + + final override string getStatementName() { result = "break" } + + final override Generated::ArgumentList getArgumentList() { result = generated.getChild() } + } +} + +module NextStmt { + class Range extends ReturningStatement::Range, @next { + final override Generated::Next generated; + + final override string getStatementName() { result = "next" } + + final override Generated::ArgumentList getArgumentList() { result = generated.getChild() } + } +}