diff --git a/ql/src/codeql_ruby/ast/Control.qll b/ql/src/codeql_ruby/ast/Control.qll index 1b3d1c752a1..f41b711a8f7 100644 --- a/ql/src/codeql_ruby/ast/Control.qll +++ b/ql/src/codeql_ruby/ast/Control.qll @@ -308,6 +308,17 @@ class Loop extends ControlExpr { Expr getBody() { result = range.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; + + /** Gets the condition expression of this loop. */ + final Expr getCondition() { result = range.getCondition() } +} + /** * A `while` loop. * ```rb @@ -317,7 +328,7 @@ class Loop extends ControlExpr { * end * ``` */ -class WhileExpr extends Loop, @while { +class WhileExpr extends ConditionalLoop, @while { final override WhileExpr::Range range; final override string getAPrimaryQlClass() { result = "WhileExpr" } @@ -326,9 +337,6 @@ class WhileExpr extends Loop, @while { /** Gets the body of this `while` loop. */ final override ExprSequence getBody() { result = range.getBody() } - - /** Gets the condition expression of this `while` loop. */ - final Expr getCondition() { result = range.getCondition() } } /** @@ -340,7 +348,7 @@ class WhileExpr extends Loop, @while { * end * ``` */ -class UntilExpr extends Loop, @until { +class UntilExpr extends ConditionalLoop, @until { final override UntilExpr::Range range; final override string getAPrimaryQlClass() { result = "UntilExpr" } @@ -349,9 +357,6 @@ class UntilExpr extends Loop, @until { /** Gets the body of this `until` loop. */ final override ExprSequence getBody() { result = range.getBody() } - - /** Gets the condition expression of this `until` loop. */ - final Expr getCondition() { result = range.getCondition() } } /** @@ -360,21 +365,12 @@ class UntilExpr extends Loop, @until { * foo while bar * ``` */ -class WhileModifierExpr extends Loop, @while_modifier { +class WhileModifierExpr extends ConditionalLoop, @while_modifier { final override WhileModifierExpr::Range range; final override string getAPrimaryQlClass() { result = "WhileModifierExpr" } final override string toString() { result = "... while ..." } - - /** - * Gets the condition expression of this `while`-modifier. In the following - * example, the result is the `Expr` for `bar`. - * ```rb - * foo while bar - * ``` - */ - final Expr getCondition() { result = range.getCondition() } } /** @@ -383,21 +379,12 @@ class WhileModifierExpr extends Loop, @while_modifier { * foo until bar * ``` */ -class UntilModifierExpr extends Loop, @until_modifier { +class UntilModifierExpr extends ConditionalLoop, @until_modifier { final override UntilModifierExpr::Range range; final override string getAPrimaryQlClass() { result = "UntilModifierExpr" } final override string toString() { result = "... until ..." } - - /** - * Gets the condition expression of this `until`-modifier. In the following - * example, the result is the `Expr` for `bar`. - * ```rb - * foo until bar - * ``` - */ - final Expr getCondition() { result = range.getCondition() } } /** diff --git a/ql/src/codeql_ruby/ast/internal/Control.qll b/ql/src/codeql_ruby/ast/internal/Control.qll index 7e4164a999b..add4448581a 100644 --- a/ql/src/codeql_ruby/ast/internal/Control.qll +++ b/ql/src/codeql_ruby/ast/internal/Control.qll @@ -141,43 +141,49 @@ module Loop { } } +module ConditionalLoop { + abstract class Range extends Loop::Range { + abstract Expr getCondition(); + } +} + module WhileExpr { - class Range extends Loop::Range, @while { + class Range extends ConditionalLoop::Range, @while { final override Generated::While generated; final override ExprSequence getBody() { result = generated.getBody() } - final Expr getCondition() { result = generated.getCondition() } + final override Expr getCondition() { result = generated.getCondition() } } } module UntilExpr { - class Range extends Loop::Range, @until { + class Range extends ConditionalLoop::Range, @until { final override Generated::Until generated; final override ExprSequence getBody() { result = generated.getBody() } - final Expr getCondition() { result = generated.getCondition() } + final override Expr getCondition() { result = generated.getCondition() } } } module WhileModifierExpr { - class Range extends Loop::Range, @while_modifier { + class Range extends ConditionalLoop::Range, @while_modifier { final override Generated::WhileModifier generated; final override Expr getBody() { result = generated.getBody() } - final Expr getCondition() { result = generated.getCondition() } + final override Expr getCondition() { result = generated.getCondition() } } } module UntilModifierExpr { - class Range extends Loop::Range, @until_modifier { + class Range extends ConditionalLoop::Range, @until_modifier { final override Generated::UntilModifier generated; final override Expr getBody() { result = generated.getBody() } - final Expr getCondition() { result = generated.getCondition() } + final override Expr getCondition() { result = generated.getCondition() } } } diff --git a/ql/test/library-tests/ast/control/Loop.expected b/ql/test/library-tests/ast/control/Loop.expected index 6f46d76883c..81f479ae4e6 100644 --- a/ql/test/library-tests/ast/control/Loop.expected +++ b/ql/test/library-tests/ast/control/Loop.expected @@ -9,6 +9,13 @@ loops | loops.rb:49:1:52:3 | until ... | UntilExpr | loops.rb:49:13:52:3 | ...; ... | ExprSequence | | loops.rb:55:1:58:3 | until ... | UntilExpr | loops.rb:55:13:58:3 | ...; ... | ExprSequence | | loops.rb:61:1:61:19 | ... until ... | UntilModifierExpr | loops.rb:61:1:61:6 | ... -= ... | AssignSubExpr | +conditionalLoops +| loops.rb:34:1:37:3 | while ... | WhileExpr | loops.rb:34:7:34:11 | ... < ... | loops.rb:34:12:37:3 | ...; ... | ExprSequence | +| loops.rb:40:1:43:3 | while ... | WhileExpr | loops.rb:40:7:40:11 | ... < ... | loops.rb:40:13:43:3 | ...; ... | ExprSequence | +| loops.rb:46:1:46:19 | ... while ... | WhileModifierExpr | loops.rb:46:14:46:19 | ... >= ... | loops.rb:46:1:46:6 | ... += ... | AssignAddExpr | +| loops.rb:49:1:52:3 | until ... | UntilExpr | loops.rb:49:7:49:12 | ... == ... | loops.rb:49:13:52:3 | ...; ... | ExprSequence | +| loops.rb:55:1:58:3 | until ... | UntilExpr | loops.rb:55:7:55:11 | ... > ... | loops.rb:55:13:58:3 | ...; ... | ExprSequence | +| loops.rb:61:1:61:19 | ... until ... | UntilModifierExpr | loops.rb:61:14:61:19 | ... == ... | loops.rb:61:1:61:6 | ... -= ... | AssignSubExpr | forExprs | loops.rb:9:1:12:3 | for ... in ... | loops.rb:9:5:9:5 | n | loops.rb:9:15:12:3 | ...; ... | 0 | loops.rb:10:5:10:12 | ... += ... | | loops.rb:9:1:12:3 | for ... in ... | loops.rb:9:5:9:5 | n | loops.rb:9:15:12:3 | ...; ... | 1 | loops.rb:11:5:11:11 | ... = ... | diff --git a/ql/test/library-tests/ast/control/Loop.ql b/ql/test/library-tests/ast/control/Loop.ql index cde967a7d9a..e8b2b30b107 100644 --- a/ql/test/library-tests/ast/control/Loop.ql +++ b/ql/test/library-tests/ast/control/Loop.ql @@ -4,6 +4,15 @@ query predicate loops(Loop l, string lClass, Expr body, string bodyClass) { l.getBody() = body and lClass = l.getAPrimaryQlClass() and bodyClass = body.getAPrimaryQlClass() } +query predicate conditionalLoops( + ConditionalLoop l, string lClass, Expr cond, Expr body, string bodyClass +) { + l.getBody() = body and + lClass = l.getAPrimaryQlClass() and + bodyClass = body.getAPrimaryQlClass() and + cond = l.getCondition() +} + query predicate forExprs(ForExpr f, Pattern p, ExprSequence body, int i, Expr bodyChild) { p = f.getPattern() and body = f.getBody() and