mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
2531 lines
87 KiB
Plaintext
Executable File
2531 lines
87 KiB
Plaintext
Executable File
/**
|
|
* Provides classes for working with Java expressions.
|
|
*/
|
|
|
|
import java
|
|
private import semmle.code.java.frameworks.android.Compose
|
|
|
|
/** A common super-class that represents all kinds of expressions. */
|
|
class Expr extends ExprParent, @expr {
|
|
/*abstract*/ override string toString() { result = "expr" }
|
|
|
|
/**
|
|
* Gets the callable in which this expression occurs, if any.
|
|
*/
|
|
Callable getEnclosingCallable() { callableEnclosingExpr(this, result) }
|
|
|
|
/** Gets the index of this expression as a child of its parent. */
|
|
int getIndex() { exprs(this, _, _, _, result) }
|
|
|
|
/** Gets the parent of this expression. */
|
|
ExprParent getParent() { exprs(this, _, _, result, _) }
|
|
|
|
/** Holds if this expression is the child of the specified parent at the specified (zero-based) position. */
|
|
predicate isNthChildOf(ExprParent parent, int index) { exprs(this, _, _, parent, index) }
|
|
|
|
/** Gets the type of this expression. */
|
|
Type getType() { exprs(this, _, result, _, _) }
|
|
|
|
/** Gets the Kotlin type of this expression. */
|
|
KotlinType getKotlinType() { exprsKotlinType(this, result) }
|
|
|
|
/** Gets the compilation unit in which this expression occurs. */
|
|
CompilationUnit getCompilationUnit() { result = this.getFile() }
|
|
|
|
/**
|
|
* Gets the kind of this expression.
|
|
*
|
|
* Each kind of expression has a unique (integer) identifier.
|
|
* This is an implementation detail that should not normally
|
|
* be referred to by library users, since the kind of an expression
|
|
* is also represented by its QL type.
|
|
*
|
|
* In a few rare situations, referring to the kind of an expression
|
|
* via its unique identifier may be appropriate; for example, when
|
|
* comparing whether two expressions have the same kind (as opposed
|
|
* to checking whether an expression has a particular kind).
|
|
*/
|
|
int getKind() { exprs(this, result, _, _, _) }
|
|
|
|
/** Gets the statement containing this expression, if any. */
|
|
Stmt getEnclosingStmt() { statementEnclosingExpr(this, result) }
|
|
|
|
/**
|
|
* Gets a statement that directly or transitively contains this expression, if any.
|
|
* This is equivalent to `this.getEnclosingStmt().getEnclosingStmt*()`.
|
|
*/
|
|
Stmt getAnEnclosingStmt() { result = this.getEnclosingStmt().getEnclosingStmt*() }
|
|
|
|
/** Gets a child of this expression. */
|
|
Expr getAChildExpr() { exprs(result, _, _, this, _) }
|
|
|
|
/** Gets the basic block in which this expression occurs, if any. */
|
|
BasicBlock getBasicBlock() { result.getANode() = this }
|
|
|
|
/** Gets the `ControlFlowNode` corresponding to this expression. */
|
|
ControlFlowNode getControlFlowNode() { result = this }
|
|
|
|
/** This statement's Halstead ID (used to compute Halstead metrics). */
|
|
string getHalsteadID() { result = this.toString() }
|
|
|
|
/**
|
|
* Holds if this expression is a compile-time constant.
|
|
*
|
|
* See JLS v8, section 15.28 (Constant Expressions).
|
|
*/
|
|
predicate isCompileTimeConstant() { this instanceof CompileTimeConstantExpr }
|
|
|
|
/** Holds if this expression occurs in a static context. */
|
|
predicate isInStaticContext() {
|
|
/*
|
|
* JLS 8.1.3 (Inner Classes and Enclosing Instances)
|
|
* A statement or expression occurs in a static context if and only if the
|
|
* innermost method, constructor, instance initializer, static initializer,
|
|
* field initializer, or explicit constructor invocation statement
|
|
* enclosing the statement or expression is a static method, a static
|
|
* initializer, the variable initializer of a static variable, or an
|
|
* explicit constructor invocation statement.
|
|
*/
|
|
|
|
this.getEnclosingCallable().isStatic()
|
|
or
|
|
this.getParent+() instanceof ThisConstructorInvocationStmt
|
|
or
|
|
this.getParent+() instanceof SuperConstructorInvocationStmt
|
|
or
|
|
exists(LambdaExpr lam |
|
|
lam.asMethod() = this.getEnclosingCallable() and lam.isInStaticContext()
|
|
)
|
|
}
|
|
|
|
/** Holds if this expression is parenthesized. */
|
|
predicate isParenthesized() { isParenthesized(this, _) }
|
|
|
|
/**
|
|
* Gets the underlying expression looking through casts and not-nulls, if any.
|
|
* Otherwise just gets this expression.
|
|
*/
|
|
Expr getUnderlyingExpr() {
|
|
if this instanceof CastingExpr or this instanceof NotNullExpr
|
|
then
|
|
result = this.(CastingExpr).getExpr().getUnderlyingExpr() or
|
|
result = this.(NotNullExpr).getExpr().getUnderlyingExpr()
|
|
else result = this
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Holds if the specified type is either a primitive type or type `String`.
|
|
*
|
|
* Auxiliary predicate used by `CompileTimeConstantExpr`.
|
|
*/
|
|
private predicate primitiveOrString(Type t) {
|
|
t instanceof PrimitiveType or
|
|
t instanceof TypeString
|
|
}
|
|
|
|
/**
|
|
* A compile-time constant expression.
|
|
*
|
|
* See JLS v8, section 15.28 (Constant Expressions).
|
|
*/
|
|
class CompileTimeConstantExpr extends Expr {
|
|
CompileTimeConstantExpr() {
|
|
primitiveOrString(this.getType()) and
|
|
(
|
|
// Literals of primitive type and literals of type `String`.
|
|
this instanceof Literal
|
|
or
|
|
// Casts to primitive types and casts to type `String`.
|
|
this.(CastingExpr).getExpr().isCompileTimeConstant()
|
|
or
|
|
// The unary operators `+`, `-`, `~`, and `!` (but not `++` or `--`).
|
|
this.(PlusExpr).getExpr().isCompileTimeConstant()
|
|
or
|
|
this.(MinusExpr).getExpr().isCompileTimeConstant()
|
|
or
|
|
this.(BitNotExpr).getExpr().isCompileTimeConstant()
|
|
or
|
|
this.(LogNotExpr).getExpr().isCompileTimeConstant()
|
|
or
|
|
// The multiplicative operators `*`, `/`, and `%`,
|
|
// the additive operators `+` and `-`,
|
|
// the shift operators `<<`, `>>`, and `>>>`,
|
|
// the relational operators `<`, `<=`, `>`, and `>=` (but not `instanceof`),
|
|
// the equality operators `==` and `!=`,
|
|
// the bitwise and logical operators `&`, `^`, and `|`,
|
|
// the conditional-and operator `&&` and the conditional-or operator `||`.
|
|
// These are precisely the operators represented by `BinaryExpr`.
|
|
this.(BinaryExpr).getLeftOperand().isCompileTimeConstant() and
|
|
this.(BinaryExpr).getRightOperand().isCompileTimeConstant()
|
|
or
|
|
// The ternary conditional operator ` ? : `.
|
|
exists(ConditionalExpr e | this = e |
|
|
e.getCondition().isCompileTimeConstant() and
|
|
e.getTrueExpr().isCompileTimeConstant() and
|
|
e.getFalseExpr().isCompileTimeConstant()
|
|
)
|
|
or
|
|
// Access to a final variable initialized by a compile-time constant.
|
|
exists(Variable v | this = v.getAnAccess() |
|
|
v.isFinal() and
|
|
v.getInitializer().isCompileTimeConstant()
|
|
)
|
|
or
|
|
this instanceof LiveLiteral
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the string value of this expression, where possible.
|
|
*/
|
|
pragma[nomagic]
|
|
string getStringValue() {
|
|
result = this.(StringLiteral).getValue()
|
|
or
|
|
result =
|
|
this.(AddExpr).getLeftOperand().(CompileTimeConstantExpr).getStringValue() +
|
|
this.(AddExpr).getRightOperand().(CompileTimeConstantExpr).getStringValue()
|
|
or
|
|
// Ternary conditional, with compile-time constant condition.
|
|
exists(ConditionalExpr ce, boolean condition |
|
|
ce = this and
|
|
condition = ce.getCondition().(CompileTimeConstantExpr).getBooleanValue() and
|
|
result = ce.getBranchExpr(condition).(CompileTimeConstantExpr).getStringValue()
|
|
)
|
|
or
|
|
exists(Variable v | this = v.getAnAccess() |
|
|
result = v.getInitializer().(CompileTimeConstantExpr).getStringValue()
|
|
)
|
|
or
|
|
result = this.(LiveLiteral).getValue().getStringValue()
|
|
}
|
|
|
|
/**
|
|
* Gets the boolean value of this expression, where possible.
|
|
*/
|
|
boolean getBooleanValue() {
|
|
// Literal value.
|
|
result = this.(BooleanLiteral).getBooleanValue()
|
|
or
|
|
// No casts relevant to booleans.
|
|
// `!` is the only unary operator that evaluates to a boolean.
|
|
result = this.(LogNotExpr).getExpr().(CompileTimeConstantExpr).getBooleanValue().booleanNot()
|
|
or
|
|
// Handle binary expressions that have integer operands and a boolean result.
|
|
exists(BinaryExpr b, int left, int right |
|
|
b = this and
|
|
left = b.getLeftOperand().(CompileTimeConstantExpr).getIntValue() and
|
|
right = b.getRightOperand().(CompileTimeConstantExpr).getIntValue()
|
|
|
|
|
(
|
|
b instanceof LTExpr and
|
|
if left < right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof LEExpr and
|
|
if left <= right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof GTExpr and
|
|
if left > right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof GEExpr and
|
|
if left >= right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof ValueOrReferenceEqualsExpr and
|
|
if left = right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof ValueOrReferenceNotEqualsExpr and
|
|
if left != right then result = true else result = false
|
|
)
|
|
)
|
|
or
|
|
// Handle binary expressions that have boolean operands and a boolean result.
|
|
exists(BinaryExpr b, boolean left, boolean right |
|
|
b = this and
|
|
left = b.getLeftOperand().(CompileTimeConstantExpr).getBooleanValue() and
|
|
right = b.getRightOperand().(CompileTimeConstantExpr).getBooleanValue()
|
|
|
|
|
(
|
|
b instanceof ValueOrReferenceEqualsExpr and
|
|
if left = right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof ValueOrReferenceNotEqualsExpr and
|
|
if left != right then result = true else result = false
|
|
)
|
|
or
|
|
(b instanceof AndBitwiseExpr or b instanceof AndLogicalExpr) and
|
|
result = left.booleanAnd(right)
|
|
or
|
|
(b instanceof OrBitwiseExpr or b instanceof OrLogicalExpr) and
|
|
result = left.booleanOr(right)
|
|
or
|
|
b instanceof XorBitwiseExpr and result = left.booleanXor(right)
|
|
)
|
|
or
|
|
// Handle binary expressions that have `String` operands and a boolean result.
|
|
exists(BinaryExpr b, string left, string right |
|
|
b = this and
|
|
left = b.getLeftOperand().(CompileTimeConstantExpr).getStringValue() and
|
|
right = b.getRightOperand().(CompileTimeConstantExpr).getStringValue()
|
|
|
|
|
/*
|
|
* JLS 15.28 specifies that compile-time `String` constants are interned. Therefore `==`
|
|
* equality can be interpreted as equality over the constant values, not the references.
|
|
*
|
|
* Kotlin's `==` and `===` operators will return the same result for `String`s, so they
|
|
* can be handled alike:
|
|
*/
|
|
|
|
(
|
|
b instanceof ValueOrReferenceEqualsExpr and
|
|
if left = right then result = true else result = false
|
|
)
|
|
or
|
|
(
|
|
b instanceof ValueOrReferenceNotEqualsExpr and
|
|
if left != right then result = true else result = false
|
|
)
|
|
)
|
|
or
|
|
// Note: no `getFloatValue()`, so we cannot support binary expressions with float or double operands.
|
|
// Ternary expressions, where the `true` and `false` expressions are boolean compile-time constants.
|
|
exists(ConditionalExpr ce, boolean condition |
|
|
ce = this and
|
|
condition = ce.getCondition().(CompileTimeConstantExpr).getBooleanValue() and
|
|
result = ce.getBranchExpr(condition).(CompileTimeConstantExpr).getBooleanValue()
|
|
)
|
|
or
|
|
// Simple or qualified names where the variable is final and the initializer is a constant.
|
|
exists(Variable v | this = v.getAnAccess() |
|
|
result = v.getInitializer().(CompileTimeConstantExpr).getBooleanValue()
|
|
)
|
|
or
|
|
result = this.(LiveLiteral).getValue().getBooleanValue()
|
|
}
|
|
|
|
/**
|
|
* Gets the integer value of this expression, where possible.
|
|
*
|
|
* Note that this does not handle the following cases:
|
|
*
|
|
* - values of type `long`.
|
|
*/
|
|
cached
|
|
int getIntValue() {
|
|
exists(IntegralType t | this.getType() = t | t.getName().toLowerCase() != "long") and
|
|
(
|
|
result = this.(IntegerLiteral).getIntValue()
|
|
or
|
|
result = this.(CharacterLiteral).getCodePointValue()
|
|
or
|
|
exists(CastingExpr cast, int val |
|
|
cast = this and val = cast.getExpr().(CompileTimeConstantExpr).getIntValue()
|
|
|
|
|
if cast.getType().hasName("byte")
|
|
then result = (val + 128).bitAnd(255) - 128
|
|
else
|
|
if cast.getType().hasName("short")
|
|
then result = (val + 32768).bitAnd(65535) - 32768
|
|
else
|
|
if cast.getType().hasName("char")
|
|
then result = val.bitAnd(65535)
|
|
else result = val
|
|
)
|
|
or
|
|
result = this.(PlusExpr).getExpr().(CompileTimeConstantExpr).getIntValue()
|
|
or
|
|
result = -this.(MinusExpr).getExpr().(CompileTimeConstantExpr).getIntValue()
|
|
or
|
|
result = this.(BitNotExpr).getExpr().(CompileTimeConstantExpr).getIntValue().bitNot()
|
|
or
|
|
// No `int` value for `LogNotExpr`.
|
|
exists(BinaryExpr b, int v1, int v2 |
|
|
b = this and
|
|
v1 = b.getLeftOperand().(CompileTimeConstantExpr).getIntValue() and
|
|
v2 = b.getRightOperand().(CompileTimeConstantExpr).getIntValue()
|
|
|
|
|
b instanceof MulExpr and result = v1 * v2
|
|
or
|
|
b instanceof DivExpr and result = v1 / v2
|
|
or
|
|
b instanceof RemExpr and result = v1 % v2
|
|
or
|
|
b instanceof AddExpr and result = v1 + v2
|
|
or
|
|
b instanceof SubExpr and result = v1 - v2
|
|
or
|
|
b instanceof LShiftExpr and result = v1.bitShiftLeft(v2)
|
|
or
|
|
b instanceof RShiftExpr and result = v1.bitShiftRightSigned(v2)
|
|
or
|
|
b instanceof URShiftExpr and result = v1.bitShiftRight(v2)
|
|
or
|
|
b instanceof AndBitwiseExpr and result = v1.bitAnd(v2)
|
|
or
|
|
b instanceof OrBitwiseExpr and result = v1.bitOr(v2)
|
|
or
|
|
b instanceof XorBitwiseExpr and result = v1.bitXor(v2)
|
|
// No `int` value for `AndLogicalExpr` or `OrLogicalExpr`.
|
|
// No `int` value for `LTExpr`, `GTExpr`, `LEExpr`, `GEExpr`, `ValueOrReferenceEqualsExpr` or `ValueOrReferenceNotEqualsExpr`.
|
|
)
|
|
or
|
|
// Ternary conditional, with compile-time constant condition.
|
|
exists(ConditionalExpr ce, boolean condition |
|
|
ce = this and
|
|
condition = ce.getCondition().(CompileTimeConstantExpr).getBooleanValue() and
|
|
result = ce.getBranchExpr(condition).(CompileTimeConstantExpr).getIntValue()
|
|
)
|
|
or
|
|
// If a `Variable` is a `CompileTimeConstantExpr`, its value is its initializer.
|
|
exists(Variable v | this = v.getAnAccess() |
|
|
result = v.getInitializer().(CompileTimeConstantExpr).getIntValue()
|
|
)
|
|
)
|
|
or
|
|
result = this.(LiveLiteral).getValue().getIntValue()
|
|
}
|
|
}
|
|
|
|
/** An expression parent is an element that may have an expression as its child. */
|
|
class ExprParent extends @exprparent, Top { }
|
|
|
|
/**
|
|
* An error expression.
|
|
*
|
|
* These may be generated by upgrade or downgrade scripts when databases
|
|
* cannot be fully converted.
|
|
*/
|
|
class ErrorExpr extends Expr, @errorexpr {
|
|
override string toString() { result = "<error expr>" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ErrorExpr" }
|
|
}
|
|
|
|
/**
|
|
* An array access.
|
|
*
|
|
* For example, `a[i++]` is an array access, where
|
|
* `a` is the accessed array and `i++` is
|
|
* the index expression of the array access.
|
|
*/
|
|
class ArrayAccess extends Expr, @arrayaccess {
|
|
/** Gets the array that is accessed in this array access. */
|
|
Expr getArray() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the index expression of this array access. */
|
|
Expr getIndexExpr() { result.isNthChildOf(this, 1) }
|
|
|
|
override string toString() { result = "...[...]" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ArrayAccess" }
|
|
}
|
|
|
|
/**
|
|
* An array creation expression.
|
|
*
|
|
* For example, an expression such as `new String[2][3]` or
|
|
* `new String[][] { { "a", "b", "c" } , { "d", "e", "f" } }`.
|
|
*
|
|
* In both examples, `String` is the type name. In the first
|
|
* example, `2` and `3` are the 0th and 1st dimensions,
|
|
* respectively. In the second example,
|
|
* `{ { "a", "b", "c" } , { "d", "e", "f" } }` is the initializer.
|
|
*/
|
|
class ArrayCreationExpr extends Expr, @arraycreationexpr {
|
|
/** Gets a dimension of this array creation expression. */
|
|
Expr getADimension() { result.getParent() = this and result.getIndex() >= 0 }
|
|
|
|
/** Gets the dimension of this array creation expression at the specified (zero-based) position. */
|
|
Expr getDimension(int index) {
|
|
result = this.getADimension() and
|
|
result.getIndex() = index
|
|
}
|
|
|
|
/** Gets the initializer of this array creation expression, if any. */
|
|
ArrayInit getInit() { result.isNthChildOf(this, -2) }
|
|
|
|
/**
|
|
* Gets the size of the first dimension, if it can be statically determined.
|
|
*/
|
|
int getFirstDimensionSize() {
|
|
if exists(this.getInit())
|
|
then result = this.getInit().getSize()
|
|
else result = this.getDimension(0).(CompileTimeConstantExpr).getIntValue()
|
|
}
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "new " + this.getType().toString() }
|
|
|
|
override string getAPrimaryQlClass() { result = "ArrayCreationExpr" }
|
|
}
|
|
|
|
/**
|
|
* An array initializer consisting of an opening and closing curly bracket and
|
|
* optionally containing expressions (which themselves can be array initializers)
|
|
* representing the elements of the array. For example: `{ 'a', 'b' }`.
|
|
*
|
|
* This expression type matches array initializers representing the values for
|
|
* annotation elements as well, despite the Java Language Specification considering
|
|
* them a separate type, `ElementValueArrayInitializer`. It does however not match
|
|
* values for an array annotation element which consist of a single element
|
|
* without enclosing curly brackets (as per JLS).
|
|
*/
|
|
class ArrayInit extends Expr, @arrayinit {
|
|
/**
|
|
* An expression occurring in this initializer.
|
|
* This may either be an initializer itself or an
|
|
* expression representing an element of the array,
|
|
* depending on the level of nesting.
|
|
*/
|
|
Expr getAnInit() { result.getParent() = this }
|
|
|
|
/** Gets the initializer occurring at the specified (zero-based) position. */
|
|
Expr getInit(int index) { result = this.getAnInit() and result.getIndex() = index }
|
|
|
|
/**
|
|
* Gets the number of expressions in this initializer, that is, the size the
|
|
* created array will have.
|
|
*/
|
|
int getSize() { result = count(this.getAnInit()) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "{...}" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ArrayInit" }
|
|
}
|
|
|
|
/** A common super-class that represents all varieties of assignments. */
|
|
class Assignment extends Expr, @assignment {
|
|
/** Gets the destination (left-hand side) of the assignment. */
|
|
Expr getDest() { result.isNthChildOf(this, 0) }
|
|
|
|
/**
|
|
* Gets the source (right-hand side) of the assignment.
|
|
*
|
|
* For assignments with an implicit operator such as `x += 23`,
|
|
* the left-hand side is also a source.
|
|
*/
|
|
Expr getSource() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Gets the right-hand side of the assignment. */
|
|
Expr getRhs() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...=..." }
|
|
}
|
|
|
|
/**
|
|
* A simple assignment expression using the `=` operator.
|
|
*
|
|
* For example, `x = 23`.
|
|
*/
|
|
class AssignExpr extends Assignment, @assignexpr {
|
|
override string getAPrimaryQlClass() { result = "AssignExpr" }
|
|
}
|
|
|
|
/**
|
|
* A Kotlin class member initializer assignment.
|
|
*
|
|
* For example, `class X { val y = 1 }`
|
|
*/
|
|
class KtInitializerAssignExpr extends AssignExpr {
|
|
KtInitializerAssignExpr() { ktInitializerAssignment(this) }
|
|
|
|
override string getAPrimaryQlClass() { result = "KtInitializerAssignExpr" }
|
|
}
|
|
|
|
/**
|
|
* A common super-class to represent compound assignments, which include an implicit operator.
|
|
*
|
|
* For example, the compound assignment `x += 23`
|
|
* uses `+` as the implicit operator.
|
|
*/
|
|
class AssignOp extends Assignment, @assignop {
|
|
/**
|
|
* Gets a source of the compound assignment, which includes both the right-hand side
|
|
* and the left-hand side of the assignment.
|
|
*/
|
|
override Expr getSource() { result.getParent() = this }
|
|
|
|
/** Gets a string representation of the assignment operator of this compound assignment. */
|
|
/*abstract*/ string getOp() { result = "??=" }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "..." + this.getOp() + "..." }
|
|
}
|
|
|
|
/** A compound assignment expression using the `+=` operator. */
|
|
class AssignAddExpr extends AssignOp, @assignaddexpr {
|
|
override string getOp() { result = "+=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignAddExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `-=` operator. */
|
|
class AssignSubExpr extends AssignOp, @assignsubexpr {
|
|
override string getOp() { result = "-=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignSubExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `*=` operator. */
|
|
class AssignMulExpr extends AssignOp, @assignmulexpr {
|
|
override string getOp() { result = "*=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignMulExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `/=` operator. */
|
|
class AssignDivExpr extends AssignOp, @assigndivexpr {
|
|
override string getOp() { result = "/=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignDivExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `%=` operator. */
|
|
class AssignRemExpr extends AssignOp, @assignremexpr {
|
|
override string getOp() { result = "%=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignRemExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `&=` operator. */
|
|
class AssignAndExpr extends AssignOp, @assignandexpr {
|
|
override string getOp() { result = "&=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignAndExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `|=` operator. */
|
|
class AssignOrExpr extends AssignOp, @assignorexpr {
|
|
override string getOp() { result = "|=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignOrExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `^=` operator. */
|
|
class AssignXorExpr extends AssignOp, @assignxorexpr {
|
|
override string getOp() { result = "^=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignXorExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `<<=` operator. */
|
|
class AssignLShiftExpr extends AssignOp, @assignlshiftexpr {
|
|
override string getOp() { result = "<<=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignLShiftExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `>>=` operator. */
|
|
class AssignRShiftExpr extends AssignOp, @assignrshiftexpr {
|
|
override string getOp() { result = ">>=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignRShiftExpr" }
|
|
}
|
|
|
|
/** A compound assignment expression using the `>>>=` operator. */
|
|
class AssignURShiftExpr extends AssignOp, @assignurshiftexpr {
|
|
override string getOp() { result = ">>>=" }
|
|
|
|
override string getAPrimaryQlClass() { result = "AssignURShiftExpr" }
|
|
}
|
|
|
|
/** A common super-class to represent constant literals. */
|
|
class Literal extends Expr, @literal {
|
|
/**
|
|
* Gets a string representation of this literal as it appeared
|
|
* in the source code.
|
|
*
|
|
* **Important:** Unless a query explicitly wants to check how
|
|
* a literal was written in the source code, the predicate
|
|
* `getValue()` (or value predicates of subclasses) should be
|
|
* used instead. For example for the integer literal `0x7fff_ffff`
|
|
* the result of `getLiteral()` would be `0x7fff_ffff`, while
|
|
* the result of `getValue()` would be `2147483647`.
|
|
*/
|
|
string getLiteral() { namestrings(result, _, this) }
|
|
|
|
/**
|
|
* Gets a string representation of the value this literal
|
|
* represents.
|
|
*/
|
|
string getValue() { namestrings(_, result, this) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = this.getLiteral() }
|
|
|
|
/** Holds if this literal is a compile-time constant expression (as per JLS v8, section 15.28). */
|
|
override predicate isCompileTimeConstant() {
|
|
this.getType() instanceof PrimitiveType or
|
|
this.getType() instanceof TypeString
|
|
}
|
|
}
|
|
|
|
/** A boolean literal. Either `true` or `false`. */
|
|
class BooleanLiteral extends Literal, @booleanliteral {
|
|
/** Gets the boolean representation of this literal. */
|
|
boolean getBooleanValue() {
|
|
result = true and this.getValue() = "true"
|
|
or
|
|
result = false and this.getValue() = "false"
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "BooleanLiteral" }
|
|
}
|
|
|
|
/**
|
|
* An integer literal. For example, `23`.
|
|
*
|
|
* An integer literal can never be negative except when:
|
|
* - It is written in binary, octal or hexadecimal notation
|
|
* - It is written in decimal notation, has the value `2147483648` and is preceded
|
|
* by a minus; in this case the value of the IntegerLiteral is -2147483648 and
|
|
* the preceding minus will *not* be modeled as `MinusExpr`.
|
|
*
|
|
* In all other cases the preceding minus, if any, will be modeled as a separate
|
|
* `MinusExpr`.
|
|
*
|
|
* The last exception is necessary because `2147483648` on its own would not be
|
|
* a valid integer literal (and could also not be parsed as CodeQL `int`).
|
|
*/
|
|
class IntegerLiteral extends Literal, @integerliteral {
|
|
/** Gets the int representation of this literal. */
|
|
int getIntValue() { result = this.getValue().toInt() }
|
|
|
|
override string getAPrimaryQlClass() { result = "IntegerLiteral" }
|
|
}
|
|
|
|
/**
|
|
* A long literal. For example, `23L`.
|
|
*
|
|
* A long literal can never be negative except when:
|
|
* - It is written in binary, octal or hexadecimal notation
|
|
* - It is written in decimal notation, has the value `9223372036854775808` and
|
|
* is preceded by a minus; in this case the value of the LongLiteral is
|
|
* -9223372036854775808 and the preceding minus will *not* be modeled as
|
|
* `MinusExpr`.
|
|
*
|
|
* In all other cases the preceding minus, if any, will be modeled as a separate
|
|
* `MinusExpr`.
|
|
*
|
|
* The last exception is necessary because `9223372036854775808` on its own
|
|
* would not be a valid long literal.
|
|
*/
|
|
class LongLiteral extends Literal, @longliteral {
|
|
override string getAPrimaryQlClass() { result = "LongLiteral" }
|
|
}
|
|
|
|
/** DEPRECATED: Alias for FloatLiteral */
|
|
deprecated class FloatingPointLiteral = FloatLiteral;
|
|
|
|
/**
|
|
* A float literal. For example, `4.2f`.
|
|
*
|
|
* A float literal is never negative; a preceding minus, if any, will always
|
|
* be modeled as separate `MinusExpr`.
|
|
*/
|
|
class FloatLiteral extends Literal, @floatingpointliteral {
|
|
/**
|
|
* Gets the value of this literal as CodeQL 64-bit `float`. The value will
|
|
* be parsed as Java 32-bit `float` and then converted to a CodeQL `float`.
|
|
*/
|
|
float getFloatValue() { result = this.getValue().toFloat() }
|
|
|
|
override string getAPrimaryQlClass() { result = "FloatLiteral" }
|
|
}
|
|
|
|
/**
|
|
* A double literal. For example, `4.2`.
|
|
*
|
|
* A double literal is never negative; a preceding minus, if any, will always
|
|
* be modeled as separate `MinusExpr`.
|
|
*/
|
|
class DoubleLiteral extends Literal, @doubleliteral {
|
|
/**
|
|
* Gets the value of this literal as CodeQL 64-bit `float`. The result will
|
|
* have the same effective value as the Java `double` literal.
|
|
*/
|
|
float getDoubleValue() { result = this.getValue().toFloat() }
|
|
|
|
override string getAPrimaryQlClass() { result = "DoubleLiteral" }
|
|
}
|
|
|
|
bindingset[s]
|
|
private int fromHex(string s) {
|
|
exists(string digits | s.toUpperCase() = digits |
|
|
result =
|
|
sum(int i |
|
|
|
|
|
"0123456789ABCDEF".indexOf(digits.charAt(i)).bitShiftLeft((digits.length() - i - 1) * 4)
|
|
)
|
|
)
|
|
}
|
|
|
|
/** A character literal. For example, `'\n'`. */
|
|
class CharacterLiteral extends Literal, @characterliteral {
|
|
override string getAPrimaryQlClass() { result = "CharacterLiteral" }
|
|
|
|
/**
|
|
* Gets a string which consists of the single character represented by
|
|
* this literal.
|
|
*
|
|
* Unicode surrogate characters (U+D800 to U+DFFF) have the replacement character
|
|
* U+FFFD as result instead.
|
|
*/
|
|
override string getValue() { result = super.getValue() }
|
|
|
|
/**
|
|
* Gets the Unicode code point value of the character represented by
|
|
* this literal. The result is the same as if the Java code had cast
|
|
* the character to an `int`.
|
|
*/
|
|
int getCodePointValue() {
|
|
if this.getLiteral().matches("'\\u____'")
|
|
then result = fromHex(this.getLiteral().substring(3, 7))
|
|
else result.toUnicode() = this.getValue()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A string literal or text block (Java 15 feature). For example, `"hello world"`
|
|
* or
|
|
* ```java
|
|
* """
|
|
* Text with "quotes"
|
|
* """
|
|
* ```
|
|
*/
|
|
class StringLiteral extends Literal, @stringliteral {
|
|
/**
|
|
* Gets the string represented by this string literal, that is, the content
|
|
* of the literal without enclosing quotes and with escape sequences translated.
|
|
*
|
|
* Unpaired Unicode surrogate characters (U+D800 to U+DFFF) are replaced with the
|
|
* replacement character U+FFFD.
|
|
*/
|
|
override string getValue() { result = super.getValue() }
|
|
|
|
/**
|
|
* DEPRECATED: This predicate will be removed in a future version because
|
|
* it is just an alias for `getValue()`; that predicate should be used instead.
|
|
*
|
|
* Gets the literal string without the quotes.
|
|
*/
|
|
deprecated string getRepresentedString() { result = this.getValue() }
|
|
|
|
/** Holds if this string literal is a text block (`""" ... """`). */
|
|
predicate isTextBlock() { this.getLiteral().matches("\"\"\"%") }
|
|
|
|
override string getAPrimaryQlClass() { result = "StringLiteral" }
|
|
}
|
|
|
|
/** The null literal, written `null`. */
|
|
class NullLiteral extends Literal, @nullliteral {
|
|
// Override these predicates because the inherited ones have no result
|
|
override string getLiteral() { result = "null" }
|
|
|
|
override string getValue() { result = "null" }
|
|
|
|
override string getAPrimaryQlClass() { result = "NullLiteral" }
|
|
}
|
|
|
|
/** A common super-class to represent binary operator expressions. */
|
|
class BinaryExpr extends Expr, @binaryexpr {
|
|
/** Gets the operand on the left-hand side of this binary expression. */
|
|
Expr getLeftOperand() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the operand on the right-hand side of this binary expression. */
|
|
Expr getRightOperand() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Gets an operand (left or right). */
|
|
Expr getAnOperand() { result = this.getLeftOperand() or result = this.getRightOperand() }
|
|
|
|
/** The operands of this binary expression are `e` and `f`, in either order. */
|
|
predicate hasOperands(Expr e, Expr f) {
|
|
exists(int i | i in [0 .. 1] |
|
|
e.isNthChildOf(this, i) and
|
|
f.isNthChildOf(this, 1 - i)
|
|
)
|
|
}
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "..." + this.getOp() + "..." }
|
|
|
|
/** Gets a string representation of the operator of this binary expression. */
|
|
/*abstract*/ string getOp() { result = " ?? " }
|
|
}
|
|
|
|
/** A binary expression using the `*` operator. */
|
|
class MulExpr extends BinaryExpr, @mulexpr {
|
|
override string getOp() { result = " * " }
|
|
|
|
override string getAPrimaryQlClass() { result = "MulExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `/` operator. */
|
|
class DivExpr extends BinaryExpr, @divexpr {
|
|
override string getOp() { result = " / " }
|
|
|
|
override string getAPrimaryQlClass() { result = "DivExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `%` operator. */
|
|
class RemExpr extends BinaryExpr, @remexpr {
|
|
override string getOp() { result = " % " }
|
|
|
|
override string getAPrimaryQlClass() { result = "RemExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `+` operator. */
|
|
class AddExpr extends BinaryExpr, @addexpr {
|
|
override string getOp() { result = " + " }
|
|
|
|
override string getAPrimaryQlClass() { result = "AddExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `-` operator. */
|
|
class SubExpr extends BinaryExpr, @subexpr {
|
|
override string getOp() { result = " - " }
|
|
|
|
override string getAPrimaryQlClass() { result = "SubExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `<<` operator. */
|
|
class LShiftExpr extends BinaryExpr, @lshiftexpr {
|
|
override string getOp() { result = " << " }
|
|
|
|
override string getAPrimaryQlClass() { result = "LShiftExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `>>` operator. */
|
|
class RShiftExpr extends BinaryExpr, @rshiftexpr {
|
|
override string getOp() { result = " >> " }
|
|
|
|
override string getAPrimaryQlClass() { result = "RShiftExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `>>>` operator. */
|
|
class URShiftExpr extends BinaryExpr, @urshiftexpr {
|
|
override string getOp() { result = " >>> " }
|
|
|
|
override string getAPrimaryQlClass() { result = "URShiftExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `&` operator. */
|
|
class AndBitwiseExpr extends BinaryExpr, @andbitexpr {
|
|
override string getOp() { result = " & " }
|
|
|
|
override string getAPrimaryQlClass() { result = "AndBitwiseExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `|` operator. */
|
|
class OrBitwiseExpr extends BinaryExpr, @orbitexpr {
|
|
override string getOp() { result = " | " }
|
|
|
|
override string getAPrimaryQlClass() { result = "OrBitwiseExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `^` operator. */
|
|
class XorBitwiseExpr extends BinaryExpr, @xorbitexpr {
|
|
override string getOp() { result = " ^ " }
|
|
|
|
override string getAPrimaryQlClass() { result = "XorBitwiseExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `&&` operator. */
|
|
class AndLogicalExpr extends BinaryExpr, @andlogicalexpr {
|
|
override string getOp() { result = " && " }
|
|
|
|
override string getAPrimaryQlClass() { result = "AndLogicalExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `||` operator. */
|
|
class OrLogicalExpr extends BinaryExpr, @orlogicalexpr {
|
|
override string getOp() { result = " || " }
|
|
|
|
override string getAPrimaryQlClass() { result = "OrLogicalExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `<` operator. */
|
|
class LTExpr extends BinaryExpr, @ltexpr {
|
|
override string getOp() { result = " < " }
|
|
|
|
override string getAPrimaryQlClass() { result = "LTExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `>` operator. */
|
|
class GTExpr extends BinaryExpr, @gtexpr {
|
|
override string getOp() { result = " > " }
|
|
|
|
override string getAPrimaryQlClass() { result = "GTExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `<=` operator. */
|
|
class LEExpr extends BinaryExpr, @leexpr {
|
|
override string getOp() { result = " <= " }
|
|
|
|
override string getAPrimaryQlClass() { result = "LEExpr" }
|
|
}
|
|
|
|
/** A binary expression using the `>=` operator. */
|
|
class GEExpr extends BinaryExpr, @geexpr {
|
|
override string getOp() { result = " >= " }
|
|
|
|
override string getAPrimaryQlClass() { result = "GEExpr" }
|
|
}
|
|
|
|
/** A binary expression using Java's `==` or Kotlin's `===` operator. */
|
|
class EQExpr extends BinaryExpr, @eqexpr {
|
|
override string getOp() { result = " == " }
|
|
|
|
override string getAPrimaryQlClass() { result = "EQExpr" }
|
|
}
|
|
|
|
/** A binary expression using the Kotlin `==` operator, semantically equivalent to `Objects.equals`. */
|
|
class ValueEQExpr extends BinaryExpr, @valueeqexpr {
|
|
override string getOp() { result = " (value equals) " }
|
|
|
|
override string getAPrimaryQlClass() { result = "ValueEQExpr" }
|
|
}
|
|
|
|
/** A binary expression using Java's `!=` or Kotlin's `!==` operator. */
|
|
class NEExpr extends BinaryExpr, @neexpr {
|
|
override string getOp() { result = " != " }
|
|
|
|
override string getAPrimaryQlClass() { result = "NEExpr" }
|
|
}
|
|
|
|
/** A binary expression using the Kotlin `!=` operator, semantically equivalent to `Objects.equals`. */
|
|
class ValueNEExpr extends BinaryExpr, @valueneexpr {
|
|
override string getOp() { result = " (value not-equals) " }
|
|
|
|
override string getAPrimaryQlClass() { result = "ValueNEExpr" }
|
|
}
|
|
|
|
/**
|
|
* A binary expression using either Java or Kotlin's `==` operator.
|
|
*
|
|
* This might test for reference equality or might function like `Objects.equals`. If you
|
|
* need to distinguish them, use `EQExpr` or `ValueEQExpr` instead.
|
|
*/
|
|
class ValueOrReferenceEqualsExpr extends BinaryExpr {
|
|
ValueOrReferenceEqualsExpr() { this instanceof EQExpr or this instanceof ValueEQExpr }
|
|
}
|
|
|
|
/**
|
|
* A binary expression using either Java or Kotlin's `!=` operator.
|
|
*
|
|
* This might test for reference equality or might function like `Objects.equals`. If you
|
|
* need to distinguish them, use `NEExpr` or `ValueNEExpr` instead.
|
|
*/
|
|
class ValueOrReferenceNotEqualsExpr extends BinaryExpr {
|
|
ValueOrReferenceNotEqualsExpr() { this instanceof NEExpr or this instanceof ValueNEExpr }
|
|
}
|
|
|
|
/**
|
|
* A bitwise expression.
|
|
*
|
|
* This includes expressions involving the operators
|
|
* `&`, `|`, `^`, or `~`.
|
|
*/
|
|
class BitwiseExpr extends Expr {
|
|
BitwiseExpr() {
|
|
this instanceof AndBitwiseExpr or
|
|
this instanceof OrBitwiseExpr or
|
|
this instanceof XorBitwiseExpr or
|
|
this instanceof BitNotExpr
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A logical expression.
|
|
*
|
|
* This includes expressions involving the operators
|
|
* `&&`, `||`, or `!`.
|
|
*/
|
|
class LogicExpr extends Expr {
|
|
LogicExpr() {
|
|
this instanceof AndLogicalExpr or
|
|
this instanceof OrLogicalExpr or
|
|
this instanceof LogNotExpr
|
|
}
|
|
|
|
/** Gets an operand of this logical expression. */
|
|
Expr getAnOperand() {
|
|
this.(BinaryExpr).getAnOperand() = result or
|
|
this.(UnaryExpr).getExpr() = result
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A comparison expression.
|
|
*
|
|
* This includes expressions using the operators
|
|
* `<=`, `>=`, `<` or `>`.
|
|
*/
|
|
abstract class ComparisonExpr extends BinaryExpr {
|
|
/**
|
|
* Gets the lesser operand of this comparison expression.
|
|
*
|
|
* For example, `x` is the lesser operand
|
|
* in `x < 0`, and `0` is the
|
|
* lesser operand in `x > 0`.
|
|
*/
|
|
abstract Expr getLesserOperand();
|
|
|
|
/**
|
|
* Gets the greater operand of this comparison expression.
|
|
*
|
|
* For example, `x` is the greater operand
|
|
* in `x > 0`, and `0` is the
|
|
* greater operand in `x < 0`.
|
|
*/
|
|
abstract Expr getGreaterOperand();
|
|
|
|
/** Holds if this comparison is strict, i.e. `<` or `>`. */
|
|
predicate isStrict() { this instanceof LTExpr or this instanceof GTExpr }
|
|
}
|
|
|
|
/** A comparison expression using the operator `<` or `<=`. */
|
|
class LessThanComparison extends ComparisonExpr {
|
|
LessThanComparison() { this instanceof LTExpr or this instanceof LEExpr }
|
|
|
|
/** Gets the lesser operand of this comparison expression. */
|
|
override Expr getLesserOperand() { result = this.getLeftOperand() }
|
|
|
|
/** Gets the greater operand of this comparison expression. */
|
|
override Expr getGreaterOperand() { result = this.getRightOperand() }
|
|
}
|
|
|
|
/** A comparison expression using the operator `>` or `>=`. */
|
|
class GreaterThanComparison extends ComparisonExpr {
|
|
GreaterThanComparison() { this instanceof GTExpr or this instanceof GEExpr }
|
|
|
|
/** Gets the lesser operand of this comparison expression. */
|
|
override Expr getLesserOperand() { result = this.getRightOperand() }
|
|
|
|
/** Gets the greater operand of this comparison expression. */
|
|
override Expr getGreaterOperand() { result = this.getLeftOperand() }
|
|
}
|
|
|
|
/**
|
|
* An equality test is a binary expression using
|
|
* Java's `==` or `!=` operators, or Kotlin's `==`, `!=`, `===` or `!==` operators.
|
|
*
|
|
* This could be a reference- or a value-(in)equality test.
|
|
*/
|
|
class EqualityTest extends BinaryExpr {
|
|
EqualityTest() {
|
|
this instanceof EQExpr or
|
|
this instanceof NEExpr or
|
|
this instanceof ValueEQExpr or
|
|
this instanceof ValueNEExpr
|
|
}
|
|
|
|
/** Gets a boolean indicating whether this is `==` (true) or `!=` (false). */
|
|
boolean polarity() {
|
|
result = true and this instanceof EQExpr
|
|
or
|
|
result = false and this instanceof NEExpr
|
|
or
|
|
result = true and this instanceof ValueEQExpr
|
|
or
|
|
result = false and this instanceof ValueNEExpr
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An equality test is a binary expression using
|
|
* Java's `==` or `!=` operators or Kotlin's `===` or `!==` operators.
|
|
*
|
|
* If either operand is a reference type, this is a reference-in/equality test.
|
|
*/
|
|
class ReferenceEqualityTest extends EqualityTest {
|
|
ReferenceEqualityTest() {
|
|
this instanceof EQExpr or
|
|
this instanceof NEExpr
|
|
}
|
|
}
|
|
|
|
/** A common super-class that represents unary operator expressions. */
|
|
class UnaryExpr extends Expr, @unaryexpr {
|
|
/** Gets the operand expression. */
|
|
Expr getExpr() { result.getParent() = this }
|
|
}
|
|
|
|
/**
|
|
* A unary assignment expression is a unary expression using the
|
|
* prefix or postfix `++` or `--` operator.
|
|
*/
|
|
class UnaryAssignExpr extends UnaryExpr, @unaryassignment { }
|
|
|
|
/** A post-increment expression. For example, `i++`. */
|
|
class PostIncExpr extends UnaryAssignExpr, @postincexpr {
|
|
override string toString() { result = "...++" }
|
|
|
|
override string getAPrimaryQlClass() { result = "PostIncExpr" }
|
|
}
|
|
|
|
/** A post-decrement expression. For example, `i--`. */
|
|
class PostDecExpr extends UnaryAssignExpr, @postdecexpr {
|
|
override string toString() { result = "...--" }
|
|
|
|
override string getAPrimaryQlClass() { result = "PostDecExpr" }
|
|
}
|
|
|
|
/** A pre-increment expression. For example, `++i`. */
|
|
class PreIncExpr extends UnaryAssignExpr, @preincexpr {
|
|
override string toString() { result = "++..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "PreIncExpr" }
|
|
}
|
|
|
|
/** A pre-decrement expression. For example, `--i`. */
|
|
class PreDecExpr extends UnaryAssignExpr, @predecexpr {
|
|
override string toString() { result = "--..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "PreDecExpr" }
|
|
}
|
|
|
|
/** A unary minus expression. For example, `-i`. */
|
|
class MinusExpr extends UnaryExpr, @minusexpr {
|
|
override string toString() { result = "-..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "MinusExpr" }
|
|
}
|
|
|
|
/** A unary plus expression. For example, `+i`. */
|
|
class PlusExpr extends UnaryExpr, @plusexpr {
|
|
override string toString() { result = "+..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "PlusExpr" }
|
|
}
|
|
|
|
/** A bit negation expression. For example, `~x`. */
|
|
class BitNotExpr extends UnaryExpr, @bitnotexpr {
|
|
override string toString() { result = "~..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "BitNotExpr" }
|
|
}
|
|
|
|
/** A logical negation expression. For example, `!b`. */
|
|
class LogNotExpr extends UnaryExpr, @lognotexpr {
|
|
override string toString() { result = "!..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "LogNotExpr" }
|
|
}
|
|
|
|
/**
|
|
* Any kind of expression that casts values from one type to another.
|
|
*
|
|
* For Java, this is only `CastExpr`, but for Kotlin it includes
|
|
* various other explicit or implicit casting operators.
|
|
*/
|
|
class CastingExpr extends Expr {
|
|
CastingExpr() {
|
|
this instanceof @castexpr or
|
|
this instanceof @safecastexpr or
|
|
this instanceof @implicitcastexpr or
|
|
this instanceof @implicitnotnullexpr or
|
|
this instanceof @implicitcoerciontounitexpr or
|
|
this instanceof @unsafecoerceexpr
|
|
}
|
|
|
|
/** Gets the target type of this casting expression. */
|
|
Expr getTypeExpr() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the expression to which the casting operator is applied. */
|
|
Expr getExpr() { result.isNthChildOf(this, 1) }
|
|
}
|
|
|
|
/** A cast expression. */
|
|
class CastExpr extends CastingExpr, @castexpr {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "(...)..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "CastExpr" }
|
|
}
|
|
|
|
/** A safe cast expression. */
|
|
class SafeCastExpr extends CastingExpr, @safecastexpr {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "... as? ..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "SafeCastExpr" }
|
|
}
|
|
|
|
/** An implicit cast expression. */
|
|
class ImplicitCastExpr extends CastingExpr, @implicitcastexpr {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "<implicit cast>" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ImplicitCastExpr" }
|
|
}
|
|
|
|
/** An implicit cast-to-non-null expression. */
|
|
class ImplicitNotNullExpr extends CastingExpr, @implicitnotnullexpr {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "<implicit not null>" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ImplicitNotNullExpr" }
|
|
}
|
|
|
|
/** An implicit coercion-to-unit expression. */
|
|
class ImplicitCoercionToUnitExpr extends CastingExpr, @implicitcoerciontounitexpr {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "<implicit coercion to unit>" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ImplicitCoercionToUnitExpr" }
|
|
}
|
|
|
|
/** An unsafe coerce expression. */
|
|
class UnsafeCoerceExpr extends CastingExpr, @unsafecoerceexpr {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "<unsafe coerce>" }
|
|
|
|
override string getAPrimaryQlClass() { result = "UnsafeCoerceExpr" }
|
|
}
|
|
|
|
/** A class instance creation expression. */
|
|
class ClassInstanceExpr extends Expr, ConstructorCall, @classinstancexpr {
|
|
/** Gets the number of arguments provided to the constructor of the class instance creation expression. */
|
|
override int getNumArgument() { count(this.getAnArgument()) = result }
|
|
|
|
/** Gets an argument provided to the constructor of this class instance creation expression. */
|
|
override Expr getAnArgument() { result.getIndex() >= 0 and result.getParent() = this }
|
|
|
|
/**
|
|
* Gets the argument provided to the constructor of this class instance creation expression
|
|
* at the specified (zero-based) position.
|
|
*/
|
|
override Expr getArgument(int index) {
|
|
result.getIndex() = index and
|
|
result = this.getAnArgument()
|
|
}
|
|
|
|
/**
|
|
* Gets a type argument provided to the constructor of this class instance creation expression.
|
|
*
|
|
* This is used for instantiations of parameterized classes.
|
|
*/
|
|
Expr getATypeArgument() { result = this.getTypeName().(TypeAccess).getATypeArgument() }
|
|
|
|
/**
|
|
* Gets the type argument provided to the constructor of this class instance creation expression
|
|
* at the specified (zero-based) position.
|
|
*/
|
|
Expr getTypeArgument(int index) {
|
|
result = this.getTypeName().(TypeAccess).getTypeArgument(index)
|
|
}
|
|
|
|
/** Gets the qualifier of this class instance creation expression, if any. */
|
|
override Expr getQualifier() { result.isNthChildOf(this, -2) }
|
|
|
|
/**
|
|
* Gets the access to the type that is instantiated or subclassed by this
|
|
* class instance creation expression.
|
|
*/
|
|
Expr getTypeName() { result.isNthChildOf(this, -3) }
|
|
|
|
/** Gets the constructor invoked by this class instance creation expression. */
|
|
override Constructor getConstructor() { callableBinding(this, result) }
|
|
|
|
/** Gets the anonymous class created by this class instance creation expression, if any. */
|
|
AnonymousClass getAnonymousClass() { isAnonymClass(result, this) }
|
|
|
|
/**
|
|
* Holds if this class instance creation expression has an
|
|
* empty type argument list of the form `<>`.
|
|
*/
|
|
predicate isDiamond() {
|
|
this.getType() instanceof ParameterizedClass and
|
|
not exists(this.getATypeArgument())
|
|
}
|
|
|
|
/** Gets the immediately enclosing callable of this class instance creation expression. */
|
|
override Callable getEnclosingCallable() { result = Expr.super.getEnclosingCallable() }
|
|
|
|
/** Gets the immediately enclosing statement of this class instance creation expression. */
|
|
override Stmt getEnclosingStmt() { result = Expr.super.getEnclosingStmt() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "new " + this.getConstructor().getName() + "(...)" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ClassInstanceExpr" }
|
|
}
|
|
|
|
/** A functional expression is either a lambda expression or a member reference expression. */
|
|
abstract class FunctionalExpr extends ClassInstanceExpr {
|
|
/** Gets the implicit method corresponding to this functional expression. */
|
|
abstract Method asMethod();
|
|
}
|
|
|
|
/**
|
|
* Lambda expressions are represented by their implicit class instance creation expressions,
|
|
* which instantiate an anonymous class that overrides the unique method designated by
|
|
* their functional interface type. The parameters of the lambda expression correspond
|
|
* to the parameters of the overriding method, and the lambda body corresponds to the
|
|
* body of the overriding method (enclosed by a return statement and a block in the case
|
|
* of lambda expressions whose body is an expression).
|
|
*
|
|
* For details, see JLS v8 section 15.27.4: Run-Time Evaluation of Lambda Expressions.
|
|
*/
|
|
class LambdaExpr extends FunctionalExpr, @lambdaexpr {
|
|
/**
|
|
* Gets the implicit method corresponding to this lambda expression.
|
|
* The parameters of the lambda expression are the parameters of this method.
|
|
*/
|
|
override Method asMethod() {
|
|
not this.isKotlinFunctionN() and
|
|
result = this.getAnonymousClass().getAMethod()
|
|
or
|
|
this.isKotlinFunctionN() and
|
|
result = this.getAnonymousClass().getAMethod() and
|
|
result.getNumberOfParameters() > 1
|
|
}
|
|
|
|
/**
|
|
* Holds if this expression is a big-arity lambda expression in Kotlin.
|
|
*/
|
|
predicate isKotlinFunctionN() {
|
|
exists(RefType r |
|
|
this.getAnonymousClass().extendsOrImplements(r) and
|
|
r.getSourceDeclaration().hasQualifiedName("kotlin.jvm.functions", "FunctionN")
|
|
)
|
|
}
|
|
|
|
/** Holds if the body of this lambda is an expression. */
|
|
predicate hasExprBody() { lambdaKind(this, 0) }
|
|
|
|
/** Holds if the body of this lambda is a statement. */
|
|
predicate hasStmtBody() { lambdaKind(this, 1) }
|
|
|
|
/** Gets the body of this lambda expression, if it is an expression. */
|
|
Expr getExprBody() {
|
|
this.hasExprBody() and result = this.asMethod().getBody().getAChild().(ReturnStmt).getResult()
|
|
}
|
|
|
|
/** Gets the body of this lambda expression, if it is a statement. */
|
|
BlockStmt getStmtBody() { this.hasStmtBody() and result = this.asMethod().getBody() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...->..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "LambdaExpr" }
|
|
}
|
|
|
|
/**
|
|
* Member references are represented by their implicit class instance expressions,
|
|
* which instantiate an anonymous class that overrides the unique method designated by
|
|
* their functional interface type. The correspondence of the parameters of the overriding
|
|
* method in the anonymous class with the parameters of the callable that is referenced
|
|
* differs depending on the particular kind of member reference expression.
|
|
*
|
|
* For details, see JLS v8 section 15.13.3: Run-Time Evaluation of Method References.
|
|
*/
|
|
class MemberRefExpr extends FunctionalExpr, @memberref {
|
|
/**
|
|
* Gets the implicit method corresponding to this member reference expression.
|
|
* The body of this method is a return statement (enclosed in a block) whose expression
|
|
* is either a method access (if the reference is to a method), a class instance creation expression
|
|
* (if the reference is to a constructor) or an array creation expression (if the reference
|
|
* is to an array constructor).
|
|
*/
|
|
override Method asMethod() { result = this.getAnonymousClass().getAMethod() }
|
|
|
|
/**
|
|
* Gets the receiver type whose member this expression refers to. The result might not be
|
|
* the type which actually declares the member. For example, for the member reference `ArrayList::toString`,
|
|
* this predicate has the result `java.util.ArrayList`, the type explicitly referred to, while
|
|
* `getReferencedCallable` will have `java.util.AbstractCollection.toString` as result, which `ArrayList` inherits.
|
|
*/
|
|
RefType getReceiverType() {
|
|
exists(Stmt stmt, Expr resultExpr |
|
|
stmt = this.asMethod().getBody().(SingletonBlock).getStmt() and
|
|
(
|
|
resultExpr = stmt.(ReturnStmt).getResult()
|
|
or
|
|
// Note: Currently never an ExprStmt, but might change once https://github.com/github/codeql/issues/3605 is fixed
|
|
resultExpr = stmt.(ExprStmt).getExpr()
|
|
)
|
|
|
|
|
result = resultExpr.(MethodAccess).getReceiverType() or
|
|
result = resultExpr.(ClassInstanceExpr).getConstructedType() or
|
|
result = resultExpr.(ArrayCreationExpr).getType()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the method or constructor referenced by this member reference expression.
|
|
*/
|
|
Callable getReferencedCallable() { memberRefBinding(this, result) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...::..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "MemberRefExpr" }
|
|
}
|
|
|
|
/**
|
|
* Property references are represented by their implicit class instance expressions,
|
|
* which instantiate an anonymous class that overrides the `get` and `set` methods designated by
|
|
* their functional interface type.
|
|
*/
|
|
class PropertyRefExpr extends ClassInstanceExpr, @propertyref {
|
|
/**
|
|
* Gets the implicit `get` method corresponding to this property reference expression, if any.
|
|
*/
|
|
Method asGetMethod() {
|
|
result = this.getAnonymousClass().getAMethod() and result.getName() = "get"
|
|
}
|
|
|
|
/**
|
|
* Gets the implicit `set` method corresponding to this property reference expression, if any.
|
|
*/
|
|
Method asSetMethod() {
|
|
result = this.getAnonymousClass().getAMethod() and result.getName() = "set"
|
|
}
|
|
|
|
/**
|
|
* Gets the property getter referenced by this property reference expression, if any.
|
|
*/
|
|
Callable getGetterCallable() { propertyRefGetBinding(this, result) }
|
|
|
|
/**
|
|
* Gets the field referenced by this property reference expression, if any.
|
|
*/
|
|
Field getField() { propertyRefFieldBinding(this, result) }
|
|
|
|
/**
|
|
* Gets the property setter referenced by this property reference expression, if any.
|
|
*/
|
|
Callable getSetterCallable() { propertyRefSetBinding(this, result) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...::..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "PropertyRefExpr" }
|
|
}
|
|
|
|
/** A conditional expression or a `switch` expression. */
|
|
class ChooseExpr extends Expr {
|
|
ChooseExpr() { this instanceof ConditionalExpr or this instanceof SwitchExpr }
|
|
|
|
/** Gets a result expression of this `switch` or conditional expression. */
|
|
Expr getAResultExpr() {
|
|
result = this.(ConditionalExpr).getABranchExpr() or
|
|
result = this.(SwitchExpr).getAResult()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A conditional expression of the form `a ? b : c`, where `a` is the condition,
|
|
* `b` is the expression that is evaluated if the condition evaluates to `true`,
|
|
* and `c` is the expression that is evaluated if the condition evaluates to `false`.
|
|
*/
|
|
class ConditionalExpr extends Expr, @conditionalexpr {
|
|
/** Gets the condition of this conditional expression. */
|
|
Expr getCondition() { result.isNthChildOf(this, 0) }
|
|
|
|
/**
|
|
* Gets the expression that is evaluated if the condition of this
|
|
* conditional expression evaluates to `true`.
|
|
*/
|
|
Expr getTrueExpr() { result.isNthChildOf(this, 1) }
|
|
|
|
/**
|
|
* Gets the expression that is evaluated if the condition of this
|
|
* conditional expression evaluates to `false`.
|
|
*/
|
|
Expr getFalseExpr() { result.isNthChildOf(this, 2) }
|
|
|
|
/**
|
|
* Gets the expression that is evaluated by the specific branch of this
|
|
* conditional expression. If `true` that is `getTrueExpr()`, if `false`
|
|
* it is `getFalseExpr()`.
|
|
*/
|
|
Expr getBranchExpr(boolean branch) {
|
|
branch = true and result = this.getTrueExpr()
|
|
or
|
|
branch = false and result = this.getFalseExpr()
|
|
}
|
|
|
|
/**
|
|
* Gets the expressions that is evaluated by one of the branches (`true`
|
|
* or `false` branch) of this conditional expression.
|
|
*/
|
|
Expr getABranchExpr() { result = this.getBranchExpr(_) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...?...:..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "ConditionalExpr" }
|
|
}
|
|
|
|
/**
|
|
* A `switch` expression.
|
|
*/
|
|
class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
|
/** Gets an immediate child statement of this `switch` expression. */
|
|
Stmt getAStmt() { result.getParent() = this }
|
|
|
|
/**
|
|
* Gets the immediate child statement of this `switch` expression
|
|
* that occurs at the specified (zero-based) position.
|
|
*/
|
|
Stmt getStmt(int index) { result = this.getAStmt() and result.getIndex() = index }
|
|
|
|
/**
|
|
* Gets a case of this `switch` expression,
|
|
* which may be either a normal `case` or a `default`.
|
|
*/
|
|
SwitchCase getACase() { result = this.getAConstCase() or result = this.getDefaultCase() }
|
|
|
|
/** Gets a (non-default) `case` of this `switch` expression. */
|
|
ConstCase getAConstCase() { result.getParent() = this }
|
|
|
|
/** Gets the `default` case of this switch expression, if any. */
|
|
DefaultCase getDefaultCase() { result.getParent() = this }
|
|
|
|
/** Gets the expression of this `switch` expression. */
|
|
Expr getExpr() { result.getParent() = this }
|
|
|
|
/** Gets a result expression of this `switch` expression. */
|
|
Expr getAResult() {
|
|
result = this.getACase().getRuleExpression()
|
|
or
|
|
exists(YieldStmt yield | yield.getTarget() = this and result = yield.getValue())
|
|
}
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "switch (...)" }
|
|
|
|
override string getAPrimaryQlClass() { result = "SwitchExpr" }
|
|
}
|
|
|
|
/** An `instanceof` expression. */
|
|
class InstanceOfExpr extends Expr, @instanceofexpr {
|
|
/** Gets the expression on the left-hand side of the `instanceof` operator. */
|
|
Expr getExpr() {
|
|
if this.isPattern()
|
|
then result = this.getLocalVariableDeclExpr().getInit()
|
|
else result.isNthChildOf(this, 0)
|
|
}
|
|
|
|
/**
|
|
* Holds if this `instanceof` expression uses pattern matching.
|
|
*/
|
|
predicate isPattern() { exists(this.getLocalVariableDeclExpr()) }
|
|
|
|
/**
|
|
* Gets the local variable declaration of this `instanceof` expression if pattern matching is used.
|
|
*/
|
|
LocalVariableDeclExpr getLocalVariableDeclExpr() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the access to the type on the right-hand side of the `instanceof` operator. */
|
|
Expr getTypeName() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Gets the type this `instanceof` expression checks for. */
|
|
RefType getCheckedType() { result = this.getTypeName().getType() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...instanceof..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "InstanceOfExpr" }
|
|
}
|
|
|
|
// TODO: Should this be desugared into instanceof.not()?
|
|
// Note expressions/IrTypeOperatorCall.kt says:
|
|
// NOT_INSTANCEOF, // TODO drop and replace with `INSTANCEOF<T>(x).not()`?
|
|
/** An `instanceof` expression. */
|
|
class NotInstanceOfExpr extends Expr, @notinstanceofexpr {
|
|
/** Gets the expression on the left-hand side of the `!is` operator. */
|
|
Expr getExpr() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the access to the type on the right-hand side of the `!is` operator. */
|
|
Expr getTypeName() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Gets the type this `!is` expression checks for. */
|
|
RefType getCheckedType() { result = this.getTypeName().getType() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "... !is ..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "NotInstanceOfExpr" }
|
|
}
|
|
|
|
/**
|
|
* A local variable declaration expression.
|
|
*
|
|
* Contexts in which such expressions may occur include
|
|
* local variable declaration statements and `for` loops.
|
|
*/
|
|
class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr {
|
|
/** Gets an access to the variable declared by this local variable declaration expression. */
|
|
VarAccess getAnAccess() { variableBinding(result, this.getVariable()) }
|
|
|
|
/** Gets the local variable declared by this local variable declaration expression. */
|
|
LocalVariableDecl getVariable() { localvars(result, _, _, this) }
|
|
|
|
/** Gets the type access of this local variable declaration expression. */
|
|
Expr getTypeAccess() {
|
|
exists(LocalVariableDeclStmt lvds | lvds.getAVariable() = this | result.isNthChildOf(lvds, 0))
|
|
or
|
|
exists(CatchClause cc | cc.getVariable() = this | result.isNthChildOf(cc, -1))
|
|
or
|
|
exists(ForStmt fs | fs.getAnInit() = this | result.isNthChildOf(fs, 0))
|
|
or
|
|
exists(EnhancedForStmt efs | efs.getVariable() = this | result.isNthChildOf(efs, -1))
|
|
or
|
|
exists(InstanceOfExpr ioe | this.getParent() = ioe | result.isNthChildOf(ioe, 1))
|
|
}
|
|
|
|
/** Gets the name of the variable declared by this local variable declaration expression. */
|
|
string getName() { result = this.getVariable().getName() }
|
|
|
|
/** Gets the initializer expression of this local variable declaration expression, if any. */
|
|
Expr getInit() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Holds if this variable declaration implicitly initializes the variable. */
|
|
predicate hasImplicitInit() {
|
|
exists(CatchClause cc | cc.getVariable() = this) or
|
|
exists(EnhancedForStmt efs | efs.getVariable() = this)
|
|
}
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = this.getName() }
|
|
|
|
override string getAPrimaryQlClass() { result = "LocalVariableDeclExpr" }
|
|
}
|
|
|
|
/** An update of a variable or an initialization of the variable. */
|
|
class VariableUpdate extends Expr {
|
|
VariableUpdate() {
|
|
this.(Assignment).getDest() instanceof VarAccess or
|
|
this instanceof LocalVariableDeclExpr or
|
|
this.(UnaryAssignExpr).getExpr() instanceof VarAccess
|
|
}
|
|
|
|
/** Gets the destination of this variable update. */
|
|
Variable getDestVar() {
|
|
result.getAnAccess() = this.(Assignment).getDest() or
|
|
result = this.(LocalVariableDeclExpr).getVariable() or
|
|
result.getAnAccess() = this.(UnaryAssignExpr).getExpr()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An assignment to a variable or an initialization of the variable.
|
|
*/
|
|
class VariableAssign extends VariableUpdate {
|
|
VariableAssign() {
|
|
this instanceof AssignExpr or
|
|
this instanceof LocalVariableDeclExpr
|
|
}
|
|
|
|
/**
|
|
* Gets the source (right-hand side) of this assignment, if any.
|
|
*
|
|
* An initialization in a `CatchClause` or `EnhancedForStmt` is implicit and
|
|
* does not have a source.
|
|
*/
|
|
Expr getSource() {
|
|
result = this.(AssignExpr).getSource() or
|
|
result = this.(LocalVariableDeclExpr).getInit()
|
|
}
|
|
}
|
|
|
|
/** A type literal. For example, `String.class`. */
|
|
class TypeLiteral extends Expr, @typeliteral {
|
|
/** Gets the access to the type whose class is accessed. */
|
|
Expr getTypeName() { result.getParent() = this }
|
|
|
|
/**
|
|
* Gets the type this type literal refers to. For example for `String.class` the
|
|
* result is the type representing `String`.
|
|
*/
|
|
Type getReferencedType() { result = this.getTypeName().getType() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = this.getTypeName().toString() + ".class" }
|
|
|
|
override string getAPrimaryQlClass() { result = "TypeLiteral" }
|
|
}
|
|
|
|
/**
|
|
* A use of one of the keywords `this` or `super`, which may be qualified.
|
|
*/
|
|
abstract class InstanceAccess extends Expr {
|
|
/**
|
|
* Gets the qualifying expression, if any.
|
|
*
|
|
* For example, the qualifying expression of `A.this` is `A`.
|
|
*/
|
|
Expr getQualifier() { result.getParent() = this }
|
|
|
|
/**
|
|
* Holds if this instance access gets the value of `this`. That is, it is not
|
|
* an enclosing instance.
|
|
* This never holds for accesses in lambda expressions as they cannot access
|
|
* their own instance directly.
|
|
*/
|
|
predicate isOwnInstanceAccess() { not this.isEnclosingInstanceAccess(_) }
|
|
|
|
/** Holds if this instance access is to an enclosing instance of type `t`. */
|
|
predicate isEnclosingInstanceAccess(RefType t) {
|
|
t = this.getQualifier().getType().(RefType).getSourceDeclaration() and
|
|
t != this.getEnclosingCallable().getDeclaringType()
|
|
or
|
|
not exists(this.getQualifier()) and
|
|
exists(LambdaExpr lam | lam.asMethod() = this.getEnclosingCallable() |
|
|
t = lam.getAnonymousClass().getEnclosingType()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A use of the keyword `this`, which may be qualified.
|
|
*
|
|
* Such an expression allows access to an enclosing instance.
|
|
* For example, `A.this` refers to the enclosing instance
|
|
* of type `A`.
|
|
*/
|
|
class ThisAccess extends InstanceAccess, @thisaccess {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() {
|
|
if exists(this.getQualifier()) then result = this.getQualifier() + ".this" else result = "this"
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "ThisAccess" }
|
|
}
|
|
|
|
/**
|
|
* A use of the keyword `super`, which may be qualified.
|
|
*
|
|
* Such an expression allows access to super-class members of an enclosing instance.
|
|
* For example, `A.super.x`.
|
|
*/
|
|
class SuperAccess extends InstanceAccess, @superaccess {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() {
|
|
if exists(this.getQualifier())
|
|
then result = this.getQualifier() + ".super"
|
|
else result = "super"
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "SuperAccess" }
|
|
}
|
|
|
|
/**
|
|
* A variable access is a (possibly qualified) reference to
|
|
* a field, parameter or local variable.
|
|
*/
|
|
class VarAccess extends Expr, @varaccess {
|
|
/** Gets the qualifier of this variable access, if any. */
|
|
Expr getQualifier() { result.getParent() = this }
|
|
|
|
/** Holds if this variable access has a qualifier. */
|
|
predicate hasQualifier() { exists(this.getQualifier()) }
|
|
|
|
/** Gets the variable accessed by this variable access. */
|
|
Variable getVariable() { variableBinding(this, result) }
|
|
|
|
/**
|
|
* Holds if this variable access is an l-value.
|
|
*
|
|
* An l-value is a write access to a variable, which occurs as the destination of an assignment.
|
|
*/
|
|
predicate isLValue() {
|
|
exists(Assignment a | a.getDest() = this) or
|
|
exists(UnaryAssignExpr e | e.getExpr() = this)
|
|
}
|
|
|
|
/**
|
|
* Holds if this variable access is an r-value.
|
|
*
|
|
* An r-value is a read access to a variable.
|
|
* In other words, it is a variable access that does _not_ occur as the destination of
|
|
* a simple assignment, but it may occur as the destination of a compound assignment
|
|
* or a unary assignment.
|
|
*/
|
|
predicate isRValue() { not exists(AssignExpr a | a.getDest() = this) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() {
|
|
exists(Expr q | q = this.getQualifier() |
|
|
if q.isParenthesized()
|
|
then result = "(...)." + this.getVariable().getName()
|
|
else result = q.toString() + "." + this.getVariable().getName()
|
|
)
|
|
or
|
|
not this.hasQualifier() and result = this.getVariable().getName()
|
|
}
|
|
|
|
/**
|
|
* Holds if this access refers to a local variable or a field of
|
|
* the receiver of the enclosing method or constructor.
|
|
*/
|
|
predicate isLocal() {
|
|
// The access has no qualifier, or...
|
|
not this.hasQualifier()
|
|
or
|
|
// the qualifier is either `this` or `A.this`, where `A` is the enclosing type, or
|
|
// the qualifier is either `super` or `A.super`, where `A` is the enclosing type.
|
|
this.getQualifier().(InstanceAccess).isOwnInstanceAccess()
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "VarAccess" }
|
|
}
|
|
|
|
/**
|
|
* An access to an extension receiver parameter. This is a parameter access that takes the form of `this` in Kotlin.
|
|
*/
|
|
class ExtensionReceiverAccess extends VarAccess {
|
|
ExtensionReceiverAccess() {
|
|
exists(Parameter p |
|
|
this.getVariable() = p and p.getPosition() = 0 and p.getCallable() instanceof ExtensionMethod
|
|
)
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "ExtensionReceiverAccess" }
|
|
|
|
override string toString() { result = "this" }
|
|
}
|
|
|
|
/**
|
|
* An l-value is a write access to a variable, which occurs as the destination of an assignment.
|
|
*/
|
|
class LValue extends VarAccess {
|
|
LValue() { this.isLValue() }
|
|
|
|
/**
|
|
* Gets a source expression used in an assignment to this l-value.
|
|
*
|
|
* For assignments using the `=` operator, the source expression
|
|
* is simply the RHS of the assignment.
|
|
*
|
|
* Note that for l-values occurring on the LHS of compound assignment operators
|
|
* (such as (`+=`), both the RHS and the LHS of the compound assignment
|
|
* are source expressions of the assignment.
|
|
*/
|
|
Expr getRhs() { exists(Assignment e | e.getDest() = this and e.getSource() = result) }
|
|
|
|
/** DEPRECATED: Alias for getRhs */
|
|
deprecated Expr getRHS() { result = this.getRhs() }
|
|
}
|
|
|
|
/**
|
|
* An r-value is a read access to a variable.
|
|
*
|
|
* In other words, it is a variable access that does _not_ occur as the destination of
|
|
* a simple assignment, but it may occur as the destination of a compound assignment
|
|
* or a unary assignment.
|
|
*/
|
|
class RValue extends VarAccess {
|
|
RValue() { this.isRValue() }
|
|
}
|
|
|
|
/** A method access is an invocation of a method with a list of arguments. */
|
|
class MethodAccess extends Expr, Call, @methodaccess {
|
|
/** Gets the qualifying expression of this method access, if any. */
|
|
override Expr getQualifier() { result.isNthChildOf(this, -1) }
|
|
|
|
/** Holds if this method access has a qualifier. */
|
|
predicate hasQualifier() { exists(this.getQualifier()) }
|
|
|
|
/** Gets an argument supplied to the method that is invoked using this method access. */
|
|
override Expr getAnArgument() { result.getIndex() >= 0 and result.getParent() = this }
|
|
|
|
/** Gets the argument at the specified (zero-based) position in this method access. */
|
|
override Expr getArgument(int index) { exprs(result, _, _, this, index) and index >= 0 }
|
|
|
|
/** Gets a type argument supplied as part of this method access, if any. */
|
|
Expr getATypeArgument() { result.getIndex() <= -2 and result.getParent() = this }
|
|
|
|
/** Gets the type argument at the specified (zero-based) position in this method access, if any. */
|
|
Expr getTypeArgument(int index) {
|
|
result = this.getATypeArgument() and
|
|
(-2 - result.getIndex()) = index
|
|
}
|
|
|
|
/** Gets the method accessed by this method access. */
|
|
Method getMethod() { callableBinding(this, result) }
|
|
|
|
/** Gets the immediately enclosing callable that contains this method access. */
|
|
override Callable getEnclosingCallable() { result = Expr.super.getEnclosingCallable() }
|
|
|
|
/** Gets the immediately enclosing statement that contains this method access. */
|
|
override Stmt getEnclosingStmt() { result = Expr.super.getEnclosingStmt() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = this.printAccess() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
string printAccess() { result = this.getMethod().getName() + "(...)" }
|
|
|
|
/**
|
|
* Gets the type of the qualifier on which this method is invoked, or
|
|
* the enclosing type if there is no qualifier.
|
|
*/
|
|
RefType getReceiverType() {
|
|
result = this.getQualifier().getType()
|
|
or
|
|
not this.hasQualifier() and result = this.getEnclosingCallable().getDeclaringType()
|
|
}
|
|
|
|
/**
|
|
* Holds if this is a method access to an instance method of `this`. That is,
|
|
* the qualifier is either an explicit or implicit unqualified `this` or `super`.
|
|
*/
|
|
predicate isOwnMethodAccess() { Qualifier::ownMemberAccess(this) }
|
|
|
|
/**
|
|
* Holds if this is a method access to an instance method of the enclosing
|
|
* class `t`. That is, the qualifier is either an explicit or implicit
|
|
* `t`-qualified `this` or `super`.
|
|
*/
|
|
predicate isEnclosingMethodAccess(RefType t) { Qualifier::enclosingMemberAccess(this, t) }
|
|
|
|
override string getAPrimaryQlClass() { result = "MethodAccess" }
|
|
}
|
|
|
|
/** A type access is a (possibly qualified) reference to a type. */
|
|
class TypeAccess extends Expr, Annotatable, @typeaccess {
|
|
/** Gets the qualifier of this type access, if any. */
|
|
Expr getQualifier() { result.isNthChildOf(this, -1) }
|
|
|
|
/** Holds if this type access has a qualifier. */
|
|
predicate hasQualifier() { exists(this.getQualifier()) }
|
|
|
|
/** Gets a type argument supplied to this type access. */
|
|
Expr getATypeArgument() { result.getIndex() >= 0 and result.getParent() = this }
|
|
|
|
/** Gets the type argument at the specified (zero-based) position in this type access. */
|
|
Expr getTypeArgument(int index) {
|
|
result = this.getATypeArgument() and
|
|
result.getIndex() = index
|
|
}
|
|
|
|
/** Holds if this type access has a type argument. */
|
|
predicate hasTypeArgument() { exists(this.getATypeArgument()) }
|
|
|
|
/** Gets the compilation unit in which this type access occurs. */
|
|
override CompilationUnit getCompilationUnit() { result = Expr.super.getCompilationUnit() }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() {
|
|
result = this.getQualifier().toString() + "." + this.getType().toString()
|
|
or
|
|
not this.hasQualifier() and result = this.getType().toString()
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "TypeAccess" }
|
|
}
|
|
|
|
/** An array type access is a type access of the form `String[]`. */
|
|
class ArrayTypeAccess extends Expr, @arraytypeaccess {
|
|
/**
|
|
* Gets the expression representing the component type of this array type access.
|
|
*
|
|
* For example, in the array type access `String[][]`,
|
|
* the component type is `String[]` and the
|
|
* element type is `String`.
|
|
*/
|
|
Expr getComponentName() { result.getParent() = this }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...[]" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ArrayTypeAccess" }
|
|
}
|
|
|
|
/**
|
|
* A union type access is a type access of the form `T1 | T2`.
|
|
*
|
|
* Such a type access can only occur in a multi-catch clause.
|
|
*/
|
|
class UnionTypeAccess extends Expr, @uniontypeaccess {
|
|
/** One of the alternatives in the union type access. */
|
|
Expr getAnAlternative() { result.getParent() = this }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...|..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "UnionTypeAccess" }
|
|
}
|
|
|
|
/**
|
|
* An intersection type access expression is a type access
|
|
* of the form `T0 & T1 & ... & Tn`,
|
|
* where `T0` is a class or interface type and
|
|
* `T1, ..., Tn` are interface types.
|
|
*
|
|
* An intersection type access _expression_ can only
|
|
* occur in a cast expression.
|
|
*
|
|
* Note that intersection types can also occur as the bound
|
|
* of a bounded type, such as a type variable. Each component
|
|
* of such a bound is represented by the QL class `TypeBound`.
|
|
*/
|
|
class IntersectionTypeAccess extends Expr, @intersectiontypeaccess {
|
|
/**
|
|
* One of the bounds of this intersection type access expression.
|
|
*
|
|
* For example, in the intersection type access expression
|
|
* `Runnable & Cloneable`, both `Runnable`
|
|
* and `Cloneable` are bounds.
|
|
*/
|
|
Expr getABound() { result.getParent() = this }
|
|
|
|
/**
|
|
* Gets the bound at a specified (zero-based) position in this intersection type access expression.
|
|
*
|
|
* For example, in the intersection type access expression
|
|
* `Runnable & Cloneable`, the bound at position 0 is
|
|
* `Runnable` and the bound at position 1 is `Cloneable`.
|
|
*/
|
|
Expr getBound(int index) { result.isNthChildOf(this, index) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "...&..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "IntersectionTypeAccess" }
|
|
}
|
|
|
|
/** A package access. */
|
|
class PackageAccess extends Expr, @packageaccess {
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "package" }
|
|
|
|
override string getAPrimaryQlClass() { result = "PackageAccess" }
|
|
}
|
|
|
|
/** A wildcard type access, which may have either a lower or an upper bound. */
|
|
class WildcardTypeAccess extends Expr, @wildcardtypeaccess {
|
|
/** Gets the upper bound of this wildcard type access, if any. */
|
|
Expr getUpperBound() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the lower bound of this wildcard type access, if any. */
|
|
Expr getLowerBound() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Holds if this wildcard is not bounded by any type bounds. */
|
|
predicate hasNoBound() { not exists(TypeAccess t | t.getParent() = this) }
|
|
|
|
/** Gets a printable representation of this expression. */
|
|
override string toString() { result = "? ..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "WildcardTypeAccess" }
|
|
}
|
|
|
|
/**
|
|
* Any call to a callable.
|
|
*
|
|
* This includes method calls, constructor and super constructor invocations,
|
|
* and constructors invoked through class instantiation.
|
|
*/
|
|
class Call extends ExprParent, @caller {
|
|
/** Gets an argument supplied in this call. */
|
|
/*abstract*/ Expr getAnArgument() { none() }
|
|
|
|
/** Gets the argument specified at the (zero-based) position in this call. */
|
|
/*abstract*/ Expr getArgument(int n) { none() }
|
|
|
|
/** Gets the immediately enclosing callable that contains this call. */
|
|
/*abstract*/ Callable getEnclosingCallable() { none() }
|
|
|
|
/** Gets the qualifying expression of this call, if any. */
|
|
/*abstract*/ Expr getQualifier() { none() }
|
|
|
|
/** Gets the enclosing statement of this call. */
|
|
/*abstract*/ Stmt getEnclosingStmt() { none() }
|
|
|
|
/** Gets the number of arguments provided in this call. */
|
|
int getNumArgument() { count(this.getAnArgument()) = result }
|
|
|
|
/** Gets the target callable of this call. */
|
|
Callable getCallee() { callableBinding(this, result) }
|
|
|
|
/** Gets the callable invoking this call. */
|
|
Callable getCaller() { result = this.getEnclosingCallable() }
|
|
}
|
|
|
|
/** A polymorphic call to an instance method. */
|
|
class VirtualMethodAccess extends MethodAccess {
|
|
VirtualMethodAccess() {
|
|
this.getMethod().isVirtual() and
|
|
not this.getQualifier() instanceof SuperAccess
|
|
}
|
|
}
|
|
|
|
/** A static method call. */
|
|
class StaticMethodAccess extends MethodAccess {
|
|
StaticMethodAccess() { this.getMethod().isStatic() }
|
|
}
|
|
|
|
/** A call to a method in the superclass. */
|
|
class SuperMethodAccess extends MethodAccess {
|
|
SuperMethodAccess() { this.getQualifier() instanceof SuperAccess }
|
|
}
|
|
|
|
/**
|
|
* A constructor call, which occurs either as a constructor invocation inside a
|
|
* constructor, or as part of a class instance expression.
|
|
*/
|
|
abstract class ConstructorCall extends Call {
|
|
/** Gets the target constructor of the class being instantiated. */
|
|
abstract Constructor getConstructor();
|
|
|
|
/** Holds if this constructor call is an explicit call to `this(...)`. */
|
|
predicate callsThis() { this instanceof ThisConstructorInvocationStmt }
|
|
|
|
/** Holds if this constructor call is an explicit call to `super(...)`. */
|
|
predicate callsSuper() { this instanceof SuperConstructorInvocationStmt }
|
|
|
|
/** Gets the type of the object instantiated by this constructor call. */
|
|
RefType getConstructedType() { result = this.getConstructor().getDeclaringType() }
|
|
}
|
|
|
|
/** An expression that accesses a field. */
|
|
class FieldAccess extends VarAccess {
|
|
FieldAccess() { this.getVariable() instanceof Field }
|
|
|
|
/** Gets the field accessed by this field access expression. */
|
|
Field getField() { this.getVariable() = result }
|
|
|
|
/** Gets the immediately enclosing callable that contains this field access expression. */
|
|
Callable getSite() { this.getEnclosingCallable() = result }
|
|
|
|
/**
|
|
* Holds if this is a field access to an instance field of `this`. That is,
|
|
* the qualifier is either an explicit or implicit unqualified `this` or `super`.
|
|
*/
|
|
predicate isOwnFieldAccess() { Qualifier::ownMemberAccess(this) }
|
|
|
|
/**
|
|
* Holds if this is a field access to an instance field of the enclosing
|
|
* class `t`. That is, the qualifier is either an explicit or implicit
|
|
* `t`-qualified `this` or `super`.
|
|
*/
|
|
predicate isEnclosingFieldAccess(RefType t) { Qualifier::enclosingMemberAccess(this, t) }
|
|
}
|
|
|
|
private module Qualifier {
|
|
/** A type qualifier for an `InstanceAccess`. */
|
|
private newtype TThisQualifier =
|
|
TThis() or
|
|
TEnclosing(RefType t)
|
|
|
|
/** An expression that accesses a member. That is, either a `FieldAccess` or a `MethodAccess`. */
|
|
class MemberAccess extends Expr {
|
|
MemberAccess() {
|
|
this instanceof FieldAccess or
|
|
this instanceof MethodAccess
|
|
}
|
|
|
|
/** Gets the member accessed by this member access. */
|
|
Member getMember() {
|
|
result = this.(FieldAccess).getField() or
|
|
result = this.(MethodAccess).getMethod()
|
|
}
|
|
|
|
/** Gets the qualifier of this member access, if any. */
|
|
Expr getQualifier() {
|
|
result = this.(FieldAccess).getQualifier() or
|
|
result = this.(MethodAccess).getQualifier()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the implicit type qualifier of the implicit `ThisAccess` qualifier of
|
|
* an access to `m` from within `ic`, which does not itself inherit `m`.
|
|
*/
|
|
private RefType getImplicitEnclosingQualifier(InnerClass ic, Member m) {
|
|
exists(RefType enclosing | enclosing = ic.getEnclosingType() |
|
|
if enclosing.inherits(m)
|
|
then result = enclosing
|
|
else result = getImplicitEnclosingQualifier(enclosing, m)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the implicit type qualifier of the implicit `ThisAccess` qualifier of `ma`.
|
|
*/
|
|
private TThisQualifier getImplicitQualifier(MemberAccess ma) {
|
|
exists(Member m | m = ma.getMember() |
|
|
not m.isStatic() and
|
|
not exists(ma.getQualifier()) and
|
|
exists(RefType t | t = ma.getEnclosingCallable().getDeclaringType() |
|
|
not t instanceof InnerClass and result = TThis()
|
|
or
|
|
exists(InnerClass ic | ic = t |
|
|
if ic.inherits(m)
|
|
then result = TThis()
|
|
else result = TEnclosing(getImplicitEnclosingQualifier(ic, m))
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the type qualifier of the `InstanceAccess` qualifier of `ma`.
|
|
*/
|
|
private TThisQualifier getThisQualifier(MemberAccess ma) {
|
|
result = getImplicitQualifier(ma)
|
|
or
|
|
exists(Expr q |
|
|
not ma.getMember().isStatic() and
|
|
q = ma.getQualifier()
|
|
|
|
|
exists(InstanceAccess ia | ia = q and ia.isOwnInstanceAccess() and result = TThis())
|
|
or
|
|
exists(InstanceAccess ia, RefType qt |
|
|
ia = q and ia.isEnclosingInstanceAccess(qt) and result = TEnclosing(qt)
|
|
)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Holds if `ma` is a member access to an instance field or method of `this`. That is,
|
|
* the qualifier is either an explicit or implicit unqualified `this` or `super`.
|
|
*/
|
|
predicate ownMemberAccess(MemberAccess ma) { TThis() = getThisQualifier(ma) }
|
|
|
|
/**
|
|
* Holds if `ma` is a member access to an instance field or method of the enclosing
|
|
* class `t`. That is, the qualifier is either an explicit or implicit
|
|
* `t`-qualified `this` or `super`.
|
|
*/
|
|
predicate enclosingMemberAccess(MemberAccess ma, RefType t) {
|
|
TEnclosing(t) = getThisQualifier(ma)
|
|
}
|
|
}
|
|
|
|
/** An expression that assigns a value to a field. */
|
|
class FieldWrite extends FieldAccess, LValue { }
|
|
|
|
/** An expression that reads a field. */
|
|
class FieldRead extends FieldAccess, RValue { }
|
|
|
|
private predicate hasInstantiation(RefType t) {
|
|
t instanceof TypeVariable or
|
|
t instanceof Wildcard or
|
|
hasInstantiation(t.(Array).getComponentType()) or
|
|
hasInstantiation(t.(ParameterizedType).getATypeArgument())
|
|
}
|
|
|
|
/** An argument to a call. */
|
|
class Argument extends Expr {
|
|
Call call;
|
|
int pos;
|
|
|
|
Argument() { call.getArgument(pos) = this }
|
|
|
|
/** Gets the call that has this argument. */
|
|
Call getCall() { result = call }
|
|
|
|
/** Gets the position of this argument. */
|
|
int getPosition() { result = pos }
|
|
|
|
/**
|
|
* Holds if this argument is an array of the appropriate type passed to a
|
|
* varargs parameter.
|
|
*/
|
|
predicate isExplicitVarargsArray() {
|
|
exists(Array typ, Parameter p, Type ptyp |
|
|
typ = this.getType() and
|
|
call.getCallee().getParameter(pos) = p and
|
|
p.isVarargs() and
|
|
ptyp = p.getType() and
|
|
(
|
|
hasDescendant(ptyp, typ)
|
|
or
|
|
// If the types don't match then we'll guess based on whether there are type variables involved.
|
|
hasInstantiation(ptyp.(Array).getComponentType())
|
|
)
|
|
)
|
|
}
|
|
|
|
/** Holds if this argument is part of an implicit varargs array. */
|
|
predicate isVararg() { this.isNthVararg(_) }
|
|
|
|
/**
|
|
* Holds if this argument is part of an implicit varargs array at the
|
|
* given array index.
|
|
*/
|
|
predicate isNthVararg(int arrayindex) {
|
|
not this.isExplicitVarargsArray() and
|
|
exists(Callable tgt, int varargsParamPos |
|
|
call.getCallee() = tgt and
|
|
tgt.getParameter(varargsParamPos).isVarargs() and
|
|
arrayindex = pos - varargsParamPos and
|
|
arrayindex >= 0 and
|
|
arrayindex <= call.getNumArgument() - tgt.getNumberOfParameters()
|
|
)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* An expression for which the value of the expression as a whole is discarded. Only cases
|
|
* of discarded values at the language level (as described by the JLS) are considered;
|
|
* data flow, for example to determine if an assigned variable value is ever read, is not
|
|
* considered. Such expressions can for example appear as part of an `ExprStmt` or as
|
|
* initializer of a `for` loop.
|
|
*
|
|
* For example, for the statement `i++;` the value of the increment expression, that is the
|
|
* old value of variable `i`, is discarded. Whereas for the statement `println(i++);` the
|
|
* value of the increment expression is not discarded but used as argument for the method call.
|
|
*/
|
|
class ValueDiscardingExpr extends Expr {
|
|
ValueDiscardingExpr() {
|
|
(
|
|
this = any(ExprStmt s).getExpr()
|
|
or
|
|
this = any(ForStmt s).getAnInit() and not this instanceof LocalVariableDeclExpr
|
|
or
|
|
this = any(ForStmt s).getAnUpdate()
|
|
or
|
|
// Only applies to SwitchStmt, but not to SwitchExpr, see JLS 17 section 14.11.2
|
|
this = any(SwitchStmt s).getACase().getRuleExpression()
|
|
or
|
|
// TODO: Workarounds for https://github.com/github/codeql/issues/3605
|
|
exists(LambdaExpr lambda |
|
|
this = lambda.getExprBody() and
|
|
lambda.asMethod().getReturnType() instanceof VoidType
|
|
)
|
|
or
|
|
exists(MemberRefExpr memberRef, Method implicitMethod, Method overridden |
|
|
implicitMethod = memberRef.asMethod()
|
|
|
|
|
this.getParent().(ReturnStmt).getEnclosingCallable() = implicitMethod and
|
|
// asMethod() has bogus method with wrong return type as result, e.g. `run(): String` (overriding `Runnable.run(): void`)
|
|
// Therefore need to check the overridden method
|
|
implicitMethod.getSourceDeclaration().overridesOrInstantiates*(overridden) and
|
|
overridden.getReturnType() instanceof VoidType
|
|
)
|
|
) and
|
|
// Ignore if this expression is a method call with `void` as return type
|
|
not this.getType() instanceof VoidType
|
|
}
|
|
}
|
|
|
|
/** A Kotlin `when` expression. */
|
|
class WhenExpr extends Expr, StmtParent, @whenexpr {
|
|
override string toString() { result = "when ..." }
|
|
|
|
override string getHalsteadID() { result = "WhenExpr" }
|
|
|
|
override string getAPrimaryQlClass() { result = "WhenExpr" }
|
|
|
|
/** Gets the `i`th branch. */
|
|
WhenBranch getBranch(int i) { result.isNthChildOf(this, i) }
|
|
|
|
/** Holds if this was written as an `if` expression. */
|
|
predicate isIf() { when_if(this) }
|
|
}
|
|
|
|
/** A Kotlin `when` branch. */
|
|
class WhenBranch extends Stmt, @whenbranch {
|
|
/** Gets the condition of this branch. */
|
|
Expr getCondition() { result.isNthChildOf(this, 0) }
|
|
|
|
/** Gets the result of this branch. */
|
|
Stmt getRhs() { result.isNthChildOf(this, 1) }
|
|
|
|
/** Gets a result expression of this `when` branch. */
|
|
Expr getAResult() { result = getAResult(this.getRhs()) }
|
|
|
|
/** Holds if this is an `else` branch. */
|
|
predicate isElseBranch() { when_branch_else(this) }
|
|
|
|
/** Gets the `when` expression this is a branch of. */
|
|
WhenExpr getWhenExpr() { this = result.getBranch(_) }
|
|
|
|
override string toString() { result = "... -> ..." }
|
|
|
|
override string getAPrimaryQlClass() { result = "WhenBranch" }
|
|
}
|
|
|
|
// TODO: This might need more cases. It might be better as a predicate
|
|
// on Stmt, overridden in each subclass.
|
|
private Expr getAResult(Stmt s) {
|
|
result = s.(ExprStmt).getExpr() or
|
|
result = getAResult(s.(BlockStmt).getLastStmt())
|
|
}
|
|
|
|
/** A Kotlin `::class` expression. */
|
|
class ClassExpr extends Expr, @getclassexpr {
|
|
/** Gets the expression whose class is being returned. */
|
|
Expr getExpr() { result.isNthChildOf(this, 0) }
|
|
|
|
override string toString() { result = "::class" }
|
|
|
|
override string getAPrimaryQlClass() { result = "ClassExpr" }
|
|
}
|
|
|
|
/**
|
|
* A statement expression.
|
|
*
|
|
* In some contexts, a Kotlin expression can contain a statement.
|
|
*/
|
|
class StmtExpr extends Expr, @stmtexpr {
|
|
/** Gets the statement of this statement expression. */
|
|
Stmt getStmt() { result.getParent() = this }
|
|
|
|
override string toString() { result = "<Stmt>" }
|
|
|
|
override string getHalsteadID() { result = "StmtExpr" }
|
|
|
|
override string getAPrimaryQlClass() { result = "StmtExpr" }
|
|
|
|
/**
|
|
* Gets the result expression of the enclosed statement.
|
|
*/
|
|
Expr getResultExpr() { result = getStmtResultExpr(this.getStmt()) }
|
|
}
|
|
|
|
private Expr getStmtResultExpr(Stmt stmt) {
|
|
result = stmt.(ExprStmt).getExpr() or
|
|
result = getStmtResultExpr(stmt.(BlockStmt).getLastStmt())
|
|
}
|
|
|
|
/**
|
|
* A Kotlin string template expression. For example, `"foo${bar}baz"`.
|
|
*/
|
|
class StringTemplateExpr extends Expr, @stringtemplateexpr {
|
|
/**
|
|
* Gets the `i`th component of this string template.
|
|
*
|
|
* For example, in the string template `"foo${bar}baz"`, the 0th
|
|
* component is the string literal `"foo"`, the 1st component is
|
|
* the variable access `bar`, and the 2nd component is the string
|
|
* literal `"bar"`.
|
|
*/
|
|
Expr getComponent(int i) { result.isNthChildOf(this, i) }
|
|
|
|
override string toString() { result = "\"...\"" }
|
|
|
|
override string getHalsteadID() { result = "StringTemplateExpr" }
|
|
|
|
override string getAPrimaryQlClass() { result = "StringTemplateExpr" }
|
|
}
|
|
|
|
/** A Kotlin not-null expression. For example, `expr!!`. */
|
|
class NotNullExpr extends UnaryExpr, @notnullexpr {
|
|
override string toString() { result = "...!!" }
|
|
|
|
override string getAPrimaryQlClass() { result = "NotNullExpr" }
|
|
}
|