mirror of
https://github.com/github/codeql.git
synced 2026-02-20 17:03:41 +01:00
Add AST library for control expressions (conditionals and loops)
This commit is contained in:
404
ql/src/codeql_ruby/ast/Control.qll
Normal file
404
ql/src/codeql_ruby/ast/Control.qll
Normal file
@@ -0,0 +1,404 @@
|
||||
private import codeql_ruby.AST
|
||||
private import internal.Control
|
||||
|
||||
/**
|
||||
* A control expression that can be any of the following:
|
||||
* - `case`
|
||||
* - `if`/`unless` (including expression-modifier variants)
|
||||
* - ternary-if (`?:`)
|
||||
* - `while`/`until` (including expression-modifier variants)
|
||||
* - `for`
|
||||
*/
|
||||
class ControlExpr extends Expr {
|
||||
override ControlExpr::Range range;
|
||||
}
|
||||
|
||||
/**
|
||||
* A conditional expression: `if`/`unless` (including expression-modifier
|
||||
* variants), and ternary-if (`?:`) expressions.
|
||||
*/
|
||||
class ConditionalExpr extends Expr {
|
||||
override ConditionalExpr::Range range;
|
||||
|
||||
/**
|
||||
* Gets the condition expression. For example, the result is `foo` in the
|
||||
* following:
|
||||
* ```rb
|
||||
* if foo
|
||||
* bar = 1
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
final Expr getCondition() { result = range.getCondition() }
|
||||
|
||||
/** Gets the 'then' branch of this conditional expression. */
|
||||
Expr getThen() { result = range.getThen() }
|
||||
|
||||
/** Gets the 'else' branch of this conditional expression, if any. */
|
||||
Expr getElse() { result = range.getElse() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `if` or `elsif` expression.
|
||||
* ```
|
||||
*/
|
||||
class IfOrElsifExpr extends ConditionalExpr {
|
||||
override IfOrElsifExpr::Range range;
|
||||
|
||||
/** Gets the 'then' branch of this `if`/`elsif` expression. */
|
||||
final override ThenExpr getThen() { result = range.getThen() }
|
||||
|
||||
/**
|
||||
* Gets the `elsif`/`else` branch of this `if`/`elsif` expression, if any. In
|
||||
* the following example, the result is an `ElseExpr` containing `b`.
|
||||
* ```rb
|
||||
* if foo
|
||||
* a
|
||||
* else
|
||||
* b
|
||||
* end
|
||||
* ```
|
||||
* But there is no result for the following:
|
||||
* ```rb
|
||||
* if foo
|
||||
* a
|
||||
* end
|
||||
* ```
|
||||
* There can be at most one result, since `elsif` branches nest. In the
|
||||
* following example, `ifExpr.getElse()` returns an `ElsifExpr`, and the
|
||||
* `else` branch is nested inside that. To get the `ElseExpr` for the `else`
|
||||
* branch, i.e. the one containing `c`, use
|
||||
* `getElse().(ElsifExpr).getElse()`.
|
||||
* ```rb
|
||||
* if foo
|
||||
* a
|
||||
* elsif bar
|
||||
* b
|
||||
* else
|
||||
* c
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
final override Expr getElse() { result = range.getElse() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `if` expression.
|
||||
* ```rb
|
||||
* if x
|
||||
* y += 1
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class IfExpr extends IfOrElsifExpr, @if {
|
||||
final override IfExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "IfExpr" }
|
||||
|
||||
final override string toString() { result = "if ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `elsif` expression.
|
||||
* ```rb
|
||||
* if x
|
||||
* a += 1
|
||||
* elsif y
|
||||
* a += 2
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class ElsifExpr extends ConditionalExpr {
|
||||
final override ElsifExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ElsifExpr" }
|
||||
|
||||
final override string toString() { result = "elsif ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `unless` expression.
|
||||
* ```rb
|
||||
* unless x == 0
|
||||
* y /= x
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class UnlessExpr extends ConditionalExpr, @unless {
|
||||
final override UnlessExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "UnlessExpr" }
|
||||
|
||||
final override string toString() { result = "unless ..." }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression modified using `if`. In the following example, `getCondition`
|
||||
* returns the `Expr` for `bar`, and `getThen` returns the `Expr` for `foo`.
|
||||
* ```rb
|
||||
* foo if bar
|
||||
* ```
|
||||
*/
|
||||
class IfModifierExpr extends ConditionalExpr, @if_modifier {
|
||||
final override IfModifierExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "IfModifierExpr" }
|
||||
|
||||
final override string toString() { result = "... if ..." }
|
||||
|
||||
/**
|
||||
* Does not hold, since `if`-modified expressions cannot have `else`
|
||||
* branches.
|
||||
*/
|
||||
final override Expr getElse() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression modified using `unless`. For example, in:
|
||||
* ```rb
|
||||
* y /= x unless x == 0
|
||||
* ```
|
||||
* `getCondition` returns the `x == 0` expression, and `getThen` returns the
|
||||
* `y /= x` expression.
|
||||
*/
|
||||
class UnlessModifierExpr extends ConditionalExpr, @unless_modifier {
|
||||
final override UnlessModifierExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "UnlessModifierExpr" }
|
||||
|
||||
final override string toString() { result = "... unless ..." }
|
||||
|
||||
/**
|
||||
* Does not hold, since `unless`-modified expressions cannot have `else`
|
||||
* branches.
|
||||
*/
|
||||
final override Expr getElse() { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A conditional expression using the ternary (`?:`) operator.
|
||||
* ```rb
|
||||
* (a > b) ? a : b
|
||||
* ```
|
||||
*/
|
||||
class TernaryIfExpr extends ConditionalExpr, @conditional {
|
||||
final override TernaryIfExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "TernaryIfExpr" }
|
||||
|
||||
final override string toString() { result = "... ? ... : ..." }
|
||||
}
|
||||
|
||||
class CaseExpr extends ControlExpr, @case__ {
|
||||
final override CaseExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "CaseExpr" }
|
||||
|
||||
final override string toString() { result = "case ..." }
|
||||
|
||||
/**
|
||||
* Gets the expression being compared, if any. For example, `foo` in the following example.
|
||||
* ```rb
|
||||
* case foo
|
||||
* when 0
|
||||
* puts 'zero'
|
||||
* when 1
|
||||
* puts 'one'
|
||||
* end
|
||||
* ```
|
||||
* There is no result for the following example:
|
||||
* ```rb
|
||||
* case
|
||||
* when a then 0
|
||||
* when b then 1
|
||||
* else 2
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
final Expr getValue() { result = range.getValue() }
|
||||
|
||||
/**
|
||||
* Gets the `n`th branch of this case expression, either a `WhenExpr` or an
|
||||
* `ElseExpr`.
|
||||
*/
|
||||
final Expr getBranch(int n) { result = range.getBranch(n) }
|
||||
|
||||
/**
|
||||
* Gets a branch of this case expression, either a `WhenExpr` or an
|
||||
* `ElseExpr`.
|
||||
*/
|
||||
final Expr getABranch() { result = this.getBranch(_) }
|
||||
|
||||
/** Gets a `when` branch of this case expression. */
|
||||
final WhenExpr getAWhenBranch() { result = range.getAWhenBranch() }
|
||||
|
||||
/** Gets the `else` branch of this case expression, if any. */
|
||||
final ElseExpr getElseBranch() { result = range.getElseBranch() }
|
||||
|
||||
/**
|
||||
* Gets the number of branches of this case expression.
|
||||
*/
|
||||
final int getNumberOfBranches() { result = count(this.getBranch(_)) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `when` branch of a `case` expression.
|
||||
* ```rb
|
||||
* case
|
||||
* when a>b then x
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class WhenExpr extends Expr, @when {
|
||||
final override WhenExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "WhenExpr" }
|
||||
|
||||
final override string toString() { result = "when ..." }
|
||||
|
||||
/** Gets the body of this case-when expression. */
|
||||
final ThenExpr getBody() { result = range.getBody() }
|
||||
|
||||
/**
|
||||
* Gets the `n`th pattern (or condition) in this case-when expression.
|
||||
*/
|
||||
final Expr getPattern(int n) { result = range.getPattern(n) }
|
||||
|
||||
/**
|
||||
* Gets a pattern (or condition) in this case-when expression.
|
||||
*/
|
||||
final Expr getAPattern() { result = this.getPattern(_) }
|
||||
|
||||
/**
|
||||
* Gets the number of patterns in this case-when expression.
|
||||
*/
|
||||
final int getNumberOfPatterns() { result = count(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;
|
||||
|
||||
/** Gets the body of this loop. */
|
||||
Expr getBody() { result = range.getBody() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `while` loop.
|
||||
* ```rb
|
||||
* while a < b
|
||||
* p a
|
||||
* a += 2
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class WhileExpr extends Loop, @while {
|
||||
final override WhileExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "WhileExpr" }
|
||||
|
||||
final override string toString() { result = "while ..." }
|
||||
|
||||
/** Gets the body of this `while` loop. */
|
||||
final override DoExpr getBody() { result = range.getBody() }
|
||||
|
||||
/** Gets the condition expression of this `while` loop. */
|
||||
final Expr getCondition() { result = range.getCondition() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An `until` loop.
|
||||
* ```rb
|
||||
* until a >= b
|
||||
* p a
|
||||
* a += 1
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class UntilExpr extends Loop, @until {
|
||||
final override UntilExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "UntilExpr" }
|
||||
|
||||
final override string toString() { result = "until ..." }
|
||||
|
||||
/** Gets the body of this `until` loop. */
|
||||
final override DoExpr getBody() { result = range.getBody() }
|
||||
|
||||
/** Gets the condition expression of this `until` loop. */
|
||||
final Expr getCondition() { result = range.getCondition() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression looped using the `while` modifier. In the following example,
|
||||
* `getCondition` returns the `Expr` for `bar`, and `getBody` returns the
|
||||
* `Expr` for `foo`.
|
||||
* ```rb
|
||||
* foo while bar
|
||||
* ```
|
||||
*/
|
||||
class WhileModifierExpr extends Loop, @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. */
|
||||
final Expr getCondition() { result = range.getCondition() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression looped using the `until` modifier. In the following example,
|
||||
* `getCondition` returns the `Expr` for `bar`, and `getBody` returns the
|
||||
* `Expr` for `foo`.
|
||||
* ```rb
|
||||
* foo until bar
|
||||
* ```
|
||||
*/
|
||||
class UntilModifierExpr extends Loop, @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. */
|
||||
final Expr getCondition() { result = range.getCondition() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `for` loop.
|
||||
* ```rb
|
||||
* for val in 1..n
|
||||
* sum += val
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class ForExpr extends Loop, @for {
|
||||
final override ForExpr::Range range;
|
||||
|
||||
final override string getAPrimaryQlClass() { result = "ForExpr" }
|
||||
|
||||
final override string toString() { result = "for ... in ..." }
|
||||
|
||||
/** Gets the body of this `for` loop. */
|
||||
final override Expr getBody() { result = range.getBody() }
|
||||
|
||||
/** Gets the pattern representing the iteration argument. */
|
||||
final Pattern getPattern() { result = range.getPattern() }
|
||||
|
||||
/**
|
||||
* Gets the value being iterated over. In the following example, the result
|
||||
* is the expression `1..10`:
|
||||
* ```rb
|
||||
* for n in 1..10 do
|
||||
* puts n
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
final Expr getValue() { result = range.getValue() }
|
||||
}
|
||||
Reference in New Issue
Block a user