Files
codeql/cpp/ql/lib/semmle/code/cpp/Specifier.qll
2025-06-27 14:42:08 +02:00

337 lines
11 KiB
Plaintext

/**
* Provides classes for modeling specifiers and attributes.
*/
import semmle.code.cpp.Element
private import semmle.code.cpp.internal.ResolveClass
/**
* A C/C++ specifier: `friend`, `auto`, `register`, `static`, `extern`,
* `mutable`, `inline`, `virtual`, or `explicit`.
*/
class Specifier extends Element, @specifier {
/** Gets a dummy location for the specifier. */
override Location getLocation() {
exists(this) and
result instanceof UnknownLocation
}
override string getAPrimaryQlClass() { result = "Specifier" }
/** Gets the name of this specifier. */
string getName() { specifiers(underlyingElement(this), result) }
/** Holds if the name of this specifier is `name`. */
predicate hasName(string name) { name = this.getName() }
override string toString() { result = this.getName() }
}
/**
* A C/C++ function specifier: `inline`, `virtual`, or `explicit`.
*/
class FunctionSpecifier extends Specifier {
FunctionSpecifier() { this.hasName(["inline", "virtual", "explicit"]) }
override string getAPrimaryQlClass() { result = "FunctionSpecifier" }
}
/**
* A C/C++ storage class specifier: `auto`, `register`, `static`, `extern`,
* or `mutable`.
*/
class StorageClassSpecifier extends Specifier {
StorageClassSpecifier() { this.hasName(["auto", "register", "static", "extern", "mutable"]) }
override string getAPrimaryQlClass() { result = "StorageClassSpecifier" }
}
/**
* A C++ access specifier: `public`, `protected`, or `private`.
*/
class AccessSpecifier extends Specifier {
AccessSpecifier() { this.hasName(["public", "protected", "private"]) }
/**
* Gets the visibility of a field with access specifier `this` if it is
* directly inherited with access specifier `baseAccess`. For example:
*
* ```
* class A { protected int f; };
* class B : private A {};
* ```
*
* In this example, `this` is `protected`, `baseAccess` is `private`, and
* `result` is `private` because the visibility of field `f` in class `B`
* is `private`.
*
* This method encodes the rules of N4140 11.2/1, tabulated here:
*
* ```
* `this` | `baseAccess` | `result`
* (access in base) | (base class specifier) | (access in derived)
* ----------------------------------------------------------
* private | private | N/A
* private | protected | N/A
* private | public | N/A
* protected | private | private
* protected | protected | protected
* protected | public | protected
* public | private | private
* public | protected | protected
* public | public | public
* ```
*/
AccessSpecifier accessInDirectDerived(AccessSpecifier baseAccess) {
this.getName() != "private" and
(
// Alphabetically, "private" < "protected" < "public". This disjunction
// encodes that `result` is the minimum access of `this` and
// `baseAccess`.
baseAccess.getName() < this.getName() and result = baseAccess
or
baseAccess.getName() >= this.getName() and result = this
)
}
override string getAPrimaryQlClass() { result = "AccessSpecifier" }
}
/**
* A C/C++ calling convention specifier: `cdecl`, `fastcall`, `stdcall`, `thiscall`,
* `vectorcall`, or `clrcall`.
*/
class CallingConventionSpecifier extends Specifier {
CallingConventionSpecifier() {
this.hasName(["cdecl", "fastcall", "stdcall", "thiscall", "vectorcall", "clrcall"])
}
override string getAPrimaryQlClass() { result = "CallingConventionSpecifier" }
}
/**
* An attribute introduced by GNU's `__attribute__((name))` syntax,
* Microsoft's `__declspec(name)` syntax, Microsoft's `[name]` syntax, the
* C++11 standard `[[name]]` syntax, or the C++11 `alignas` syntax.
*/
class Attribute extends Element, @attribute {
/**
* Gets the name of this attribute.
*
* As examples, this is "noreturn" for `__attribute__((__noreturn__))`,
* "fallthrough" for `[[clang::fallthrough]]`, and "dllimport" for
* `__declspec(dllimport)`.
*
* Note that the name does not include the namespace. For example, the
* name of `[[clang::fallthrough]]` is "fallthrough".
*/
string getName() { attributes(underlyingElement(this), _, result, _, _) }
override Location getLocation() { attributes(underlyingElement(this), _, _, _, result) }
/** Holds if the name of this attribute is `name`. */
predicate hasName(string name) { name = this.getName() }
override string toString() { result = this.getName() }
/** Gets the `i`th argument of the attribute. */
AttributeArgument getArgument(int i) { result.getAttribute() = this and result.getIndex() = i }
/** Gets an argument of the attribute. */
AttributeArgument getAnArgument() { result = this.getArgument(_) }
}
/**
* An attribute introduced by GNU's `__attribute__((name))` syntax, for
* example: `__attribute__((__noreturn__))`.
*/
class GnuAttribute extends Attribute, @gnuattribute { }
/**
* An attribute introduced by the C++11 standard `[[name]]` syntax, for
* example: `[[clang::fallthrough]]`.
*/
class StdAttribute extends Attribute, @stdattribute {
/**
* Gets the namespace of this attribute.
*
* As examples, this is "" for `[[carries_dependency]]`, and "clang" for
* `[[clang::fallthrough]]`.
*/
string getNamespace() { attributes(underlyingElement(this), _, _, result, _) }
/**
* Holds if this attribute has the given namespace and name.
*/
predicate hasQualifiedName(string namespace, string name) {
namespace = this.getNamespace() and this.hasName(name)
}
}
/**
* An attribute introduced by Microsoft's `__declspec(name)` syntax. For
* example the attribute on the following declaration:
* ```
* __declspec(dllimport) void myFunction();
* ```
*/
class Declspec extends Attribute, @declspec { }
/**
* An attribute introduced by Microsoft's "[name]" syntax, for example "[SA_Pre(Deref=1,Access=SA_Read)]".
*/
class MicrosoftAttribute extends Attribute, @msattribute {
AttributeArgument getNamedArgument(string name) {
result = this.getAnArgument() and result.getName() = name
}
}
/**
* A C++11 `alignas` construct. For example the attribute in the following
* code:
* ```
* struct alignas(16) MyStruct {
* int x;
* };
* ```
* Though it doesn't use the attribute syntax, `alignas(...)` is presented
* as an `Attribute` for consistency with the `[[align(...)]]` attribute.
*/
class AlignAs extends Attribute, @alignas {
override string toString() { result = "alignas(...)" }
}
/**
* A GNU `format` attribute of the form `__attribute__((format(archetype, format-index, first-arg)))`
* that declares a function to accept a `printf` style format string. For example the attribute
* on the following declaration:
* ```
* int myPrintf(const char *format, ...) __attribute__((format(printf, 1, 2)));
* ```
*/
class FormatAttribute extends GnuAttribute {
FormatAttribute() { this.getName() = "format" }
/**
* Gets the archetype of this format attribute, for example
* `"printf"`.
*/
string getArchetype() { result = this.getArgument(0).getValueText() }
/**
* Gets the index in (1-based) format attribute notation associated
* with the first argument of the function.
*/
private int firstArgumentNumber() {
if exists(MemberFunction f | f.getAnAttribute() = this and not f.isStatic())
then
// 1 is `this`, so the first parameter is 2
result = 2
else result = 1
}
/**
* Gets the (0-based) index of the format string,
* according to this attribute.
*/
int getFormatIndex() { result = this.getArgument(1).getValueInt() - this.firstArgumentNumber() }
/**
* Gets the (0-based) index of the first format argument (if any),
* according to this attribute.
*/
int getFirstFormatArgIndex() {
exists(int val |
val = this.getArgument(2).getValueInt() and
result = val - this.firstArgumentNumber() and
not val = 0 // indicates a `vprintf` style format function with arguments not directly available.
)
}
override string getAPrimaryQlClass() { result = "FormatAttribute" }
}
/**
* An argument to an `Attribute`. For example the argument "dllimport" on the
* attribute in the following code:
* ```
* __declspec(dllimport) void myFunction();
* ```
*/
class AttributeArgument extends Element, @attribute_arg {
/**
* Gets the name of this argument, if it is a named argument. Named
* arguments are a Microsoft feature, so only a `MicrosoftAttribute` can
* have a named argument.
*/
string getName() { attribute_arg_name(underlyingElement(this), result) }
/**
* Gets the text for the value of this argument, if its value is
* a constant or a token.
*/
string getValueText() {
if underlyingElement(this) instanceof @attribute_arg_constant_expr
then result = this.getValueConstant().getValue()
else attribute_arg_value(underlyingElement(this), result)
}
/**
* Gets the value of this argument, if its value is integral.
*/
int getValueInt() { result = this.getValueText().toInt() }
/**
* Gets the value of this argument, if its value is a type.
*/
Type getValueType() { attribute_arg_type(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the value of this argument, if its value is a constant.
*/
Expr getValueConstant() {
attribute_arg_constant(underlyingElement(this), unresolveElement(result))
}
/**
* Gets the value of this argument, if its value is an expression.
*/
Expr getValueExpr() { attribute_arg_expr(underlyingElement(this), unresolveElement(result)) }
/**
* Gets the attribute to which this is an argument.
*/
Attribute getAttribute() {
attribute_args(underlyingElement(this), _, unresolveElement(result), _, _)
}
/**
* Gets the zero-based index of this argument in the containing
* attribute's argument list.
*/
int getIndex() { attribute_args(underlyingElement(this), _, _, result, _) }
override Location getLocation() { attribute_args(underlyingElement(this), _, _, _, result) }
override string toString() {
if underlyingElement(this) instanceof @attribute_arg_empty
then result = "empty argument"
else
exists(string prefix, string tail |
(if exists(this.getName()) then prefix = this.getName() + "=" else prefix = "") and
(
if underlyingElement(this) instanceof @attribute_arg_type
then tail = this.getValueType().getName()
else
if underlyingElement(this) instanceof @attribute_arg_constant_expr
then tail = this.getValueConstant().toString()
else
if underlyingElement(this) instanceof @attribute_arg_expr
then tail = this.getValueExpr().toString()
else tail = this.getValueText()
) and
result = prefix + tail
)
}
}