Files
codeql/java/ql/lib/semmle/code/java/Statement.qll
2025-04-21 10:08:56 +01:00

990 lines
33 KiB
Plaintext

/**
* Provides classes and predicates for working with Java statements.
*/
overlay[local?]
module;
import Expr
import metrics.MetricStmt
/** A common super-class of all statements. */
class Stmt extends StmtParent, ExprParent, @stmt {
/*abstract*/ override string toString() { result = "stmt" }
/** Gets a printable representation of this statement. May include more detail than `toString()`. */
string pp() { result = "stmt" }
/**
* Gets the immediately enclosing callable (method or constructor)
* whose body contains this statement.
*/
Callable getEnclosingCallable() { stmts(this, _, _, _, result) }
/** Gets the index of this statement as a child of its parent. */
int getIndex() { stmts(this, _, _, result, _) }
/** Gets the parent of this statement. */
StmtParent getParent() { stmts(this, _, result, _, _) }
/**
* Gets the statement containing this statement, if any.
*/
Stmt getEnclosingStmt() {
result = this.getParent() or
result = this.getParent().(SwitchExpr).getEnclosingStmt() or
result = this.getParent().(WhenExpr).getEnclosingStmt()
}
/** Holds if this statement is the child of the specified parent at the specified (zero-based) position. */
predicate isNthChildOf(StmtParent parent, int index) {
this.getParent() = parent and this.getIndex() = index
}
/** Gets the compilation unit in which this statement occurs. */
CompilationUnit getCompilationUnit() { result = this.getFile() }
/** Gets a child of this statement, if any. */
Stmt getAChild() { result.getParent() = this }
/** Gets the basic block in which this statement occurs. */
BasicBlock getBasicBlock() { result.getANode().asStmt() = this }
/** Gets the `ControlFlowNode` corresponding to this statement. */
ControlFlowNode getControlFlowNode() { result.asStmt() = this }
/** Cast this statement to a class that provides access to metrics information. */
MetricStmt getMetrics() { result = this }
/** This statement's Halstead ID (used to compute Halstead metrics). */
string getHalsteadID() { result = "Stmt" }
}
/** A statement parent is any element that can have a statement as its child. */
class StmtParent extends @stmtparent, Top { }
/**
* An error statement.
*
* These may be generated by upgrade or downgrade scripts when databases
* cannot be fully converted.
*/
class ErrorStmt extends Stmt, @errorstmt {
override string toString() { result = "<error stmt>" }
override string getAPrimaryQlClass() { result = "ErrorStmt" }
}
/** A block of statements. */
class BlockStmt extends Stmt, @block {
/** Gets a statement that is an immediate child of this block. */
Stmt getAStmt() { result.getParent() = this }
/** Gets the immediate child statement of this block that occurs at the specified (zero-based) position. */
Stmt getStmt(int index) { result.getIndex() = index and result.getParent() = this }
/** Gets the number of immediate child statements in this block. */
int getNumStmt() { result = count(this.getAStmt()) }
/** Gets the last statement in this block. */
Stmt getLastStmt() { result = this.getStmt(this.getNumStmt() - 1) }
override string pp() { result = "{ ... }" }
override string toString() { result = "{ ... }" }
override string getHalsteadID() { result = "BlockStmt" }
override string getAPrimaryQlClass() { result = "BlockStmt" }
}
/** A block with only a single statement. */
class SingletonBlock extends BlockStmt {
SingletonBlock() { this.getNumStmt() = 1 }
/** Gets the single statement in this block. */
Stmt getStmt() { result = this.getStmt(0) }
}
/**
* A conditional statement, including `if`, `for`,
* `while` and `dowhile` statements.
*/
abstract class ConditionalStmt extends Stmt {
/** Gets the boolean condition of this conditional statement. */
abstract Expr getCondition();
}
/** An `if` statement. */
class IfStmt extends ConditionalStmt, @ifstmt {
/** Gets the boolean condition of this `if` statement. */
override Expr getCondition() { result.isNthChildOf(this, 0) }
/** Gets the `then` branch of this `if` statement. */
Stmt getThen() { result.isNthChildOf(this, 1) }
/** Gets the `else` branch of this `if` statement. */
Stmt getElse() { result.isNthChildOf(this, 2) }
override string pp() {
result = "if (...) " + this.getThen().pp() + " else " + this.getElse().pp()
or
not exists(this.getElse()) and result = "if (...) " + this.getThen().pp()
}
override string toString() { result = "if (...)" }
override string getHalsteadID() { result = "IfStmt" }
override string getAPrimaryQlClass() { result = "IfStmt" }
}
/** A `for` loop. */
class ForStmt extends ConditionalStmt, @forstmt {
/**
* Gets an initializer expression of the loop.
*
* This may be an assignment expression or a
* local variable declaration expression.
*/
Expr getAnInit() { exists(int index | result.isNthChildOf(this, index) | index <= -1) }
/** Gets the initializer expression of the loop at the specified (zero-based) position. */
Expr getInit(int index) {
result = this.getAnInit() and
index = -1 - result.getIndex()
}
/** Gets the boolean condition of this `for` loop. */
override Expr getCondition() { result.isNthChildOf(this, 1) }
/** Gets an update expression of this `for` loop. */
Expr getAnUpdate() { exists(int index | result.isNthChildOf(this, index) | index >= 3) }
/** Gets the update expression of this loop at the specified (zero-based) position. */
Expr getUpdate(int index) {
result = this.getAnUpdate() and
index = result.getIndex() - 3
}
/** Gets the body of this `for` loop. */
Stmt getStmt() { result.getParent() = this and result.getIndex() = 2 }
/**
* Gets a variable that is used as an iteration variable: it is defined,
* updated or tested in the head of the `for` statement.
*
* This only returns variables that are quite certainly loop variables;
* for complex iterations, it may not return anything.
*
* More precisely, it returns variables that are both accessed in the
* condition of this `for` statement and updated in the update expression
* of this for statement but may be initialized elsewhere.
*/
Variable getAnIterationVariable() {
// Check that the variable is assigned to, incremented or decremented in the update expression, and...
exists(Expr update | update = this.getAnUpdate().getAChildExpr*() |
update.(UnaryAssignExpr).getExpr() = result.getAnAccess() or
update = result.getAnAssignedValue()
) and
// ...that it is checked or used in the condition.
this.getCondition().getAChildExpr*() = result.getAnAccess()
}
override string pp() { result = "for (...;...;...) " + this.getStmt().pp() }
override string toString() { result = "for (...;...;...)" }
override string getHalsteadID() { result = "ForStmt" }
override string getAPrimaryQlClass() { result = "ForStmt" }
}
/** An enhanced `for` loop. (Introduced in Java 5.) */
class EnhancedForStmt extends Stmt, @enhancedforstmt {
/** Gets the local variable declaration expression of this enhanced `for` loop. */
LocalVariableDeclExpr getVariable() { result.getParent() = this }
/** Gets the expression over which this enhanced `for` loop iterates. */
Expr getExpr() { result.isNthChildOf(this, 1) }
/** Gets the body of this enhanced `for` loop. */
Stmt getStmt() { result.getParent() = this }
override string pp() { result = "for (... : ...) " + this.getStmt().pp() }
override string toString() { result = "for (... : ...)" }
override string getHalsteadID() { result = "EnhancedForStmt" }
override string getAPrimaryQlClass() { result = "EnhancedForStmt" }
}
/** A `while` loop. */
class WhileStmt extends ConditionalStmt, @whilestmt {
/** Gets the boolean condition of this `while` loop. */
override Expr getCondition() { result.getParent() = this }
/** Gets the body of this `while` loop. */
Stmt getStmt() { result.getParent() = this }
override string pp() { result = "while (...) " + this.getStmt().pp() }
override string toString() { result = "while (...)" }
override string getHalsteadID() { result = "WhileStmt" }
override string getAPrimaryQlClass() { result = "WhileStmt" }
}
/** A `do` loop. */
class DoStmt extends ConditionalStmt, @dostmt {
/** Gets the condition of this `do` loop. */
override Expr getCondition() { result.getParent() = this }
/** Gets the body of this `do` loop. */
Stmt getStmt() { result.getParent() = this }
override string pp() { result = "do " + this.getStmt().pp() + " while (...)" }
override string toString() { result = "do ... while (...)" }
override string getHalsteadID() { result = "DoStmt" }
override string getAPrimaryQlClass() { result = "DoStmt" }
}
/**
* A loop statement, including `for`, enhanced `for`,
* `while` and `do` statements.
*/
class LoopStmt extends Stmt {
LoopStmt() {
this instanceof ForStmt or
this instanceof EnhancedForStmt or
this instanceof WhileStmt or
this instanceof DoStmt
}
/** Gets the body of this loop statement. */
Stmt getBody() {
result = this.(ForStmt).getStmt() or
result = this.(EnhancedForStmt).getStmt() or
result = this.(WhileStmt).getStmt() or
result = this.(DoStmt).getStmt()
}
/** Gets the boolean condition of this loop statement. */
Expr getCondition() {
result = this.(ForStmt).getCondition() or
result = this.(WhileStmt).getCondition() or
result = this.(DoStmt).getCondition()
}
}
/** A `try` statement. */
class TryStmt extends Stmt, @trystmt {
/** Gets the block of the `try` statement. */
Stmt getBlock() { result.isNthChildOf(this, -1) }
/** Gets a `catch` clause of this `try` statement. */
CatchClause getACatchClause() { result.getParent() = this }
/**
* Gets the `catch` clause at the specified (zero-based) position
* in this `try` statement.
*/
CatchClause getCatchClause(int index) {
result = this.getACatchClause() and
result.getIndex() = index
}
/** Gets the `finally` block, if any. */
BlockStmt getFinally() { result.isNthChildOf(this, -2) }
/** Gets a resource variable declaration, if any. */
LocalVariableDeclStmt getAResourceDecl() { result.getParent() = this and result.getIndex() <= -3 }
/** Gets the resource variable declaration at the specified position in this `try` statement. */
LocalVariableDeclStmt getResourceDecl(int index) {
result = this.getAResourceDecl() and
index = -3 - result.getIndex()
}
/** Gets a resource expression, if any. */
VarAccess getAResourceExpr() { result.getParent() = this and result.getIndex() <= -3 }
/** Gets the resource expression at the specified position in this `try` statement. */
VarAccess getResourceExpr(int index) {
result = this.getAResourceExpr() and
index = -3 - result.getIndex()
}
/** Gets a resource in this `try` statement, if any. */
ExprParent getAResource() { result = this.getAResourceDecl() or result = this.getAResourceExpr() }
/** Gets the resource at the specified position in this `try` statement. */
ExprParent getResource(int index) {
result = this.getResourceDecl(index) or result = this.getResourceExpr(index)
}
/** Gets a resource variable, if any, either from a resource variable declaration or resource expression. */
Variable getAResourceVariable() {
result = this.getAResourceDecl().getAVariable().getVariable() or
result = this.getAResourceExpr().getVariable()
}
override string pp() { result = "try " + this.getBlock().pp() + " catch (...)" }
override string toString() { result = "try ..." }
override string getHalsteadID() { result = "TryStmt" }
override string getAPrimaryQlClass() { result = "TryStmt" }
}
/** A `catch` clause in a `try` statement. */
class CatchClause extends Stmt, @catchclause {
/** Gets the block of this `catch` clause. */
BlockStmt getBlock() { result.getParent() = this }
/** Gets the `try` statement in which this `catch` clause occurs. */
TryStmt getTry() { this = result.getACatchClause() }
/** Gets the parameter of this `catch` clause. */
LocalVariableDeclExpr getVariable() { result.getParent() = this }
/** Holds if this `catch` clause is a _multi_-`catch` clause. */
predicate isMultiCatch() { this.getVariable().getTypeAccess() instanceof UnionTypeAccess }
/** Gets a type caught by this `catch` clause. */
RefType getACaughtType() {
exists(Expr ta | ta = this.getVariable().getTypeAccess() |
result = ta.(TypeAccess).getType() or
result = ta.(UnionTypeAccess).getAnAlternative().getType()
)
}
override string pp() { result = "catch (...) " + this.getBlock().pp() }
override string toString() { result = "catch (...)" }
override string getHalsteadID() { result = "CatchClause" }
override string getAPrimaryQlClass() { result = "CatchClause" }
}
/** A `switch` statement. */
class SwitchStmt extends Stmt, @switchstmt {
/** Gets an immediate child statement of this `switch` statement. */
Stmt getAStmt() { result.getParent() = this }
/**
* Gets the immediate child statement of this `switch` statement
* that occurs at the specified (zero-based) position.
*/
Stmt getStmt(int index) { result = this.getAStmt() and result.getIndex() = index }
/**
* Gets the `i`th case of this `switch` statement,
* which may be either a normal `case` or a `default`.
*/
SwitchCase getCase(int i) {
result =
rank[i + 1](SwitchCase case, int idx | case.isNthChildOf(this, idx) | case order by idx)
}
/**
* Gets a case of this `switch` statement,
* which may be either a normal `case` or a `default`.
*/
SwitchCase getACase() { result.getParent() = this }
/** Gets a (non-default) constant `case` of this `switch` statement. */
ConstCase getAConstCase() { result = this.getACase() }
/** Gets a (non-default) pattern `case` of this `switch` statement. */
PatternCase getAPatternCase() { result = this.getACase() }
/**
* Gets the `default` case of this switch statement, if any.
*
* Note this may be `default` or `case null, default`.
*/
DefaultCase getDefaultCase() { result = this.getACase() }
/** Gets the expression of this `switch` statement. */
Expr getExpr() { result.getParent() = this }
/** Holds if this switch has a case handling a null literal. */
predicate hasNullCase() {
this.getAConstCase().getValue(_) instanceof NullLiteral or
this.getACase() instanceof NullDefaultCase
}
override string pp() { result = "switch (...)" }
override string toString() { result = "switch (...)" }
override string getHalsteadID() { result = "SwitchStmt" }
override string getAPrimaryQlClass() { result = "SwitchStmt" }
}
/**
* A `switch` statement or expression.
*/
class SwitchBlock extends StmtParent {
SwitchBlock() { this instanceof SwitchStmt or this instanceof SwitchExpr }
}
/**
* A case of a `switch` statement or expression.
*
* This includes both normal `case`s and the `default` case.
*/
class SwitchCase extends Stmt, @case {
/** Gets the switch statement to which this case belongs, if any. */
SwitchStmt getSwitch() { result.getACase() = this }
/**
* Gets the switch expression to which this case belongs, if any.
*/
SwitchExpr getSwitchExpr() { result.getACase() = this }
/**
* Gets the expression of the surrounding switch that this case is compared
* against.
*/
Expr getSelectorExpr() {
result = this.getSwitch().getExpr() or result = this.getSwitchExpr().getExpr()
}
/**
* Gets this case's ordinal in its switch block.
*/
int getCaseIndex() {
this = any(SwitchStmt ss).getCase(result) or this = any(SwitchExpr se).getCase(result)
}
/**
* Holds if this is the `n`th case of switch block `parent`.
*/
pragma[nomagic]
predicate isNthCaseOf(SwitchBlock parent, int n) {
this.getCaseIndex() = n and this.getParent() = parent
}
/**
* Holds if this `case` is a switch labeled rule of the form `... -> ...`.
*/
predicate isRule() {
exists(Expr e | e.getParent() = this | e.getIndex() = -1)
or
exists(Stmt s | s.getParent() = this | s.getIndex() = -1)
}
/**
* Gets the expression on the right-hand side of the arrow, if any.
*
* Note, this predicate gets a value when this switch case is of the form
* `case e1 -> e2`, where `e2` is neither a block nor a throw statement.
* This predicate is mutually exclusive with `getRuleStatement`.
*/
Expr getRuleExpression() {
result.getParent() = this and result.getIndex() = -1
or
exists(ExprStmt es | es.getParent() = this and es.getIndex() = -1 | result = es.getExpr())
}
/**
* Gets the statement on the right-hand side of the arrow, if any.
*
* Note, this predicate gets a value when this switch case is of the form
* `case e1 -> { s1; s2; ... }` or `case e1 -> throw ...`.
* This predicate is mutually exclusive with `getRuleExpression`.
*/
Stmt getRuleStatement() {
result.getParent() = this and result.getIndex() = -1 and not result instanceof ExprStmt
}
}
/**
* A constant `case` of a switch statement.
*
* Note this excludes `case null, default` even though that includes a null constant. It
* does however include plain `case null`.
*/
class ConstCase extends SwitchCase {
ConstCase() {
exists(Expr e | e.getParent() = this and e.getIndex() >= 0 and not e instanceof PatternExpr) and
// For backward compatibility, we don't include `case null, default:` here, on the assumption
// this will come as a surprise to CodeQL that predates that statement's validity.
not isNullDefaultCase(this)
}
/** Gets the `case` constant at index 0. */
Expr getValue() { result.isNthChildOf(this, 0) }
/**
* Gets the `case` constant at index `i`.
*/
Expr getValue(int i) { result.isNthChildOf(this, i) and i >= 0 }
override string pp() { result = "case ..." }
override string toString() { result = "case ..." }
override string getHalsteadID() { result = "ConstCase" }
override string getAPrimaryQlClass() { result = "ConstCase" }
}
/** A pattern case of a `switch` statement */
class PatternCase extends SwitchCase {
PatternCase() { exists(PatternExpr pe | pe.isNthChildOf(this, _)) }
/**
* DEPRECATED: alias for getPattern(0)
*/
deprecated PatternExpr getPattern() { result = this.getPattern(0) }
/**
* Gets this case's `n`th pattern.
*/
PatternExpr getPattern(int n) { result.isNthChildOf(this, n) }
/**
* Gets any of this case's patterns.
*/
PatternExpr getAPattern() { result = this.getPattern(_) }
/**
* Gets this case's sole pattern, if there is exactly one.
*/
PatternExpr getUniquePattern() { result = unique(PatternExpr pe | pe = this.getAPattern()) }
/** Gets the guard applicable to this pattern case, if any. */
Expr getGuard() { result.isNthChildOf(this, -3) }
override string pp() { result = "case <Pattern>" }
override string toString() { result = "case <Pattern>" }
override string getHalsteadID() { result = "PatternCase" }
override string getAPrimaryQlClass() { result = "PatternCase" }
}
/**
* A `default` or `case null, default` case of a `switch` statement or expression.
*/
class DefaultCase extends SwitchCase {
DefaultCase() {
isNullDefaultCase(this)
or
not exists(Expr e | e.getParent() = this | e.getIndex() >= 0)
}
override string pp() { result = "default" }
override string toString() { result = "default" }
override string getHalsteadID() { result = "DefaultCase" }
override string getAPrimaryQlClass() { result = "DefaultCase" }
}
/** A `case null, default` statement of a `switch` statement or expression. */
class NullDefaultCase extends DefaultCase {
NullDefaultCase() { isNullDefaultCase(this) }
override string pp() { result = "case null, default" }
override string toString() { result = "case null, default" }
override string getHalsteadID() { result = "NullDefaultCase" }
override string getAPrimaryQlClass() { result = "NullDefaultCase" }
}
/** A `synchronized` statement. */
class SynchronizedStmt extends Stmt, @synchronizedstmt {
/** Gets the expression on which this `synchronized` statement synchronizes. */
Expr getExpr() { result.getParent() = this }
/** Gets the block of this `synchronized` statement. */
Stmt getBlock() { result.getParent() = this }
override string pp() { result = "synchronized (...) " + this.getBlock().pp() }
override string toString() { result = "synchronized (...)" }
override string getHalsteadID() { result = "SynchronizedStmt" }
override string getAPrimaryQlClass() { result = "SynchronizedStmt" }
}
/** A `return` statement. */
class ReturnStmt extends Stmt, @returnstmt {
/** Gets the expression returned by this `return` statement, if any. */
Expr getResult() { result.getParent() = this }
override string pp() { result = "return ..." }
override string toString() { result = "return ..." }
override string getHalsteadID() { result = "ReturnStmt" }
override string getAPrimaryQlClass() { result = "ReturnStmt" }
}
/** A `throw` statement. */
class ThrowStmt extends Stmt, @throwstmt {
/** Gets the expression thrown by this `throw` statement. */
Expr getExpr() { result.getParent() = this }
override string pp() { result = "throw ..." }
override string toString() { result = "throw ..." }
override string getHalsteadID() { result = "ThrowStmt" }
/** Gets the type of the expression thrown by this `throw` statement. */
RefType getThrownExceptionType() { result = this.getExpr().getType() }
/**
* Gets the `catch` clause that catches the exception
* thrown by this `throw` statement and occurs
* in the same method as this `throw` statement,
* provided such a `catch` exists.
*/
CatchClause getLexicalCatchIfAny() {
exists(TryStmt try | try = this.findEnclosing() and result = this.catchClauseForThis(try))
}
private Stmt findEnclosing() {
result = this.getEnclosingStmt()
or
exists(Stmt mid |
mid = this.findEnclosing() and
not exists(this.catchClauseForThis(mid)) and
result = mid.getEnclosingStmt()
)
}
private CatchClause catchClauseForThis(TryStmt try) {
result = try.getACatchClause() and
result.getEnclosingCallable() = this.getEnclosingCallable() and
this.getExpr().getType().(RefType).hasSupertype*(result.getVariable().getType()) and
not this.getEnclosingStmt+() = result
}
override string getAPrimaryQlClass() { result = "ThrowStmt" }
}
private class JumpStmt_ = @breakstmt or @yieldstmt or @continuestmt;
/** A `break`, `yield` or `continue` statement. */
class JumpStmt extends Stmt, JumpStmt_ {
/**
* Gets the labeled statement that this `break` or
* `continue` statement refers to, if any.
*/
LabeledStmt getTargetLabel() {
this.getEnclosingStmt+() = result and
namestrings(result.getLabel(), _, this)
}
private Stmt getLabelTarget() { result = this.getTargetLabel().getStmt() }
private Stmt getAPotentialTarget() {
this.getEnclosingStmt+() = result and
(
result instanceof LoopStmt
or
this instanceof BreakStmt and result instanceof SwitchStmt
)
}
private StmtParent getEnclosingTarget() {
result = this.getAPotentialTarget() and
not exists(Stmt other | other = this.getAPotentialTarget() | other.getEnclosingStmt+() = result)
}
/**
* Gets the statement or `switch` expression that this `break`, `yield` or `continue` jumps to.
*/
StmtParent getTarget() {
// Note: This implementation only considers `break` and `continue`; YieldStmt overrides this predicate
result = this.getLabelTarget()
or
not exists(this.getLabelTarget()) and result = this.getEnclosingTarget()
}
}
/** A `break` statement. */
class BreakStmt extends JumpStmt, @breakstmt {
/** Gets the label targeted by this `break` statement, if any. */
string getLabel() { namestrings(result, _, this) }
/** Holds if this `break` statement has an explicit label. */
predicate hasLabel() { exists(this.getLabel()) }
override string pp() {
if this.hasLabel() then result = "break " + this.getLabel() else result = "break"
}
override string toString() { result = "break" }
override string getHalsteadID() { result = "BreakStmt" }
override string getAPrimaryQlClass() { result = "BreakStmt" }
}
/**
* A `yield` statement.
*/
class YieldStmt extends JumpStmt, @yieldstmt {
/**
* Gets the value of this `yield` statement.
*/
Expr getValue() { result.getParent() = this }
/**
* Gets the `switch` expression target of this `yield` statement.
*/
override SwitchExpr getTarget() {
// Get the innermost enclosing SwitchExpr; this works because getParent() is defined for Stmt and
// therefore won't proceed after the innermost SwitchExpr (due to it being an Expr)
result = this.getParent+()
}
override string pp() { result = "yield ..." }
override string toString() { result = "yield ..." }
override string getHalsteadID() { result = "YieldStmt" }
override string getAPrimaryQlClass() { result = "YieldStmt" }
}
/** A `continue` statement. */
class ContinueStmt extends JumpStmt, @continuestmt {
/** Gets the label targeted by this `continue` statement, if any. */
string getLabel() { namestrings(result, _, this) }
/** Holds if this `continue` statement has an explicit label. */
predicate hasLabel() { exists(this.getLabel()) }
override string pp() {
if this.hasLabel() then result = "continue " + this.getLabel() else result = "continue"
}
override string toString() { result = "continue" }
override string getHalsteadID() { result = "ContinueStmt" }
override string getAPrimaryQlClass() { result = "ContinueStmt" }
}
/** The empty statement. */
class EmptyStmt extends Stmt, @emptystmt {
override string pp() { result = ";" }
override string toString() { result = ";" }
override string getHalsteadID() { result = "EmptyStmt" }
override string getAPrimaryQlClass() { result = "EmptyStmt" }
}
/**
* An expression statement.
*
* Certain kinds of expressions may be used as statements by appending a semicolon.
*/
class ExprStmt extends Stmt, @exprstmt {
/** Gets the expression of this expression statement. */
Expr getExpr() { result.getParent() = this }
override string pp() { result = "<Expr>;" }
override string toString() { result = "<Expr>;" }
override string getHalsteadID() { result = "ExprStmt" }
/** Holds if this statement represents a field declaration with an initializer. */
predicate isFieldDecl() {
this.getEnclosingCallable() instanceof InitializerMethod and
exists(FieldDeclaration fd, Location fdl, Location sl |
fdl = fd.getLocation() and sl = this.getLocation()
|
fdl.getFile() = sl.getFile() and
fdl.getStartLine() = sl.getStartLine() and
fdl.getStartColumn() = sl.getStartColumn()
)
}
override string getAPrimaryQlClass() { result = "ExprStmt" }
}
/** A labeled statement. */
class LabeledStmt extends Stmt, @labeledstmt {
/** Gets the statement of this labeled statement. */
Stmt getStmt() { result.getParent() = this }
/** Gets the label of this labeled statement. */
string getLabel() { namestrings(result, _, this) }
override string pp() { result = this.getLabel() + ": " + this.getStmt().pp() }
override string getHalsteadID() { result = this.getLabel() + ":" }
override string toString() { result = "<Label>: ..." }
override string getAPrimaryQlClass() { result = "LabeledStmt" }
}
/** An `assert` statement. */
class AssertStmt extends Stmt, @assertstmt {
/** Gets the boolean expression of this `assert` statement. */
Expr getExpr() { exprs(result, _, _, this, _) and result.getIndex() = 0 }
/** Gets the assertion message expression, if any. */
Expr getMessage() { exprs(result, _, _, this, _) and result.getIndex() = 1 }
override string pp() {
if exists(this.getMessage()) then result = "assert ... : ..." else result = "assert ..."
}
override string toString() { result = "assert ..." }
override string getHalsteadID() { result = "AssertStmt" }
override string getAPrimaryQlClass() { result = "AssertStmt" }
}
/** A statement that declares one or more local variables. */
class LocalVariableDeclStmt extends Stmt, @localvariabledeclstmt {
/** Gets a declared variable. */
LocalVariableDeclExpr getAVariable() { result.getParent() = this }
/** Gets the variable declared at the specified (one-based) position in this local variable declaration statement. */
LocalVariableDeclExpr getVariable(int index) {
result = this.getAVariable() and
result.getIndex() = index
}
/** Gets an index of a variable declared in this local variable declaration statement. */
int getAVariableIndex() { exists(this.getVariable(result)) }
override string pp() { result = "var ...;" }
override string toString() { result = "var ...;" }
override string getHalsteadID() { result = "LocalVariableDeclStmt" }
override string getAPrimaryQlClass() { result = "LocalVariableDeclStmt" }
}
/** A statement that declares a local class or interface. */
class LocalTypeDeclStmt extends Stmt, @localtypedeclstmt {
/** Gets the local type declared by this statement. */
LocalClassOrInterface getLocalType() { isLocalClassOrInterface(result, this) }
private string getDeclKeyword() {
result = "class" and this.getLocalType() instanceof Class
or
result = "interface" and this.getLocalType() instanceof Interface
}
override string pp() { result = this.getDeclKeyword() + " " + this.getLocalType().toString() }
override string toString() { result = this.getDeclKeyword() + " ..." }
override string getHalsteadID() { result = "LocalTypeDeclStmt" }
override string getAPrimaryQlClass() { result = "LocalTypeDeclStmt" }
}
/** An explicit `this(...)` constructor invocation. */
class ThisConstructorInvocationStmt extends Stmt, ConstructorCall, @constructorinvocationstmt {
/** Gets an argument of this constructor invocation. */
override Expr getAnArgument() { result.getIndex() >= 0 and result.getParent() = this }
/** Gets the argument at the specified (zero-based) position in this constructor invocation. */
override Expr getArgument(int index) {
result = this.getAnArgument() and
result.getIndex() = index
}
/** Gets a type argument of this constructor invocation. */
Expr getATypeArgument() { result.getIndex() <= -2 and result.getParent() = this }
/** Gets the type argument at the specified (zero-based) position in this constructor invocation. */
Expr getTypeArgument(int index) {
result = this.getATypeArgument() and
(-2 - result.getIndex()) = index
}
/** Gets the constructor invoked by this constructor invocation. */
override Constructor getConstructor() { callableBinding(this, result) }
override Expr getQualifier() { none() }
/** Gets the immediately enclosing callable of this constructor invocation. */
override Callable getEnclosingCallable() { result = Stmt.super.getEnclosingCallable() }
/** Gets the immediately enclosing statement of this constructor invocation. */
override Stmt getEnclosingStmt() { result = this }
override string pp() { result = "this(...)" }
override string toString() { result = "this(...)" }
override string getHalsteadID() { result = "ConstructorInvocationStmt" }
override string getAPrimaryQlClass() { result = "ThisConstructorInvocationStmt" }
}
/** An explicit `super(...)` constructor invocation. */
class SuperConstructorInvocationStmt extends Stmt, ConstructorCall, @superconstructorinvocationstmt {
/** Gets an argument of this constructor invocation. */
override Expr getAnArgument() { result.getIndex() >= 0 and result.getParent() = this }
/** Gets the argument at the specified (zero-based) position in this constructor invocation. */
override Expr getArgument(int index) {
result = this.getAnArgument() and
result.getIndex() = index
}
/** Gets a type argument of this constructor invocation. */
Expr getATypeArgument() { result.getIndex() <= -2 and result.getParent() = this }
/** Gets the type argument at the specified (zero-based) position in this constructor invocation. */
Expr getTypeArgument(int index) {
result = this.getATypeArgument() and
(-2 - result.getIndex()) = index
}
/** Gets the constructor invoked by this constructor invocation. */
override Constructor getConstructor() { callableBinding(this, result) }
/** Gets the qualifier expression of this `super(...)` constructor invocation, if any. */
override Expr getQualifier() { result.isNthChildOf(this, -1) }
/** Gets the immediately enclosing callable of this constructor invocation. */
override Callable getEnclosingCallable() { result = Stmt.super.getEnclosingCallable() }
/** Gets the immediately enclosing statement of this constructor invocation. */
override Stmt getEnclosingStmt() { result = this }
override string pp() { result = "super(...)" }
override string toString() { result = "super(...)" }
override string getHalsteadID() { result = "SuperConstructorInvocationStmt" }
override string getAPrimaryQlClass() { result = "SuperConstructorInvocationStmt" }
}