mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
927 lines
30 KiB
Plaintext
927 lines
30 KiB
Plaintext
/**
|
|
* Provides classes for working with functions, including template functions.
|
|
*/
|
|
|
|
import semmle.code.cpp.Location
|
|
import semmle.code.cpp.Class
|
|
import semmle.code.cpp.Parameter
|
|
import semmle.code.cpp.exprs.Call
|
|
import semmle.code.cpp.metrics.MetricFunction
|
|
import semmle.code.cpp.Linkage
|
|
private import semmle.code.cpp.internal.ResolveClass
|
|
private import semmle.code.cpp.internal.ResolveFunction
|
|
|
|
/**
|
|
* A C/C++ function [N4140 8.3.5]. Both member functions and non-member
|
|
* functions are included. For example the function `MyFunction` in:
|
|
* ```
|
|
* void MyFunction() {
|
|
* DoSomething();
|
|
* }
|
|
* ```
|
|
*
|
|
* Function has a one-to-many relationship with FunctionDeclarationEntry,
|
|
* because the same function can be declared in multiple locations. This
|
|
* relationship between `Declaration` and `DeclarationEntry` is explained
|
|
* in more detail in `Declaration.qll`.
|
|
*/
|
|
class Function extends Declaration, ControlFlowNode, AccessHolder, @function {
|
|
Function() { isFunction(underlyingElement(this)) }
|
|
|
|
override string getName() { functions(underlyingElement(this), result, _) }
|
|
|
|
/** Gets a specifier of this function. */
|
|
override Specifier getASpecifier() {
|
|
funspecifiers(underlyingElement(this), unresolveElement(result)) or
|
|
result.hasName(this.getADeclarationEntry().getASpecifier())
|
|
}
|
|
|
|
/** Gets an attribute of this function. */
|
|
Attribute getAnAttribute() { funcattributes(underlyingElement(this), unresolveElement(result)) }
|
|
|
|
/** Holds if this function is generated by the compiler. */
|
|
predicate isCompilerGenerated() { compgenerated(underlyingElement(this)) }
|
|
|
|
/** Holds if this function is inline. */
|
|
predicate isInline() { this.hasSpecifier("inline") }
|
|
|
|
/**
|
|
* Holds if this function is virtual.
|
|
*
|
|
* Unlike `isDeclaredVirtual()`, `isVirtual()` holds even if the function
|
|
* is not explicitly declared with the `virtual` specifier.
|
|
*/
|
|
predicate isVirtual() { this.hasSpecifier("virtual") }
|
|
|
|
/** Holds if this function is declared with the `virtual` specifier. */
|
|
predicate isDeclaredVirtual() { this.hasSpecifier("declared_virtual") }
|
|
|
|
/** Holds if this function is declared with the `override` specifier. */
|
|
predicate isOverride() { this.hasSpecifier("override") }
|
|
|
|
/** Holds if this function is declared with the `final` specifier. */
|
|
predicate isFinal() { this.hasSpecifier("final") }
|
|
|
|
/**
|
|
* Holds if this function is deleted.
|
|
* This may be because it was explicitly deleted with an `= delete`
|
|
* definition, or because the compiler was unable to auto-generate a
|
|
* definition for it.
|
|
*
|
|
* Most implicitly deleted functions are omitted from the database.
|
|
* `Class.implicitCopyConstructorDeleted` and
|
|
* `Class.implicitCopyAssignmentOperatorDeleted` can be used to find
|
|
* whether a class would have had those members implicitly deleted.
|
|
*/
|
|
predicate isDeleted() { function_deleted(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if this function has a prototyped interface.
|
|
*
|
|
* Functions generally have a prototyped interface, unless they are
|
|
* K&R-style functions either without any forward function declaration,
|
|
* or with all the forward declarations omitting the parameters of the
|
|
* function.
|
|
*/
|
|
predicate isPrototyped() { function_prototyped(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if this function is explicitly defaulted with the `= default`
|
|
* specifier.
|
|
*/
|
|
predicate isDefaulted() { function_defaulted(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if this function is declared to be `constexpr`.
|
|
*
|
|
* Note that this does not hold if the function has been declared
|
|
* `consteval`.
|
|
*/
|
|
predicate isDeclaredConstexpr() { this.hasSpecifier("declared_constexpr") }
|
|
|
|
/**
|
|
* Holds if this function is `constexpr`. Normally, this holds if and
|
|
* only if `isDeclaredConstexpr()` holds, but in some circumstances
|
|
* they differ. For example, with
|
|
* ```
|
|
* int f(int i) { return 6; }
|
|
* template <typename T> constexpr int g(T x) { return f(x); }
|
|
* ```
|
|
* `g<int>` is declared constexpr, but is not constexpr.
|
|
*
|
|
* Will also hold if this function is `consteval`.
|
|
*/
|
|
predicate isConstexpr() { this.hasSpecifier("is_constexpr") }
|
|
|
|
/**
|
|
* Holds if this function is declared to be `consteval`.
|
|
*/
|
|
predicate isConsteval() { this.hasSpecifier("is_consteval") }
|
|
|
|
/**
|
|
* Holds if this function is declared to be `explicit`.
|
|
*/
|
|
predicate isExplicit() { this.hasSpecifier("explicit") }
|
|
|
|
/**
|
|
* Gets the constant expression that determines whether the function is explicit.
|
|
*
|
|
* For example, for the following code the result is the expression `sizeof(T) == 1`:
|
|
* ```
|
|
* template<typename T> struct C {
|
|
* explicit(sizeof(T) == 1)
|
|
* C(const T);
|
|
* };
|
|
* ```
|
|
*/
|
|
Expr getExplicitExpr() {
|
|
explicit_specifier_exprs(underlyingElement(this), unresolveElement(result))
|
|
}
|
|
|
|
/**
|
|
* Holds if this function is declared with `__attribute__((naked))` or
|
|
* `__declspec(naked)`.
|
|
*/
|
|
predicate isNaked() { this.getAnAttribute().hasName("naked") }
|
|
|
|
/**
|
|
* Holds if this function has a trailing return type.
|
|
*
|
|
* Note that this is true whether or not deduction took place. For example,
|
|
* this holds for both `e` and `f`, but not `g` or `h`:
|
|
* ```
|
|
* auto e() -> int { return 0; }
|
|
* auto f() -> auto { return 0; }
|
|
* auto g() { return 0; }
|
|
* int h() { return 0; }
|
|
* ```
|
|
*/
|
|
predicate hasTrailingReturnType() { this.hasSpecifier("has_trailing_return_type") }
|
|
|
|
/** Gets the return type of this function. */
|
|
Type getType() { function_return_type(underlyingElement(this), unresolveElement(result)) }
|
|
|
|
/**
|
|
* Gets the return type of this function after specifiers have been deeply
|
|
* stripped and typedefs have been resolved.
|
|
*/
|
|
Type getUnspecifiedType() { result = this.getType().getUnspecifiedType() }
|
|
|
|
/**
|
|
* Gets the nth parameter of this function. There is no result for the
|
|
* implicit `this` parameter, and there is no `...` varargs pseudo-parameter.
|
|
*/
|
|
pragma[nomagic]
|
|
Parameter getParameter(int n) { params(unresolveElement(result), underlyingElement(this), n, _) }
|
|
|
|
/**
|
|
* Gets a parameter of this function. There is no result for the implicit
|
|
* `this` parameter, and there is no `...` varargs pseudo-parameter.
|
|
*/
|
|
pragma[nomagic]
|
|
Parameter getAParameter() { params(unresolveElement(result), underlyingElement(this), _, _) }
|
|
|
|
/**
|
|
* Gets an access of this function.
|
|
*
|
|
* To get calls to this function, use `getACallToThisFunction` instead.
|
|
*/
|
|
FunctionAccess getAnAccess() { result.getTarget() = this }
|
|
|
|
/**
|
|
* Gets the number of parameters of this function, _not_ including any
|
|
* implicit `this` parameter or any `...` varargs pseudo-parameter.
|
|
*/
|
|
int getNumberOfParameters() { result = count(this.getAParameter()) }
|
|
|
|
/**
|
|
* Gets the number of parameters of this function, _including_ any implicit
|
|
* `this` parameter but _not_ including any `...` varargs pseudo-parameter.
|
|
*/
|
|
int getEffectiveNumberOfParameters() {
|
|
// This method is overridden in `MemberFunction`, where the result is
|
|
// adjusted to account for the implicit `this` parameter.
|
|
result = this.getNumberOfParameters()
|
|
}
|
|
|
|
/**
|
|
* Gets a string representing the parameters of this function.
|
|
*
|
|
* For example: for a function `int Foo(int p1, int p2)` this would
|
|
* return `int p1, int p2`.
|
|
*/
|
|
string getParameterString() {
|
|
result = concat(int i | | min(this.getParameter(i).getTypedName()), ", " order by i)
|
|
}
|
|
|
|
/** Gets a call to this function. */
|
|
FunctionCall getACallToThisFunction() { result.getTarget() = this }
|
|
|
|
/**
|
|
* Gets a declaration entry corresponding to this declaration. The
|
|
* relationship between `Declaration` and `DeclarationEntry` is explained
|
|
* in `Declaration.qll`.
|
|
*/
|
|
override FunctionDeclarationEntry getADeclarationEntry() {
|
|
if fun_decls(_, underlyingElement(this), _, _, _)
|
|
then this.declEntry(result)
|
|
else
|
|
exists(Function f |
|
|
this.isConstructedFrom(f) and
|
|
fun_decls(unresolveElement(result), unresolveElement(f), _, _, _)
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets a non-implicit function declaration entry.
|
|
*/
|
|
FunctionDeclarationEntry getAnExplicitDeclarationEntry() {
|
|
result = this.getADeclarationEntry() and
|
|
not result.isImplicit()
|
|
}
|
|
|
|
private predicate declEntry(FunctionDeclarationEntry fde) {
|
|
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
|
|
// If one .cpp file specializes a function, and another calls the
|
|
// specialized function, then when extracting the second we only see an
|
|
// instantiation, not the specialization. We Therefore need to ignore
|
|
// any non-specialized declarations if there are any specialized ones.
|
|
(this.isSpecialization() implies fde.isSpecialization())
|
|
}
|
|
|
|
/**
|
|
* Gets the location of a `FunctionDeclarationEntry` corresponding to this
|
|
* declaration.
|
|
*/
|
|
override Location getADeclarationLocation() { result = this.getADeclarationEntry().getLocation() }
|
|
|
|
/** Holds if this function is a template specialization. */
|
|
predicate isSpecialization() {
|
|
exists(FunctionDeclarationEntry fde |
|
|
fun_decls(unresolveElement(fde), underlyingElement(this), _, _, _) and
|
|
fde.isSpecialization()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the declaration entry corresponding to this declaration that is a
|
|
* definition, if any.
|
|
*/
|
|
override FunctionDeclarationEntry getDefinition() {
|
|
result = this.getADeclarationEntry() and
|
|
result.isDefinition()
|
|
}
|
|
|
|
/** Gets the location of the definition, if any. */
|
|
override Location getDefinitionLocation() {
|
|
if exists(this.getDefinition())
|
|
then result = this.getDefinition().getLocation()
|
|
else exists(Function f | this.isConstructedFrom(f) and result = f.getDefinition().getLocation())
|
|
}
|
|
|
|
/**
|
|
* Gets the preferred location of this declaration. (The location of the
|
|
* definition, if possible.)
|
|
*/
|
|
override Location getLocation() {
|
|
if this instanceof BuiltInFunction
|
|
then result instanceof UnknownLocation // a dummy location for the built-in function
|
|
else
|
|
if exists(this.getDefinition())
|
|
then result = this.getDefinitionLocation()
|
|
else result = this.getADeclarationLocation()
|
|
}
|
|
|
|
/** Gets a child declaration of this function. */
|
|
Declaration getADeclaration() { result = this.getAParameter() }
|
|
|
|
/**
|
|
* Gets the block that is the function body.
|
|
*
|
|
* For C++ functions whose body is a function try statement rather than a
|
|
* block, this gives the block guarded by the try statement. See
|
|
* `FunctionTryStmt` for further information.
|
|
*/
|
|
BlockStmt getBlock() { result.getParentScope() = this }
|
|
|
|
/** Holds if this function has an entry point. */
|
|
predicate hasEntryPoint() { exists(this.getEntryPoint()) }
|
|
|
|
/**
|
|
* Gets the first node in this function's control flow graph.
|
|
*
|
|
* For most functions, this first node will be the `BlockStmt` returned by
|
|
* `getBlock`. However in C++, the first node can also be a
|
|
* `FunctionTryStmt`.
|
|
*/
|
|
Stmt getEntryPoint() { function_entry_point(underlyingElement(this), unresolveElement(result)) }
|
|
|
|
/**
|
|
* Gets the metric class. `MetricFunction` has methods for computing
|
|
* various metrics, such as "number of lines of code" and "number of
|
|
* function calls".
|
|
*/
|
|
MetricFunction getMetrics() { result = this }
|
|
|
|
/** Holds if this function calls the function `f`. */
|
|
pragma[nomagic]
|
|
predicate calls(Function f) { this.calls(f, _) }
|
|
|
|
/**
|
|
* Holds if this function calls the function `f` in the `FunctionCall`
|
|
* expression `l`.
|
|
*/
|
|
predicate calls(Function f, Locatable l) {
|
|
exists(FunctionCall call |
|
|
call.getEnclosingFunction() = this and call.getTarget() = f and call = l
|
|
)
|
|
}
|
|
|
|
/** Holds if this function accesses a function or variable or enumerator `a`. */
|
|
predicate accesses(Declaration a) { this.accesses(a, _) }
|
|
|
|
/**
|
|
* Holds if this function accesses a function or variable or enumerator `a`
|
|
* in the `Access` expression `l`.
|
|
*/
|
|
predicate accesses(Declaration a, Locatable l) {
|
|
exists(Access access |
|
|
access.getEnclosingFunction() = this and
|
|
a = access.getTarget() and
|
|
access = l
|
|
)
|
|
}
|
|
|
|
/** Gets a variable that is written-to in this function. */
|
|
Variable getAWrittenVariable() {
|
|
exists(ConstructorFieldInit cfi |
|
|
cfi.getEnclosingFunction() = this and result = cfi.getTarget()
|
|
)
|
|
or
|
|
exists(VariableAccess va |
|
|
va = result.getAnAccess() and
|
|
va.isUsedAsLValue() and
|
|
va.getEnclosingFunction() = this
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the class of which this function, called `memberName`, is a member.
|
|
*
|
|
* Prefer to use `getDeclaringType()` or `getName()` directly if you do not
|
|
* need to reason about both.
|
|
*/
|
|
pragma[nomagic]
|
|
Class getClassAndName(string memberName) {
|
|
this.hasName(memberName) and
|
|
this.getDeclaringType() = result
|
|
}
|
|
|
|
/**
|
|
* Implements `ControlFlowNode.getControlFlowScope`. The `Function` is
|
|
* used to represent the exit node of the control flow graph, so it is
|
|
* its own scope.
|
|
*/
|
|
override Function getControlFlowScope() { result = this }
|
|
|
|
/**
|
|
* Implements `ControlFlowNode.getEnclosingStmt`. The `Function` is
|
|
* used to represent the exit node of the control flow graph, so it
|
|
* has no enclosing statement.
|
|
*/
|
|
override Stmt getEnclosingStmt() { none() }
|
|
|
|
/**
|
|
* Holds if this function has C linkage, as specified by one of its
|
|
* declaration entries. For example: `extern "C" void foo();`.
|
|
*/
|
|
predicate hasCLinkage() { this.getADeclarationEntry().hasCLinkage() }
|
|
|
|
/**
|
|
* Holds if this function is constructed from `f` as a result
|
|
* of template instantiation. If so, it originates either from a template
|
|
* function or from a function nested in a template class.
|
|
*/
|
|
predicate isConstructedFrom(Function f) {
|
|
function_instantiation(underlyingElement(this), unresolveElement(f))
|
|
}
|
|
|
|
/**
|
|
* Holds if this function is defined in several files. This is illegal in
|
|
* C (though possible in some C++ compilers), and likely indicates that
|
|
* several functions that are not linked together have been compiled. An
|
|
* example would be a project with many 'main' functions.
|
|
*/
|
|
predicate isMultiplyDefined() { strictcount(this.getFile()) > 1 }
|
|
|
|
/** Holds if this function is a varargs function. */
|
|
predicate isVarargs() { this.hasSpecifier("varargs") }
|
|
|
|
/** Gets a type that is specified to be thrown by the function. */
|
|
Type getAThrownType() { result = this.getADeclarationEntry().getAThrownType() }
|
|
|
|
/**
|
|
* Gets the `i`th type specified to be thrown by the function.
|
|
*/
|
|
Type getThrownType(int i) { result = this.getADeclarationEntry().getThrownType(i) }
|
|
|
|
/** Holds if the function has an exception specification. */
|
|
predicate hasExceptionSpecification() { this.getADeclarationEntry().hasExceptionSpecification() }
|
|
|
|
/** Holds if this function has a `throw()` exception specification. */
|
|
predicate isNoThrow() { this.getADeclarationEntry().isNoThrow() }
|
|
|
|
/** Holds if this function has a `noexcept` exception specification. */
|
|
predicate isNoExcept() { this.getADeclarationEntry().isNoExcept() }
|
|
|
|
/**
|
|
* Gets a function that overloads this one.
|
|
*
|
|
* Note: if _overrides_ are wanted rather than _overloads_ then
|
|
* `MemberFunction::getAnOverridingFunction` should be used instead.
|
|
*/
|
|
Function getAnOverload() {
|
|
(
|
|
// If this function is declared in a class, only consider other
|
|
// functions from the same class.
|
|
exists(string name, Class declaringType |
|
|
candGetAnOverloadMember(name, declaringType, this) and
|
|
candGetAnOverloadMember(name, declaringType, result)
|
|
)
|
|
or
|
|
// Conversely, if this function is not
|
|
// declared in a class, only consider other functions not declared in a
|
|
// class.
|
|
exists(string name, Namespace namespace |
|
|
candGetAnOverloadNonMember(name, namespace, this) and
|
|
candGetAnOverloadNonMember(name, namespace, result)
|
|
)
|
|
) and
|
|
result != this and
|
|
// Instantiations and specializations don't participate in overload
|
|
// resolution.
|
|
not (
|
|
this instanceof FunctionTemplateInstantiation or
|
|
result instanceof FunctionTemplateInstantiation
|
|
) and
|
|
not (
|
|
this instanceof FunctionTemplateSpecialization or
|
|
result instanceof FunctionTemplateSpecialization
|
|
)
|
|
}
|
|
|
|
/** Gets a link target which compiled or referenced this function. */
|
|
LinkTarget getALinkTarget() { this = result.getAFunction() }
|
|
|
|
/**
|
|
* Holds if this function is side-effect free (conservative
|
|
* approximation).
|
|
*/
|
|
predicate isSideEffectFree() { not this.mayHaveSideEffects() }
|
|
|
|
/**
|
|
* Holds if this function may have side-effects; if in doubt, we assume it
|
|
* may.
|
|
*/
|
|
predicate mayHaveSideEffects() {
|
|
// If we cannot see the definition then we assume that it may have
|
|
// side-effects.
|
|
if exists(this.getEntryPoint())
|
|
then
|
|
// If it might be globally impure (we don't care about it modifying
|
|
// temporaries) then it may have side-effects.
|
|
this.getEntryPoint().mayBeGloballyImpure()
|
|
or
|
|
// Constructor initializers are separate from the entry point ...
|
|
this.(Constructor).getAnInitializer().mayBeGloballyImpure()
|
|
or
|
|
// ... and likewise for destructors.
|
|
this.(Destructor).getADestruction().mayBeGloballyImpure()
|
|
else
|
|
// Unless it's a function that we know is side-effect free, it may
|
|
// have side-effects.
|
|
not this.hasGlobalOrStdName([
|
|
"strcmp", "wcscmp", "_mbscmp", "strlen", "wcslen", "_mbslen", "_mbslen_l", "_mbstrlen",
|
|
"_mbstrlen_l", "strnlen", "strnlen_s", "wcsnlen", "wcsnlen_s", "_mbsnlen", "_mbsnlen_l",
|
|
"_mbstrnlen", "_mbstrnlen_l", "strncmp", "wcsncmp", "_mbsncmp", "_mbsncmp_l", "strchr",
|
|
"memchr", "wmemchr", "memcmp", "wmemcmp", "_memicmp", "_memicmp_l", "feof", "isdigit",
|
|
"isxdigit", "abs", "fabs", "labs", "floor", "ceil", "atoi", "atol", "atoll", "atof"
|
|
])
|
|
}
|
|
|
|
/**
|
|
* Gets the nearest enclosing AccessHolder.
|
|
*/
|
|
override AccessHolder getEnclosingAccessHolder() { result = this.getDeclaringType() }
|
|
|
|
/**
|
|
* Holds if this function has extraction errors that create an `ErrorExpr`.
|
|
*/
|
|
predicate hasErrors() {
|
|
exists(ErrorExpr e |
|
|
e.getEnclosingFunction() = this and
|
|
// Exclude the first allocator call argument because it is always extracted as `ErrorExpr`.
|
|
not exists(NewOrNewArrayExpr new | e = new.getAllocatorCall().getArgument(0))
|
|
)
|
|
}
|
|
}
|
|
|
|
pragma[noinline]
|
|
private predicate candGetAnOverloadMember(string name, Class declaringType, Function f) {
|
|
f.getName() = name and
|
|
f.getDeclaringType() = declaringType
|
|
}
|
|
|
|
pragma[noinline]
|
|
private predicate candGetAnOverloadNonMember(string name, Namespace namespace, Function f) {
|
|
f.getName() = name and
|
|
f.getNamespace() = namespace and
|
|
not exists(f.getDeclaringType())
|
|
}
|
|
|
|
/**
|
|
* A particular declaration or definition of a C/C++ function. For example the
|
|
* declaration and definition of `MyFunction` in the following code are each a
|
|
* `FunctionDeclarationEntry`:
|
|
* ```
|
|
* void MyFunction();
|
|
*
|
|
* void MyFunction() {
|
|
* DoSomething();
|
|
* }
|
|
* ```
|
|
*/
|
|
class FunctionDeclarationEntry extends DeclarationEntry, @fun_decl {
|
|
/** Gets the function which is being declared or defined. */
|
|
override Function getDeclaration() { result = this.getFunction() }
|
|
|
|
override string getAPrimaryQlClass() { result = "FunctionDeclarationEntry" }
|
|
|
|
/** Gets the function which is being declared or defined. */
|
|
Function getFunction() { fun_decls(underlyingElement(this), unresolveElement(result), _, _, _) }
|
|
|
|
/** Gets the name of the function. */
|
|
override string getName() { fun_decls(underlyingElement(this), _, _, result, _) }
|
|
|
|
/**
|
|
* Gets the return type of the function which is being declared or
|
|
* defined.
|
|
*/
|
|
override Type getType() { fun_decls(underlyingElement(this), _, unresolveElement(result), _, _) }
|
|
|
|
/** Gets the location of this declaration entry. */
|
|
override Location getLocation() { fun_decls(underlyingElement(this), _, _, _, result) }
|
|
|
|
/** Gets a specifier associated with this declaration entry. */
|
|
override string getASpecifier() { fun_decl_specifiers(underlyingElement(this), result) }
|
|
|
|
/**
|
|
* Implements `Element.getEnclosingElement`. A function declaration does
|
|
* not have an enclosing element.
|
|
*/
|
|
override Element getEnclosingElement() { none() }
|
|
|
|
/**
|
|
* Gets the typedef type (if any) used for this function declaration. As
|
|
* an example, the typedef type in the declaration of function foo in the
|
|
* following is Foo:
|
|
*
|
|
* typedef int Foo();
|
|
* static Foo foo;
|
|
*/
|
|
TypedefType getTypedefType() {
|
|
fun_decl_typedef_type(underlyingElement(this), unresolveElement(result))
|
|
}
|
|
|
|
/**
|
|
* Gets the cyclomatic complexity of this function:
|
|
*
|
|
* The number of branching statements (if, while, do, for, switch,
|
|
* case, catch) plus the number of branching expressions (`?`, `&&`,
|
|
* `||`) plus one.
|
|
*/
|
|
int getCyclomaticComplexity() { result = 1 + cyclomaticComplexityBranches(this.getBlock()) }
|
|
|
|
/**
|
|
* If this is a function definition, get the block containing the
|
|
* function body.
|
|
*/
|
|
BlockStmt getBlock() {
|
|
this.isDefinition() and
|
|
result = this.getFunction().getBlock() and
|
|
result.getFile() = this.getFile()
|
|
}
|
|
|
|
/**
|
|
* If this is a function definition, get the number of lines of code
|
|
* associated with it.
|
|
*/
|
|
pragma[noopt]
|
|
int getNumberOfLines() {
|
|
exists(BlockStmt b, Location l, int start, int end, int diff | b = this.getBlock() |
|
|
l = b.getLocation() and
|
|
start = l.getStartLine() and
|
|
end = l.getEndLine() and
|
|
diff = end - start and
|
|
result = diff + 1
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the declaration entry for a parameter of this function
|
|
* declaration.
|
|
*/
|
|
ParameterDeclarationEntry getAParameterDeclarationEntry() {
|
|
result = this.getParameterDeclarationEntry(_)
|
|
}
|
|
|
|
/**
|
|
* Gets the declaration entry for the nth parameter of this function
|
|
* declaration.
|
|
*/
|
|
ParameterDeclarationEntry getParameterDeclarationEntry(int n) {
|
|
param_decl_bind(unresolveElement(result), n, underlyingElement(this))
|
|
}
|
|
|
|
/** Gets the number of parameters of this function declaration. */
|
|
int getNumberOfParameters() { result = count(this.getAParameterDeclarationEntry()) }
|
|
|
|
/**
|
|
* Gets a string representing the parameters of this function declaration.
|
|
*
|
|
* For example: for a function 'int Foo(int p1, int p2)' this would
|
|
* return 'int p1, int p2'.
|
|
*/
|
|
string getParameterString() {
|
|
result =
|
|
concat(int i | | min(this.getParameterDeclarationEntry(i).getTypedName()), ", " order by i)
|
|
}
|
|
|
|
/**
|
|
* Holds if this declaration entry specifies C linkage:
|
|
*
|
|
* `extern "C" void foo();`
|
|
*/
|
|
predicate hasCLinkage() { this.getASpecifier() = "c_linkage" }
|
|
|
|
/** Holds if this declaration entry has a void parameter list. */
|
|
predicate hasVoidParamList() { this.getASpecifier() = "void_param_list" }
|
|
|
|
/** Holds if this declaration is also a definition of its function. */
|
|
override predicate isDefinition() { fun_def(underlyingElement(this)) }
|
|
|
|
/** Holds if this declaration is a template specialization. */
|
|
predicate isSpecialization() { fun_specialized(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if this declaration is an implicit function declaration, that is,
|
|
* where a function is used before it is declared (under older C standards,
|
|
* or when there were parse errors).
|
|
*/
|
|
predicate isImplicit() { fun_implicit(underlyingElement(this)) }
|
|
|
|
/** Gets a type that is specified to be thrown by the declared function. */
|
|
Type getAThrownType() { result = this.getThrownType(_) }
|
|
|
|
/**
|
|
* Gets the `i`th type specified to be thrown by the declared function
|
|
* (where `i` is indexed from 0). For example, if a function is declared
|
|
* to `throw(int,float)`, then the thrown type with index 0 would be
|
|
* `int`, and that with index 1 would be `float`.
|
|
*/
|
|
Type getThrownType(int i) {
|
|
fun_decl_throws(underlyingElement(this), i, unresolveElement(result))
|
|
}
|
|
|
|
/**
|
|
* If this declaration has a noexcept-specification [N4140 15.4], then
|
|
* this predicate returns the argument to `noexcept` if one was given.
|
|
*/
|
|
Expr getNoExceptExpr() { fun_decl_noexcept(underlyingElement(this), unresolveElement(result)) }
|
|
|
|
/**
|
|
* Holds if the declared function has an exception specification [N4140
|
|
* 15.4].
|
|
*/
|
|
predicate hasExceptionSpecification() {
|
|
fun_decl_throws(underlyingElement(this), _, _) or
|
|
fun_decl_noexcept(underlyingElement(this), _) or
|
|
this.isNoThrow() or
|
|
this.isNoExcept()
|
|
}
|
|
|
|
/**
|
|
* Holds if the declared function has a `throw()` exception specification.
|
|
*/
|
|
predicate isNoThrow() { fun_decl_empty_throws(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Holds if the declared function has an empty `noexcept` exception
|
|
* specification.
|
|
*/
|
|
predicate isNoExcept() { fun_decl_empty_noexcept(underlyingElement(this)) }
|
|
|
|
/**
|
|
* Gets a requires clause if this declaration is a template with such a clause.
|
|
*/
|
|
Expr getARequiresClause() { fun_requires(underlyingElement(this), _, unresolveElement(result)) }
|
|
|
|
/**
|
|
* Gets the requires clause that appears after the template argument list if this
|
|
* declaration is a template with such a clause.
|
|
*/
|
|
Expr getTemplateRequiresClause() {
|
|
fun_requires(underlyingElement(this), 1, unresolveElement(result))
|
|
}
|
|
|
|
/**
|
|
* Gets the requires clause that appears after the declarator if this declaration
|
|
* is a template with such a clause.
|
|
*/
|
|
Expr getFunctionRequiresClause() {
|
|
fun_requires(underlyingElement(this), 2, unresolveElement(result))
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A C/C++ non-member function (a function that is not a member of any
|
|
* class). For example, in the following code, `MyFunction` is a
|
|
* `TopLevelFunction` but `MyMemberFunction` is not:
|
|
* ```
|
|
* void MyFunction() {
|
|
* DoSomething();
|
|
* }
|
|
*
|
|
* class MyClass {
|
|
* public:
|
|
* void MyMemberFunction() {
|
|
* DoSomething();
|
|
* }
|
|
* };
|
|
* ```
|
|
*/
|
|
class TopLevelFunction extends Function {
|
|
TopLevelFunction() { not this.isMember() }
|
|
|
|
override string getAPrimaryQlClass() { result = "TopLevelFunction" }
|
|
}
|
|
|
|
/**
|
|
* A C++ user-defined operator [N4140 13.5].
|
|
*/
|
|
class Operator extends Function {
|
|
Operator() { functions(underlyingElement(this), _, 5) }
|
|
|
|
override string getAPrimaryQlClass() {
|
|
not this instanceof MemberFunction and result = "Operator"
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A C++ function which has a non-empty template argument list. For example
|
|
* the function `myTemplateFunction` in the following code:
|
|
* ```
|
|
* template<class T>
|
|
* void myTemplateFunction(T t) {
|
|
* ...
|
|
* }
|
|
* ```
|
|
*
|
|
* This comprises function declarations which are immediately preceded by
|
|
* `template <...>`, where the "..." part is not empty, and therefore it does
|
|
* not include:
|
|
*
|
|
* 1. Full specializations of template functions, as they have an empty
|
|
* template argument list.
|
|
* 2. Instantiations of template functions, as they don't have an
|
|
* explicit template argument list.
|
|
* 3. Member functions of template classes - unless they have their own
|
|
* (non-empty) template argument list.
|
|
*/
|
|
class TemplateFunction extends Function {
|
|
TemplateFunction() {
|
|
is_function_template(underlyingElement(this)) and exists(this.getATemplateArgument())
|
|
}
|
|
|
|
override string getAPrimaryQlClass() { result = "TemplateFunction" }
|
|
|
|
/**
|
|
* Gets a compiler-generated instantiation of this function template.
|
|
*/
|
|
Function getAnInstantiation() {
|
|
result.isConstructedFrom(this) and
|
|
not result.isSpecialization()
|
|
}
|
|
|
|
/**
|
|
* Gets a full specialization of this function template.
|
|
*
|
|
* Note that unlike classes, functions overload rather than specialize
|
|
* partially. Therefore this does not include things which "look like"
|
|
* partial specializations, nor does it include full specializations of
|
|
* such things -- see FunctionTemplateSpecialization for further details.
|
|
*/
|
|
FunctionTemplateSpecialization getASpecialization() { result.getPrimaryTemplate() = this }
|
|
}
|
|
|
|
/**
|
|
* A function that is an instantiation of a template. For example
|
|
* the instantiation `myTemplateFunction<int>` in the following code:
|
|
* ```
|
|
* template<class T>
|
|
* void myTemplateFunction(T t) {
|
|
* ...
|
|
* }
|
|
*
|
|
* void caller(int i) {
|
|
* myTemplateFunction<int>(i);
|
|
* }
|
|
* ```
|
|
*/
|
|
class FunctionTemplateInstantiation extends Function {
|
|
TemplateFunction tf;
|
|
|
|
FunctionTemplateInstantiation() { tf.getAnInstantiation() = this }
|
|
|
|
override string getAPrimaryQlClass() { result = "FunctionTemplateInstantiation" }
|
|
|
|
/**
|
|
* Gets the function template from which this instantiation was instantiated.
|
|
*
|
|
* Example: For `int const& std::min<int>(int const&, int const&)`, returns `T const& min<T>(T const&, T const&)`.
|
|
*/
|
|
TemplateFunction getTemplate() { result = tf }
|
|
}
|
|
|
|
/**
|
|
* An explicit specialization of a C++ function template. For example the
|
|
* function `myTemplateFunction<int>` in the following code:
|
|
* ```
|
|
* template<class T>
|
|
* void myTemplateFunction(T t) {
|
|
* ...
|
|
* }
|
|
*
|
|
* template<>
|
|
* void myTemplateFunction<int>(int i) {
|
|
* ...
|
|
* }
|
|
* ```
|
|
*
|
|
* Note that unlike classes, functions overload rather than specialize
|
|
* partially. Therefore this only includes the last two of the following
|
|
* four definitions, and in particular does not include the second one:
|
|
*
|
|
* ```
|
|
* template <typename T> void f(T) {...}
|
|
* template <typename T> void f(T*) {...}
|
|
* template <> void f<int>(int *) {...}
|
|
* template <> void f<int*>(int *) {...}
|
|
* ```
|
|
*
|
|
* Furthermore, this does not include compiler-generated instantiations of
|
|
* function templates.
|
|
*
|
|
* For further reference on function template specializations, see:
|
|
* http://www.gotw.ca/publications/mill17.htm
|
|
*/
|
|
class FunctionTemplateSpecialization extends Function {
|
|
FunctionTemplateSpecialization() { this.isSpecialization() }
|
|
|
|
override string getAPrimaryQlClass() { result = "FunctionTemplateSpecialization" }
|
|
|
|
/**
|
|
* Gets the primary template for the specialization (the function template
|
|
* this specializes).
|
|
*/
|
|
TemplateFunction getPrimaryTemplate() { this.isConstructedFrom(result) }
|
|
}
|
|
|
|
/**
|
|
* A GCC built-in function. For example: `__builtin___memcpy_chk`.
|
|
*/
|
|
class BuiltInFunction extends Function {
|
|
BuiltInFunction() { builtin_functions(underlyingElement(this)) }
|
|
}
|
|
|
|
/**
|
|
* A C++ user-defined literal [N4140 13.5.8].
|
|
*/
|
|
class UserDefinedLiteral extends Function {
|
|
UserDefinedLiteral() { functions(underlyingElement(this), _, 7) }
|
|
}
|
|
|
|
/**
|
|
* A C++ deduction guide [N4659 17.9].
|
|
*/
|
|
class DeductionGuide extends Function {
|
|
DeductionGuide() { functions(underlyingElement(this), _, 8) }
|
|
|
|
/**
|
|
* Gets the class template for which this is a deduction guide.
|
|
*/
|
|
TemplateClass getTemplateClass() {
|
|
deduction_guide_for_class(underlyingElement(this), unresolveElement(result))
|
|
}
|
|
}
|