Files
codeql/cpp/ql/lib/semmle/code/cpp/Variable.qll
2022-03-09 18:28:07 +01:00

602 lines
18 KiB
Plaintext

/**
* Provides classes for modeling variables and their declarations.
*/
import semmle.code.cpp.Element
import semmle.code.cpp.exprs.Access
import semmle.code.cpp.Initializer
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ variable. For example, in the following code there are four
* variables, `a`, `b`, `c` and `d`:
* ```
* extern int a;
* int a;
*
* void myFunction(int b) {
* int c;
* }
*
* namespace N {
* extern int d;
* int d = 1;
* }
* ```
*
* For local variables, there is a one-to-one correspondence between
* `Variable` and `VariableDeclarationEntry`.
*
* For other types of variable, there is a one-to-many relationship between
* `Variable` and `VariableDeclarationEntry`. For example, a `Parameter`
* can have multiple declarations.
*/
class Variable extends Declaration, @variable {
override string getAPrimaryQlClass() { result = "Variable" }
/** Gets the initializer of this variable, if any. */
Initializer getInitializer() { result.getDeclaration() = this }
/** Holds if this variable has an initializer. */
predicate hasInitializer() { exists(this.getInitializer()) }
/** Gets an access to this variable. */
VariableAccess getAnAccess() { result.getTarget() = this }
/**
* Gets a specifier of this variable. This includes `extern`, `static`,
* `auto`, `private`, `protected`, `public`. Specifiers of the *type* of
* this variable, such as `const` and `volatile`, are instead accessed
* through `this.getType().getASpecifier()`.
*/
override Specifier getASpecifier() {
varspecifiers(underlyingElement(this), unresolveElement(result))
}
/** Gets an attribute of this variable. */
Attribute getAnAttribute() { varattributes(underlyingElement(this), unresolveElement(result)) }
/** Holds if this variable is `const`. */
predicate isConst() { this.getType().isConst() }
/** Holds if this variable is `volatile`. */
predicate isVolatile() { this.getType().isVolatile() }
/** Gets the name of this variable. */
override string getName() { none() }
/** Gets the type of this variable. */
Type getType() { none() }
/** Gets the type of this variable, after typedefs have been resolved. */
Type getUnderlyingType() { result = this.getType().getUnderlyingType() }
/**
* Gets the type of this variable, after specifiers have been deeply
* stripped and typedefs have been resolved.
*/
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
/**
* Gets the type of this variable prior to deduction caused by the C++11
* `auto` keyword.
*
* If the type of this variable was not declared with the C++11 `auto`
* keyword, then this predicate does not hold.
*
* If the type of this variable is completely `auto`, then `result` is an
* instance of `AutoType`. For example:
*
* `auto four = 4;`
*
* If the type of this variable is partially `auto`, then a descendant of
* `result` is an instance of `AutoType`. For example:
*
* `const auto& c = container;`
*/
Type getTypeWithAuto() { autoderivation(underlyingElement(this), unresolveElement(result)) }
/**
* Holds if the type of this variable is declared using the C++ `auto`
* keyword.
*/
predicate declaredUsingAutoType() { autoderivation(underlyingElement(this), _) }
override VariableDeclarationEntry getADeclarationEntry() { result.getDeclaration() = this }
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
override VariableDeclarationEntry getDefinition() {
result = this.getADeclarationEntry() and
result.isDefinition()
}
override Location getDefinitionLocation() { result = this.getDefinition().getLocation() }
override Location getLocation() {
if exists(this.getDefinition())
then result = this.getDefinitionLocation()
else result = this.getADeclarationLocation()
}
/**
* Gets an expression that is assigned to this variable somewhere in the
* program.
*/
Expr getAnAssignedValue() {
result = this.getInitializer().getExpr()
or
exists(ConstructorFieldInit cfi | cfi.getTarget() = this and result = cfi.getExpr())
or
exists(AssignExpr ae | ae.getLValue().(Access).getTarget() = this and result = ae.getRValue())
or
exists(ClassAggregateLiteral l | result = l.getFieldExpr(this))
}
/**
* Gets an assignment expression that assigns to this variable.
* For example: `x=...` or `x+=...`.
*
* This does _not_ include the initialization of the variable. Use
* `Variable.getInitializer()` to get the variable's initializer,
* or use `Variable.getAnAssignedValue()` to get an expression that
* is the right-hand side of an assignment or an initialization of
* the varible.
*/
Assignment getAnAssignment() { result.getLValue() = this.getAnAccess() }
/**
* Holds if this variable is `constexpr`.
*/
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
/**
* Holds if this variable is declared `constinit`.
*/
predicate isConstinit() { this.hasSpecifier("declared_constinit") }
/**
* Holds if this variable is `thread_local`.
*/
predicate isThreadLocal() { this.hasSpecifier("is_thread_local") }
/**
* Holds if this variable is constructed from `v` as a result
* of template instantiation. If so, it originates either from a template
* variable or from a variable nested in a template class.
*/
predicate isConstructedFrom(Variable v) {
variable_instantiation(underlyingElement(this), unresolveElement(v))
}
/**
* Holds if this variable is declated as part of a structured binding
* declaration. For example, `x` in `auto [x, y] = ...`.
*/
predicate isStructuredBinding() { is_structured_binding(underlyingElement(this)) }
/**
* Holds if this is a compiler-generated variable. For example, a
* [range-based for loop](http://en.cppreference.com/w/cpp/language/range-for)
* typically has three compiler-generated variables, named `__range`,
* `__begin`, and `__end`:
*
* `for (char c : str) { ... }`
*/
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
}
/**
* A particular declaration or definition of a C/C++ variable. For example, in
* the following code there are six variable declaration entries - two each for
* `a` and `d`, and one each for `b` and `c`:
* ```
* extern int a;
* int a;
*
* void myFunction(int b) {
* int c;
* }
*
* namespace N {
* extern int d;
* int d = 1;
* }
* ```
*/
class VariableDeclarationEntry extends DeclarationEntry, @var_decl {
override Variable getDeclaration() { result = this.getVariable() }
override string getAPrimaryQlClass() { result = "VariableDeclarationEntry" }
/**
* Gets the variable which is being declared or defined.
*/
Variable getVariable() { var_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
/**
* Gets the name, if any, used for the variable at this declaration or
* definition.
*
* In most cases, this will be the name of the variable itself. The only
* case in which it can differ is in a parameter declaration entry,
* because the parameter may have a different name in the declaration
* than in the definition. For example:
*
* ```
* // Declaration. Parameter is named "x".
* int f(int x);
*
* // Definition. Parameter is named "y".
* int f(int y) { return y; }
* ```
*/
override string getName() { var_decls(underlyingElement(this), _, _, result, _) and result != "" }
/**
* Gets the type of the variable which is being declared or defined.
*/
override Type getType() { var_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
override Location getLocation() { var_decls(underlyingElement(this), _, _, _, result) }
/**
* Holds if this is a definition of a variable.
*
* This always holds for local variables and member variables, but need
* not hold for global variables. In the case of function parameters,
* this holds precisely when the enclosing `FunctionDeclarationEntry` is
* a definition.
*/
override predicate isDefinition() { var_def(underlyingElement(this)) }
override string getASpecifier() { var_decl_specifiers(underlyingElement(this), result) }
}
/**
* A parameter as described within a particular declaration or definition
* of a C/C++ function. For example the declaration of `a` in the following
* code:
* ```
* void myFunction(int a) {
* int b;
* }
* ```
*/
class ParameterDeclarationEntry extends VariableDeclarationEntry {
ParameterDeclarationEntry() { param_decl_bind(underlyingElement(this), _, _) }
override string getAPrimaryQlClass() { result = "ParameterDeclarationEntry" }
/**
* Gets the function declaration or definition which this parameter
* description is part of.
*/
FunctionDeclarationEntry getFunctionDeclarationEntry() {
param_decl_bind(underlyingElement(this), _, unresolveElement(result))
}
/**
* Gets the zero-based index of this parameter.
*/
int getIndex() { param_decl_bind(underlyingElement(this), result, _) }
private string getAnonymousParameterDescription() {
not exists(this.getName()) and
exists(string idx |
idx =
((this.getIndex() + 1).toString() + "th")
.replaceAll("1th", "1st")
.replaceAll("2th", "2nd")
.replaceAll("3th", "3rd")
.replaceAll("11st", "11th")
.replaceAll("12nd", "12th")
.replaceAll("13rd", "13th") and
if exists(this.getCanonicalName())
then
result = "declaration of " + this.getCanonicalName() + " as anonymous " + idx + " parameter"
else result = "declaration of " + idx + " parameter"
)
}
override string toString() {
this.isDefinition() and
result = "definition of " + this.getName()
or
not this.isDefinition() and
if this.getName() = this.getCanonicalName()
then result = "declaration of " + this.getName()
else result = "declaration of " + this.getCanonicalName() + " as " + this.getName()
or
result = this.getAnonymousParameterDescription()
}
/**
* Gets the name of this `ParameterDeclarationEntry` including it's type.
*
* For example: "int p".
*/
string getTypedName() {
exists(string typeString, string nameString |
(
if exists(this.getType().getName())
then typeString = this.getType().getName()
else typeString = ""
) and
(if exists(this.getName()) then nameString = this.getName() else nameString = "") and
if typeString != "" and nameString != ""
then result = typeString + " " + nameString
else result = typeString + nameString
)
}
}
/**
* A C/C++ variable with block scope [N4140 3.3.3]. In other words, a local
* variable or a function parameter. For example, the variables `a`, `b` and
* `c` in the following code:
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*
* See also `StackVariable`, which is the class of local-scope variables
* without statics and thread-locals.
*/
class LocalScopeVariable extends Variable, @localscopevariable {
/** Gets the function to which this variable belongs. */
Function getFunction() { none() } // overridden in subclasses
}
/**
* A C/C++ variable with _automatic storage duration_. In other words, a
* function parameter or a local variable that is not static or thread-local.
* For example, the variables `a` and `b` in the following code.
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*/
class StackVariable extends LocalScopeVariable {
StackVariable() {
not this.isStatic() and
not this.isThreadLocal()
}
}
/**
* A C/C++ local variable. In other words, any variable that has block
* scope [N4140 3.3.3], but is not a parameter of a `Function` or `CatchBlock`.
* For example the variables `b` and `c` in the following code:
* ```
* void myFunction(int a) {
* int b;
* static int c;
* }
* ```
*
* Local variables can be static; use the `isStatic` member predicate to detect
* those.
*
* A local variable can be declared by a `DeclStmt` or a `ConditionDeclExpr`.
*/
class LocalVariable extends LocalScopeVariable, @localvariable {
override string getAPrimaryQlClass() { result = "LocalVariable" }
override string getName() { localvariables(underlyingElement(this), _, result) }
override Type getType() { localvariables(underlyingElement(this), unresolveElement(result), _) }
override Function getFunction() {
exists(DeclStmt s | s.getADeclaration() = this and s.getEnclosingFunction() = result)
or
exists(ConditionDeclExpr e | e.getVariable() = this and e.getEnclosingFunction() = result)
}
}
/**
* A variable whose contents always have static storage duration. This can be a
* global variable, a namespace variable, a static local variable, or a static
* member variable.
*/
class StaticStorageDurationVariable extends Variable {
StaticStorageDurationVariable() {
this instanceof GlobalOrNamespaceVariable
or
this.(LocalVariable).isStatic()
or
this.(MemberVariable).isStatic()
}
/**
* Holds if the initializer for this variable is evaluated at runtime.
*/
predicate hasDynamicInitialization() {
runtimeExprInStaticInitializer(this.getInitializer().getExpr())
}
}
/**
* Holds if `e` is an expression in a static initializer that must be evaluated
* at run time. This predicate computes "is non-const" instead of "is const"
* since computing "is const" for an aggregate literal with many children would
* either involve recursion through `forall` on those children or an iteration
* through the rank numbers of the children, both of which can be slow.
*/
private predicate runtimeExprInStaticInitializer(Expr e) {
inStaticInitializer(e) and
if e instanceof AggregateLiteral // in sync with the cast in `inStaticInitializer`
then runtimeExprInStaticInitializer(e.getAChild())
else not e.getFullyConverted().isConstant()
}
/**
* Holds if `e` is the initializer of a `StaticStorageDurationVariable`, either
* directly or below some top-level `AggregateLiteral`s.
*/
private predicate inStaticInitializer(Expr e) {
exists(StaticStorageDurationVariable var | e = var.getInitializer().getExpr())
or
// The cast to `AggregateLiteral` ensures we only compute what'll later be
// needed by `runtimeExprInStaticInitializer`.
inStaticInitializer(e.getParent().(AggregateLiteral))
}
/**
* A C++ local variable declared as `static`.
*/
class StaticLocalVariable extends LocalVariable, StaticStorageDurationVariable { }
/**
* A C/C++ variable which has global scope or namespace scope. For example the
* variables `a` and `b` in the following code:
* ```
* int a;
*
* namespace N {
* int b;
* }
* ```
*/
class GlobalOrNamespaceVariable extends Variable, @globalvariable {
override string getName() { globalvariables(underlyingElement(this), _, result) }
override Type getType() { globalvariables(underlyingElement(this), unresolveElement(result), _) }
override Element getEnclosingElement() { none() }
}
/**
* A C/C++ variable which has namespace scope. For example the variable `b`
* in the following code:
* ```
* int a;
*
* namespace N {
* int b;
* }
* ```
*/
class NamespaceVariable extends GlobalOrNamespaceVariable {
NamespaceVariable() {
exists(Namespace n | namespacembrs(unresolveElement(n), underlyingElement(this)))
}
override string getAPrimaryQlClass() { result = "NamespaceVariable" }
}
/**
* A C/C++ variable which has global scope. For example the variable `a`
* in the following code:
* ```
* int a;
*
* namespace N {
* int b;
* }
* ```
*
* Note that variables declared in anonymous namespaces have namespace scope,
* even though they are accessed in the same manner as variables declared in
* the enclosing scope of said namespace (which may be the global scope).
*/
class GlobalVariable extends GlobalOrNamespaceVariable {
GlobalVariable() { not this instanceof NamespaceVariable }
override string getAPrimaryQlClass() { result = "GlobalVariable" }
}
/**
* A C structure member or C++ member variable. For example the member
* variables `m` and `s` in the following code:
* ```
* class MyClass {
* public:
* int m;
* static int s;
* };
* ```
*
* This includes static member variables in C++. To exclude static member
* variables, use `Field` instead of `MemberVariable`.
*/
class MemberVariable extends Variable, @membervariable {
MemberVariable() { this.isMember() }
override string getAPrimaryQlClass() { result = "MemberVariable" }
/** Holds if this member is private. */
predicate isPrivate() { this.hasSpecifier("private") }
/** Holds if this member is protected. */
predicate isProtected() { this.hasSpecifier("protected") }
/** Holds if this member is public. */
predicate isPublic() { this.hasSpecifier("public") }
override string getName() { membervariables(underlyingElement(this), _, result) }
override Type getType() {
if strictcount(this.getAType()) = 1
then result = this.getAType()
else
// In rare situations a member variable may have multiple types in
// different translation units. In that case, we return the unspecified
// type.
result = this.getAType().getUnspecifiedType()
}
/** Holds if this member is mutable. */
predicate isMutable() { this.getADeclarationEntry().hasSpecifier("mutable") }
private Type getAType() { membervariables(underlyingElement(this), unresolveElement(result), _) }
}
/**
* A C++14 variable template. For example, in the following code the variable
* template `v` defines a family of variables:
* ```
* template<class T>
* T v;
* ```
*/
class TemplateVariable extends Variable {
TemplateVariable() { is_variable_template(underlyingElement(this)) }
/**
* Gets an instantiation of this variable template.
*/
Variable getAnInstantiation() { result.isConstructedFrom(this) }
}
/**
* A non-static local variable or parameter that is not part of an
* uninstantiated template. Uninstantiated templates are purely syntax, and
* only on instantiation will they be complete with information about types,
* conversions, call targets, etc. For example in the following code, the
* variables `a` in `myFunction` and `b` in the instantiation
* `myTemplateFunction<int>`, but not `b` in the template
* `myTemplateFunction<T>`:
* ```
* void myFunction() {
* float a;
* }
*
* template<typename T>
* void myTemplateFunction() {
* T b;
* }
*
* ...
*
* myTemplateFunction<int>();
* ```
*/
class SemanticStackVariable extends StackVariable {
SemanticStackVariable() { not this.isFromUninstantiatedTemplate(_) }
}