From 214f1130165ada0ee47d2711aef6c11b6dad28c5 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 17 Feb 2021 13:36:01 +0100 Subject: [PATCH 1/8] AST: add getChild/getParent method --- ql/src/codeql_ruby/AST.qll | 6 ++++++ ql/src/codeql_ruby/ast/internal/AST.qll | 2 ++ 2 files changed, 8 insertions(+) diff --git a/ql/src/codeql_ruby/AST.qll b/ql/src/codeql_ruby/AST.qll index 11c41330aa4..28a05877c5f 100644 --- a/ql/src/codeql_ruby/AST.qll +++ b/ql/src/codeql_ruby/AST.qll @@ -36,4 +36,10 @@ class AstNode extends @ast_node { /** Gets the location of this node. */ final Location getLocation() { result = range.getLocation() } + + /** Gets a child node of this `AstNode`. */ + final AstNode getAChild() { range.child(_, result) } + + /** Gets the parent of this `AstNode`, if this node is not a root node. */ + final AstNode getParent() { result.getAChild() = this } } diff --git a/ql/src/codeql_ruby/ast/internal/AST.qll b/ql/src/codeql_ruby/ast/internal/AST.qll index b09355148a1..8fb1159c83f 100644 --- a/ql/src/codeql_ruby/ast/internal/AST.qll +++ b/ql/src/codeql_ruby/ast/internal/AST.qll @@ -11,6 +11,8 @@ module AstNode { abstract string toString(); Location getLocation() { result = generated.getLocation() } + + predicate child(string label, AstNode::Range child) { none() } } // TODO: Remove From e42d1ff936b5c9efef4c52999777194dfe037ca9 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 13:19:22 +0100 Subject: [PATCH 2/8] Change Expr to LhsExpr for getVariableExpr --- ql/src/codeql_ruby/ast/Expr.qll | 2 +- ql/src/codeql_ruby/ast/internal/Expr.qll | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Expr.qll b/ql/src/codeql_ruby/ast/Expr.qll index fa0bc6af7c9..7b6537e8166 100644 --- a/ql/src/codeql_ruby/ast/Expr.qll +++ b/ql/src/codeql_ruby/ast/Expr.qll @@ -177,7 +177,7 @@ class RescueClause extends Expr, @rescue { * end * ``` */ - final Expr getVariableExpr() { result = range.getVariableExpr() } + final LhsExpr getVariableExpr() { result = range.getVariableExpr() } /** * Gets the exception handler body. diff --git a/ql/src/codeql_ruby/ast/internal/Expr.qll b/ql/src/codeql_ruby/ast/internal/Expr.qll index fabab40880c..f258501e309 100644 --- a/ql/src/codeql_ruby/ast/internal/Expr.qll +++ b/ql/src/codeql_ruby/ast/internal/Expr.qll @@ -123,9 +123,9 @@ module RescueClause { class Range extends Expr::Range, @rescue { final override Generated::Rescue generated; - final LhsExpr getException(int n) { result = generated.getExceptions().getChild(n) } + final Expr getException(int n) { result = generated.getExceptions().getChild(n) } - final Expr getVariableExpr() { result = generated.getVariable() } + final LhsExpr getVariableExpr() { result = generated.getVariable().getChild() } final StmtSequence getBody() { result = generated.getBody() } From 095eb803b3f7152838f00842f3ac770ef2ba2053 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 13:43:14 +0100 Subject: [PATCH 3/8] AST: improve type of getDefaultValue --- ql/src/codeql_ruby/ast/Parameter.qll | 6 ++---- ql/src/codeql_ruby/ast/internal/Parameter.qll | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Parameter.qll b/ql/src/codeql_ruby/ast/Parameter.qll index 76cba8c717f..88d77727488 100644 --- a/ql/src/codeql_ruby/ast/Parameter.qll +++ b/ql/src/codeql_ruby/ast/Parameter.qll @@ -120,9 +120,8 @@ class KeywordParameter extends @keyword_parameter, NamedParameter { * 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. - * TODO: better return type (Expr?) */ - final AstNode getDefaultValue() { result = range.getDefaultValue() } + final Expr getDefaultValue() { result = range.getDefaultValue() } /** * Holds if the parameter is optional. That is, there is a default value that @@ -148,9 +147,8 @@ 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. - * TODO: better return type (Expr?) */ - final AstNode getDefaultValue() { result = range.getDefaultValue() } + final Expr getDefaultValue() { result = range.getDefaultValue() } } /** diff --git a/ql/src/codeql_ruby/ast/internal/Parameter.qll b/ql/src/codeql_ruby/ast/internal/Parameter.qll index a891df165f0..5d4026eda5c 100644 --- a/ql/src/codeql_ruby/ast/internal/Parameter.qll +++ b/ql/src/codeql_ruby/ast/internal/Parameter.qll @@ -100,7 +100,7 @@ module KeywordParameter { result = TLocalVariable(_, _, generated.getName()) } - final Generated::AstNode getDefaultValue() { result = generated.getValue() } + final Expr::Range getDefaultValue() { result = generated.getValue() } final override string toString() { result = this.getName() } @@ -116,7 +116,7 @@ module OptionalParameter { result = TLocalVariable(_, _, generated.getName()) } - final Generated::AstNode getDefaultValue() { result = generated.getValue() } + final Expr::Range getDefaultValue() { result = generated.getValue() } final override string toString() { result = this.getName() } From c0b5ac760adce486a90a2c8aba3a71035bfe26a1 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 13:25:03 +0100 Subject: [PATCH 4/8] AST: rename getLhs/getRhs to getLeftOperand/getRightOperand --- ql/src/codeql_ruby/ast/Operation.qll | 4 ++-- ql/src/codeql_ruby/ast/Variable.qll | 2 +- ql/src/codeql_ruby/ast/internal/Operation.qll | 16 +++++++++------- ql/src/codeql_ruby/controlflow/CfgNodes.qll | 4 ++-- .../library-tests/ast/operations/assignment.ql | 8 ++++---- 5 files changed, 18 insertions(+), 16 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Operation.qll b/ql/src/codeql_ruby/ast/Operation.qll index 3544a9dd3f4..1b2458777f5 100644 --- a/ql/src/codeql_ruby/ast/Operation.qll +++ b/ql/src/codeql_ruby/ast/Operation.qll @@ -445,10 +445,10 @@ class Assignment extends Operation { override Assignment::Range range; /** Gets the left hand side of this assignment. */ - final Expr getLhs() { result = range.getLhs() } + final Expr getLeftOperand() { result = range.getLeftOperand() } /** Gets the right hand side of this assignment. */ - final Expr getRhs() { result = range.getRhs() } + final Expr getRightOperand() { result = range.getRightOperand() } } /** diff --git a/ql/src/codeql_ruby/ast/Variable.qll b/ql/src/codeql_ruby/ast/Variable.qll index 0159c63b6f0..6f283d813ed 100644 --- a/ql/src/codeql_ruby/ast/Variable.qll +++ b/ql/src/codeql_ruby/ast/Variable.qll @@ -156,7 +156,7 @@ class VariableReadAccess extends VariableAccess { not this instanceof VariableWriteAccess or // `x` in `x += y` is considered both a read and a write - this = any(AssignOperation a).getLhs() + this = any(AssignOperation a).getLeftOperand() } } diff --git a/ql/src/codeql_ruby/ast/internal/Operation.qll b/ql/src/codeql_ruby/ast/internal/Operation.qll index 0a0715961f3..c55af802ef8 100644 --- a/ql/src/codeql_ruby/ast/internal/Operation.qll +++ b/ql/src/codeql_ruby/ast/internal/Operation.qll @@ -218,11 +218,13 @@ module NoRegexMatchExpr { module Assignment { abstract class Range extends Operation::Range { - abstract Expr getLhs(); + abstract Pattern getLeftOperand(); - abstract Expr getRhs(); + abstract Expr getRightOperand(); - final override Expr getAnOperand() { result = this.getLhs() or result = this.getRhs() } + final override Expr getAnOperand() { + result = this.getLeftOperand() or result = this.getRightOperand() + } override string toString() { result = "... " + this.getOperator() + " ..." } } @@ -232,9 +234,9 @@ module AssignExpr { class Range extends Assignment::Range, @assignment { final override Generated::Assignment generated; - final override Expr getLhs() { result = generated.getLeft() } + final override Pattern getLeftOperand() { result = generated.getLeft() } - final override Expr getRhs() { result = generated.getRight() } + final override Expr getRightOperand() { result = generated.getRight() } final override string getOperator() { result = "=" } } @@ -246,9 +248,9 @@ module AssignOperation { final override string getOperator() { result = generated.getOperator() } - final override LhsExpr getLhs() { result = generated.getLeft() } + final override LhsExpr getLeftOperand() { result = generated.getLeft() } - final override Expr getRhs() { result = generated.getRight() } + final override Expr getRightOperand() { result = generated.getRight() } } } diff --git a/ql/src/codeql_ruby/controlflow/CfgNodes.qll b/ql/src/codeql_ruby/controlflow/CfgNodes.qll index 58b0bf717df..2258f02c28b 100644 --- a/ql/src/codeql_ruby/controlflow/CfgNodes.qll +++ b/ql/src/codeql_ruby/controlflow/CfgNodes.qll @@ -218,10 +218,10 @@ module ExprNodes { final override Assignment getExpr() { result = ExprCfgNode.super.getExpr() } /** Gets the LHS of this assignment. */ - final ExprCfgNode getLhs() { e.hasCfgChild(e.getLhs(), this, result) } + final ExprCfgNode getLhs() { e.hasCfgChild(e.getLeftOperand(), this, result) } /** Gets the RHS of this assignment. */ - final ExprCfgNode getRhs() { e.hasCfgChild(e.getRhs(), this, result) } + final ExprCfgNode getRhs() { e.hasCfgChild(e.getRightOperand(), this, result) } } /** A control-flow node that wraps an `AssignExpr` AST expression. */ diff --git a/ql/test/library-tests/ast/operations/assignment.ql b/ql/test/library-tests/ast/operations/assignment.ql index 83828da3ce7..3ff3b3f7684 100644 --- a/ql/test/library-tests/ast/operations/assignment.ql +++ b/ql/test/library-tests/ast/operations/assignment.ql @@ -2,8 +2,8 @@ import ruby query predicate assignments(Assignment a, string operator, Expr left, Expr right, string pClass) { operator = a.getOperator() and - left = a.getLhs() and - right = a.getRhs() and + left = a.getLeftOperand() and + right = a.getRightOperand() and pClass = a.getAPrimaryQlClass() } @@ -11,8 +11,8 @@ query predicate assignOperations( AssignOperation o, string operator, Expr left, Expr right, string pClass ) { operator = o.getOperator() and - left = o.getLhs() and - right = o.getRhs() and + left = o.getLeftOperand() and + right = o.getRightOperand() and pClass = o.getAPrimaryQlClass() } From 5659388ec087b2f297a38203be2dff6cc2d46609 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 13:33:08 +0100 Subject: [PATCH 5/8] AST: implement AstNode::child --- ql/src/codeql_ruby/ast/internal/Call.qll | 21 ++++++ ql/src/codeql_ruby/ast/internal/Constant.qll | 5 ++ ql/src/codeql_ruby/ast/internal/Control.qll | 73 +++++++++++++++++++ ql/src/codeql_ruby/ast/internal/Expr.qll | 39 ++++++++++ ql/src/codeql_ruby/ast/internal/Literal.qll | 22 ++++++ ql/src/codeql_ruby/ast/internal/Method.qll | 35 +++++++++ ql/src/codeql_ruby/ast/internal/Module.qll | 27 +++++++ ql/src/codeql_ruby/ast/internal/Operation.qll | 37 +++++++++- ql/src/codeql_ruby/ast/internal/Parameter.qll | 6 ++ ql/src/codeql_ruby/ast/internal/Pattern.qll | 4 + ql/src/codeql_ruby/ast/internal/Statement.qll | 16 ++++ ql/src/codeql_ruby/ast/internal/Variable.qll | 2 + 12 files changed, 286 insertions(+), 1 deletion(-) diff --git a/ql/src/codeql_ruby/ast/internal/Call.qll b/ql/src/codeql_ruby/ast/internal/Call.qll index b8eb59965d6..ca35a21e07c 100644 --- a/ql/src/codeql_ruby/ast/internal/Call.qll +++ b/ql/src/codeql_ruby/ast/internal/Call.qll @@ -1,4 +1,5 @@ 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 private import codeql_ruby.ast.internal.Variable @@ -14,6 +15,14 @@ module Call { abstract Block getBlock(); override string toString() { result = "call to " + this.getMethodName() } + + final override predicate child(string label, AstNode::Range child) { + label = "getReceiver" and child = getReceiver() + or + label = "getArgument" and child = getArgument(_) + or + label = "getBlock" and child = getBlock() + } } private class IdentifierCallRange extends Call::Range, @token_identifier { @@ -144,6 +153,10 @@ module BlockArgument { final Expr getExpr() { result = generated.getChild() } final override string toString() { result = "&..." } + + final override predicate child(string label, AstNode::Range child) { + label = "getExpr" and child = getExpr() + } } } @@ -154,6 +167,10 @@ module SplatArgument { final Expr getExpr() { result = generated.getChild() } final override string toString() { result = "*..." } + + final override predicate child(string label, AstNode::Range child) { + label = "getExpr" and child = getExpr() + } } } @@ -164,5 +181,9 @@ module HashSplatArgument { final Expr getExpr() { result = generated.getChild() } final override string toString() { result = "**..." } + + final override predicate child(string label, AstNode::Range child) { + label = "getExpr" and child = getExpr() + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Constant.qll b/ql/src/codeql_ruby/ast/internal/Constant.qll index f40feda50b3..87082ae35c7 100644 --- a/ql/src/codeql_ruby/ast/internal/Constant.qll +++ b/ql/src/codeql_ruby/ast/internal/Constant.qll @@ -1,3 +1,4 @@ +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 @@ -12,6 +13,10 @@ module ConstantAccess { abstract Expr::Range getScopeExpr(); abstract predicate hasGlobalScope(); + + override predicate child(string label, AstNode::Range child) { + label = "getScopeExpr" and child = getScopeExpr() + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Control.qll b/ql/src/codeql_ruby/ast/internal/Control.qll index 518cb8e7505..c8a1e9d300c 100644 --- a/ql/src/codeql_ruby/ast/internal/Control.qll +++ b/ql/src/codeql_ruby/ast/internal/Control.qll @@ -1,4 +1,5 @@ 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 @@ -12,6 +13,12 @@ module ConditionalExpr { abstract Expr getCondition(); abstract Expr getBranch(boolean cond); + + override predicate child(string label, AstNode::Range child) { + label = "getCondition" and child = getCondition() + or + label = "getBranch" and child = getBranch(_) + } } } @@ -24,6 +31,14 @@ module IfExpr { 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 { @@ -76,6 +91,14 @@ module UnlessExpr { } 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() + } } } @@ -90,6 +113,12 @@ module IfModifierExpr { final override Expr getBranch(boolean cond) { cond = true and result = getExpr() } final override string toString() { result = "... if ..." } + + override predicate child(string label, AstNode::Range child) { + ConditionalExpr::Range.super.child(label, child) + or + label = "getExpr" and child = getExpr() + } } } @@ -104,6 +133,12 @@ module UnlessModifierExpr { final override Expr getBranch(boolean cond) { cond = false and result = getExpr() } final override string toString() { result = "... unless ..." } + + override predicate child(string label, AstNode::Range child) { + ConditionalExpr::Range.super.child(label, child) + or + label = "getExpr" and child = getExpr() + } } } @@ -124,6 +159,14 @@ module TernaryIfExpr { } 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() + } } } @@ -136,6 +179,12 @@ module CaseExpr { 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(_) + } } } @@ -148,18 +197,34 @@ module WhenExpr { 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 Expr 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() + } } } @@ -222,5 +287,13 @@ module ForExpr { 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 index f258501e309..1d10bf85680 100644 --- a/ql/src/codeql_ruby/ast/internal/Expr.qll +++ b/ql/src/codeql_ruby/ast/internal/Expr.qll @@ -1,4 +1,5 @@ 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 @@ -30,6 +31,10 @@ module StmtSequence { c > 1 and result = "...; ..." ) } + + override predicate child(string label, AstNode::Range child) { + label = "getStmt" and child = getStmt(_) + } } } @@ -56,6 +61,16 @@ module BodyStatement { 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() + } } } @@ -130,6 +145,14 @@ module RescueClause { 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() + } } } @@ -142,6 +165,12 @@ module RescueModifierExpr { 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() + } } } @@ -154,6 +183,12 @@ module Pair { 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() + } } } @@ -164,5 +199,9 @@ module StringConcatenation { 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 index bd2243b93c5..2aab1ede55e 100644 --- a/ql/src/codeql_ruby/ast/internal/Literal.qll +++ b/ql/src/codeql_ruby/ast/internal/Literal.qll @@ -128,6 +128,10 @@ module StringInterpolationComponent { } final override string getValueText() { none() } + + override predicate child(string label, AstNode::Range child) { + StmtSequence::Range.super.child(label, child) + } } } @@ -176,6 +180,10 @@ module StringlikeLiteral { result = this.getStartDelimiter() + summary + this.getEndDelimiter() ) } + + override predicate child(string label, AstNode::Range child) { + label = "getComponent" and child = getComponent(_) + } } } @@ -340,6 +348,10 @@ module ArrayLiteral { 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 { @@ -376,6 +388,10 @@ module HashLiteral { 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(_) + } } } @@ -394,6 +410,12 @@ module RangeLiteral { 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() + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Method.qll b/ql/src/codeql_ruby/ast/internal/Method.qll index f4d3779e639..49540f9b788 100644 --- a/ql/src/codeql_ruby/ast/internal/Method.qll +++ b/ql/src/codeql_ruby/ast/internal/Method.qll @@ -1,4 +1,5 @@ private import codeql_ruby.AST +private import codeql_ruby.ast.internal.AST private import codeql_ruby.ast.internal.Expr private import codeql_ruby.ast.internal.Parameter private import TreeSitter @@ -6,6 +7,10 @@ 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(_) + } } } @@ -25,6 +30,10 @@ 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) + } } } @@ -45,6 +54,14 @@ module SingletonMethod { final override Generated::AstNode getChild(int i) { result = generated.getChild(i) } final override string toString() { result = this.getName() } + + override predicate child(string label, AstNode::Range child) { + Callable::Range.super.child(label, child) + or + BodyStatement::Range.super.child(label, child) + or + label = "getObject" and child = getObject() + } } } @@ -62,12 +79,24 @@ module Lambda { } final override string toString() { result = "-> { ... }" } + + override predicate child(string label, AstNode::Range child) { + Callable::Range.super.child(label, child) + or + BodyStatement::Range.super.child(label, child) + } } } module Block { abstract class Range extends Callable::Range, StmtSequence::Range { Range() { not generated.getParent() instanceof Generated::Lambda } + + override predicate child(string label, AstNode::Range child) { + Callable::Range.super.child(label, child) + or + StmtSequence::Range.super.child(label, child) + } } } @@ -82,6 +111,12 @@ module DoBlock { } 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) + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Module.qll b/ql/src/codeql_ruby/ast/internal/Module.qll index 3897435bf4b..79bb29429cd 100644 --- a/ql/src/codeql_ruby/ast/internal/Module.qll +++ b/ql/src/codeql_ruby/ast/internal/Module.qll @@ -1,4 +1,5 @@ private import codeql_ruby.AST +private import codeql_ruby.ast.internal.AST private import codeql_ruby.ast.internal.Constant private import codeql_ruby.ast.internal.Expr private import codeql_ruby.ast.internal.TreeSitter @@ -23,6 +24,12 @@ module Toplevel { } 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(_) + } } } @@ -52,6 +59,14 @@ module Class { final Expr getSuperclassExpr() { result = generated.getSuperclass().getChild() } final override string toString() { result = this.getName() } + + override predicate child(string label, AstNode::Range child) { + ModuleBase::Range.super.child(label, child) + or + ConstantWriteAccess::Range.super.child(label, child) + or + label = "getSuperclassExpr" and child = getSuperclassExpr() + } } } @@ -64,6 +79,12 @@ module SingletonClass { 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() + } } } @@ -91,5 +112,11 @@ module Module { } final override string toString() { result = this.getName() } + + override predicate child(string label, AstNode::Range child) { + ModuleBase::Range.super.child(label, child) + or + ConstantWriteAccess::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 index c55af802ef8..f6ad5e158d0 100644 --- a/ql/src/codeql_ruby/ast/internal/Operation.qll +++ b/ql/src/codeql_ruby/ast/internal/Operation.qll @@ -1,4 +1,5 @@ -import codeql_ruby.AST +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 @@ -7,6 +8,10 @@ module Operation { abstract string getOperator(); abstract Expr getAnOperand(); + + override predicate child(string label, AstNode::Range child) { + label = "getAnOperand" and child = getAnOperand() + } } } @@ -21,6 +26,12 @@ module UnaryOperation { 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() + } } } @@ -73,6 +84,14 @@ module BinaryOperation { } 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() + } } } @@ -169,6 +188,14 @@ module RelationalOperation { 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() + } } } @@ -227,6 +254,14 @@ module Assignment { } 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() + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Parameter.qll b/ql/src/codeql_ruby/ast/internal/Parameter.qll index 5d4026eda5c..d73339e4da4 100644 --- a/ql/src/codeql_ruby/ast/internal/Parameter.qll +++ b/ql/src/codeql_ruby/ast/internal/Parameter.qll @@ -1,6 +1,7 @@ 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_ruby.ast.internal.Pattern @@ -61,6 +62,11 @@ module TuplePatternParameter { 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) + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Pattern.qll b/ql/src/codeql_ruby/ast/internal/Pattern.qll index 205439881d2..44ea94e2704 100644 --- a/ql/src/codeql_ruby/ast/internal/Pattern.qll +++ b/ql/src/codeql_ruby/ast/internal/Pattern.qll @@ -101,5 +101,9 @@ module TuplePattern { 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/Statement.qll b/ql/src/codeql_ruby/ast/internal/Statement.qll index 7c0aee758bd..8fd7291b503 100644 --- a/ql/src/codeql_ruby/ast/internal/Statement.qll +++ b/ql/src/codeql_ruby/ast/internal/Statement.qll @@ -12,6 +12,8 @@ module EmptyStmt { final override Generated::EmptyStatement generated; final override string toString() { result = ";" } + + override predicate child(string label, AstNode::Range child) { none() } } } @@ -32,6 +34,10 @@ module UndefStmt { 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(_) + } } } @@ -44,6 +50,12 @@ module AliasStmt { 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() + } } } @@ -60,6 +72,10 @@ module ReturningStmt { result = a and c > 1 ) } + + override predicate child(string label, AstNode::Range child) { + label = "getValue" and child = getValue() + } } } diff --git a/ql/src/codeql_ruby/ast/internal/Variable.qll b/ql/src/codeql_ruby/ast/internal/Variable.qll index 24f1cb1e733..a0e2ea83056 100644 --- a/ql/src/codeql_ruby/ast/internal/Variable.qll +++ b/ql/src/codeql_ruby/ast/internal/Variable.qll @@ -578,6 +578,8 @@ module VariableAccess { abstract Variable getVariable(); final override string toString() { result = this.getVariable().getName() } + + override predicate child(string label, AstNode::Range child) { none() } } } From 3ee83870b6def22a1cf75ed1b01b81149b407271 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 13:41:47 +0100 Subject: [PATCH 6/8] AST: add begin expressions --- ql/src/codeql_ruby/ast/Statement.qll | 16 ++++++++++++++- ql/src/codeql_ruby/ast/internal/Expr.qll | 10 ---------- ql/src/codeql_ruby/ast/internal/Statement.qll | 20 +++++++++++++++++++ 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Statement.qll b/ql/src/codeql_ruby/ast/Statement.qll index e30f0c15fd7..2e6e6c37509 100644 --- a/ql/src/codeql_ruby/ast/Statement.qll +++ b/ql/src/codeql_ruby/ast/Statement.qll @@ -36,7 +36,21 @@ class EmptyStmt extends Stmt, @token_empty_statement { } /** - * An `BEGIN` block. + * A `begin` statement. + * ```rb + * begin + * puts "hello world" + * end + * ``` + */ +class BeginExpr extends BodyStatement, @begin { + final override Begin::Range range; + + final override string getAPrimaryQlClass() { result = "BeginExpr" } +} + +/** + * A `BEGIN` block. * ```rb * BEGIN { puts "starting ..." } * ``` diff --git a/ql/src/codeql_ruby/ast/internal/Expr.qll b/ql/src/codeql_ruby/ast/internal/Expr.qll index 1d10bf85680..337589a5653 100644 --- a/ql/src/codeql_ruby/ast/internal/Expr.qll +++ b/ql/src/codeql_ruby/ast/internal/Expr.qll @@ -90,16 +90,6 @@ module ParenthesizedExpr { } } -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 ThenExpr { class Range extends StmtSequence::Range, @then { final override Generated::Then generated; diff --git a/ql/src/codeql_ruby/ast/internal/Statement.qll b/ql/src/codeql_ruby/ast/internal/Statement.qll index 8fd7291b503..08d5d2a19bc 100644 --- a/ql/src/codeql_ruby/ast/internal/Statement.qll +++ b/ql/src/codeql_ruby/ast/internal/Statement.qll @@ -17,6 +17,26 @@ module EmptyStmt { } } +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; From c877eb46428020f6a46ad36b04a84a85d4af5a4b Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 13:42:52 +0100 Subject: [PATCH 7/8] AST: add additional token-types to variable patterns --- ql/src/codeql_ruby/ast/Pattern.qll | 2 +- ql/src/codeql_ruby/ast/internal/Pattern.qll | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/ql/src/codeql_ruby/ast/Pattern.qll b/ql/src/codeql_ruby/ast/Pattern.qll index 4be8084c198..82ed5d1ff90 100644 --- a/ql/src/codeql_ruby/ast/Pattern.qll +++ b/ql/src/codeql_ruby/ast/Pattern.qll @@ -35,7 +35,7 @@ class LhsExpr extends Pattern, Expr { } /** A simple variable pattern. */ -class VariablePattern extends Pattern { +class VariablePattern extends Pattern, VariablePattern::VariableToken { override VariablePattern::Range range; /** Gets the variable used in (or introduced by) this pattern. */ diff --git a/ql/src/codeql_ruby/ast/internal/Pattern.qll b/ql/src/codeql_ruby/ast/internal/Pattern.qll index 44ea94e2704..275e483107a 100644 --- a/ql/src/codeql_ruby/ast/internal/Pattern.qll +++ b/ql/src/codeql_ruby/ast/internal/Pattern.qll @@ -64,8 +64,11 @@ module LhsExpr { } module VariablePattern { - class Range extends LhsExpr::Range, @token_identifier { - override Generated::Identifier generated; + 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() } From 1c8a76f44a71f781e648c6a083a6ec9b389c2a61 Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Thu, 18 Feb 2021 18:14:55 +0100 Subject: [PATCH 8/8] AST: make Assignment::getLeftOperand a Pattern again --- ql/src/codeql_ruby/ast/internal/Operation.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ql/src/codeql_ruby/ast/internal/Operation.qll b/ql/src/codeql_ruby/ast/internal/Operation.qll index f6ad5e158d0..9144598e139 100644 --- a/ql/src/codeql_ruby/ast/internal/Operation.qll +++ b/ql/src/codeql_ruby/ast/internal/Operation.qll @@ -245,7 +245,7 @@ module NoRegexMatchExpr { module Assignment { abstract class Range extends Operation::Range { - abstract Pattern getLeftOperand(); + abstract Expr getLeftOperand(); abstract Expr getRightOperand(); @@ -269,7 +269,7 @@ module AssignExpr { class Range extends Assignment::Range, @assignment { final override Generated::Assignment generated; - final override Pattern getLeftOperand() { result = generated.getLeft() } + final override Expr getLeftOperand() { result = generated.getLeft() } final override Expr getRightOperand() { result = generated.getRight() }