Files
codeql/cpp/ql/lib/semmle/code/cpp/exprs/Literal.qll
2023-04-04 15:30:02 +01:00

399 lines
13 KiB
Plaintext

/**
* Provides classes for modeling literals in the source code such as `0`, `'c'`
* or `"string"`.
*/
import semmle.code.cpp.exprs.Expr
/**
* A C/C++ literal.
*
* The is the QL root class for all literals.
*/
class Literal extends Expr, @literal {
/** Gets a textual representation of this literal. */
override string toString() {
result = this.getValue()
or
not exists(this.getValue()) and
result = "Unknown literal"
}
override string getAPrimaryQlClass() { result = "Literal" }
override predicate mayBeImpure() { none() }
override predicate mayBeGloballyImpure() { none() }
}
/**
* A label literal, that is, a use of the '&&' operator to take the address of a
* label for use in a computed goto statement. This is a non-standard C/C++ extension.
*
* For example:
* ```
* void *label_ptr = &&myLabel; // &&myLabel is a LabelLiteral
* goto *label_ptr; // this is a ComputedGotoStmt
* myLabel: // this is a LabelStmt
* ```
*/
class LabelLiteral extends Literal {
LabelLiteral() { jumpinfo(underlyingElement(this), _, _) }
override string getAPrimaryQlClass() { result = "LabelLiteral" }
/** Gets the corresponding label statement. */
LabelStmt getLabel() { jumpinfo(underlyingElement(this), _, unresolveElement(result)) }
}
/** A character literal or a string literal. */
class TextLiteral extends Literal {
TextLiteral() {
// String Literal
// Note that `AggregateLiteral`s can also have an array type, but they derive from
// @aggregateliteral rather than @literal.
this.getType() instanceof ArrayType
or
// Char literal
this.getValueText().regexpMatch("(?s)\\s*L?'.*")
}
/** Gets a hex escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
string getAHexEscapeSequence(int occurrence, int offset) {
result = this.getValueText().regexpFind("(?<!\\\\)\\\\x[0-9a-fA-F]+", occurrence, offset)
}
/** Gets an octal escape sequence that appears in the character or string literal (see [lex.ccon] in the C++ Standard). */
string getAnOctalEscapeSequence(int occurrence, int offset) {
result = this.getValueText().regexpFind("(?<!\\\\)\\\\[0-7]{1,3}", occurrence, offset)
}
/**
* Gets a non-standard escape sequence that appears in the character or string literal. This is one that has the
* form of an escape sequence but is not one of the valid types of escape sequence in the C++ Standard.
*/
string getANonStandardEscapeSequence(int occurrence, int offset) {
// Find all single character escape sequences (ignoring the start of octal escape sequences),
// together with anything starting like a hex escape sequence but not followed by a hex digit.
result = this.getValueText().regexpFind("\\\\[^x0-7\\s]|\\\\x[^0-9a-fA-F]", occurrence, offset) and
// From these, exclude all standard escape sequences.
not result = this.getAStandardEscapeSequence(_, _)
}
/** Gets a simple escape sequence that appears in the char or string literal (see [lex.ccon] in the C++ Standard). */
string getASimpleEscapeSequence(int occurrence, int offset) {
result = this.getValueText().regexpFind("\\\\['\"?\\\\abfnrtv]", occurrence, offset)
}
/** Gets a standard escape sequence that appears in the char or string literal (see [lex.ccon] in the C++ Standard). */
string getAStandardEscapeSequence(int occurrence, int offset) {
result = this.getASimpleEscapeSequence(occurrence, offset) or
result = this.getAnOctalEscapeSequence(occurrence, offset) or
result = this.getAHexEscapeSequence(occurrence, offset)
}
/**
* Gets the length of the string literal (including null) before escape sequences added by the extractor.
*/
int getOriginalLength() { result = this.getValue().length() + 1 }
}
/**
* A character literal. For example:
* ```
* char c1 = 'a';
* wchar_t c2 = L'b';
* ```
*/
class CharLiteral extends TextLiteral {
CharLiteral() { this.getValueText().regexpMatch("(?s)\\s*L?'.*") }
override string getAPrimaryQlClass() { result = "CharLiteral" }
/**
* Gets the character of this literal. For example `L'a'` has character `"a"`.
*/
string getCharacter() { result = this.getValueText().regexpCapture("(?s)\\s*L?'(.*)'", 1) }
}
/**
* A string literal. For example:
* ```
* const char *s1 = "abcdef";
* const wchar_t *s2 = L"123456";
* ```
*/
class StringLiteral extends TextLiteral {
StringLiteral() {
this.getType() instanceof ArrayType
// Note that `AggregateLiteral`s can also have an array type, but they derive from
// @aggregateliteral rather than @literal.
}
override string getAPrimaryQlClass() { result = "StringLiteral" }
}
/**
* An octal literal. For example:
* ```
* char esc = 033;
* ```
* Octal literals must always start with the digit `0`.
*/
class OctalLiteral extends Literal {
OctalLiteral() { super.getValueText().regexpMatch("\\s*0[0-7]+[uUlL]*\\s*") }
override string getAPrimaryQlClass() { result = "OctalLiteral" }
}
/**
* A hexadecimal literal.
* ```
* unsigned int32_t minus2 = 0xfffffffe;
* ```
*/
class HexLiteral extends Literal {
HexLiteral() { super.getValueText().regexpMatch("\\s*0[xX][0-9a-fA-F]+[uUlL]*\\s*") }
override string getAPrimaryQlClass() { result = "HexLiteral" }
}
/**
* A C/C++ aggregate literal.
*/
class AggregateLiteral extends Expr, @aggregateliteral {
override string getAPrimaryQlClass() { result = "AggregateLiteral" }
override predicate mayBeImpure() { this.getAChild().mayBeImpure() }
override predicate mayBeGloballyImpure() { this.getAChild().mayBeGloballyImpure() }
/** Gets a textual representation of this aggregate literal. */
override string toString() { result = "{...}" }
}
/**
* A C/C++ aggregate literal that initializes a `class`, `struct`, or `union`.
* For example:
* ```
* S s = { arg1, arg2, { arg3, arg4 }, arg5 };
* ```
*/
class ClassAggregateLiteral extends AggregateLiteral {
Class classType;
ClassAggregateLiteral() { classType = this.getUnspecifiedType() }
override string getAPrimaryQlClass() { result = "ClassAggregateLiteral" }
/**
* Gets an expression within the aggregate literal that is used to initialize
* field `field`, if present.
*
* This predicate may have multiple results since a field can be initialized
* multiple times in the same initializer.
*/
Expr getAFieldExpr(Field field) { result = this.getFieldExpr(field, _) }
/**
* DEPRECATED: Use `getAFieldExpr` instead.
*
* Gets the expression within the aggregate literal that is used to initialize
* field `field`, if present.
*
* This predicate may have multiple results since a field can be initialized
* multiple times in the same initializer.
*/
deprecated Expr getFieldExpr(Field field) { result = this.getFieldExpr(field, _) }
/**
* Gets the expression within the aggregate literal that is used to initialize
* field `field`, if present. The expression is the `position`'th entry in the
* aggregate literal.
*
* For example, if `aggr` represents the initialization literal `{.x = 123, .y = 456 .x = 789}` in
* ```cpp
* struct Foo { int x; int y; };
* struct Foo foo = {.x = 123, .y = 456 .x = 789};
* ```
* then:
* - `aggr.getFieldExpr(x, 0)` gives `123`.
* - `aggr.getFieldExpr(y, 1)` gives `456`.
* - `aggr.getFieldExpr(x, 2)` gives `789`.
*/
Expr getFieldExpr(Field field, int position) {
field = classType.getAField() and
aggregate_field_init(underlyingElement(this), unresolveElement(result), unresolveElement(field),
position)
}
/**
* Holds if the field `field` is initialized by this initializer list, either
* explicitly with an expression, or implicitly value initialized.
*/
pragma[inline]
predicate isInitialized(Field field) {
field = classType.getAField() and
field.isInitializable() and
(
// If the field has an explicit initializer expression, then the field is
// initialized.
exists(this.getAFieldExpr(field))
or
// If the type is not a union, all fields without initializers are value
// initialized.
not classType instanceof Union
or
// If the type is a union, and there are no explicit initializers, then
// the first declared field is value initialized.
not exists(this.getAChild()) and
field.getInitializationOrder() = 0
)
}
/**
* Holds if the field `field` is value initialized because it is not
* explicitly initialized by this initializer list.
*
* Value initialization (see [dcl.init]/8) recursively initializes all fields
* of an object to `false`, `0`, `nullptr`, or by calling the default
* constructor, as appropriate to the type.
*/
pragma[inline]
predicate isValueInitialized(Field field) {
this.isInitialized(field) and
not exists(this.getAFieldExpr(field))
}
}
/**
* A C/C++ aggregate literal that initializes an array or a GNU vector type.
*/
class ArrayOrVectorAggregateLiteral extends AggregateLiteral {
ArrayOrVectorAggregateLiteral() {
exists(DerivedType type |
type = this.getUnspecifiedType() and
(
type instanceof ArrayType or
type instanceof GNUVectorType
)
)
}
/**
* Gets the number of elements initialized by this initializer list, either explicitly with an
* expression, or by implicit value initialization.
*/
int getArraySize() { none() }
/**
* Gets the type of the elements in the initializer list.
*/
Type getElementType() { none() }
/**
* Gets an expression within the aggregate literal that is used to initialize
* element `elementIndex`, if present.
*
* This predicate may have multiple results since an element can be initialized
* multiple times in the same initializer.
*/
Expr getAnElementExpr(int elementIndex) { result = this.getElementExpr(elementIndex, _) }
/**
* DEPRECATED: Use `getAnElementExpr` instead.
*
* Gets the expression within the aggregate literal that is used to initialize
* element `elementIndex`, if present.
*
* This predicate may have multiple results since an element can be initialized
* multiple times in the same initializer.
*/
deprecated Expr getElementExpr(int elementIndex) { result = this.getElementExpr(elementIndex, _) }
/**
* Gets the expression within the aggregate literal that is used to initialize
* element `elementIndex`, if present. The expression is the `position`'th entry
* in the aggregate literal.
*
* For example, if `a` represents the initialization literal `{[0] = 123, [1] = 456, [0] = 789 }` in
* ```cpp
* int x[2] = {[0] = 123, [1] = 456, [0] = 789 };
* ```
* then:
* - `a.getElementExpr(0, 0)` gives `123`.
* - `a.getElementExpr(1, 1)` gives `456`.
* - `a.getElementExpr(0, 2)` gives `789`.
*/
Expr getElementExpr(int elementIndex, int position) {
aggregate_array_init(underlyingElement(this), unresolveElement(result), elementIndex, position)
}
/**
* Holds if the element `elementIndex` is initialized by this initializer
* list, either explicitly with an expression, or implicitly value
* initialized.
*/
bindingset[elementIndex]
predicate isInitialized(int elementIndex) {
elementIndex >= 0 and
elementIndex < this.getArraySize()
}
/**
* Holds if the element `elementIndex` is value initialized because it is not
* explicitly initialized by this initializer list.
*
* Value initialization (see [dcl.init]/8) recursively initializes all fields
* of an object to `false`, `0`, `nullptr`, or by calling the default
* constructor, as appropriate to the type.
*/
bindingset[elementIndex]
predicate isValueInitialized(int elementIndex) {
this.isInitialized(elementIndex) and
not exists(this.getAnElementExpr(elementIndex))
}
}
/**
* A C/C++ aggregate literal that initializes an array
* ```
* S s[4] = { s_1, s_2, s_3, s_n };
* ```
*/
class ArrayAggregateLiteral extends ArrayOrVectorAggregateLiteral {
ArrayType arrayType;
ArrayAggregateLiteral() { arrayType = this.getUnspecifiedType() }
override string getAPrimaryQlClass() { result = "ArrayAggregateLiteral" }
override int getArraySize() { result = arrayType.getArraySize() }
override Type getElementType() { result = arrayType.getBaseType() }
}
/**
* A C/C++ aggregate literal that initializes a GNU vector type.
*
* Braced initializer lists are used, similarly to what is done
* for arrays.
* ```
* typedef int v4si __attribute__ (( vector_size(4*sizeof(int)) ));
* v4si v = (v4si){ 1, 2, 3, 4 };
* typedef float float4 __attribute__((ext_vector_type(4)));
* float4 vf = {1.0f, 2.0f, 3.0f, 4.0f};
* ```
*/
class VectorAggregateLiteral extends ArrayOrVectorAggregateLiteral {
GNUVectorType vectorType;
VectorAggregateLiteral() { vectorType = this.getUnspecifiedType() }
override string getAPrimaryQlClass() { result = "VectorAggregateLiteral" }
override int getArraySize() { result = vectorType.getNumElements() }
override Type getElementType() { result = vectorType.getBaseType() }
}