Files
codeql/cpp/ql/lib/semmle/code/cpp/exprs/Lambda.qll
2023-04-04 15:14:27 +01:00

154 lines
5.2 KiB
Plaintext

/**
* Provides classes for modeling lambda expressions and their captures.
*/
import semmle.code.cpp.exprs.Expr
import semmle.code.cpp.Class
/**
* A C++11 lambda expression, for example the expression initializing `a` in
* the following code:
* ```
* auto a = [x, y](int z) -> int {
* return x + y + z;
* };
* ```
*
* The type given by `getType()` will be an instance of `Closure`.
*/
class LambdaExpression extends Expr, @lambdaexpr {
override string toString() { result = "[...](...){...}" }
override string getAPrimaryQlClass() { result = "LambdaExpression" }
/**
* Gets an implicitly or explicitly captured value of this lambda expression.
*/
LambdaCapture getACapture() { result = this.getCapture(_) }
/**
* Gets the nth implicitly or explicitly captured value of this lambda expression.
*/
LambdaCapture getCapture(int index) {
lambda_capture(result, underlyingElement(this), index, _, _, _, _)
}
/**
* Gets the default variable capture mode for the lambda expression.
*
* Will be one of:
* - "" if no default was specified, meaning that all captures must be explicit.
* - "&" if capture-by-reference is the default for implicit captures.
* - "=" if capture-by-value is the default for implicit captures.
*/
string getDefaultCaptureMode() { lambdas(underlyingElement(this), result, _) }
/**
* Holds if the return type (of the call operator of the resulting object) was explicitly specified.
*/
predicate returnTypeIsExplicit() { lambdas(underlyingElement(this), _, true) }
/**
* Gets the function which will be invoked when the resulting object is called.
*
* Various components of the lambda expression can be obtained from components of this
* function, such as:
* - The number and type of parameters.
* - Whether the mutable keyword was used (iff this function is not const).
* - The return type.
* - The statements comprising the lambda body.
*/
Operator getLambdaFunction() { result = this.getType().(Closure).getLambdaFunction() }
/**
* Gets the initializer that initializes the captured variables in the closure, if any.
* A lambda that does not capture any variables will not have an initializer.
*/
ClassAggregateLiteral getInitializer() { result = this.getChild(0) }
}
/**
* A class written by the compiler to be the type of a C++11 lambda expression.
* For example the variable `a` in the following code has a closure type:
* ```
* auto a = [x, y](int z) -> int {
* return x + y + z;
* };
* ```
*/
class Closure extends Class {
Closure() { exists(LambdaExpression e | this = e.getType()) }
override string getAPrimaryQlClass() { result = "Closure" }
/** Gets the lambda expression of which this is the type. */
LambdaExpression getLambdaExpression() { result.getType() = this }
/** Gets the compiler-generated operator() of this closure type. */
Operator getLambdaFunction() {
result = this.getAMember() and
result.getName() = "operator()"
}
override string getDescription() { result = "decltype([...](...){...})" }
}
/**
* Information about a value captured as part of a lambda expression. For
* example in the following code, information about `x` and `y` is captured:
* ```
* auto a = [x, y](int z) -> int {
* return x + y + z;
* };
* ```
*/
class LambdaCapture extends Locatable, @lambdacapture {
override string toString() { result = this.getField().getName() }
override string getAPrimaryQlClass() { result = "LambdaCapture" }
/**
* Holds if this capture was made implicitly.
*/
predicate isImplicit() { lambda_capture(this, _, _, _, _, true, _) }
/**
* Holds if the variable was captured by reference.
*
* An identifier is captured by reference if:
* - It is explicitly captured by reference.
* - It is implicitly captured, and the lambda's default capture mode is by-reference.
* - The identifier is "this". [Said behavior is dictated by the C++11 standard, but it
* is actually "*this" being captured rather than "this".]
*/
predicate isCapturedByReference() { lambda_capture(this, _, _, _, true, _, _) }
/**
* Gets the location of the declaration of this capture.
*
* For explicit captures, this is a location within the "[...]" part of the lambda expression.
*
* For implicit captures, this is the first location within the "{...}" part of the lambda
* expression which accesses the captured variable.
*/
override Location getLocation() { lambda_capture(this, _, _, _, _, _, result) }
/**
* Gets the field of the lambda expression's closure type which is used to store this capture.
*/
MemberVariable getField() { lambda_capture(this, _, _, result, _, _, _) }
/**
* Gets the expression which yields the final captured value.
*
* In many cases, this will be an instance of VariableAccess.
* If a this-pointer is being captured, this will be an instance of ThisExpr.
* For by-value captures of non-primitive types, this will be a call to a copy constructor.
*/
Expr getInitializer() {
exists(LambdaExpression lambda | this = lambda.getCapture(_) |
result = lambda.getInitializer().getAFieldExpr(this.getField())
)
}
}