cppcheck/lib/symboldatabase.h

1479 lines
46 KiB
C++

/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2023 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
#ifndef symboldatabaseH
#define symboldatabaseH
//---------------------------------------------------------------------------
#include "config.h"
#include "errortypes.h"
#include "library.h"
#include "mathlib.h"
#include "sourcelocation.h"
#include "token.h"
#include "utils.h"
#include <algorithm>
#include <cctype>
#include <iosfwd>
#include <list>
#include <map>
#include <set>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace cppcheck {
class Platform;
}
class ErrorLogger;
class Function;
class Scope;
class Settings;
class SymbolDatabase;
class Tokenizer;
class ValueType;
/**
* @brief Access control enumerations.
*/
enum class AccessControl { Public, Protected, Private, Global, Namespace, Argument, Local, Throw };
/**
* @brief Array dimension information.
*/
struct Dimension {
const Token* tok{}; ///< size token
MathLib::bigint num{}; ///< (assumed) dimension length when size is a number, 0 if not known
bool known = true; ///< Known size
};
/** @brief Information about a class type. */
class CPPCHECKLIB Type {
public:
const Token* classDef; ///< Points to "class" token
const Scope* classScope;
const Scope* enclosingScope;
enum class NeedInitialization {
Unknown, True, False
} needInitialization = NeedInitialization::Unknown;
struct BaseInfo {
std::string name;
const Type* type{};
const Token* nameTok{};
AccessControl access{}; // public/protected/private
bool isVirtual{};
// allow ordering within containers
bool operator<(const BaseInfo& rhs) const {
return this->type < rhs.type;
}
};
struct FriendInfo {
const Token* nameStart{};
const Token* nameEnd{};
const Type* type{};
};
std::vector<BaseInfo> derivedFrom;
std::vector<FriendInfo> friendList;
const Token* typeStart{};
const Token* typeEnd{};
MathLib::bigint sizeOf{};
explicit Type(const Token* classDef_ = nullptr, const Scope* classScope_ = nullptr, const Scope* enclosingScope_ = nullptr) :
classDef(classDef_),
classScope(classScope_),
enclosingScope(enclosingScope_) {
if (classDef_ && classDef_->str() == "enum")
needInitialization = NeedInitialization::True;
else if (classDef_ && classDef_->str() == "using") {
typeStart = classDef->tokAt(3);
typeEnd = typeStart;
while (typeEnd->next() && typeEnd->next()->str() != ";") {
if (Token::simpleMatch(typeEnd, "decltype ("))
typeEnd = typeEnd->linkAt(1);
else
typeEnd = typeEnd->next();
}
}
}
std::string name() const;
const std::string& type() const {
return classDef ? classDef->str() : emptyString;
}
bool isClassType() const;
bool isEnumType() const;
bool isStructType() const;
bool isUnionType() const;
bool isTypeAlias() const {
return classDef && classDef->str() == "using";
}
const Token *initBaseInfo(const Token *tok, const Token *tok1);
const Function* getFunction(const std::string& funcName) const;
/**
* Check for circulare dependencies, i.e. loops within the class hierarchy
* @param ancestors list of ancestors. For internal usage only, clients should not supply this argument.
* @return true if there is a circular dependency
*/
bool hasCircularDependencies(std::set<BaseInfo>* ancestors = nullptr) const;
/**
* Check for dependency
* @param ancestor potential ancestor
* @return true if there is a dependency
*/
bool findDependency(const Type* ancestor) const;
bool isDerivedFrom(const std::string & ancestor) const;
};
struct CPPCHECKLIB Enumerator {
explicit Enumerator(const Scope * scope_) : scope(scope_) {}
const Scope * scope;
const Token* name{};
MathLib::bigint value{};
const Token* start{};
const Token* end{};
bool value_known{};
};
/** @brief Information about a member variable. */
class CPPCHECKLIB Variable {
/** @brief flags mask used to access specific bit. */
enum {
fIsMutable = (1 << 0), /** @brief mutable variable */
fIsStatic = (1 << 1), /** @brief static variable */
fIsConst = (1 << 2), /** @brief const variable */
fIsExtern = (1 << 3), /** @brief extern variable */
fIsClass = (1 << 4), /** @brief user defined type */
fIsArray = (1 << 5), /** @brief array variable */
fIsPointer = (1 << 6), /** @brief pointer variable */
fIsReference = (1 << 7), /** @brief reference variable */
fIsRValueRef = (1 << 8), /** @brief rvalue reference variable */
fHasDefault = (1 << 9), /** @brief function argument with default value */
fIsStlType = (1 << 10), /** @brief STL type ('std::') */
fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string&lt;T&gt;|u16string|u32string */
fIsFloatType = (1 << 12), /** @brief Floating point type */
fIsVolatile = (1 << 13), /** @brief volatile */
fIsSmartPointer = (1 << 14),/** @brief std::shared_ptr|unique_ptr */
fIsMaybeUnused = (1 << 15), /** @brief marked [[maybe_unused]] */
fIsInit = (1 << 16), /** @brief Is variable initialized in declaration */
};
/**
* Get specified flag state.
* @param flag_ flag to get state of
* @return true if flag set or false in flag not set
*/
bool getFlag(unsigned int flag_) const {
return ((mFlags & flag_) != 0);
}
/**
* Set specified flag state.
* @param flag_ flag to set state
* @param state_ new state of flag
*/
void setFlag(unsigned int flag_, bool state_) {
mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_;
}
/**
* @brief parse and save array dimension information
* @param settings Platform settings and library
* @param isContainer Is the array container-like?
* @return true if array, false if not
*/
bool arrayDimensions(const Settings* settings, bool& isContainer);
public:
Variable(const Token *name_, const Token *start_, const Token *end_,
nonneg int index_, AccessControl access_, const Type *type_,
const Scope *scope_, const Settings* settings)
: mNameToken(name_),
mTypeStartToken(start_),
mTypeEndToken(end_),
mIndex(index_),
mAccess(access_),
mFlags(0),
mType(type_),
mScope(scope_) {
evaluate(settings);
}
Variable(const Token *name_, const std::string &clangType, const Token *typeStart,
const Token *typeEnd, nonneg int index_, AccessControl access_,
const Type *type_, const Scope *scope_);
Variable(const Variable &var, const Scope *scope);
Variable(const Variable &var);
~Variable();
Variable &operator=(const Variable &var);
/**
* Get name token.
* @return name token
*/
const Token *nameToken() const {
return mNameToken;
}
/**
* Get type start token.
* The type start token doesn't account 'static' and 'const' qualifiers
* E.g.:
* static const int * const p = ...;
* type start token ^
* @return type start token
*/
const Token *typeStartToken() const {
return mTypeStartToken;
}
/**
* Get type end token.
* The type end token doesn't account the forward 'const' qualifier
* E.g.:
* static const int * const p = ...;
* type end token ^
* @return type end token
*/
const Token *typeEndToken() const {
return mTypeEndToken;
}
/**
* Get end token of variable declaration
* E.g.
* int i[2][3] = ...
* end token ^
* @return variable declaration end token
*/
const Token *declEndToken() const;
/**
* Get name string.
* @return name string
*/
const std::string &name() const {
// name may not exist for function arguments
if (mNameToken)
return mNameToken->str();
return emptyString;
}
/**
* Get declaration ID (varId used for variable in its declaration).
* @return declaration ID
*/
nonneg int declarationId() const {
// name may not exist for function arguments
if (mNameToken)
return mNameToken->varId();
return 0;
}
/**
* Get index of variable in declared order.
* @return variable index
*/
nonneg int index() const {
return mIndex;
}
/**
* Is variable public.
* @return true if public, false if not
*/
bool isPublic() const {
return mAccess == AccessControl::Public;
}
/**
* Is variable protected.
* @return true if protected, false if not
*/
bool isProtected() const {
return mAccess == AccessControl::Protected;
}
/**
* Is variable private.
* @return true if private, false if not
*/
bool isPrivate() const {
return mAccess == AccessControl::Private;
}
/**
* Is variable global.
* @return true if global, false if not
*/
bool isGlobal() const {
return mAccess == AccessControl::Global;
}
/**
* Is variable in a namespace.
* @return true if in a namespace, false if not
*/
// cppcheck-suppress unusedFunction
bool isNamespace() const {
return mAccess == AccessControl::Namespace;
}
/**
* Is variable a function argument.
* @return true if a function argument, false if not
*/
bool isArgument() const {
return mAccess == AccessControl::Argument;
}
/**
* Is variable local.
* @return true if local, false if not
*/
bool isLocal() const {
return (mAccess == AccessControl::Local) && !isExtern();
}
/**
* Is variable a member of a user-defined type.
* @return true if member, false if not or unknown
*/
bool isMember() const;
/**
* Is variable mutable.
* @return true if mutable, false if not
*/
bool isMutable() const {
return getFlag(fIsMutable);
}
/**
* Is variable volatile.
* @return true if volatile, false if not
*/
bool isVolatile() const {
return getFlag(fIsVolatile);
}
/**
* Is variable static.
* @return true if static, false if not
*/
bool isStatic() const {
return getFlag(fIsStatic);
}
/**
* Is variable extern.
* @return true if extern, false if not
*/
bool isExtern() const {
return getFlag(fIsExtern);
}
/**
* Is variable const.
* @return true if const, false if not
*/
bool isConst() const {
return getFlag(fIsConst);
}
/**
* Is variable a throw type.
* @return true if throw type, false if not
*/
bool isThrow() const {
return mAccess == AccessControl::Throw;
}
/**
* Is variable a user defined (or unknown) type.
* @return true if user defined type, false if not
*/
bool isClass() const {
return getFlag(fIsClass);
}
/**
* Is variable an array.
* @return true if array, false if not
*/
bool isArray() const {
return getFlag(fIsArray) && !getFlag(fIsPointer);
}
/**
* Is pointer variable.
* @return true if pointer, false otherwise
*/
bool isPointer() const {
return getFlag(fIsPointer);
}
/**
* Is variable a pointer to an array
* @return true if pointer to array, false otherwise
*/
bool isPointerToArray() const {
return isPointer() && getFlag(fIsArray);
}
/**
* Is variable an array of pointers
* @return true if array or pointers, false otherwise
*/
bool isPointerArray() const;
/**
* Is array or pointer variable.
* @return true if pointer or array, false otherwise
*/
bool isArrayOrPointer() const {
return getFlag(fIsArray) || getFlag(fIsPointer);
}
/**
* Is reference variable.
* @return true if reference, false otherwise
*/
bool isReference() const {
return getFlag(fIsReference);
}
/**
* Is reference variable.
* @return true if reference, false otherwise
*/
bool isRValueReference() const {
return getFlag(fIsRValueRef);
}
/**
* Is variable unsigned.
* @return true only if variable _is_ unsigned. if the sign is unknown, false is returned.
*/
bool isUnsigned() const;
/**
* Does variable have a default value.
* @return true if has a default falue, false if not
*/
bool hasDefault() const {
return getFlag(fHasDefault);
}
/**
* Is variable initialized in its declaration
* @return true if variable declaration contains initialization
*/
bool isInit() const {
return getFlag(fIsInit);
}
/**
* Get Type pointer of known type.
* @return pointer to type if known, NULL if not known
*/
const Type *type() const {
return mType;
}
/**
* Get Scope pointer of known type.
* @return pointer to type scope if known, NULL if not known
*/
const Scope *typeScope() const {
return mType ? mType->classScope : nullptr;
}
/**
* Get Scope pointer of enclosing scope.
* @return pointer to enclosing scope
*/
const Scope *scope() const {
return mScope;
}
/**
* Get array dimensions.
* @return array dimensions vector
*/
const std::vector<Dimension> &dimensions() const {
return mDimensions;
}
/**
* Get array dimension length.
* @return length of dimension
*/
MathLib::bigint dimension(nonneg int index_) const {
return mDimensions.at(index_).num;
}
/**
* Get array dimension known.
* @return length of dimension known
*/
bool dimensionKnown(nonneg int index_) const {
return mDimensions.at(index_).known;
}
/**
* Checks if the variable is an STL type ('std::')
* E.g.:
* std::string s;
* ...
* sVar->isStlType() == true
* @return true if it is an stl type and its type matches any of the types in 'stlTypes'
*/
bool isStlType() const {
return getFlag(fIsStlType);
}
/**
* Checks if the variable is an STL type ('std::')
* E.g.:
* std::string s;
* ...
* sVar->isStlType() == true
* @return true if it is an stl type and its type matches any of the types in 'stlTypes'
*/
bool isStlStringType() const {
return getFlag(fIsStlString);
}
bool isStlStringViewType() const;
bool isSmartPointer() const {
return getFlag(fIsSmartPointer);
}
const Type* smartPointerType() const;
const Type* iteratorType() const;
/**
* Checks if the variable is of any of the STL types passed as arguments ('std::')
* E.g.:
* std::string s;
* ...
* const char *str[] = {"string", "wstring"};
* sVar->isStlType(str) == true
* @param stlType stl type
* @return true if it is an stl type and its type matches any of the types in 'stlTypes'
*/
bool isStlType(const std::string& stlType) const {
return isStlType() && stlType==mTypeStartToken->strAt(2);
}
/**
* Checks if the variable is of any of the STL types passed as arguments ('std::')
* E.g.:
* std::string s;
* ...
* const std::set<std::string> str = make_container< std::set<std::string> >() << "string" << "wstring";
* sVar->isStlType(str) == true
* @param stlTypes set of stl types
* @return true if it is an stl type and its type matches any of the types in 'stlTypes'
*/
bool isStlType(const std::set<std::string>& stlTypes) const {
return isStlType() && stlTypes.find(mTypeStartToken->strAt(2))!=stlTypes.end();
}
/**
* Determine whether it's a floating number type
* @return true if the type is known and it's a floating type (float, double and long double) or a pointer/array to it
*/
bool isFloatingType() const {
return getFlag(fIsFloatType);
}
/**
* Determine whether it's an enumeration type
* @return true if the type is known and it's an enumeration type
*/
bool isEnumType() const {
return type() && type()->isEnumType();
}
bool isMaybeUnused() const {
return getFlag(fIsMaybeUnused);
}
const ValueType *valueType() const {
return mValueType;
}
void setValueType(const ValueType &valueType);
AccessControl accessControl() const {
return mAccess;
}
std::string getTypeName() const;
private:
// only symbol database can change the type
friend class SymbolDatabase;
/**
* Set Type pointer to known type.
* @param t type
*/
void type(const Type * t) {
mType = t;
}
/** @brief variable name token */
const Token *mNameToken;
/** @brief variable type start token */
const Token *mTypeStartToken;
/** @brief variable type end token */
const Token *mTypeEndToken;
/** @brief order declared */
nonneg int mIndex;
/** @brief what section is this variable declared in? */
AccessControl mAccess; // public/protected/private
/** @brief flags */
unsigned int mFlags;
/** @brief pointer to user defined type info (for known types) */
const Type *mType;
/** @brief pointer to scope this variable is in */
const Scope *mScope;
const ValueType* mValueType{};
/** @brief array dimensions */
std::vector<Dimension> mDimensions;
/** @brief fill in information, depending on Tokens given at instantiation */
void evaluate(const Settings* settings);
};
class CPPCHECKLIB Function {
// only symbol database can change this
friend class SymbolDatabase;
/** @brief flags mask used to access specific bit. */
enum {
fHasBody = (1 << 0), ///< @brief has implementation
fIsInline = (1 << 1), ///< @brief implementation in class definition
fIsConst = (1 << 2), ///< @brief is const
fHasVirtualSpecifier = (1 << 3), ///< @brief does declaration contain 'virtual' specifier
fIsPure = (1 << 4), ///< @brief is pure virtual
fIsStatic = (1 << 5), ///< @brief is static
fIsStaticLocal = (1 << 6), ///< @brief is static local
fIsExtern = (1 << 7), ///< @brief is extern
fIsFriend = (1 << 8), ///< @brief is friend
fIsExplicit = (1 << 9), ///< @brief is explicit
fIsDefault = (1 << 10), ///< @brief is default
fIsDelete = (1 << 11), ///< @brief is delete
fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier?
fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier?
fIsNoExcept = (1 << 14), ///< @brief is noexcept
fIsThrow = (1 << 15), ///< @brief is throw
fIsOperator = (1 << 16), ///< @brief is operator
fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier
fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier
fIsVariadic = (1 << 19), ///< @brief is variadic
fIsVolatile = (1 << 20), ///< @brief is volatile
fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type
fIsEscapeFunction = (1 << 22), ///< @brief Function throws or exits
fIsInlineKeyword = (1 << 23), ///< @brief Function has "inline" keyword
fIsConstexpr = (1 << 24), ///< @brief is constexpr
};
/**
* Get specified flag state.
* @param flag flag to get state of
* @return true if flag set or false in flag not set
*/
bool getFlag(unsigned int flag) const {
return ((mFlags & flag) != 0);
}
/**
* Set specified flag state.
* @param flag flag to set state
* @param state new state of flag
*/
void setFlag(unsigned int flag, bool state) {
mFlags = state ? mFlags | flag : mFlags & ~flag;
}
public:
enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction, eLambda };
Function(const Tokenizer *mTokenizer, const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef);
Function(const Token *tokenDef, const std::string &clangType);
const std::string &name() const {
return tokenDef->str();
}
std::string fullName() const;
nonneg int argCount() const {
return argumentList.size();
}
nonneg int minArgCount() const {
return argumentList.size() - initArgCount;
}
const Variable* getArgumentVar(nonneg int num) const;
nonneg int initializedArgCount() const {
return initArgCount;
}
void addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope);
/** @brief check if this function is virtual in the base classes */
bool isImplicitlyVirtual(bool defaultVal = false) const;
std::vector<const Function*> getOverloadedFunctions() const;
/** @brief get function in base class that is overridden */
const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const;
bool isLambda() const {
return type==eLambda;
}
bool isConstructor() const {
return type==eConstructor ||
type==eCopyConstructor ||
type==eMoveConstructor;
}
bool isDestructor() const {
return type==eDestructor;
}
bool isAttributeConstructor() const {
return tokenDef->isAttributeConstructor();
}
bool isAttributeDestructor() const {
return tokenDef->isAttributeDestructor();
}
bool isAttributePure() const {
return tokenDef->isAttributePure();
}
bool isAttributeConst() const {
return tokenDef->isAttributeConst();
}
bool isAttributeNoreturn() const {
return tokenDef->isAttributeNoreturn();
}
bool isAttributeNothrow() const {
return tokenDef->isAttributeNothrow();
}
bool isAttributeNodiscard() const {
return tokenDef->isAttributeNodiscard();
}
bool hasBody() const {
return getFlag(fHasBody);
}
bool isInline() const {
return getFlag(fIsInline);
}
bool isConst() const {
return getFlag(fIsConst);
}
bool hasVirtualSpecifier() const {
return getFlag(fHasVirtualSpecifier);
}
bool isPure() const {
return getFlag(fIsPure);
}
bool isStatic() const {
return getFlag(fIsStatic);
}
bool isStaticLocal() const {
return getFlag(fIsStaticLocal);
}
bool isExtern() const {
return getFlag(fIsExtern);
}
bool isFriend() const {
return getFlag(fIsFriend);
}
bool isExplicit() const {
return getFlag(fIsExplicit);
}
bool isDefault() const {
return getFlag(fIsDefault);
}
bool isDelete() const {
return getFlag(fIsDelete);
}
bool isNoExcept() const {
return getFlag(fIsNoExcept);
}
bool isThrow() const {
return getFlag(fIsThrow);
}
bool hasOverrideSpecifier() const {
return getFlag(fHasOverrideSpecifier);
}
bool hasFinalSpecifier() const {
return getFlag(fHasFinalSpecifier);
}
bool isOperator() const {
return getFlag(fIsOperator);
}
bool hasLvalRefQualifier() const {
return getFlag(fHasLvalRefQual);
}
bool hasRvalRefQualifier() const {
return getFlag(fHasRvalRefQual);
}
bool isVariadic() const {
return getFlag(fIsVariadic);
}
bool isVolatile() const {
return getFlag(fIsVolatile);
}
bool hasTrailingReturnType() const {
return getFlag(fHasTrailingReturnType);
}
void hasBody(bool state) {
setFlag(fHasBody, state);
}
bool isInlineKeyword() const {
return getFlag(fIsInlineKeyword);
}
bool isEscapeFunction() const {
return getFlag(fIsEscapeFunction);
}
void isEscapeFunction(bool state) {
setFlag(fIsEscapeFunction, state);
}
bool isConstexpr() const {
return getFlag(fIsConstexpr);
}
void isConstexpr(bool state) {
setFlag(fIsConstexpr, state);
}
bool isSafe(const Settings *settings) const;
const Token* tokenDef{}; ///< function name token in class definition
const Token* argDef{}; ///< function argument start '(' in class definition
const Token* token{}; ///< function name token in implementation
const Token* arg{}; ///< function argument start '('
const Token* retDef{}; ///< function return type token
const ::Type* retType{}; ///< function return type
const Scope* functionScope{}; ///< scope of function body
const Scope* nestedIn{}; ///< Scope the function is declared in
std::list<Variable> argumentList; ///< argument list, must remain list due to clangimport usage!
nonneg int initArgCount{}; ///< number of args with default values
Type type = eFunction; ///< constructor, destructor, ...
const Token* noexceptArg{}; ///< noexcept token
const Token* throwArg{}; ///< throw token
const Token* templateDef{}; ///< points to 'template <' before function
const Token* functionPointerUsage{}; ///< function pointer usage
AccessControl access{}; ///< public/protected/private
bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const;
static bool returnsConst(const Function* function, bool unknown = false);
static bool returnsPointer(const Function* function, bool unknown = false);
static bool returnsReference(const Function* function, bool unknown = false, bool includeRValueRef = false);
static bool returnsStandardType(const Function* function, bool unknown = false);
static bool returnsVoid(const Function* function, bool unknown = false);
static std::vector<const Token*> findReturns(const Function* f);
const Token* returnDefEnd() const {
if (this->hasTrailingReturnType())
return Token::findmatch(retDef, "{|;");
return tokenDef;
}
/**
* @return token to ":" if the function is a constructor
* and it contains member initialization otherwise a nullptr is returned
*/
const Token * constructorMemberInitialization() const;
private:
/** Recursively determine if this function overrides a virtual function in a base class */
const Function * getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const;
unsigned int mFlags{};
void isInline(bool state) {
setFlag(fIsInline, state);
}
void isConst(bool state) {
setFlag(fIsConst, state);
}
void hasVirtualSpecifier(bool state) {
setFlag(fHasVirtualSpecifier, state);
}
void isPure(bool state) {
setFlag(fIsPure, state);
}
void isStatic(bool state) {
setFlag(fIsStatic, state);
}
void isStaticLocal(bool state) {
setFlag(fIsStaticLocal, state);
}
void isExtern(bool state) {
setFlag(fIsExtern, state);
}
void isFriend(bool state) {
setFlag(fIsFriend, state);
}
void isExplicit(bool state) {
setFlag(fIsExplicit, state);
}
void isDefault(bool state) {
setFlag(fIsDefault, state);
}
void isDelete(bool state) {
setFlag(fIsDelete, state);
}
void isNoExcept(bool state) {
setFlag(fIsNoExcept, state);
}
void isThrow(bool state) {
setFlag(fIsThrow, state);
}
void isOperator(bool state) {
setFlag(fIsOperator, state);
}
void hasLvalRefQualifier(bool state) {
setFlag(fHasLvalRefQual, state);
}
void hasRvalRefQualifier(bool state) {
setFlag(fHasRvalRefQual, state);
}
void isVariadic(bool state) {
setFlag(fIsVariadic, state);
}
void isVolatile(bool state) {
setFlag(fIsVolatile, state);
}
void hasTrailingReturnType(bool state) {
return setFlag(fHasTrailingReturnType, state);
}
void isInlineKeyword(bool state) {
setFlag(fIsInlineKeyword, state);
}
const Token *setFlags(const Token *tok1, const Scope *scope);
};
class CPPCHECKLIB Scope {
// let tests access private function for testing
friend class TestSymbolDatabase;
public:
struct UsingInfo {
const Token *start;
const Scope *scope;
};
enum ScopeType { eGlobal, eClass, eStruct, eUnion, eNamespace, eFunction, eIf, eElse, eFor, eWhile, eDo, eSwitch, eUnconditional, eTry, eCatch, eLambda, eEnum };
Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_);
Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_);
const SymbolDatabase* check{};
std::string className;
const Token* classDef{}; ///< class/struct/union/namespace token
const Token* bodyStart{}; ///< '{' token
const Token* bodyEnd{}; ///< '}' token
std::list<Function> functionList;
std::multimap<std::string, const Function *> functionMap;
std::list<Variable> varlist;
const Scope* nestedIn{};
std::vector<Scope *> nestedList;
nonneg int numConstructors{};
nonneg int numCopyOrMoveConstructors{};
std::vector<UsingInfo> usingList;
ScopeType type{};
Type* definedType{};
std::map<std::string, Type*> definedTypesMap;
std::vector<const Token *> bodyStartList;
// function specific fields
const Scope* functionOf{}; ///< scope this function belongs to
Function* function{}; ///< function info for this function
// enum specific fields
const Token* enumType{};
bool enumClass{};
std::vector<Enumerator> enumeratorList;
void setBodyStartEnd(const Token *start) {
bodyStart = start;
bodyEnd = start ? start->link() : nullptr;
if (start)
bodyStartList.push_back(start);
}
bool isAnonymous() const {
// TODO: Check if class/struct is anonymous
return className.size() > 9 && startsWith(className,"Anonymous") && std::isdigit(className[9]);
}
const Enumerator * findEnumerator(const std::string & name) const {
auto it = std::find_if(enumeratorList.cbegin(), enumeratorList.cend(), [&](const Enumerator& i) {
return i.name->str() == name;
});
return it == enumeratorList.end() ? nullptr : &*it;
}
bool isNestedIn(const Scope * outer) const {
if (!outer)
return false;
if (outer == this)
return true;
const Scope * parent = nestedIn;
while (outer != parent && parent)
parent = parent->nestedIn;
return parent && parent == outer;
}
static Function* nestedInFunction(const Scope* scope) {
while (scope) {
if (scope->type == Scope::eFunction)
break;
scope = scope->nestedIn;
}
if (!scope)
return nullptr;
return scope->function;
}
bool isClassOrStruct() const {
return (type == eClass || type == eStruct);
}
bool isClassOrStructOrUnion() const {
return (type == eClass || type == eStruct || type == eUnion);
}
bool isExecutable() const {
return type != eClass && type != eStruct && type != eUnion && type != eGlobal && type != eNamespace && type != eEnum;
}
bool isLoopScope() const {
return type == Scope::ScopeType::eFor || type == Scope::ScopeType::eWhile || type == Scope::ScopeType::eDo;
}
bool isLocal() const {
return (type == eIf || type == eElse ||
type == eFor || type == eWhile || type == eDo ||
type == eSwitch || type == eUnconditional ||
type == eTry || type == eCatch);
}
// Is there lambda/inline function(s) in this scope?
bool hasInlineOrLambdaFunction() const;
/**
* @brief find a function
* @param tok token of function call
* @param requireConst if const refers to a const variable only const methods should be matched
* @return pointer to function if found or NULL if not found
*/
const Function *findFunction(const Token *tok, bool requireConst=false) const;
const Scope *findRecordInNestedList(const std::string & name, bool isC = false) const;
Scope *findRecordInNestedList(const std::string & name) {
return const_cast<Scope *>(const_cast<const Scope *>(this)->findRecordInNestedList(name));
}
const Type* findType(const std::string& name) const;
Type* findType(const std::string& name) {
return const_cast<Type*>(const_cast<const Scope *>(this)->findType(name));
}
/**
* @brief find if name is in nested list
* @param name name of nested scope
*/
Scope *findInNestedListRecursive(const std::string & name);
void addVariable(const Token *token_, const Token *start_,
const Token *end_, AccessControl access_, const Type *type_,
const Scope *scope_, const Settings* settings);
/** @brief initialize varlist */
void getVariableList(const Settings* settings);
const Function *getDestructor() const;
void addFunction(Function func) {
functionList.push_back(std::move(func));
const Function * back = &functionList.back();
functionMap.insert(make_pair(back->tokenDef->str(), back));
}
AccessControl defaultAccess() const;
/**
* @brief check if statement is variable declaration and add it if it is
* @param tok pointer to start of statement
* @param varaccess access control of statement
* @param settings Settings
* @return pointer to last token
*/
const Token *checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings);
/**
* @brief get variable from name
* @param varname name of variable
* @return pointer to variable
*/
const Variable *getVariable(const std::string &varname) const;
const Token * addEnum(const Token * tok, bool isCpp);
const Scope *findRecordInBase(const std::string &name) const;
std::vector<const Scope*> findAssociatedScopes() const;
private:
/**
* @brief helper function for getVariableList()
* @param tok pointer to token to check
* @param vartok populated with pointer to the variable token, if found
* @param typetok populated with pointer to the type token, if found
* @return true if tok points to a variable declaration, false otherwise
*/
bool isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const;
void findFunctionInBase(const std::string & name, nonneg int args, std::vector<const Function *> & matches) const;
/** @brief initialize varlist */
void getVariableList(const Settings* settings, const Token *start, const Token *end);
};
enum class Reference {
None,
LValue,
RValue
};
/** Value type */
class CPPCHECKLIB ValueType {
public:
enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign = UNKNOWN_SIGN;
enum Type {
UNKNOWN_TYPE,
POD,
NONSTD,
RECORD,
SMART_POINTER,
CONTAINER,
ITERATOR,
VOID,
BOOL,
CHAR,
SHORT,
WCHAR_T,
INT,
LONG,
LONGLONG,
UNKNOWN_INT,
FLOAT,
DOUBLE,
LONGDOUBLE
} type = UNKNOWN_TYPE;
nonneg int bits{}; ///< bitfield bitcount
nonneg int pointer{}; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc
nonneg int constness{}; ///< bit 0=data, bit 1=*, bit 2=**
Reference reference = Reference::None; ///< Is the outermost indirection of this type a reference or rvalue
///< reference or not? pointer=2, Reference=LValue would be a T**&
const Scope* typeScope{}; ///< if the type definition is seen this point out the type scope
const ::Type* smartPointerType{}; ///< Smart pointer type
const Token* smartPointerTypeToken{}; ///< Smart pointer type token
const Library::SmartPointer* smartPointer{}; ///< Smart pointer
const Library::Container* container{}; ///< If the type is a container defined in a cfg file, this is the used
///< container
const Token* containerTypeToken{}; ///< The container type token. the template argument token that defines the
///< container element type.
std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t"
///< when type is CHAR.
ErrorPath debugPath; ///< debug path to the type
ValueType() = default;
ValueType(Sign s, Type t, nonneg int p)
: sign(s),
type(t),
pointer(p)
{}
ValueType(Sign s, Type t, nonneg int p, nonneg int c)
: sign(s),
type(t),
pointer(p),
constness(c)
{}
ValueType(Sign s, Type t, nonneg int p, nonneg int c, std::string otn)
: sign(s),
type(t),
pointer(p),
constness(c),
originalTypeName(std::move(otn))
{}
static ValueType parseDecl(const Token *type, const Settings &settings);
static Type typeFromString(const std::string &typestr, bool longType);
enum class MatchResult { UNKNOWN, SAME, FALLBACK1, FALLBACK2, NOMATCH };
static MatchResult matchParameter(const ValueType *call, const ValueType *func);
static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar);
bool isPrimitive() const {
return (type >= ValueType::Type::BOOL);
}
bool isIntegral() const {
return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT);
}
bool isFloat() const {
return (type >= ValueType::Type::FLOAT && type <= ValueType::Type::LONGDOUBLE);
}
bool fromLibraryType(const std::string &typestr, const Settings &settings);
bool isEnum() const {
return typeScope && typeScope->type == Scope::eEnum;
}
bool isConst(nonneg int indirect = 0) const;
MathLib::bigint typeSize(const cppcheck::Platform &platform, bool p=false) const;
/// Check if type is the same ignoring const and references
bool isTypeEqual(const ValueType* that) const;
std::string str() const;
std::string dump() const;
void setDebugPath(const Token* tok, SourceLocation ctx, SourceLocation local = SourceLocation::current());
};
class CPPCHECKLIB SymbolDatabase {
friend class TestSymbolDatabase;
public:
SymbolDatabase(Tokenizer& tokenizer, const Settings& settings, ErrorLogger* errorLogger);
~SymbolDatabase();
/** @brief Information about all namespaces/classes/structures */
std::list<Scope> scopeList;
/** @brief Fast access to function scopes */
std::vector<const Scope*> functionScopes;
/** @brief Fast access to class and struct scopes */
std::vector<const Scope*> classAndStructScopes;
/** @brief Fast access to types */
std::list<Type> typeList;
/**
* @brief find a variable type if it's a user defined type
* @param start scope to start looking in
* @param typeTok token containing variable type
* @return pointer to type if found or NULL if not found
*/
const Type* findVariableType(const Scope* start, const Token* typeTok) const;
/**
* @brief find a function
* @param tok token of function call
* @return pointer to function if found or NULL if not found
*/
const Function* findFunction(const Token* tok) const;
/** For unit testing only */
const Scope* findScopeByName(const std::string& name) const;
const Type* findType(const Token* startTok, const Scope* startScope, bool lookOutside = false) const;
Type* findType(const Token* startTok, Scope* startScope, bool lookOutside = false)
{
return const_cast<Type*>(this->findType(startTok, const_cast<const Scope*>(startScope), lookOutside));
}
const Scope *findScope(const Token *tok, const Scope *startScope) const;
Scope *findScope(const Token *tok, Scope *startScope) {
return const_cast<Scope *>(this->findScope(tok, const_cast<const Scope *>(startScope)));
}
// cppcheck-suppress unusedFunction
bool isVarId(nonneg int varid) const {
return varid < mVariableList.size();
}
const Variable *getVariableFromVarId(nonneg int varId) const {
return mVariableList.at(varId);
}
const std::vector<const Variable *> & variableList() const {
return mVariableList;
}
/**
* @brief output a debug message
*/
void debugMessage(const Token *tok, const std::string &type, const std::string &msg) const;
void printOut(const char * title = nullptr) const;
void printVariable(const Variable *var, const char *indent) const;
void printXml(std::ostream &out) const;
bool isCPP() const;
/*
* @brief Do a sanity check
*/
void validate() const;
void validateExecutableScopes() const;
/**
* @brief Check variable list, e.g. variables w/o scope
*/
void validateVariables() const;
/** Set valuetype in provided tokenlist */
void setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens=nullptr);
/**
* Calculates sizeof value for given type.
* @param type Token which will contain e.g. "int", "*", or string.
* @return sizeof for given type, or 0 if it can't be calculated.
*/
nonneg int sizeOfType(const Token *type) const;
/** Set array dimensions when valueflow analysis is completed */
void setArrayDimensionsUsingValueFlow(); // cppcheck-suppress functionConst // has side effects
void clangSetVariables(const std::vector<const Variable *> &variableList);
void createSymbolDatabaseExprIds();
private:
friend class Scope;
friend class Function;
// Create symboldatabase...
void createSymbolDatabaseFindAllScopes();
void createSymbolDatabaseClassInfo();
void createSymbolDatabaseVariableInfo();
void createSymbolDatabaseCopyAndMoveConstructors();
void createSymbolDatabaseFunctionScopes();
void createSymbolDatabaseClassAndStructScopes();
void createSymbolDatabaseFunctionReturnTypes();
void createSymbolDatabaseNeedInitialization();
void createSymbolDatabaseVariableSymbolTable();
void createSymbolDatabaseSetScopePointers();
void createSymbolDatabaseSetFunctionPointers(bool firstPass); // cppcheck-suppress functionConst // has side effects
void createSymbolDatabaseSetVariablePointers();
// cppcheck-suppress functionConst
void createSymbolDatabaseSetTypePointers();
void createSymbolDatabaseSetSmartPointerType();
void createSymbolDatabaseEnums(); // cppcheck-suppress functionConst // has side effects
void createSymbolDatabaseEscapeFunctions(); // cppcheck-suppress functionConst // has side effects
// cppcheck-suppress functionConst
void createSymbolDatabaseIncompleteVars();
void debugSymbolDatabase() const;
void addClassFunction(Scope **scope, const Token **tok, const Token *argStart);
Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart);
Function *addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart);
void addNewFunction(Scope **scope, const Token **tok);
bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const;
const Type *findTypeInNested(const Token *startTok, const Scope *startScope) const;
const Scope *findNamespace(const Token * tok, const Scope * scope) const;
static Function *findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length);
static const Type *findVariableTypeInBase(const Scope *scope, const Token *typeTok);
using MemberIdMap = std::map<unsigned int, unsigned int>;
using VarIdMap = std::map<unsigned int, MemberIdMap>;
void fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar);
/** Whether iName is a keyword as defined in http://en.cppreference.com/w/c/keyword and http://en.cppreference.com/w/cpp/keyword*/
bool isReservedName(const std::string& iName) const;
const Enumerator * findEnumerator(const Token * tok, std::set<std::string>& tokensThatAreNotEnumeratorValues) const;
void setValueType(Token* tok, const ValueType& valuetype, SourceLocation loc = SourceLocation::current());
void setValueType(Token* tok, const Variable& var, SourceLocation loc = SourceLocation::current());
void setValueType(Token* tok, const Enumerator& enumerator, SourceLocation loc = SourceLocation::current());
Tokenizer& mTokenizer;
const Settings &mSettings;
ErrorLogger *mErrorLogger;
/** variable symbol table */
std::vector<const Variable *> mVariableList;
/** list for missing types */
std::list<Type> mBlankTypes;
bool mIsCpp;
ValueType::Sign mDefaultSignedness;
};
//---------------------------------------------------------------------------
#endif // symboldatabaseH