* misra: Fix 8.2 false positives Fix false positives in rule 8.2 that occurred in cases when we have a function definition and declaration in the same file. For example, the following code generated false positives before this commit: ``` void f(uint8_t * const x); void f(uint8_t * const x) { (void)x; } ``` We need to distinguish the declaration and the definition, so the dump file generation routine was extended to keep token where the definition of the function. The analysis in the addon also been improved. Closes Trac issue: https://trac.cppcheck.net/ticket/10219
6923 lines
264 KiB
C++
6923 lines
264 KiB
C++
/*
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
|
* Copyright (C) 2007-2021 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/>.
|
|
*/
|
|
|
|
//---------------------------------------------------------------------------
|
|
#include "symboldatabase.h"
|
|
|
|
#include "astutils.h"
|
|
#include "errorlogger.h"
|
|
#include "library.h"
|
|
#include "mathlib.h"
|
|
#include "platform.h"
|
|
#include "settings.h"
|
|
#include "token.h"
|
|
#include "tokenize.h"
|
|
#include "tokenlist.h"
|
|
#include "utils.h"
|
|
#include "valueflow.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
#include <cstring>
|
|
#include <iomanip>
|
|
#include <iostream>
|
|
#include <limits>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
//---------------------------------------------------------------------------
|
|
|
|
SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger)
|
|
: mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger)
|
|
{
|
|
if (!tokenizer || !tokenizer->tokens())
|
|
return;
|
|
|
|
mIsCpp = isCPP();
|
|
|
|
if (mSettings->defaultSign == 's' || mSettings->defaultSign == 'S')
|
|
mDefaultSignedness = ValueType::SIGNED;
|
|
else if (mSettings->defaultSign == 'u' || mSettings->defaultSign == 'U')
|
|
mDefaultSignedness = ValueType::UNSIGNED;
|
|
else
|
|
mDefaultSignedness = ValueType::UNKNOWN_SIGN;
|
|
|
|
createSymbolDatabaseFindAllScopes();
|
|
createSymbolDatabaseClassInfo();
|
|
createSymbolDatabaseVariableInfo();
|
|
createSymbolDatabaseCopyAndMoveConstructors();
|
|
createSymbolDatabaseFunctionScopes();
|
|
createSymbolDatabaseClassAndStructScopes();
|
|
createSymbolDatabaseFunctionReturnTypes();
|
|
createSymbolDatabaseNeedInitialization();
|
|
createSymbolDatabaseVariableSymbolTable();
|
|
createSymbolDatabaseSetScopePointers();
|
|
createSymbolDatabaseSetVariablePointers();
|
|
setValueTypeInTokenList(false);
|
|
createSymbolDatabaseSetFunctionPointers(true);
|
|
createSymbolDatabaseSetTypePointers();
|
|
createSymbolDatabaseSetSmartPointerType();
|
|
createSymbolDatabaseEnums();
|
|
createSymbolDatabaseEscapeFunctions();
|
|
createSymbolDatabaseIncompleteVars();
|
|
createSymbolDatabaseExprIds();
|
|
}
|
|
|
|
static const Token* skipScopeIdentifiers(const Token* tok)
|
|
{
|
|
if (Token::Match(tok, ":: %name%"))
|
|
tok = tok->next();
|
|
while (Token::Match(tok, "%name% ::") ||
|
|
(Token::Match(tok, "%name% <") && Token::Match(tok->linkAt(1), ">|>> ::"))) {
|
|
if (tok->strAt(1) == "::")
|
|
tok = tok->tokAt(2);
|
|
else
|
|
tok = tok->linkAt(1)->tokAt(2);
|
|
}
|
|
|
|
return tok;
|
|
}
|
|
|
|
static bool isExecutableScope(const Token* tok)
|
|
{
|
|
if (!Token::simpleMatch(tok, "{"))
|
|
return false;
|
|
const Token * tok2 = tok->link()->previous();
|
|
if (Token::simpleMatch(tok2, "; }"))
|
|
return true;
|
|
if (Token::Match(tok2, "{|} }")) {
|
|
const Token* startTok = tok2->str() == "{" ? tok2 : tok2->link();
|
|
if (Token::Match(startTok->previous(), "do|try|else {"))
|
|
return true;
|
|
if (Token::simpleMatch(startTok->previous(), ") {"))
|
|
return !findLambdaStartToken(tok2);
|
|
if (tok->str() == "{")
|
|
return false;
|
|
else
|
|
return isExecutableScope(startTok);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseFindAllScopes()
|
|
{
|
|
// create global scope
|
|
scopeList.emplace_back(this, nullptr, nullptr);
|
|
|
|
// pointer to current scope
|
|
Scope *scope = &scopeList.back();
|
|
|
|
// Store current access in each scope (depends on evaluation progress)
|
|
std::map<const Scope*, AccessControl> access;
|
|
|
|
// find all scopes
|
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok ? tok->next() : nullptr) {
|
|
// #5593 suggested to add here:
|
|
if (mErrorLogger)
|
|
mErrorLogger->reportProgress(mTokenizer->list.getSourceFilePath(),
|
|
"SymbolDatabase",
|
|
tok->progressValue());
|
|
// Locate next class
|
|
if ((mTokenizer->isCPP() && tok->isKeyword() &&
|
|
((Token::Match(tok, "class|struct|union|namespace ::| %name% {|:|::|<") &&
|
|
!Token::Match(tok->previous(), "new|friend|const|enum|typedef|mutable|volatile|using|)|(|<")) ||
|
|
(Token::Match(tok, "enum class| %name% {") ||
|
|
Token::Match(tok, "enum class| %name% : %name% {"))))
|
|
|| (mTokenizer->isC() && tok->isKeyword() && Token::Match(tok, "struct|union|enum %name% {"))) {
|
|
const Token *tok2 = tok->tokAt(2);
|
|
|
|
if (tok->strAt(1) == "::")
|
|
tok2 = tok2->next();
|
|
else if (mTokenizer->isCPP() && tok->strAt(1) == "class")
|
|
tok2 = tok2->next();
|
|
|
|
while (Token::Match(tok2, ":: %name%"))
|
|
tok2 = tok2->tokAt(2);
|
|
while (Token::Match(tok2, "%name% :: %name%"))
|
|
tok2 = tok2->tokAt(2);
|
|
|
|
// skip over template args
|
|
while (tok2 && tok2->str() == "<" && tok2->link()) {
|
|
tok2 = tok2->link()->next();
|
|
while (Token::Match(tok2, ":: %name%"))
|
|
tok2 = tok2->tokAt(2);
|
|
}
|
|
|
|
// skip over final
|
|
if (mTokenizer->isCPP() && Token::simpleMatch(tok2, "final"))
|
|
tok2 = tok2->next();
|
|
|
|
// make sure we have valid code
|
|
if (!Token::Match(tok2, "{|:")) {
|
|
// check for qualified variable
|
|
if (tok2 && tok2->next()) {
|
|
if (tok2->next()->str() == ";")
|
|
tok = tok2->next();
|
|
else if (Token::simpleMatch(tok2->next(), "= {") &&
|
|
Token::simpleMatch(tok2->linkAt(2), "} ;"))
|
|
tok = tok2->linkAt(2)->next();
|
|
else if (Token::Match(tok2->next(), "(|{") &&
|
|
tok2->next()->link()->strAt(1) == ";")
|
|
tok = tok2->next()->link()->next();
|
|
// skip variable declaration
|
|
else if (Token::Match(tok2, "*|&|>"))
|
|
continue;
|
|
else if (Token::Match(tok2, "%name% (") && mTokenizer->isFunctionHead(tok2->next(), "{;"))
|
|
continue;
|
|
else if (Token::Match(tok2, "%name% ["))
|
|
continue;
|
|
// skip template
|
|
else if (Token::simpleMatch(tok2, ";") &&
|
|
Token::Match(tok->previous(), "template|> class|struct")) {
|
|
tok = tok2;
|
|
continue;
|
|
}
|
|
// forward declaration
|
|
else if (Token::simpleMatch(tok2, ";") &&
|
|
Token::Match(tok, "class|struct|union")) {
|
|
// TODO: see if it can be used
|
|
tok = tok2;
|
|
continue;
|
|
}
|
|
// skip constructor
|
|
else if (Token::simpleMatch(tok2, "(") &&
|
|
Token::simpleMatch(tok2->link(), ") ;")) {
|
|
tok = tok2->link()->next();
|
|
continue;
|
|
} else
|
|
throw InternalError(tok2, "SymbolDatabase bailout; unhandled code", InternalError::SYNTAX);
|
|
continue;
|
|
}
|
|
break; // bail
|
|
}
|
|
|
|
const Token * name = tok->next();
|
|
|
|
if (name->str() == "class" && name->strAt(-1) == "enum")
|
|
name = name->next();
|
|
|
|
Scope *new_scope = findScope(name, scope);
|
|
|
|
if (new_scope) {
|
|
// only create base list for classes and structures
|
|
if (new_scope->isClassOrStruct()) {
|
|
// goto initial '{'
|
|
if (!new_scope->definedType)
|
|
mTokenizer->syntaxError(nullptr); // #6808
|
|
tok2 = new_scope->definedType->initBaseInfo(tok, tok2);
|
|
// make sure we have valid code
|
|
if (!tok2) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// definition may be different than declaration
|
|
if (mTokenizer->isCPP() && tok->str() == "class") {
|
|
access[new_scope] = AccessControl::Private;
|
|
new_scope->type = Scope::eClass;
|
|
} else if (tok->str() == "struct") {
|
|
access[new_scope] = AccessControl::Public;
|
|
new_scope->type = Scope::eStruct;
|
|
}
|
|
|
|
new_scope->classDef = tok;
|
|
new_scope->bodyStart = tok2;
|
|
new_scope->bodyEnd = tok2->link();
|
|
// make sure we have valid code
|
|
if (!new_scope->bodyEnd) {
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
scope = new_scope;
|
|
tok = tok2;
|
|
} else {
|
|
scopeList.emplace_back(this, tok, scope);
|
|
new_scope = &scopeList.back();
|
|
|
|
if (tok->str() == "class")
|
|
access[new_scope] = AccessControl::Private;
|
|
else if (tok->str() == "struct" || tok->str() == "union")
|
|
access[new_scope] = AccessControl::Public;
|
|
|
|
// fill typeList...
|
|
if (new_scope->isClassOrStructOrUnion() || new_scope->type == Scope::eEnum) {
|
|
Type* new_type = findType(name, scope);
|
|
if (!new_type) {
|
|
typeList.emplace_back(new_scope->classDef, new_scope, scope);
|
|
new_type = &typeList.back();
|
|
scope->definedTypesMap[new_type->name()] = new_type;
|
|
} else
|
|
new_type->classScope = new_scope;
|
|
new_scope->definedType = new_type;
|
|
}
|
|
|
|
// only create base list for classes and structures
|
|
if (new_scope->isClassOrStruct()) {
|
|
// goto initial '{'
|
|
tok2 = new_scope->definedType->initBaseInfo(tok, tok2);
|
|
|
|
// make sure we have valid code
|
|
if (!tok2) {
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
} else if (new_scope->type == Scope::eEnum) {
|
|
if (tok2->str() == ":")
|
|
tok2 = tok2->tokAt(2);
|
|
}
|
|
|
|
new_scope->bodyStart = tok2;
|
|
new_scope->bodyEnd = tok2->link();
|
|
|
|
// make sure we have valid code
|
|
if (!new_scope->bodyEnd) {
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
|
|
if (new_scope->type == Scope::eEnum) {
|
|
tok2 = new_scope->addEnum(tok, mTokenizer->isCPP());
|
|
scope->nestedList.push_back(new_scope);
|
|
|
|
if (!tok2)
|
|
mTokenizer->syntaxError(tok);
|
|
} else {
|
|
// make the new scope the current scope
|
|
scope->nestedList.push_back(new_scope);
|
|
scope = new_scope;
|
|
}
|
|
|
|
tok = tok2;
|
|
}
|
|
}
|
|
|
|
// Namespace and unknown macro (#3854)
|
|
else if (mTokenizer->isCPP() && tok->isKeyword() &&
|
|
Token::Match(tok, "namespace %name% %type% (") &&
|
|
tok->tokAt(2)->isUpperCaseName() &&
|
|
Token::simpleMatch(tok->linkAt(3), ") {")) {
|
|
scopeList.emplace_back(this, tok, scope);
|
|
|
|
Scope *new_scope = &scopeList.back();
|
|
access[new_scope] = AccessControl::Public;
|
|
|
|
const Token *tok2 = tok->linkAt(3)->next();
|
|
|
|
new_scope->bodyStart = tok2;
|
|
new_scope->bodyEnd = tok2->link();
|
|
|
|
// make sure we have valid code
|
|
if (!new_scope->bodyEnd) {
|
|
scopeList.pop_back();
|
|
break;
|
|
}
|
|
|
|
// make the new scope the current scope
|
|
scope->nestedList.push_back(new_scope);
|
|
scope = &scopeList.back();
|
|
|
|
tok = tok2;
|
|
}
|
|
|
|
// forward declaration
|
|
else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;") &&
|
|
tok->strAt(-1) != "friend") {
|
|
if (!findType(tok->next(), scope)) {
|
|
// fill typeList..
|
|
typeList.emplace_back(tok, nullptr, scope);
|
|
Type* new_type = &typeList.back();
|
|
scope->definedTypesMap[new_type->name()] = new_type;
|
|
}
|
|
tok = tok->tokAt(2);
|
|
}
|
|
|
|
// using namespace
|
|
else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "using namespace ::| %type% ;|::")) {
|
|
Scope::UsingInfo using_info;
|
|
|
|
using_info.start = tok; // save location
|
|
using_info.scope = findNamespace(tok->tokAt(2), scope);
|
|
|
|
scope->usingList.push_back(using_info);
|
|
|
|
// check for global namespace
|
|
if (tok->strAt(2) == "::")
|
|
tok = tok->tokAt(4);
|
|
else
|
|
tok = tok->tokAt(3);
|
|
|
|
// skip over qualification
|
|
while (Token::Match(tok, "%type% ::"))
|
|
tok = tok->tokAt(2);
|
|
}
|
|
|
|
// using type alias
|
|
else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "using %name% =")) {
|
|
if (tok->strAt(-1) != ">" && !findType(tok->next(), scope)) {
|
|
// fill typeList..
|
|
typeList.emplace_back(tok, nullptr, scope);
|
|
Type* new_type = &typeList.back();
|
|
scope->definedTypesMap[new_type->name()] = new_type;
|
|
}
|
|
|
|
tok = tok->tokAt(3);
|
|
|
|
while (tok && tok->str() != ";")
|
|
tok = tok->next();
|
|
}
|
|
|
|
// unnamed struct and union
|
|
else if (tok->isKeyword() && Token::Match(tok, "struct|union {") &&
|
|
Token::Match(tok->next()->link(), "} *|&| %name% ;|[|=")) {
|
|
scopeList.emplace_back(this, tok, scope);
|
|
|
|
Scope *new_scope = &scopeList.back();
|
|
access[new_scope] = AccessControl::Public;
|
|
|
|
const Token* varNameTok = tok->next()->link()->next();
|
|
if (varNameTok->str() == "*") {
|
|
varNameTok = varNameTok->next();
|
|
} else if (varNameTok->str() == "&") {
|
|
varNameTok = varNameTok->next();
|
|
}
|
|
|
|
typeList.emplace_back(tok, new_scope, scope);
|
|
{
|
|
Type* new_type = &typeList.back();
|
|
new_scope->definedType = new_type;
|
|
scope->definedTypesMap[new_type->name()] = new_type;
|
|
}
|
|
|
|
scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope, mSettings);
|
|
|
|
const Token *tok2 = tok->next();
|
|
|
|
new_scope->bodyStart = tok2;
|
|
new_scope->bodyEnd = tok2->link();
|
|
|
|
// make sure we have valid code
|
|
if (!new_scope->bodyEnd) {
|
|
scopeList.pop_back();
|
|
break;
|
|
}
|
|
|
|
// make the new scope the current scope
|
|
scope->nestedList.push_back(new_scope);
|
|
scope = new_scope;
|
|
|
|
tok = tok2;
|
|
}
|
|
|
|
// anonymous struct, union and namespace
|
|
else if (tok->isKeyword() && ((Token::Match(tok, "struct|union {") &&
|
|
Token::simpleMatch(tok->next()->link(), "} ;")) ||
|
|
Token::simpleMatch(tok, "namespace {"))) {
|
|
scopeList.emplace_back(this, tok, scope);
|
|
|
|
Scope *new_scope = &scopeList.back();
|
|
access[new_scope] = AccessControl::Public;
|
|
|
|
const Token *tok2 = tok->next();
|
|
|
|
new_scope->bodyStart = tok2;
|
|
new_scope->bodyEnd = tok2->link();
|
|
|
|
typeList.emplace_back(tok, new_scope, scope);
|
|
{
|
|
Type* new_type = &typeList.back();
|
|
new_scope->definedType = new_type;
|
|
scope->definedTypesMap[new_type->name()] = new_type;
|
|
}
|
|
|
|
// make sure we have valid code
|
|
if (!new_scope->bodyEnd) {
|
|
scopeList.pop_back();
|
|
break;
|
|
}
|
|
|
|
// make the new scope the current scope
|
|
scope->nestedList.push_back(new_scope);
|
|
scope = new_scope;
|
|
|
|
tok = tok2;
|
|
}
|
|
|
|
// forward declared enum
|
|
else if (tok->isKeyword() && (Token::Match(tok, "enum class| %name% ;") || Token::Match(tok, "enum class| %name% : %name% ;"))) {
|
|
typeList.emplace_back(tok, nullptr, scope);
|
|
Type* new_type = &typeList.back();
|
|
scope->definedTypesMap[new_type->name()] = new_type;
|
|
tok = tok->tokAt(2);
|
|
}
|
|
|
|
// check for end of scope
|
|
else if (tok == scope->bodyEnd) {
|
|
access.erase(scope);
|
|
scope = const_cast<Scope*>(scope->nestedIn);
|
|
continue;
|
|
}
|
|
|
|
// check if in class or structure or union
|
|
else if (scope->isClassOrStructOrUnion()) {
|
|
const Token *funcStart = nullptr;
|
|
const Token *argStart = nullptr;
|
|
const Token *declEnd = nullptr;
|
|
|
|
// What section are we in..
|
|
if (tok->str() == "private:")
|
|
access[scope] = AccessControl::Private;
|
|
else if (tok->str() == "protected:")
|
|
access[scope] = AccessControl::Protected;
|
|
else if (tok->str() == "public:" || tok->str() == "__published:")
|
|
access[scope] = AccessControl::Public;
|
|
else if (Token::Match(tok, "public|protected|private %name% :")) {
|
|
if (tok->str() == "private")
|
|
access[scope] = AccessControl::Private;
|
|
else if (tok->str() == "protected")
|
|
access[scope] = AccessControl::Protected;
|
|
else
|
|
access[scope] = AccessControl::Public;
|
|
|
|
tok = tok->tokAt(2);
|
|
}
|
|
|
|
// class function?
|
|
else if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) {
|
|
if (tok->previous()->str() != "::" || tok->strAt(-2) == scope->className) {
|
|
Function function(mTokenizer, tok, scope, funcStart, argStart);
|
|
|
|
// save the access type
|
|
function.access = access[scope];
|
|
|
|
const Token *end = function.argDef->link();
|
|
|
|
// count the number of constructors
|
|
if (function.isConstructor())
|
|
scope->numConstructors++;
|
|
|
|
// assume implementation is inline (definition and implementation same)
|
|
function.token = function.tokenDef;
|
|
function.arg = function.argDef;
|
|
|
|
// out of line function
|
|
if (const Token *endTok = mTokenizer->isFunctionHead(end, ";")) {
|
|
tok = endTok;
|
|
scope->addFunction(function);
|
|
}
|
|
|
|
// inline function
|
|
else {
|
|
// find start of function '{'
|
|
bool foundInitList = false;
|
|
while (end && end->str() != "{" && end->str() != ";") {
|
|
if (end->link() && Token::Match(end, "(|<")) {
|
|
end = end->link();
|
|
} else if (foundInitList &&
|
|
Token::Match(end, "%name%|> {") &&
|
|
Token::Match(end->linkAt(1), "} ,|{")) {
|
|
end = end->linkAt(1);
|
|
} else {
|
|
if (end->str() == ":")
|
|
foundInitList = true;
|
|
end = end->next();
|
|
}
|
|
}
|
|
|
|
if (!end || end->str() == ";")
|
|
continue;
|
|
|
|
scope->addFunction(function);
|
|
|
|
Function* funcptr = &scope->functionList.back();
|
|
const Token *tok2 = funcStart;
|
|
|
|
addNewFunction(&scope, &tok2);
|
|
if (scope) {
|
|
scope->functionOf = function.nestedIn;
|
|
scope->function = funcptr;
|
|
scope->function->functionScope = scope;
|
|
}
|
|
|
|
tok = tok2;
|
|
}
|
|
}
|
|
|
|
// nested class or friend function?
|
|
else {
|
|
/** @todo check entire qualification for match */
|
|
const Scope * const nested = scope->findInNestedListRecursive(tok->strAt(-2));
|
|
|
|
if (nested)
|
|
addClassFunction(&scope, &tok, argStart);
|
|
else {
|
|
/** @todo handle friend functions */
|
|
}
|
|
}
|
|
}
|
|
|
|
// friend class declaration?
|
|
else if (mTokenizer->isCPP() && tok->isKeyword() && Token::Match(tok, "friend class| ::| %any% ;|::")) {
|
|
Type::FriendInfo friendInfo;
|
|
|
|
// save the name start
|
|
friendInfo.nameStart = tok->strAt(1) == "class" ? tok->tokAt(2) : tok->next();
|
|
friendInfo.nameEnd = friendInfo.nameStart;
|
|
|
|
// skip leading "::"
|
|
if (friendInfo.nameEnd->str() == "::")
|
|
friendInfo.nameEnd = friendInfo.nameEnd->next();
|
|
|
|
// skip qualification "name ::"
|
|
while (friendInfo.nameEnd && friendInfo.nameEnd->strAt(1) == "::")
|
|
friendInfo.nameEnd = friendInfo.nameEnd->tokAt(2);
|
|
|
|
// fill this in after parsing is complete
|
|
friendInfo.type = nullptr;
|
|
|
|
if (!scope->definedType)
|
|
mTokenizer->syntaxError(tok);
|
|
|
|
scope->definedType->friendList.push_back(friendInfo);
|
|
}
|
|
} else if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) {
|
|
const Token *funcStart = nullptr;
|
|
const Token *argStart = nullptr;
|
|
const Token *declEnd = nullptr;
|
|
|
|
// function?
|
|
if (isFunction(tok, scope, &funcStart, &argStart, &declEnd)) {
|
|
// has body?
|
|
if (declEnd && declEnd->str() == "{") {
|
|
tok = funcStart;
|
|
|
|
// class function
|
|
if (tok->previous() && tok->previous()->str() == "::")
|
|
addClassFunction(&scope, &tok, argStart);
|
|
|
|
// class destructor
|
|
else if (tok->previous() &&
|
|
tok->previous()->str() == "~" &&
|
|
tok->strAt(-2) == "::")
|
|
addClassFunction(&scope, &tok, argStart);
|
|
|
|
// regular function
|
|
else {
|
|
const Function* const function = addGlobalFunction(scope, tok, argStart, funcStart);
|
|
|
|
if (!function)
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
|
|
// syntax error?
|
|
if (!scope)
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
// function prototype?
|
|
else if (declEnd && declEnd->str() == ";") {
|
|
if (tok->previous() && tok->previous()->str() == "::" &&
|
|
Token::Match(declEnd->previous(), "default|delete")) {
|
|
addClassFunction(&scope, &tok, argStart);
|
|
continue;
|
|
}
|
|
|
|
bool newFunc = true; // Is this function already in the database?
|
|
for (std::multimap<std::string, const Function *>::const_iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) {
|
|
if (i->second->argsMatch(scope, i->second->argDef, argStart, emptyString, 0)) {
|
|
newFunc = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// save function prototype in database
|
|
if (newFunc) {
|
|
addGlobalFunctionDecl(scope, tok, argStart, funcStart);
|
|
}
|
|
|
|
tok = declEnd;
|
|
continue;
|
|
}
|
|
}
|
|
} else if (scope->isExecutable()) {
|
|
if (tok->isKeyword() && Token::Match(tok, "else|try|do {")) {
|
|
const Token* tok1 = tok->next();
|
|
if (tok->str() == "else")
|
|
scopeList.emplace_back(this, tok, scope, Scope::eElse, tok1);
|
|
else if (tok->str() == "do")
|
|
scopeList.emplace_back(this, tok, scope, Scope::eDo, tok1);
|
|
else //if (tok->str() == "try")
|
|
scopeList.emplace_back(this, tok, scope, Scope::eTry, tok1);
|
|
|
|
tok = tok1;
|
|
scope->nestedList.push_back(&scopeList.back());
|
|
scope = &scopeList.back();
|
|
} else if (tok->isKeyword() && Token::Match(tok, "if|for|while|catch|switch (") && Token::simpleMatch(tok->next()->link(), ") {")) {
|
|
const Token *scopeStartTok = tok->next()->link()->next();
|
|
if (tok->str() == "if")
|
|
scopeList.emplace_back(this, tok, scope, Scope::eIf, scopeStartTok);
|
|
else if (tok->str() == "for") {
|
|
scopeList.emplace_back(this, tok, scope, Scope::eFor, scopeStartTok);
|
|
} else if (tok->str() == "while")
|
|
scopeList.emplace_back(this, tok, scope, Scope::eWhile, scopeStartTok);
|
|
else if (tok->str() == "catch") {
|
|
scopeList.emplace_back(this, tok, scope, Scope::eCatch, scopeStartTok);
|
|
} else // if (tok->str() == "switch")
|
|
scopeList.emplace_back(this, tok, scope, Scope::eSwitch, scopeStartTok);
|
|
|
|
scope->nestedList.push_back(&scopeList.back());
|
|
scope = &scopeList.back();
|
|
if (scope->type == Scope::eFor)
|
|
scope->checkVariable(tok->tokAt(2), AccessControl::Local, mSettings); // check for variable declaration and add it to new scope if found
|
|
else if (scope->type == Scope::eCatch)
|
|
scope->checkVariable(tok->tokAt(2), AccessControl::Throw, mSettings); // check for variable declaration and add it to new scope if found
|
|
tok = scopeStartTok;
|
|
} else if (Token::Match(tok, "%var% {")) {
|
|
tok = tok->linkAt(1);
|
|
} else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) {
|
|
const Token *lambdaStartToken = lambdaEndToken->link();
|
|
const Token * argStart = lambdaStartToken->astParent();
|
|
const Token * funcStart = Token::simpleMatch(argStart, "[") ? argStart : argStart->astParent();
|
|
const Function * function = addGlobalFunction(scope, tok, argStart, funcStart);
|
|
if (!function)
|
|
mTokenizer->syntaxError(tok);
|
|
tok = lambdaStartToken;
|
|
} else if (tok->str() == "{") {
|
|
if (isExecutableScope(tok)) {
|
|
scopeList.emplace_back(this, tok, scope, Scope::eUnconditional, tok);
|
|
scope->nestedList.push_back(&scopeList.back());
|
|
scope = &scopeList.back();
|
|
} else {
|
|
tok = tok->link();
|
|
}
|
|
}
|
|
// syntax error?
|
|
if (!scope)
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseClassInfo()
|
|
{
|
|
if (mTokenizer->isC())
|
|
return;
|
|
|
|
// fill in using info
|
|
for (Scope& scope : scopeList) {
|
|
for (Scope::UsingInfo& usingInfo : scope.usingList) {
|
|
// only find if not already found
|
|
if (usingInfo.scope == nullptr) {
|
|
// check scope for match
|
|
const Scope * const found = findScope(usingInfo.start->tokAt(2), &scope);
|
|
if (found) {
|
|
// set found scope
|
|
usingInfo.scope = found;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill in base class info
|
|
for (Type& type : typeList) {
|
|
// finish filling in base class info
|
|
for (Type::BaseInfo & i : type.derivedFrom) {
|
|
const Type* found = findType(i.nameTok, type.enclosingScope);
|
|
if (found && found->findDependency(&type)) {
|
|
// circular dependency
|
|
//mTokenizer->syntaxError(nullptr);
|
|
} else {
|
|
i.type = found;
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill in friend info
|
|
for (Type & type : typeList) {
|
|
for (Type::FriendInfo &friendInfo : type.friendList) {
|
|
friendInfo.type = findType(friendInfo.nameStart, type.enclosingScope);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void SymbolDatabase::createSymbolDatabaseVariableInfo()
|
|
{
|
|
// fill in variable info
|
|
for (Scope& scope : scopeList) {
|
|
// find variables
|
|
scope.getVariableList(mSettings);
|
|
}
|
|
|
|
// fill in function arguments
|
|
for (Scope& scope : scopeList) {
|
|
std::list<Function>::iterator func;
|
|
|
|
for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) {
|
|
// add arguments
|
|
func->addArguments(this, &scope);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors()
|
|
{
|
|
// fill in class and struct copy/move constructors
|
|
for (Scope& scope : scopeList) {
|
|
if (!scope.isClassOrStruct())
|
|
continue;
|
|
|
|
std::list<Function>::iterator func;
|
|
for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) {
|
|
if (!func->isConstructor() || func->minArgCount() != 1)
|
|
continue;
|
|
|
|
const Variable* firstArg = func->getArgumentVar(0);
|
|
if (firstArg->type() == scope.definedType) {
|
|
if (firstArg->isRValueReference())
|
|
func->type = Function::eMoveConstructor;
|
|
else if (firstArg->isReference() && !firstArg->isPointer())
|
|
func->type = Function::eCopyConstructor;
|
|
}
|
|
|
|
if (func->type == Function::eCopyConstructor ||
|
|
func->type == Function::eMoveConstructor)
|
|
scope.numCopyOrMoveConstructors++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseFunctionScopes()
|
|
{
|
|
// fill in function scopes
|
|
for (Scope & scope : scopeList) {
|
|
if (scope.type == Scope::eFunction)
|
|
functionScopes.push_back(&scope);
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseClassAndStructScopes()
|
|
{
|
|
// fill in class and struct scopes
|
|
for (Scope& scope : scopeList) {
|
|
if (scope.isClassOrStruct())
|
|
classAndStructScopes.push_back(&scope);
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseFunctionReturnTypes()
|
|
{
|
|
// fill in function return types
|
|
for (Scope& scope : scopeList) {
|
|
std::list<Function>::iterator func;
|
|
|
|
for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) {
|
|
// add return types
|
|
if (func->retDef) {
|
|
const Token *type = func->retDef;
|
|
while (Token::Match(type, "static|const|struct|union|enum"))
|
|
type = type->next();
|
|
if (type) {
|
|
func->retType = findVariableTypeInBase(&scope, type);
|
|
if (!func->retType)
|
|
func->retType = findTypeInNested(type, func->nestedIn);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseNeedInitialization()
|
|
{
|
|
if (mTokenizer->isC()) {
|
|
// For C code it is easy, as there are no constructors and no default values
|
|
for (Scope& scope : scopeList) {
|
|
if (scope.definedType)
|
|
scope.definedType->needInitialization = Type::NeedInitialization::True;
|
|
}
|
|
} else {
|
|
// For C++, it is more difficult: Determine if user defined type needs initialization...
|
|
unsigned int unknowns = 0; // stop checking when there are no unknowns
|
|
unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason
|
|
|
|
do {
|
|
unknowns = 0;
|
|
|
|
for (Scope& scope : scopeList) {
|
|
if (!scope.isClassOrStructOrUnion())
|
|
continue;
|
|
|
|
if (!scope.definedType) {
|
|
mBlankTypes.emplace_back();
|
|
scope.definedType = &mBlankTypes.back();
|
|
}
|
|
|
|
if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) {
|
|
// check for default constructor
|
|
bool hasDefaultConstructor = false;
|
|
|
|
std::list<Function>::const_iterator func;
|
|
|
|
for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) {
|
|
if (func->type == Function::eConstructor) {
|
|
// check for no arguments: func ( )
|
|
if (func->argCount() == 0) {
|
|
hasDefaultConstructor = true;
|
|
break;
|
|
}
|
|
|
|
/** check for arguments with default values */
|
|
else if (func->argCount() == func->initializedArgCount()) {
|
|
hasDefaultConstructor = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// User defined types with user defined default constructor doesn't need initialization.
|
|
// We assume the default constructor initializes everything.
|
|
// Another check will figure out if the constructor actually initializes everything.
|
|
if (hasDefaultConstructor)
|
|
scope.definedType->needInitialization = Type::NeedInitialization::False;
|
|
|
|
// check each member variable to see if it needs initialization
|
|
else {
|
|
bool needInitialization = false;
|
|
bool unknown = false;
|
|
|
|
std::list<Variable>::const_iterator var;
|
|
for (var = scope.varlist.begin(); var != scope.varlist.end() && !needInitialization; ++var) {
|
|
if (var->isClass()) {
|
|
if (var->type()) {
|
|
// does this type need initialization?
|
|
if (var->type()->needInitialization == Type::NeedInitialization::True)
|
|
needInitialization = true;
|
|
else if (var->type()->needInitialization == Type::NeedInitialization::Unknown) {
|
|
if (!(var->valueType() && var->valueType()->type == ValueType::CONTAINER))
|
|
unknown = true;
|
|
}
|
|
}
|
|
} else if (!var->hasDefault() && !var->isStatic())
|
|
needInitialization = true;
|
|
}
|
|
|
|
if (needInitialization)
|
|
scope.definedType->needInitialization = Type::NeedInitialization::True;
|
|
else if (!unknown)
|
|
scope.definedType->needInitialization = Type::NeedInitialization::False;
|
|
else {
|
|
if (scope.definedType->needInitialization == Type::NeedInitialization::Unknown)
|
|
unknowns++;
|
|
}
|
|
}
|
|
} else if (scope.type == Scope::eUnion && scope.definedType->needInitialization == Type::NeedInitialization::Unknown)
|
|
scope.definedType->needInitialization = Type::NeedInitialization::True;
|
|
}
|
|
|
|
retry++;
|
|
} while (unknowns && retry < 100);
|
|
|
|
// this shouldn't happen so output a debug warning
|
|
if (retry == 100 && mSettings->debugwarnings) {
|
|
for (const Scope& scope : scopeList) {
|
|
if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown)
|
|
debugMessage(scope.classDef, "debug", "SymbolDatabase couldn't resolve all user defined types.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseVariableSymbolTable()
|
|
{
|
|
// create variable symbol table
|
|
mVariableList.resize(mTokenizer->varIdCount() + 1);
|
|
std::fill_n(mVariableList.begin(), mVariableList.size(), (const Variable*)nullptr);
|
|
|
|
// check all scopes for variables
|
|
for (Scope& scope : scopeList) {
|
|
// add all variables
|
|
for (std::list<Variable>::iterator var = scope.varlist.begin(); var != scope.varlist.end(); ++var) {
|
|
const unsigned int varId = var->declarationId();
|
|
if (varId)
|
|
mVariableList[varId] = &(*var);
|
|
// fix up variables without type
|
|
if (!var->type() && !var->typeStartToken()->isStandardType()) {
|
|
const Type *type = findType(var->typeStartToken(), &scope);
|
|
if (type)
|
|
var->type(type);
|
|
}
|
|
}
|
|
|
|
// add all function parameters
|
|
for (Function& func : scope.functionList) {
|
|
for (std::list<Variable>::iterator arg = func.argumentList.begin(); arg != func.argumentList.end(); ++arg) {
|
|
// check for named parameters
|
|
if (arg->nameToken() && arg->declarationId()) {
|
|
const unsigned int declarationId = arg->declarationId();
|
|
if (declarationId > 0U)
|
|
mVariableList[declarationId] = &(*arg);
|
|
// fix up parameters without type
|
|
if (!arg->type() && !arg->typeStartToken()->isStandardType()) {
|
|
const Type *type = findTypeInNested(arg->typeStartToken(), &scope);
|
|
if (type)
|
|
arg->type(type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// fill in missing variables if possible
|
|
const std::size_t functions = functionScopes.size();
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
const Scope *func = functionScopes[i];
|
|
for (const Token *tok = func->bodyStart->next(); tok && tok != func->bodyEnd; tok = tok->next()) {
|
|
// check for member variable
|
|
if (tok->varId() && tok->next() &&
|
|
(tok->next()->str() == "." ||
|
|
(tok->next()->str() == "[" && tok->linkAt(1)->strAt(1) == "."))) {
|
|
const Token *tok1 = tok->next()->str() == "." ? tok->tokAt(2) : tok->linkAt(1)->tokAt(2);
|
|
if (tok1 && tok1->varId() && mVariableList[tok1->varId()] == nullptr) {
|
|
const Variable *var = mVariableList[tok->varId()];
|
|
if (var && var->typeScope()) {
|
|
// find the member variable of this variable
|
|
const Variable *var1 = var->typeScope()->getVariable(tok1->str());
|
|
if (var1) {
|
|
// add this variable to the look up table
|
|
mVariableList[tok1->varId()] = var1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseSetScopePointers()
|
|
{
|
|
// Set scope pointers
|
|
for (std::list<Scope>::iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
|
Token* start = const_cast<Token*>(it->bodyStart);
|
|
Token* end = const_cast<Token*>(it->bodyEnd);
|
|
if (it->type == Scope::eGlobal) {
|
|
start = const_cast<Token*>(mTokenizer->list.front());
|
|
end = const_cast<Token*>(mTokenizer->list.back());
|
|
}
|
|
assert(start);
|
|
assert(end);
|
|
|
|
end->scope(&*it);
|
|
|
|
for (Token* tok = start; tok != end; tok = tok->next()) {
|
|
if (start != end && tok->str() == "{") {
|
|
bool isEndOfScope = false;
|
|
for (std::list<Scope*>::const_iterator innerScope = it->nestedList.begin(); innerScope != it->nestedList.end(); ++innerScope) {
|
|
if (tok == (*innerScope)->bodyStart) { // Is begin of inner scope
|
|
tok = tok->link();
|
|
if (tok->next() == end || !tok->next()) {
|
|
isEndOfScope = true;
|
|
break;
|
|
}
|
|
tok = tok->next();
|
|
break;
|
|
}
|
|
}
|
|
if (isEndOfScope)
|
|
break;
|
|
}
|
|
tok->scope(&*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass)
|
|
{
|
|
if (firstPass) {
|
|
// Set function definition and declaration pointers
|
|
for (std::list<Scope>::iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
|
for (std::list<Function>::const_iterator func = it->functionList.begin(); func != it->functionList.end(); ++func) {
|
|
if (func->tokenDef)
|
|
const_cast<Token *>(func->tokenDef)->function(&*func);
|
|
|
|
if (func->token)
|
|
const_cast<Token *>(func->token)->function(&*func);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set function call pointers
|
|
for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) {
|
|
if (tok->isName() && !tok->function() && tok->varId() == 0 && Token::Match(tok, "%name% [(,)>]") && !isReservedName(tok->str())) {
|
|
if (tok->next()->str() == ">" && !tok->next()->link())
|
|
continue;
|
|
|
|
if (tok->next()->str() != "(") {
|
|
const Token *start = tok;
|
|
while (Token::Match(start->tokAt(-2), "%name% ::"))
|
|
start = start->tokAt(-2);
|
|
if (!Token::Match(start->previous(), "[(,<]") && !Token::Match(start->tokAt(-2), "[(,<] &"))
|
|
continue;
|
|
}
|
|
|
|
const Function *function = findFunction(tok);
|
|
if (!function)
|
|
continue;
|
|
|
|
const_cast<Token *>(tok)->function(function);
|
|
|
|
if (tok->next()->str() != "(")
|
|
const_cast<Function *>(function)->functionPointerUsage = tok;
|
|
}
|
|
}
|
|
|
|
// Set C++ 11 delegate constructor function call pointers
|
|
for (std::list<Scope>::iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
|
for (std::list<Function>::const_iterator func = it->functionList.begin(); func != it->functionList.end(); ++func) {
|
|
// look for initializer list
|
|
if (func->isConstructor() && func->functionScope && func->functionScope->functionOf && func->arg) {
|
|
const Token * tok = func->arg->link()->next();
|
|
if (tok->str() == "noexcept") {
|
|
const Token * closingParenTok = tok->linkAt(1);
|
|
if (!closingParenTok || !closingParenTok->next()) {
|
|
continue;
|
|
}
|
|
tok = closingParenTok->next();
|
|
}
|
|
if (tok->str() != ":") {
|
|
continue;
|
|
}
|
|
tok = tok->next();
|
|
while (tok && tok != func->functionScope->bodyStart) {
|
|
if (Token::Match(tok, "%name% {|(")) {
|
|
if (tok->str() == func->tokenDef->str()) {
|
|
const Function *function = func->functionScope->functionOf->findFunction(tok);
|
|
if (function)
|
|
const_cast<Token *>(tok)->function(function);
|
|
break;
|
|
}
|
|
tok = tok->linkAt(1);
|
|
}
|
|
tok = tok->next();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseSetTypePointers()
|
|
{
|
|
std::set<std::string> typenames;
|
|
for (const Type &t : typeList) {
|
|
typenames.insert(t.name());
|
|
}
|
|
|
|
// Set type pointers
|
|
for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) {
|
|
if (!tok->isName() || tok->varId() || tok->function() || tok->type() || tok->enumerator())
|
|
continue;
|
|
|
|
if (typenames.find(tok->str()) == typenames.end())
|
|
continue;
|
|
|
|
const Type *type = findVariableType(tok->scope(), tok);
|
|
if (type)
|
|
const_cast<Token *>(tok)->type(type);
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseSetSmartPointerType()
|
|
{
|
|
for (Scope &scope: scopeList) {
|
|
for (Variable &var: scope.varlist) {
|
|
if (var.valueType() && var.valueType()->smartPointerTypeToken && !var.valueType()->smartPointerType) {
|
|
ValueType vt(*var.valueType());
|
|
vt.smartPointerType = vt.smartPointerTypeToken->type();
|
|
var.setValueType(vt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar)
|
|
{
|
|
VarIdMap::iterator varId = varIds.find(vartok->varId());
|
|
if (varId == varIds.end()) {
|
|
MemberIdMap memberId;
|
|
if (membertok->varId() == 0) {
|
|
memberId[membervar->nameToken()->varId()] = const_cast<Tokenizer *>(mTokenizer)->newVarId();
|
|
mVariableList.push_back(membervar);
|
|
} else
|
|
mVariableList[membertok->varId()] = membervar;
|
|
varIds.insert(std::make_pair(vartok->varId(), memberId));
|
|
varId = varIds.find(vartok->varId());
|
|
}
|
|
MemberIdMap::iterator memberId = varId->second.find(membervar->nameToken()->varId());
|
|
if (memberId == varId->second.end()) {
|
|
if (membertok->varId() == 0) {
|
|
varId->second.insert(std::make_pair(membervar->nameToken()->varId(), const_cast<Tokenizer *>(mTokenizer)->newVarId()));
|
|
mVariableList.push_back(membervar);
|
|
memberId = varId->second.find(membervar->nameToken()->varId());
|
|
} else
|
|
mVariableList[membertok->varId()] = membervar;
|
|
}
|
|
if (membertok->varId() == 0)
|
|
membertok->varId(memberId->second);
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseSetVariablePointers()
|
|
{
|
|
VarIdMap varIds;
|
|
|
|
// Set variable pointers
|
|
for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) {
|
|
if (tok->varId())
|
|
const_cast<Token *>(tok)->variable(getVariableFromVarId(tok->varId()));
|
|
|
|
// Set Token::variable pointer for array member variable
|
|
// Since it doesn't point at a fixed location it doesn't have varid
|
|
if (tok->variable() != nullptr &&
|
|
(tok->variable()->typeScope() || tok->variable()->isSmartPointer() || (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER)) &&
|
|
Token::Match(tok, "%name% [|.")) {
|
|
|
|
Token *tok2 = tok->next();
|
|
// Locate "]"
|
|
while (tok2 && tok2->str() == "[")
|
|
tok2 = tok2->link()->next();
|
|
|
|
Token *membertok = nullptr;
|
|
if (Token::Match(tok2, ". %name%"))
|
|
membertok = tok2->next();
|
|
else if (Token::Match(tok2, ") . %name%") && tok->strAt(-1) == "(")
|
|
membertok = tok2->tokAt(2);
|
|
|
|
if (membertok) {
|
|
const Variable *var = tok->variable();
|
|
if (var->typeScope()) {
|
|
const Variable *membervar = var->typeScope()->getVariable(membertok->str());
|
|
if (membervar) {
|
|
membertok->variable(membervar);
|
|
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
|
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
|
}
|
|
} else if (const ::Type *type = var->smartPointerType()) {
|
|
const Scope *classScope = type->classScope;
|
|
const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr;
|
|
if (membervar) {
|
|
membertok->variable(membervar);
|
|
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
|
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
|
}
|
|
} else if (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) {
|
|
if (Token::Match(var->typeStartToken(), "std :: %type% < %type% *| *| >")) {
|
|
const Type * type2 = var->typeStartToken()->tokAt(4)->type();
|
|
if (type2 && type2->classScope && type2->classScope->definedType) {
|
|
const Variable *membervar = type2->classScope->getVariable(membertok->str());
|
|
if (membervar) {
|
|
membertok->variable(membervar);
|
|
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)
|
|
fixVarId(varIds, tok, const_cast<Token *>(membertok), membervar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for function returning record type
|
|
// func(...).var
|
|
// func(...)[...].var
|
|
else if (tok->function() && tok->next()->str() == "(" &&
|
|
(Token::Match(tok->next()->link(), ") . %name% !!(") ||
|
|
(Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) {
|
|
const Type *type = tok->function()->retType;
|
|
if (type) {
|
|
Token *membertok;
|
|
if (tok->next()->link()->next()->str() == ".")
|
|
membertok = tok->next()->link()->next()->next();
|
|
else
|
|
membertok = tok->next()->link()->next()->link()->next()->next();
|
|
const Variable *membervar = membertok->variable();
|
|
if (!membervar) {
|
|
if (type->classScope) {
|
|
membervar = type->classScope->getVariable(membertok->str());
|
|
if (membervar) {
|
|
membertok->variable(membervar);
|
|
if (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr) {
|
|
if (tok->function()->retDef)
|
|
fixVarId(varIds, tok->function()->retDef, const_cast<Token *>(membertok), membervar);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseEnums()
|
|
{
|
|
// fill in enumerators in enum
|
|
for (std::list<Scope>::iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
|
if (it->type != Scope::eEnum)
|
|
continue;
|
|
|
|
// add enumerators to enumerator tokens
|
|
for (Enumerator & i : it->enumeratorList)
|
|
const_cast<Token *>(i.name)->enumerator(&i);
|
|
}
|
|
|
|
for (std::list<Scope>::iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
|
if (it->type != Scope::eEnum)
|
|
continue;
|
|
|
|
for (Enumerator & enumerator : it->enumeratorList) {
|
|
// look for initialization tokens that can be converted to enumerators and convert them
|
|
if (enumerator.start) {
|
|
if (!enumerator.end)
|
|
mTokenizer->syntaxError(enumerator.start);
|
|
for (const Token * tok3 = enumerator.start; tok3 && tok3 != enumerator.end->next(); tok3 = tok3->next()) {
|
|
if (tok3->tokType() == Token::eName) {
|
|
const Enumerator * e = findEnumerator(tok3);
|
|
if (e)
|
|
const_cast<Token *>(tok3)->enumerator(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// find enumerators
|
|
for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) {
|
|
if (tok->tokType() != Token::eName)
|
|
continue;
|
|
const Enumerator * enumerator = findEnumerator(tok);
|
|
if (enumerator)
|
|
const_cast<Token *>(tok)->enumerator(enumerator);
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseIncompleteVars()
|
|
{
|
|
static const std::unordered_set<std::string> cpp20keywords = {
|
|
"alignas",
|
|
"alignof",
|
|
"axiom",
|
|
"co_await",
|
|
"co_return",
|
|
"co_yield",
|
|
"concept",
|
|
"synchronized",
|
|
"consteval",
|
|
"reflexpr",
|
|
"requires",
|
|
};
|
|
static const std::unordered_set<std::string> cppkeywords = {
|
|
"asm",
|
|
"auto",
|
|
"catch",
|
|
"char",
|
|
"class",
|
|
"const",
|
|
"constexpr",
|
|
"decltype",
|
|
"default",
|
|
"do",
|
|
"enum",
|
|
"explicit",
|
|
"export",
|
|
"extern",
|
|
"final",
|
|
"friend",
|
|
"inline",
|
|
"mutable",
|
|
"namespace",
|
|
"new",
|
|
"noexcept",
|
|
"nullptr",
|
|
"override",
|
|
"private",
|
|
"protected",
|
|
"public",
|
|
"register",
|
|
"sizeof",
|
|
"static",
|
|
"static_assert",
|
|
"struct",
|
|
"template",
|
|
"this",
|
|
"thread_local",
|
|
"throw",
|
|
"try",
|
|
"typedef",
|
|
"typeid",
|
|
"typename",
|
|
"union",
|
|
"using",
|
|
"virtual",
|
|
"void",
|
|
"volatile",
|
|
"NULL",
|
|
};
|
|
for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) {
|
|
const Scope * scope = tok->scope();
|
|
if (!scope)
|
|
continue;
|
|
if (!scope->isExecutable())
|
|
continue;
|
|
if (!Token::Match(tok, "%name%"))
|
|
continue;
|
|
if (!tok->isNameOnly())
|
|
continue;
|
|
if (Token::Match(tok, "%var%"))
|
|
continue;
|
|
if (tok->type())
|
|
continue;
|
|
if (Token::Match(tok->next(), "::|.|(|:|%var%"))
|
|
continue;
|
|
if (Token::Match(tok->next(), "&|&&|* )|%var%"))
|
|
continue;
|
|
if (Token::simpleMatch(tok->next(), ")") && Token::simpleMatch(tok->next()->link()->previous(), "catch ("))
|
|
continue;
|
|
// Very likely a typelist
|
|
if (Token::Match(tok->tokAt(-2), "%type% ,"))
|
|
continue;
|
|
// Inside template brackets
|
|
if (Token::Match(tok->next(), "<|>") && tok->next()->link())
|
|
continue;
|
|
if (Token::simpleMatch(tok->previous(), "<") && tok->previous()->link())
|
|
continue;
|
|
// Skip goto labels
|
|
if (Token::simpleMatch(tok->previous(), "goto"))
|
|
continue;
|
|
if (cppkeywords.count(tok->str()) > 0)
|
|
continue;
|
|
if (mSettings->standards.cpp >= Standards::CPP20 && cpp20keywords.count(tok->str()) > 0)
|
|
continue;
|
|
const_cast<Token *>(tok)->isIncompleteVar(true);
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseEscapeFunctions()
|
|
{
|
|
for (Scope & scope : scopeList) {
|
|
if (scope.type != Scope::eFunction)
|
|
continue;
|
|
Function * function = scope.function;
|
|
if (!function)
|
|
continue;
|
|
function->isEscapeFunction(isReturnScope(scope.bodyEnd, &mSettings->library, nullptr, true));
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::createSymbolDatabaseExprIds()
|
|
{
|
|
nonneg int base = 0;
|
|
// Find highest varId
|
|
for (const Variable *var : mVariableList) {
|
|
if (!var)
|
|
continue;
|
|
base = std::max<MathLib::bigint>(base, var->declarationId());
|
|
}
|
|
nonneg int id = base + 1;
|
|
for (const Scope * scope : functionScopes) {
|
|
std::unordered_map<std::string, std::vector<Token*>> exprs;
|
|
|
|
// Assign IDs
|
|
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
|
if (tok->varId() > 0) {
|
|
tok->exprId(tok->varId());
|
|
} else if (Token::Match(tok, "(|.|[|%cop%")) {
|
|
exprs[tok->str()].push_back(tok);
|
|
tok->exprId(id++);
|
|
|
|
if (id == std::numeric_limits<nonneg int>::max()) {
|
|
throw InternalError(nullptr, "Ran out of expression ids.", InternalError::INTERNAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply CSE
|
|
for (const auto& p:exprs) {
|
|
const std::vector<Token*>& tokens = p.second;
|
|
for (Token* tok1:tokens) {
|
|
for (Token* tok2:tokens) {
|
|
if (tok1 == tok2)
|
|
continue;
|
|
if (tok1->exprId() == tok2->exprId())
|
|
continue;
|
|
if (!isSameExpression(isCPP(), true, tok1, tok2, mSettings->library, true, false))
|
|
continue;
|
|
nonneg int cid = std::min(tok1->exprId(), tok2->exprId());
|
|
tok1->exprId(cid);
|
|
tok2->exprId(cid);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::setArrayDimensionsUsingValueFlow()
|
|
{
|
|
// set all unknown array dimensions
|
|
for (const Variable *var : mVariableList) {
|
|
// check each array variable
|
|
if (!var || !var->isArray())
|
|
continue;
|
|
// check each array dimension
|
|
for (const Dimension &const_dimension : var->dimensions()) {
|
|
Dimension &dimension = const_cast<Dimension &>(const_dimension);
|
|
if (dimension.num != 0 || !dimension.tok)
|
|
continue;
|
|
|
|
if (Token::Match(dimension.tok->previous(), "[<,]")) {
|
|
if (dimension.known)
|
|
continue;
|
|
if (!Token::Match(dimension.tok->previous(), "[<,]"))
|
|
continue;
|
|
|
|
// In template arguments, there might not be AST
|
|
// Determine size by using the "raw tokens"
|
|
TokenList tokenList(mSettings);
|
|
tokenList.addtoken(";", 0, 0, 0, false);
|
|
bool fail = false;
|
|
for (const Token *tok = dimension.tok; tok && !Token::Match(tok, "[,>]"); tok = tok->next()) {
|
|
if (!tok->isName())
|
|
tokenList.addtoken(tok->str(), 0, 0, 0, false);
|
|
|
|
else if (tok->hasKnownIntValue())
|
|
tokenList.addtoken(std::to_string(tok->getKnownIntValue()), 0, 0, 0, false);
|
|
|
|
else {
|
|
fail = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fail)
|
|
continue;
|
|
|
|
tokenList.addtoken(";", 0, 0, 0, false);
|
|
|
|
for (Token *tok = tokenList.front(); tok;) {
|
|
if (TemplateSimplifier::simplifyNumericCalculations(tok, false))
|
|
tok = tokenList.front();
|
|
else
|
|
tok = tok->next();
|
|
}
|
|
|
|
if (Token::Match(tokenList.front(), "; %num% ;")) {
|
|
dimension.known = true;
|
|
dimension.num = MathLib::toLongNumber(tokenList.front()->next()->str());
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
// Normal array [..dimension..]
|
|
dimension.known = false;
|
|
|
|
// check for a single token dimension
|
|
if (dimension.tok->hasKnownIntValue()) {
|
|
dimension.known = true;
|
|
dimension.num = dimension.tok->getKnownIntValue();
|
|
continue;
|
|
}
|
|
|
|
else if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) {
|
|
int bits = 0;
|
|
switch (dimension.tok->valueType()->type) {
|
|
case ValueType::Type::CHAR:
|
|
bits = mSettings->char_bit;
|
|
break;
|
|
case ValueType::Type::SHORT:
|
|
bits = mSettings->short_bit;
|
|
break;
|
|
case ValueType::Type::INT:
|
|
bits = mSettings->int_bit;
|
|
break;
|
|
case ValueType::Type::LONG:
|
|
bits = mSettings->long_bit;
|
|
break;
|
|
case ValueType::Type::LONGLONG:
|
|
bits = mSettings->long_long_bit;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (bits > 0 && bits <= 62) {
|
|
if (dimension.tok->valueType()->sign == ValueType::Sign::UNSIGNED)
|
|
dimension.num = 1LL << bits;
|
|
else
|
|
dimension.num = 1LL << (bits - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SymbolDatabase::~SymbolDatabase()
|
|
{
|
|
// Clear scope, type, function and variable pointers
|
|
for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) {
|
|
const_cast<Token *>(tok)->scope(nullptr);
|
|
const_cast<Token *>(tok)->type(nullptr);
|
|
const_cast<Token *>(tok)->function(nullptr);
|
|
const_cast<Token *>(tok)->variable(nullptr);
|
|
const_cast<Token *>(tok)->enumerator(nullptr);
|
|
const_cast<Token *>(tok)->setValueType(nullptr);
|
|
}
|
|
}
|
|
|
|
bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart, const Token** declEnd) const
|
|
{
|
|
if (tok->varId())
|
|
return false;
|
|
|
|
// function returning function pointer? '... ( ... %name% ( ... ))( ... ) {'
|
|
// function returning reference to array '... ( & %name% ( ... ))[ ... ] {'
|
|
// TODO: Activate this again
|
|
if (false && tok->str() == "(" && tok->strAt(1) != "*" &&
|
|
(tok->link()->previous()->str() == ")" || Token::simpleMatch(tok->link()->tokAt(-2), ") const"))) {
|
|
const Token* tok2 = tok->link()->next();
|
|
if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) {
|
|
const Token* argStartTok;
|
|
if (tok->link()->previous()->str() == "const")
|
|
argStartTok = tok->link()->linkAt(-2);
|
|
else
|
|
argStartTok = tok->link()->linkAt(-1);
|
|
*funcStart = argStartTok->previous();
|
|
*argStart = argStartTok;
|
|
*declEnd = Token::findmatch(tok2->link()->next(), "{|;");
|
|
return true;
|
|
} else if (tok2 && tok2->str() == "[") {
|
|
while (tok2 && tok2->str() == "[")
|
|
tok2 = tok2->link()->next();
|
|
if (Token::Match(tok2, "{|;|const|=")) {
|
|
const Token* argStartTok;
|
|
if (tok->link()->previous()->str() == "const")
|
|
argStartTok = tok->link()->linkAt(-2);
|
|
else
|
|
argStartTok = tok->link()->linkAt(-1);
|
|
*funcStart = argStartTok->previous();
|
|
*argStart = argStartTok;
|
|
*declEnd = Token::findmatch(tok2, "{|;");
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (!tok->isName() || !tok->next() || !tok->next()->link())
|
|
return false;
|
|
|
|
// regular function?
|
|
else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) && tok->previous() &&
|
|
(Token::Match(tok->previous(), "%name%|>|&|*|::|~") || // Either a return type or scope qualifier in front of tok
|
|
outerScope->isClassOrStructOrUnion())) { // or a ctor/dtor
|
|
const Token* tok1 = tok->previous();
|
|
const Token* tok2 = tok->next()->link()->next();
|
|
|
|
if (!mTokenizer->isFunctionHead(tok->next(), ";:{"))
|
|
return false;
|
|
|
|
// skip over destructor "~"
|
|
if (tok1->str() == "~")
|
|
tok1 = tok1->previous();
|
|
|
|
// skip over qualification
|
|
while (Token::simpleMatch(tok1, "::")) {
|
|
tok1 = tok1->previous();
|
|
if (Token::Match(tok1, "%name%"))
|
|
tok1 = tok1->previous();
|
|
else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%"))
|
|
tok1 = tok1->link()->tokAt(-2);
|
|
}
|
|
|
|
// skip over const, noexcept, throw, override, final and volatile specifiers
|
|
while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) {
|
|
tok2 = tok2->next();
|
|
if (tok2 && tok2->str() == "(")
|
|
tok2 = tok2->link()->next();
|
|
}
|
|
|
|
// skip over trailing return type
|
|
if (tok2 && tok2->str() == ".") {
|
|
for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) {
|
|
if (Token::Match(tok2, ";|{|=|override|final"))
|
|
break;
|
|
if (tok2->link() && Token::Match(tok2, "<|[|("))
|
|
tok2 = tok2->link();
|
|
}
|
|
}
|
|
|
|
// done if constructor or destructor
|
|
if (!Token::Match(tok1, "{|}|;|public:|protected:|private:") && tok1) {
|
|
// skip over pointers and references
|
|
while (Token::Match(tok1, "%type%|*|&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1->str()) || tok1->str() == "const"))
|
|
tok1 = tok1->previous();
|
|
|
|
// skip over template
|
|
if (tok1 && tok1->str() == ">") {
|
|
if (tok1->link())
|
|
tok1 = tok1->link()->previous();
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// function can't have number or variable as return type
|
|
if (tok1 && (tok1->isNumber() || tok1->varId()))
|
|
return false;
|
|
|
|
// skip over return type
|
|
if (Token::Match(tok1, "%name%")) {
|
|
if (tok1->str() == "return")
|
|
return false;
|
|
tok1 = tok1->previous();
|
|
}
|
|
|
|
// skip over qualification
|
|
while (Token::simpleMatch(tok1, "::")) {
|
|
tok1 = tok1->previous();
|
|
if (Token::Match(tok1, "%name%"))
|
|
tok1 = tok1->previous();
|
|
else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%"))
|
|
tok1 = tok1->link()->tokAt(-2);
|
|
}
|
|
|
|
// skip over modifiers and other stuff
|
|
while (Token::Match(tok1, "const|static|extern|template|virtual|struct|class|enum|%name%")) {
|
|
// friend type func(); is not a function
|
|
if (isCPP() && tok1->str() == "friend" && tok2->str() == ";")
|
|
return false;
|
|
tok1 = tok1->previous();
|
|
}
|
|
|
|
// should be at a sequence point if this is a function
|
|
if (!Token::Match(tok1, ">|{|}|;|public:|protected:|private:") && tok1)
|
|
return false;
|
|
}
|
|
|
|
if (tok2 &&
|
|
(Token::Match(tok2, ";|{|=") ||
|
|
(tok2->isUpperCaseName() && Token::Match(tok2, "%name% ;|{")) ||
|
|
(tok2->isUpperCaseName() && Token::Match(tok2, "%name% (") && tok2->next()->link()->strAt(1) == "{") ||
|
|
Token::Match(tok2, ": ::| %name% (|::|<|{") ||
|
|
Token::Match(tok2, "&|&&| ;|{") ||
|
|
Token::Match(tok2, "= delete|default ;"))) {
|
|
*funcStart = tok;
|
|
*argStart = tok->next();
|
|
*declEnd = Token::findmatch(tok2, "{|;");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// UNKNOWN_MACRO(a,b) { ... }
|
|
else if (outerScope->type == Scope::eGlobal &&
|
|
Token::Match(tok, "%name% (") &&
|
|
tok->isUpperCaseName() &&
|
|
Token::simpleMatch(tok->linkAt(1), ") {") &&
|
|
(!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) {
|
|
*funcStart = tok;
|
|
*argStart = tok->next();
|
|
*declEnd = tok->linkAt(1)->next();
|
|
return true;
|
|
}
|
|
|
|
// template constructor?
|
|
else if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) {
|
|
const Token* tok2 = tok->next()->link()->next()->link();
|
|
if (Token::Match(tok2, ") const| ;|{|=") ||
|
|
Token::Match(tok2, ") : ::| %name% (|::|<|{") ||
|
|
Token::Match(tok2, ") const| noexcept {|;|(")) {
|
|
*funcStart = tok;
|
|
*argStart = tok2->link();
|
|
*declEnd = Token::findmatch(tok2->next(), "{|;");
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// regular C function with missing return or invalid C++ ?
|
|
else if (Token::Match(tok, "%name% (") && !isReservedName(tok->str()) &&
|
|
Token::simpleMatch(tok->linkAt(1), ") {") &&
|
|
(!tok->previous() || Token::Match(tok->previous(), ";|}"))) {
|
|
if (mTokenizer->isC()) {
|
|
debugMessage(tok, "debug", "SymbolDatabase::isFunction found C function '" + tok->str() + "' without a return type.");
|
|
*funcStart = tok;
|
|
*argStart = tok->next();
|
|
*declEnd = tok->linkAt(1)->next();
|
|
return true;
|
|
}
|
|
mTokenizer->syntaxError(tok);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void SymbolDatabase::validateExecutableScopes() const
|
|
{
|
|
const std::size_t functions = functionScopes.size();
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
const Scope* const scope = functionScopes[i];
|
|
const Function* const function = scope->function;
|
|
if (scope->isExecutable() && !function) {
|
|
const std::list<const Token*> callstack(1, scope->classDef);
|
|
const std::string msg = std::string("Executable scope '") + scope->classDef->str() + "' with unknown function.";
|
|
const ErrorMessage errmsg(callstack, &mTokenizer->list, Severity::debug,
|
|
"symbolDatabaseWarning",
|
|
msg,
|
|
Certainty::normal);
|
|
mErrorLogger->reportErr(errmsg);
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
const Function* getFunctionForArgumentvariable(const Variable * const var, const std::vector<const Scope *>& functionScopes)
|
|
{
|
|
const std::size_t functions = functionScopes.size();
|
|
for (std::size_t i = 0; i < functions; ++i) {
|
|
const Scope* const scope = functionScopes[i];
|
|
const Function* const function = scope->function;
|
|
if (function) {
|
|
for (std::size_t arg=0; arg < function->argCount(); ++arg) {
|
|
if (var==function->getArgumentVar(arg))
|
|
return function;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::validateVariables() const
|
|
{
|
|
for (std::vector<const Variable *>::const_iterator iter = mVariableList.begin(); iter!=mVariableList.end(); ++iter) {
|
|
const Variable * const var = *iter;
|
|
if (var) {
|
|
if (!var->scope()) {
|
|
const Function* function = getFunctionForArgumentvariable(var, functionScopes);
|
|
if (!var->isArgument() || (function && function->hasBody())) {
|
|
throw InternalError(var->nameToken(), "Analysis failed (variable without scope). If the code is valid then please report this failure.", InternalError::INTERNAL);
|
|
//std::cout << "!!!Variable found without scope: " << var->nameToken()->str() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::validate() const
|
|
{
|
|
if (mSettings->debugwarnings) {
|
|
validateExecutableScopes();
|
|
}
|
|
//validateVariables();
|
|
}
|
|
|
|
void SymbolDatabase::clangSetVariables(const std::vector<const Variable *> &variableList)
|
|
{
|
|
mVariableList = variableList;
|
|
}
|
|
|
|
Variable::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_)
|
|
: mNameToken(name_),
|
|
mTypeStartToken(typeStart),
|
|
mTypeEndToken(typeEnd),
|
|
mIndex(index_),
|
|
mAccess(access_),
|
|
mFlags(0),
|
|
mType(type_),
|
|
mScope(scope_),
|
|
mValueType(nullptr)
|
|
{
|
|
if (!mTypeStartToken && mTypeEndToken) {
|
|
mTypeStartToken = mTypeEndToken;
|
|
while (Token::Match(mTypeStartToken->previous(), "%type%|*|&"))
|
|
mTypeStartToken = mTypeStartToken->previous();
|
|
}
|
|
|
|
while (Token::Match(mTypeStartToken, "const|struct|static")) {
|
|
if (mTypeStartToken->str() == "static")
|
|
setFlag(fIsStatic, true);
|
|
mTypeStartToken = mTypeStartToken->next();
|
|
}
|
|
|
|
if (Token::simpleMatch(mTypeEndToken, "&"))
|
|
setFlag(fIsReference, true);
|
|
else if (Token::simpleMatch(mTypeEndToken, "&&")) {
|
|
setFlag(fIsReference, true);
|
|
setFlag(fIsRValueRef, true);
|
|
}
|
|
|
|
std::string::size_type pos = clangType.find("[");
|
|
if (pos != std::string::npos) {
|
|
setFlag(fIsArray, true);
|
|
do {
|
|
const std::string::size_type pos1 = pos+1;
|
|
pos = clangType.find("]", pos1);
|
|
Dimension dim;
|
|
dim.tok = nullptr;
|
|
dim.known = pos > pos1;
|
|
if (pos > pos1)
|
|
dim.num = MathLib::toLongNumber(clangType.substr(pos1, pos-pos1));
|
|
else
|
|
dim.num = 0;
|
|
mDimensions.push_back(dim);
|
|
++pos;
|
|
} while (pos < clangType.size() && clangType[pos] == '[');
|
|
}
|
|
|
|
// Is there initialization in variable declaration
|
|
const Token *initTok = mNameToken ? mNameToken->next() : nullptr;
|
|
while (initTok && initTok->str() == "[")
|
|
initTok = initTok->link()->next();
|
|
if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq()))
|
|
setFlag(fIsInit, true);
|
|
}
|
|
|
|
Variable::Variable(const Variable &var, const Scope *scope)
|
|
: mValueType(nullptr)
|
|
{
|
|
*this = var;
|
|
mScope = scope;
|
|
}
|
|
|
|
Variable::Variable(const Variable &var)
|
|
: mValueType(nullptr)
|
|
{
|
|
*this = var;
|
|
}
|
|
|
|
Variable::~Variable()
|
|
{
|
|
delete mValueType;
|
|
}
|
|
|
|
Variable& Variable::operator=(const Variable &var)
|
|
{
|
|
if (this == &var)
|
|
return *this;
|
|
|
|
mNameToken = var.mNameToken;
|
|
mTypeStartToken = var.mTypeStartToken;
|
|
mTypeEndToken = var.mTypeEndToken;
|
|
mIndex = var.mIndex;
|
|
mAccess = var.mAccess;
|
|
mFlags = var.mFlags;
|
|
mType = var.mType;
|
|
mScope = var.mScope;
|
|
mDimensions = var.mDimensions;
|
|
delete mValueType;
|
|
if (var.mValueType)
|
|
mValueType = new ValueType(*var.mValueType);
|
|
else
|
|
mValueType = nullptr;
|
|
|
|
return *this;
|
|
}
|
|
|
|
bool Variable::isPointerArray() const
|
|
{
|
|
return isArray() && nameToken() && nameToken()->previous() && (nameToken()->previous()->str() == "*");
|
|
}
|
|
|
|
bool Variable::isUnsigned() const
|
|
{
|
|
return mValueType ? (mValueType->sign == ValueType::Sign::UNSIGNED) : mTypeStartToken->isUnsigned();
|
|
}
|
|
|
|
const Token * Variable::declEndToken() const
|
|
{
|
|
Token const * declEnd = typeStartToken();
|
|
while (declEnd && !Token::Match(declEnd, "[;,)={]")) {
|
|
if (declEnd->link() && Token::Match(declEnd,"(|["))
|
|
declEnd = declEnd->link();
|
|
declEnd = declEnd->next();
|
|
}
|
|
return declEnd;
|
|
}
|
|
|
|
void Variable::evaluate(const Settings* settings)
|
|
{
|
|
// Is there initialization in variable declaration
|
|
const Token *initTok = mNameToken ? mNameToken->next() : nullptr;
|
|
while (initTok && initTok->str() == "[")
|
|
initTok = initTok->link()->next();
|
|
if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq()))
|
|
setFlag(fIsInit, true);
|
|
|
|
if (!settings)
|
|
return;
|
|
|
|
const Library * const lib = &settings->library;
|
|
|
|
if (mNameToken)
|
|
setFlag(fIsArray, arrayDimensions(settings));
|
|
|
|
if (mTypeStartToken)
|
|
setValueType(ValueType::parseDecl(mTypeStartToken,settings));
|
|
|
|
const Token* tok = mTypeStartToken;
|
|
while (tok && tok->previous() && tok->previous()->isName())
|
|
tok = tok->previous();
|
|
const Token* end = mTypeEndToken;
|
|
if (end)
|
|
end = end->next();
|
|
while (tok != end) {
|
|
if (tok->str() == "static")
|
|
setFlag(fIsStatic, true);
|
|
else if (tok->str() == "extern")
|
|
setFlag(fIsExtern, true);
|
|
else if (tok->str() == "volatile" || Token::simpleMatch(tok, "std :: atomic <"))
|
|
setFlag(fIsVolatile, true);
|
|
else if (tok->str() == "mutable")
|
|
setFlag(fIsMutable, true);
|
|
else if (tok->str() == "const")
|
|
setFlag(fIsConst, true);
|
|
else if (tok->str() == "*") {
|
|
setFlag(fIsPointer, !isArray() || Token::Match(tok->previous(), "( * %name% )"));
|
|
setFlag(fIsConst, false); // Points to const, isn't necessarily const itself
|
|
} else if (tok->str() == "&") {
|
|
if (isReference())
|
|
setFlag(fIsRValueRef, true);
|
|
setFlag(fIsReference, true);
|
|
} else if (tok->str() == "&&") { // Before simplification, && isn't split up
|
|
setFlag(fIsRValueRef, true);
|
|
setFlag(fIsReference, true); // Set also fIsReference
|
|
}
|
|
|
|
if (tok->isAttributeMaybeUnused()) {
|
|
setFlag(fIsMaybeUnused, true);
|
|
}
|
|
|
|
if (tok->str() == "<" && tok->link())
|
|
tok = tok->link();
|
|
else
|
|
tok = tok->next();
|
|
}
|
|
|
|
while (Token::Match(mTypeStartToken, "static|const|volatile %any%"))
|
|
mTypeStartToken = mTypeStartToken->next();
|
|
while (mTypeEndToken && mTypeEndToken->previous() && Token::Match(mTypeEndToken, "const|volatile"))
|
|
mTypeEndToken = mTypeEndToken->previous();
|
|
|
|
if (mTypeStartToken) {
|
|
std::string strtype = mTypeStartToken->str();
|
|
for (const Token *typeToken = mTypeStartToken; Token::Match(typeToken, "%type% :: %type%"); typeToken = typeToken->tokAt(2))
|
|
strtype += "::" + typeToken->strAt(2);
|
|
setFlag(fIsClass, !lib->podtype(strtype) && !mTypeStartToken->isStandardType() && !isEnumType() && !isPointer() && !isReference() && strtype != "...");
|
|
setFlag(fIsStlType, Token::simpleMatch(mTypeStartToken, "std ::"));
|
|
setFlag(fIsStlString, isStlType() && (Token::Match(mTypeStartToken->tokAt(2), "string|wstring|u16string|u32string !!::") || (Token::simpleMatch(mTypeStartToken->tokAt(2), "basic_string <") && !Token::simpleMatch(mTypeStartToken->linkAt(3), "> ::"))));
|
|
setFlag(fIsSmartPointer, lib->isSmartPointer(mTypeStartToken));
|
|
}
|
|
if (mAccess == AccessControl::Argument) {
|
|
tok = mNameToken;
|
|
if (!tok) {
|
|
// Argument without name
|
|
tok = mTypeEndToken;
|
|
// back up to start of array dimensions
|
|
while (tok && tok->str() == "]")
|
|
tok = tok->link()->previous();
|
|
// add array dimensions if present
|
|
if (tok && tok->next()->str() == "[")
|
|
setFlag(fIsArray, arrayDimensions(settings));
|
|
}
|
|
if (!tok)
|
|
return;
|
|
tok = tok->next();
|
|
while (tok->str() == "[")
|
|
tok = tok->link();
|
|
setFlag(fHasDefault, tok->str() == "=");
|
|
}
|
|
// check for C++11 member initialization
|
|
if (mScope && mScope->isClassOrStruct()) {
|
|
// type var = x or
|
|
// type var = {x}
|
|
// type var = x; gets simplified to: type var ; var = x ;
|
|
Token const * declEnd = declEndToken();
|
|
if ((Token::Match(declEnd, "; %name% =") && declEnd->strAt(1) == mNameToken->str()) ||
|
|
Token::Match(declEnd, "=|{"))
|
|
setFlag(fHasDefault, true);
|
|
}
|
|
|
|
if (mTypeStartToken) {
|
|
if (Token::Match(mTypeStartToken, "float|double"))
|
|
setFlag(fIsFloatType, true);
|
|
}
|
|
}
|
|
|
|
void Variable::setValueType(const ValueType &valueType)
|
|
{
|
|
if (valueType.type == ValueType::Type::UNKNOWN_TYPE) {
|
|
const Token *declType = Token::findsimplematch(mTypeStartToken, "decltype (", mTypeEndToken);
|
|
if (declType && !declType->next()->valueType())
|
|
return;
|
|
}
|
|
delete mValueType;
|
|
mValueType = new ValueType(valueType);
|
|
if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )")))
|
|
setFlag(fIsPointer, true);
|
|
setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer));
|
|
if (mValueType->smartPointerType)
|
|
setFlag(fIsSmartPointer, true);
|
|
}
|
|
|
|
const Type *Variable::smartPointerType() const
|
|
{
|
|
if (!isSmartPointer())
|
|
return nullptr;
|
|
|
|
if (mValueType->smartPointerType)
|
|
return mValueType->smartPointerType;
|
|
|
|
// TODO: Cache result
|
|
const Token *ptrType = typeStartToken();
|
|
while (Token::Match(ptrType, "%name%|::"))
|
|
ptrType = ptrType->next();
|
|
if (Token::Match(ptrType, "< %name% >"))
|
|
return ptrType->scope()->findType(ptrType->next()->str());
|
|
return nullptr;
|
|
}
|
|
|
|
std::string Variable::getTypeName() const
|
|
{
|
|
std::string ret;
|
|
// TODO: For known types, generate the full type name
|
|
for (const Token *typeTok = mTypeStartToken; Token::Match(typeTok, "%name%|::") && typeTok->varId() == 0; typeTok = typeTok->next())
|
|
ret += typeTok->str();
|
|
return ret;
|
|
}
|
|
|
|
static bool isOperator(const Token *tokenDef)
|
|
{
|
|
if (!tokenDef)
|
|
return false;
|
|
if (tokenDef->isOperatorKeyword())
|
|
return true;
|
|
const std::string &name = tokenDef->str();
|
|
return name.size() > 8 && name.compare(0,8,"operator")==0 && std::strchr("+-*/%&|~^<>!=[(", name[8]);
|
|
}
|
|
|
|
Function::Function(const Tokenizer *mTokenizer,
|
|
const Token *tok,
|
|
const Scope *scope,
|
|
const Token *tokDef,
|
|
const Token *tokArgDef)
|
|
: tokenDef(tokDef),
|
|
argDef(tokArgDef),
|
|
token(nullptr),
|
|
arg(nullptr),
|
|
retDef(nullptr),
|
|
retType(nullptr),
|
|
functionScope(nullptr),
|
|
nestedIn(scope),
|
|
initArgCount(0),
|
|
type(eFunction),
|
|
access(AccessControl::Public),
|
|
noexceptArg(nullptr),
|
|
throwArg(nullptr),
|
|
templateDef(nullptr),
|
|
functionPointerUsage(nullptr),
|
|
mFlags(0)
|
|
{
|
|
// operator function
|
|
if (::isOperator(tokenDef)) {
|
|
isOperator(true);
|
|
|
|
// 'operator =' is special
|
|
if (tokenDef->str() == "operator=")
|
|
type = Function::eOperatorEqual;
|
|
}
|
|
|
|
else if (tokenDef->str() == "[") {
|
|
type = Function::eLambda;
|
|
}
|
|
|
|
// class constructor/destructor
|
|
else if (((tokenDef->str() == scope->className) ||
|
|
(tokenDef->str().substr(0, scope->className.size()) == scope->className &&
|
|
tokenDef->str().size() > scope->className.size() + 1 &&
|
|
tokenDef->str()[scope->className.size() + 1] == '<')) &&
|
|
scope->type != Scope::ScopeType::eNamespace) {
|
|
// destructor
|
|
if (tokenDef->previous()->str() == "~")
|
|
type = Function::eDestructor;
|
|
// constructor of any kind
|
|
else
|
|
type = Function::eConstructor;
|
|
|
|
isExplicit(tokenDef->previous()->str() == "explicit");
|
|
}
|
|
|
|
const Token *tok1 = setFlags(tok, scope);
|
|
|
|
// find the return type
|
|
if (!isConstructor() && !isDestructor() && !isLambda()) {
|
|
// @todo auto type deduction should be checked
|
|
// @todo attributes and exception specification can also precede trailing return type
|
|
if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type
|
|
hasTrailingReturnType(true);
|
|
if (argDef->link()->strAt(1) == ".")
|
|
retDef = argDef->link()->tokAt(2);
|
|
else if (argDef->link()->strAt(2) == ".")
|
|
retDef = argDef->link()->tokAt(3);
|
|
else if (argDef->link()->strAt(3) == ".")
|
|
retDef = argDef->link()->tokAt(4);
|
|
} else {
|
|
if (tok1->str() == ">")
|
|
tok1 = tok1->next();
|
|
while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum"))
|
|
tok1 = tok1->next();
|
|
retDef = tok1;
|
|
}
|
|
}
|
|
|
|
const Token *end = argDef->link();
|
|
|
|
// parse function attributes..
|
|
tok = end->next();
|
|
while (tok) {
|
|
if (tok->str() == "const")
|
|
isConst(true);
|
|
else if (tok->str() == "&")
|
|
hasLvalRefQualifier(true);
|
|
else if (tok->str() == "&&")
|
|
hasRvalRefQualifier(true);
|
|
else if (tok->str() == "override")
|
|
setFlag(fHasOverrideSpecifier, true);
|
|
else if (tok->str() == "final")
|
|
setFlag(fHasFinalSpecifier, true);
|
|
else if (tok->str() == "volatile")
|
|
isVolatile(true);
|
|
else if (tok->str() == "noexcept") {
|
|
isNoExcept(!Token::simpleMatch(tok->next(), "( false )"));
|
|
if (tok->next()->str() == "(")
|
|
tok = tok->linkAt(1);
|
|
} else if (Token::simpleMatch(tok, "throw (")) {
|
|
isThrow(true);
|
|
if (tok->strAt(2) != ")")
|
|
throwArg = tok->next();
|
|
tok = tok->linkAt(1);
|
|
} else if (Token::Match(tok, "= 0|default|delete ;")) {
|
|
const std::string& modifier = tok->strAt(1);
|
|
isPure(modifier == "0");
|
|
isDefault(modifier == "default");
|
|
isDelete(modifier == "delete");
|
|
} else if (tok->str() == ".") { // trailing return type
|
|
// skip over return type
|
|
while (tok && !Token::Match(tok->next(), ";|{|override|final"))
|
|
tok = tok->next();
|
|
} else
|
|
break;
|
|
if (tok)
|
|
tok = tok->next();
|
|
}
|
|
|
|
if (mTokenizer->isFunctionHead(end, ":{")) {
|
|
// assume implementation is inline (definition and implementation same)
|
|
token = tokenDef;
|
|
arg = argDef;
|
|
isInline(true);
|
|
hasBody(true);
|
|
}
|
|
}
|
|
|
|
Function::Function(const Token *tokenDef, const std::string &clangType)
|
|
: tokenDef(tokenDef),
|
|
argDef(nullptr),
|
|
token(nullptr),
|
|
arg(nullptr),
|
|
retDef(nullptr),
|
|
retType(nullptr),
|
|
functionScope(nullptr),
|
|
nestedIn(nullptr),
|
|
initArgCount(0),
|
|
type(eFunction),
|
|
access(AccessControl::Public),
|
|
noexceptArg(nullptr),
|
|
throwArg(nullptr),
|
|
templateDef(nullptr),
|
|
functionPointerUsage(nullptr),
|
|
mFlags(0)
|
|
{
|
|
// operator function
|
|
if (::isOperator(tokenDef)) {
|
|
isOperator(true);
|
|
|
|
// 'operator =' is special
|
|
if (tokenDef->str() == "operator=")
|
|
type = Function::eOperatorEqual;
|
|
}
|
|
|
|
setFlags(tokenDef, tokenDef->scope());
|
|
|
|
if (endsWith(clangType, " const", 6))
|
|
isConst(true);
|
|
}
|
|
|
|
const Token *Function::setFlags(const Token *tok1, const Scope *scope)
|
|
{
|
|
// look for end of previous statement
|
|
while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:")) {
|
|
tok1 = tok1->previous();
|
|
|
|
// extern function
|
|
if (tok1->str() == "extern") {
|
|
isExtern(true);
|
|
}
|
|
|
|
// virtual function
|
|
else if (tok1->str() == "virtual") {
|
|
hasVirtualSpecifier(true);
|
|
}
|
|
|
|
// static function
|
|
else if (tok1->str() == "static") {
|
|
isStatic(true);
|
|
if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal)
|
|
isStaticLocal(true);
|
|
}
|
|
|
|
// friend function
|
|
else if (tok1->str() == "friend") {
|
|
isFriend(true);
|
|
}
|
|
|
|
// Function template
|
|
else if (tok1->link() && tok1->str() == ">" && Token::simpleMatch(tok1->link()->previous(), "template <")) {
|
|
templateDef = tok1->link()->previous();
|
|
break;
|
|
}
|
|
}
|
|
return tok1;
|
|
}
|
|
|
|
std::string Function::fullName() const
|
|
{
|
|
std::string ret = name();
|
|
for (const Scope *s = nestedIn; s; s = s->nestedIn) {
|
|
if (!s->className.empty())
|
|
ret = s->className + "::" + ret;
|
|
}
|
|
ret += "(";
|
|
for (const Variable &a : argumentList)
|
|
ret += (a.index() == 0 ? "" : ",") + a.name();
|
|
return ret + ")";
|
|
}
|
|
|
|
static std::string qualifiedName(const Scope *scope)
|
|
{
|
|
std::string name = scope->className;
|
|
while (scope->nestedIn) {
|
|
if (!scope->nestedIn->className.empty())
|
|
name = (scope->nestedIn->className + " :: ") + name;
|
|
scope = scope->nestedIn;
|
|
}
|
|
return name;
|
|
}
|
|
|
|
static bool usingNamespace(const Scope *scope, const Token *first, const Token *second, int &offset)
|
|
{
|
|
// check if qualifications match first before checking if using is needed
|
|
const Token *tok1 = first;
|
|
const Token *tok2 = second;
|
|
bool match = false;
|
|
while (Token::Match(tok1, "%type% :: %type%") && Token::Match(tok2, "%type% :: %type%")) {
|
|
if (tok1->str() == tok2->str()) {
|
|
tok1 = tok1->tokAt(2);
|
|
tok2 = tok2->tokAt(2);
|
|
match = true;
|
|
} else {
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (match)
|
|
return false;
|
|
|
|
offset = 0;
|
|
std::string name = first->str();
|
|
|
|
while (Token::Match(first, "%type% :: %type%")) {
|
|
if (offset)
|
|
name += (" :: " + first->str());
|
|
offset += 2;
|
|
first = first->tokAt(2);
|
|
if (first->str() == second->str()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (offset) {
|
|
while (scope) {
|
|
for (const auto & info : scope->usingList) {
|
|
if (info.scope) {
|
|
if (name == qualifiedName(info.scope))
|
|
return true;
|
|
}
|
|
// no scope so get name from using
|
|
else {
|
|
const Token *start = info.start->tokAt(2);
|
|
std::string nsName;
|
|
while (start && start->str() != ";") {
|
|
if (!nsName.empty())
|
|
nsName += " ";
|
|
nsName += start->str();
|
|
start = start->next();
|
|
}
|
|
if (nsName == name)
|
|
return true;
|
|
}
|
|
}
|
|
scope = scope->nestedIn;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool typesMatch(
|
|
const Scope *first_scope,
|
|
const Token *first_token,
|
|
const Scope *second_scope,
|
|
const Token *second_token,
|
|
const Token **new_first,
|
|
const Token **new_second)
|
|
{
|
|
// get first type
|
|
const Type * first_type = first_scope->check->findType(first_token, first_scope);
|
|
if (first_type) {
|
|
// get second type
|
|
const Type * second_type = second_scope->check->findType(second_token, second_scope);
|
|
// check if types match
|
|
if (first_type == second_type) {
|
|
const Token* tok1 = first_token;
|
|
while (tok1 && tok1->str() != first_type->name())
|
|
tok1 = tok1->next();
|
|
const Token *tok2 = second_token;
|
|
while (tok2 && tok2->str() != second_type->name())
|
|
tok2 = tok2->next();
|
|
// update parser token positions
|
|
if (tok1 && tok2) {
|
|
*new_first = tok1->previous();
|
|
*new_second = tok2->previous();
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const
|
|
{
|
|
const bool isCPP = scope->check->isCPP();
|
|
if (!isCPP) // C does not support overloads
|
|
return true;
|
|
|
|
int arg_path_length = path_length;
|
|
int offset = 0;
|
|
int openParen = 0;
|
|
|
|
// check for () == (void) and (void) == ()
|
|
if ((Token::simpleMatch(first, "( )") && Token::simpleMatch(second, "( void )")) ||
|
|
(Token::simpleMatch(first, "( void )") && Token::simpleMatch(second, "( )")))
|
|
return true;
|
|
|
|
while (first->str() == second->str() &&
|
|
first->isLong() == second->isLong() &&
|
|
first->isUnsigned() == second->isUnsigned()) {
|
|
if (first->str() == "(")
|
|
openParen++;
|
|
|
|
// at end of argument list
|
|
else if (first->str() == ")") {
|
|
if (openParen == 1)
|
|
return true;
|
|
else
|
|
--openParen;
|
|
}
|
|
|
|
// skip optional type information
|
|
if (Token::Match(first->next(), "struct|enum|union|class"))
|
|
first = first->next();
|
|
if (Token::Match(second->next(), "struct|enum|union|class"))
|
|
second = second->next();
|
|
|
|
// skip const on type passed by value
|
|
if (Token::Match(first->next(), "const %type% %name%|,|)") &&
|
|
!Token::Match(first->next(), "const %type% %name%| ["))
|
|
first = first->next();
|
|
if (Token::Match(second->next(), "const %type% %name%|,|)") &&
|
|
!Token::Match(second->next(), "const %type% %name%| ["))
|
|
second = second->next();
|
|
|
|
// skip default value assignment
|
|
else if (first->next()->str() == "=") {
|
|
first = first->nextArgument();
|
|
if (first)
|
|
first = first->tokAt(-2);
|
|
if (second->next()->str() == "=") {
|
|
second = second->nextArgument();
|
|
if (second)
|
|
second = second->tokAt(-2);
|
|
if (!first || !second) { // End of argument list (first or second)
|
|
return !first && !second;
|
|
}
|
|
} else if (!first) { // End of argument list (first)
|
|
return !second->nextArgument(); // End of argument list (second)
|
|
}
|
|
} else if (second->next()->str() == "=") {
|
|
second = second->nextArgument();
|
|
if (second)
|
|
second = second->tokAt(-2);
|
|
if (!second) { // End of argument list (second)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// definition missing variable name
|
|
else if ((first->next()->str() == "," && second->next()->str() != ",") ||
|
|
(Token::Match(first, "!!( )") && second->next()->str() != ")")) {
|
|
second = second->next();
|
|
// skip default value assignment
|
|
if (second->next()->str() == "=") {
|
|
do {
|
|
second = second->next();
|
|
} while (!Token::Match(second->next(), ",|)"));
|
|
}
|
|
} else if (first->next()->str() == "[" && second->next()->str() != "[")
|
|
second = second->next();
|
|
|
|
// function missing variable name
|
|
else if ((second->next()->str() == "," && first->next()->str() != ",") ||
|
|
(Token::Match(second, "!!( )") && first->next()->str() != ")")) {
|
|
first = first->next();
|
|
// skip default value assignment
|
|
if (first->next()->str() == "=") {
|
|
do {
|
|
first = first->next();
|
|
} while (!Token::Match(first->next(), ",|)"));
|
|
}
|
|
} else if (second->next()->str() == "[" && first->next()->str() != "[")
|
|
first = first->next();
|
|
|
|
// argument list has different number of arguments
|
|
else if (openParen == 1 && second->str() == ")" && first->str() != ")")
|
|
break;
|
|
|
|
// ckeck for type * x == type x[]
|
|
else if (Token::Match(first->next(), "* %name%| ,|)|=") &&
|
|
Token::Match(second->next(), "%name%| [ ] ,|)")) {
|
|
do {
|
|
first = first->next();
|
|
} while (!Token::Match(first->next(), ",|)"));
|
|
do {
|
|
second = second->next();
|
|
} while (!Token::Match(second->next(), ",|)"));
|
|
}
|
|
|
|
// const after *
|
|
else if (first->next()->str() == "*" && second->next()->str() == "*" &&
|
|
((first->strAt(2) != "const" && second->strAt(2) == "const") ||
|
|
(first->strAt(2) == "const" && second->strAt(2) != "const"))) {
|
|
if (first->strAt(2) != "const") {
|
|
first = first->next();
|
|
second = second->tokAt(2);
|
|
} else {
|
|
first = first->tokAt(2);
|
|
second = second->next();
|
|
}
|
|
}
|
|
|
|
// variable names are different
|
|
else if ((Token::Match(first->next(), "%name% ,|)|=|[") &&
|
|
Token::Match(second->next(), "%name% ,|)|[")) &&
|
|
(first->next()->str() != second->next()->str())) {
|
|
// skip variable names
|
|
first = first->next();
|
|
second = second->next();
|
|
|
|
// skip default value assignment
|
|
if (first->next()->str() == "=") {
|
|
do {
|
|
first = first->next();
|
|
} while (!Token::Match(first->next(), ",|)"));
|
|
}
|
|
}
|
|
|
|
// using namespace
|
|
else if (usingNamespace(scope, first->next(), second->next(), offset))
|
|
first = first->tokAt(offset);
|
|
|
|
// same type with different qualification
|
|
else if (typesMatch(scope, first->next(), nestedIn, second->next(), &first, &second))
|
|
;
|
|
|
|
// variable with class path
|
|
else if (arg_path_length && Token::Match(first->next(), "%name%") && first->strAt(1) != "const") {
|
|
std::string param = path;
|
|
|
|
if (Token::simpleMatch(second->next(), param.c_str(), param.size())) {
|
|
// check for redundant qualification before skipping it
|
|
if (!Token::simpleMatch(first->next(), param.c_str(), param.size())) {
|
|
second = second->tokAt(int(arg_path_length));
|
|
arg_path_length = 0;
|
|
}
|
|
}
|
|
|
|
// nested or base class variable
|
|
else if (arg_path_length <= 2 && Token::Match(first->next(), "%name%") &&
|
|
(Token::Match(second->next(), "%name% :: %name%") ||
|
|
(Token::Match(second->next(), "%name% <") &&
|
|
Token::Match(second->linkAt(1), "> :: %name%"))) &&
|
|
((second->next()->str() == scope->className) ||
|
|
(scope->definedType && scope->definedType->isDerivedFrom(second->next()->str()))) &&
|
|
(first->next()->str() == second->strAt(3))) {
|
|
if (Token::Match(second->next(), "%name% <"))
|
|
second = second->linkAt(1)->next();
|
|
else
|
|
second = second->tokAt(2);
|
|
}
|
|
|
|
// remove class name
|
|
else if (arg_path_length > 2 && first->strAt(1) != second->strAt(1)) {
|
|
std::string short_path = path;
|
|
unsigned int short_path_length = arg_path_length;
|
|
|
|
// remove last " :: "
|
|
short_path.resize(short_path.size() - 4);
|
|
short_path_length--;
|
|
|
|
// remove last name
|
|
std::string::size_type lastSpace = short_path.find_last_of(' ');
|
|
if (lastSpace != std::string::npos) {
|
|
short_path.resize(lastSpace+1);
|
|
short_path_length--;
|
|
if (short_path[short_path.size() - 1] == '>') {
|
|
short_path.resize(short_path.size() - 3);
|
|
while (short_path[short_path.size() - 1] == '<') {
|
|
lastSpace = short_path.find_last_of(' ');
|
|
short_path.resize(lastSpace+1);
|
|
short_path_length--;
|
|
}
|
|
}
|
|
}
|
|
|
|
param = short_path;
|
|
if (Token::simpleMatch(second->next(), param.c_str(), param.size())) {
|
|
second = second->tokAt(int(short_path_length));
|
|
arg_path_length = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
first = first->next();
|
|
second = second->next();
|
|
|
|
// reset path length
|
|
if (first->str() == "," || second->str() == ",")
|
|
arg_path_length = path_length;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool isUnknownType(const Token* start, const Token* end)
|
|
{
|
|
while (Token::Match(start, "const|volatile"))
|
|
start = start->next();
|
|
start = skipScopeIdentifiers(start);
|
|
if (start->tokAt(1) == end && !start->type() && !start->isStandardType())
|
|
return true;
|
|
// TODO: Try to deduce the type of the expression
|
|
if (Token::Match(start, "decltype|typeof"))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool Function::returnsConst(const Function* function, bool unknown)
|
|
{
|
|
if (!function)
|
|
return false;
|
|
if (function->type != Function::eFunction)
|
|
return false;
|
|
const Token* defEnd = function->returnDefEnd();
|
|
if (Token::findsimplematch(function->retDef, "const", defEnd))
|
|
return true;
|
|
// Check for unknown types, which could be a const
|
|
if (isUnknownType(function->retDef, defEnd))
|
|
return unknown;
|
|
return false;
|
|
}
|
|
|
|
bool Function::returnsReference(const Function* function, bool unknown)
|
|
{
|
|
if (!function)
|
|
return false;
|
|
if (function->type != Function::eFunction)
|
|
return false;
|
|
const Token* defEnd = function->returnDefEnd();
|
|
if (defEnd->strAt(-1) == "&")
|
|
return true;
|
|
// Check for unknown types, which could be a reference
|
|
if (isUnknownType(function->retDef, defEnd))
|
|
return unknown;
|
|
return false;
|
|
}
|
|
|
|
std::vector<const Token*> Function::findReturns(const Function* f)
|
|
{
|
|
std::vector<const Token*> result;
|
|
if (!f)
|
|
return result;
|
|
const Scope* scope = f->functionScope;
|
|
if (!scope)
|
|
return result;
|
|
for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) {
|
|
if (tok->str() == "{" && tok->scope() &&
|
|
(tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) {
|
|
tok = tok->link();
|
|
continue;
|
|
}
|
|
if (Token::simpleMatch(tok->astParent(), "return")) {
|
|
result.push_back(tok);
|
|
}
|
|
// Skip lambda functions since the scope may not be set correctly
|
|
const Token* lambdaEndToken = findLambdaEndToken(tok);
|
|
if (lambdaEndToken) {
|
|
tok = lambdaEndToken;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
const Token * Function::constructorMemberInitialization() const
|
|
{
|
|
if (!isConstructor() || !arg)
|
|
return nullptr;
|
|
if (Token::simpleMatch(arg->link(), ") :"))
|
|
return arg->link()->next();
|
|
if (Token::simpleMatch(arg->link(), ") noexcept (") && arg->link()->linkAt(2)->strAt(1) == ":")
|
|
return arg->link()->linkAt(2)->next();
|
|
return nullptr;
|
|
}
|
|
|
|
bool Function::isSafe(const Settings *settings) const
|
|
{
|
|
if (settings->safeChecks.externalFunctions) {
|
|
if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() != 0)
|
|
return true;
|
|
if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() != 0 || !isStatic()))
|
|
return true;
|
|
}
|
|
|
|
if (settings->safeChecks.internalFunctions) {
|
|
if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() == 0)
|
|
return true;
|
|
if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() == 0 || isStatic()))
|
|
return true;
|
|
}
|
|
|
|
if (settings->safeChecks.classes && access == AccessControl::Public && (nestedIn->type == Scope::ScopeType::eClass || nestedIn->type == Scope::ScopeType::eStruct))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart)
|
|
{
|
|
Function* function = nullptr;
|
|
// Lambda functions are always unique
|
|
if (tok->str() != "[") {
|
|
for (std::multimap<std::string, const Function *>::iterator i = scope->functionMap.find(tok->str()); i != scope->functionMap.end() && i->first == tok->str(); ++i) {
|
|
const Function *f = i->second;
|
|
if (f->hasBody())
|
|
continue;
|
|
if (f->argsMatch(scope, f->argDef, argStart, emptyString, 0)) {
|
|
function = const_cast<Function *>(i->second);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!function)
|
|
function = addGlobalFunctionDecl(scope, tok, argStart, funcStart);
|
|
|
|
function->arg = argStart;
|
|
function->token = funcStart;
|
|
function->hasBody(true);
|
|
|
|
addNewFunction(&scope, &tok);
|
|
|
|
if (scope) {
|
|
scope->function = function;
|
|
function->functionScope = scope;
|
|
return function;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Function* SymbolDatabase::addGlobalFunctionDecl(Scope*& scope, const Token *tok, const Token *argStart, const Token* funcStart)
|
|
{
|
|
Function function(mTokenizer, tok, scope, funcStart, argStart);
|
|
scope->addFunction(function);
|
|
return &scope->functionList.back();
|
|
}
|
|
|
|
void SymbolDatabase::addClassFunction(Scope **scope, const Token **tok, const Token *argStart)
|
|
{
|
|
const bool destructor((*tok)->previous()->str() == "~");
|
|
const bool has_const(argStart->link()->strAt(1) == "const");
|
|
const bool lval(argStart->link()->strAt(has_const ? 2 : 1) == "&");
|
|
const bool rval(argStart->link()->strAt(has_const ? 2 : 1) == "&&");
|
|
int count = 0;
|
|
std::string path;
|
|
unsigned int path_length = 0;
|
|
const Token *tok1 = (*tok);
|
|
|
|
if (destructor)
|
|
tok1 = tok1->previous();
|
|
|
|
// back up to head of path
|
|
while (tok1 && tok1->previous() && tok1->previous()->str() == "::" && tok1->tokAt(-2) &&
|
|
((tok1->tokAt(-2)->isName() && !tok1->tokAt(-2)->isStandardType()) ||
|
|
(tok1->strAt(-2) == ">" && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->previous(), "%name%")))) {
|
|
count++;
|
|
const Token * tok2 = tok1->tokAt(-2);
|
|
if (tok2->str() == ">")
|
|
tok2 = tok2->link()->previous();
|
|
|
|
if (tok2) {
|
|
do {
|
|
path = tok1->previous()->str() + " " + path;
|
|
tok1 = tok1->previous();
|
|
path_length++;
|
|
} while (tok1 != tok2);
|
|
} else
|
|
return; // syntax error ?
|
|
}
|
|
|
|
// syntax error?
|
|
if (!tok1)
|
|
return;
|
|
|
|
// add global namespace if present
|
|
if (tok1->strAt(-1) == "::") {
|
|
path_length++;
|
|
path.insert(0, ":: ");
|
|
}
|
|
|
|
std::list<Scope>::iterator it1;
|
|
|
|
// search for match
|
|
for (it1 = scopeList.begin(); it1 != scopeList.end(); ++it1) {
|
|
Scope *scope1 = &(*it1);
|
|
|
|
bool match = false;
|
|
|
|
// check in namespace if using found
|
|
if (*scope == scope1 && !scope1->usingList.empty()) {
|
|
std::list<Scope::UsingInfo>::const_iterator it2;
|
|
for (it2 = scope1->usingList.begin(); it2 != scope1->usingList.end(); ++it2) {
|
|
if (it2->scope) {
|
|
Function * func = findFunctionInScope(tok1, it2->scope, path, path_length);
|
|
if (func) {
|
|
if (!func->hasBody()) {
|
|
const Token *closeParen = (*tok)->next()->link();
|
|
if (closeParen) {
|
|
const Token *eq = mTokenizer->isFunctionHead(closeParen, ";");
|
|
if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) {
|
|
func->isDefault(true);
|
|
return;
|
|
}
|
|
}
|
|
func->hasBody(true);
|
|
func->token = *tok;
|
|
func->arg = argStart;
|
|
addNewFunction(scope, tok);
|
|
if (*scope) {
|
|
(*scope)->functionOf = func->nestedIn;
|
|
(*scope)->function = func;
|
|
(*scope)->function->functionScope = *scope;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (scope1->className == tok1->str() && (scope1->type != Scope::eFunction)) {
|
|
// do the scopes match (same scope) or do their names match (multiple namespaces)
|
|
if ((*scope == scope1->nestedIn) || (*scope &&
|
|
(*scope)->className == scope1->nestedIn->className &&
|
|
!(*scope)->className.empty() &&
|
|
(*scope)->type == scope1->nestedIn->type)) {
|
|
|
|
// nested scopes => check that they match
|
|
{
|
|
const Scope *s1 = *scope;
|
|
const Scope *s2 = scope1->nestedIn;
|
|
while (s1 && s2) {
|
|
if (s1->className != s2->className)
|
|
break;
|
|
s1 = s1->nestedIn;
|
|
s2 = s2->nestedIn;
|
|
}
|
|
// Not matching scopes
|
|
if (s1 || s2)
|
|
continue;
|
|
}
|
|
|
|
Scope *scope2 = scope1;
|
|
|
|
while (scope2 && count > 1) {
|
|
count--;
|
|
if (tok1->strAt(1) == "<")
|
|
tok1 = tok1->linkAt(1)->tokAt(2);
|
|
else
|
|
tok1 = tok1->tokAt(2);
|
|
scope2 = scope2->findRecordInNestedList(tok1->str());
|
|
}
|
|
|
|
if (count == 1 && scope2) {
|
|
match = true;
|
|
scope1 = scope2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (match) {
|
|
for (std::multimap<std::string, const Function *>::iterator it = scope1->functionMap.find((*tok)->str()); it != scope1->functionMap.end() && it->first == (*tok)->str(); ++it) {
|
|
Function * func = const_cast<Function *>(it->second);
|
|
if (!func->hasBody()) {
|
|
if (func->argsMatch(scope1, func->argDef, (*tok)->next(), path, path_length)) {
|
|
if (func->type == Function::eDestructor && destructor) {
|
|
func->hasBody(true);
|
|
} else if (func->type != Function::eDestructor && !destructor) {
|
|
// normal function?
|
|
const Token *closeParen = (*tok)->next()->link();
|
|
if (closeParen) {
|
|
const Token *eq = mTokenizer->isFunctionHead(closeParen, ";");
|
|
if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) {
|
|
func->isDefault(true);
|
|
return;
|
|
}
|
|
|
|
const bool hasConstKeyword = closeParen->next()->str() == "const";
|
|
if ((func->isConst() == hasConstKeyword) &&
|
|
(func->hasLvalRefQualifier() == lval) &&
|
|
(func->hasRvalRefQualifier() == rval)) {
|
|
func->hasBody(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (func->hasBody()) {
|
|
func->token = *tok;
|
|
func->arg = argStart;
|
|
addNewFunction(scope, tok);
|
|
if (*scope) {
|
|
(*scope)->functionOf = scope1;
|
|
(*scope)->function = func;
|
|
(*scope)->function->functionScope = *scope;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// class function of unknown class
|
|
addNewFunction(scope, tok);
|
|
}
|
|
|
|
void SymbolDatabase::addNewFunction(Scope **scope, const Token **tok)
|
|
{
|
|
const Token *tok1 = *tok;
|
|
scopeList.emplace_back(this, tok1, *scope);
|
|
Scope *newScope = &scopeList.back();
|
|
|
|
// find start of function '{'
|
|
bool foundInitList = false;
|
|
while (tok1 && tok1->str() != "{" && tok1->str() != ";") {
|
|
if (tok1->link() && Token::Match(tok1, "(|<")) {
|
|
tok1 = tok1->link();
|
|
} else if (foundInitList &&
|
|
Token::Match(tok1, "%name%|> {") &&
|
|
Token::Match(tok1->linkAt(1), "} ,|{")) {
|
|
tok1 = tok1->linkAt(1);
|
|
} else {
|
|
if (tok1->str() == ":")
|
|
foundInitList = true;
|
|
tok1 = tok1->next();
|
|
}
|
|
}
|
|
|
|
if (tok1 && tok1->str() == "{") {
|
|
newScope->bodyStart = tok1;
|
|
newScope->bodyEnd = tok1->link();
|
|
|
|
// syntax error?
|
|
if (!newScope->bodyEnd) {
|
|
scopeList.pop_back();
|
|
while (tok1->next())
|
|
tok1 = tok1->next();
|
|
*scope = nullptr;
|
|
*tok = tok1;
|
|
return;
|
|
}
|
|
|
|
(*scope)->nestedList.push_back(newScope);
|
|
*scope = newScope;
|
|
*tok = tok1;
|
|
} else {
|
|
scopeList.pop_back();
|
|
*scope = nullptr;
|
|
*tok = nullptr;
|
|
}
|
|
}
|
|
|
|
bool Type::isClassType() const
|
|
{
|
|
return classScope && classScope->type == Scope::ScopeType::eClass;
|
|
}
|
|
|
|
bool Type::isEnumType() const
|
|
{
|
|
//We explicitly check for "enum" because a forward declared enum doesn't get its own scope
|
|
return (classDef && classDef->str() == "enum") ||
|
|
(classScope && classScope->type == Scope::ScopeType::eEnum);
|
|
}
|
|
|
|
bool Type::isStructType() const
|
|
{
|
|
return classScope && classScope->type == Scope::ScopeType::eStruct;
|
|
}
|
|
|
|
bool Type::isUnionType() const
|
|
{
|
|
return classScope && classScope->type == Scope::ScopeType::eUnion;
|
|
}
|
|
|
|
const Token *Type::initBaseInfo(const Token *tok, const Token *tok1)
|
|
{
|
|
// goto initial '{'
|
|
const Token *tok2 = tok1;
|
|
while (tok2 && tok2->str() != "{") {
|
|
// skip unsupported templates
|
|
if (tok2->str() == "<")
|
|
tok2 = tok2->link();
|
|
|
|
// check for base classes
|
|
else if (Token::Match(tok2, ":|,")) {
|
|
tok2 = tok2->next();
|
|
|
|
// check for invalid code
|
|
if (!tok2 || !tok2->next())
|
|
return nullptr;
|
|
|
|
Type::BaseInfo base;
|
|
|
|
if (tok2->str() == "virtual") {
|
|
base.isVirtual = true;
|
|
tok2 = tok2->next();
|
|
}
|
|
|
|
if (tok2->str() == "public") {
|
|
base.access = AccessControl::Public;
|
|
tok2 = tok2->next();
|
|
} else if (tok2->str() == "protected") {
|
|
base.access = AccessControl::Protected;
|
|
tok2 = tok2->next();
|
|
} else if (tok2->str() == "private") {
|
|
base.access = AccessControl::Private;
|
|
tok2 = tok2->next();
|
|
} else {
|
|
if (tok->str() == "class")
|
|
base.access = AccessControl::Private;
|
|
else if (tok->str() == "struct")
|
|
base.access = AccessControl::Public;
|
|
}
|
|
if (!tok2)
|
|
return nullptr;
|
|
if (tok2->str() == "virtual") {
|
|
base.isVirtual = true;
|
|
tok2 = tok2->next();
|
|
}
|
|
if (!tok2)
|
|
return nullptr;
|
|
|
|
base.nameTok = tok2;
|
|
// handle global namespace
|
|
if (tok2->str() == "::") {
|
|
tok2 = tok2->next();
|
|
}
|
|
|
|
// handle derived base classes
|
|
while (Token::Match(tok2, "%name% ::")) {
|
|
tok2 = tok2->tokAt(2);
|
|
}
|
|
if (!tok2)
|
|
return nullptr;
|
|
|
|
base.name = tok2->str();
|
|
|
|
tok2 = tok2->next();
|
|
// add unhandled templates
|
|
if (tok2 && tok2->link() && tok2->str() == "<") {
|
|
for (const Token* const end = tok2->link()->next(); tok2 != end; tok2 = tok2->next()) {
|
|
base.name += tok2->str();
|
|
}
|
|
}
|
|
|
|
const Type * baseType = classScope->check->findType(base.nameTok, enclosingScope);
|
|
if (baseType && !baseType->findDependency(this))
|
|
base.type = baseType;
|
|
|
|
// save pattern for base class name
|
|
derivedFrom.push_back(base);
|
|
} else
|
|
tok2 = tok2->next();
|
|
}
|
|
|
|
return tok2;
|
|
}
|
|
|
|
const std::string& Type::name() const
|
|
{
|
|
const Token* next = classDef->next();
|
|
if (classScope && classScope->enumClass && isEnumType())
|
|
return next->strAt(1);
|
|
else if (next->str() == "class")
|
|
return next->strAt(1);
|
|
else if (next->isName())
|
|
return next->str();
|
|
return emptyString;
|
|
}
|
|
|
|
void SymbolDatabase::debugMessage(const Token *tok, const std::string &type, const std::string &msg) const
|
|
{
|
|
if (tok && mSettings->debugwarnings) {
|
|
const std::list<const Token*> locationList(1, tok);
|
|
const ErrorMessage errmsg(locationList, &mTokenizer->list,
|
|
Severity::debug,
|
|
type,
|
|
msg,
|
|
Certainty::normal);
|
|
if (mErrorLogger)
|
|
mErrorLogger->reportErr(errmsg);
|
|
}
|
|
}
|
|
|
|
const Function* Type::getFunction(const std::string& funcName) const
|
|
{
|
|
if (classScope) {
|
|
const std::multimap<std::string, const Function *>::const_iterator it = classScope->functionMap.find(funcName);
|
|
|
|
if (it != classScope->functionMap.end())
|
|
return it->second;
|
|
}
|
|
|
|
for (const Type::BaseInfo & i : derivedFrom) {
|
|
if (i.type) {
|
|
const Function* const func = i.type->getFunction(funcName);
|
|
if (func)
|
|
return func;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool Type::hasCircularDependencies(std::set<BaseInfo>* ancestors) const
|
|
{
|
|
std::set<BaseInfo> knownAncestors;
|
|
if (!ancestors) {
|
|
ancestors=&knownAncestors;
|
|
}
|
|
for (std::vector<BaseInfo>::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) {
|
|
if (!parent->type)
|
|
continue;
|
|
else if (this==parent->type)
|
|
return true;
|
|
else if (ancestors->find(*parent)!=ancestors->end())
|
|
return true;
|
|
else {
|
|
ancestors->insert(*parent);
|
|
if (parent->type->hasCircularDependencies(ancestors))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Type::findDependency(const Type* ancestor) const
|
|
{
|
|
if (this==ancestor)
|
|
return true;
|
|
for (std::vector<BaseInfo>::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) {
|
|
if (parent->type && (parent->type == this || parent->type->findDependency(ancestor)))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Type::isDerivedFrom(const std::string & ancestor) const
|
|
{
|
|
for (std::vector<BaseInfo>::const_iterator parent=derivedFrom.begin(); parent!=derivedFrom.end(); ++parent) {
|
|
if (parent->name == ancestor)
|
|
return true;
|
|
if (parent->type && parent->type->isDerivedFrom(ancestor))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Variable::arrayDimensions(const Settings* settings)
|
|
{
|
|
const Library::Container* container = settings->library.detectContainer(mTypeStartToken);
|
|
if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) {
|
|
const Token* tok = Token::findsimplematch(mTypeStartToken, "<");
|
|
if (tok) {
|
|
Dimension dimension_;
|
|
tok = tok->next();
|
|
for (int i = 0; i < container->size_templateArgNo && tok; i++) {
|
|
tok = tok->nextTemplateArgument();
|
|
}
|
|
if (Token::Match(tok, "%num% [,>]")) {
|
|
dimension_.tok = tok;
|
|
dimension_.known = true;
|
|
dimension_.num = MathLib::toLongNumber(tok->str());
|
|
} else if (tok) {
|
|
dimension_.tok = tok;
|
|
dimension_.known = false;
|
|
}
|
|
mDimensions.push_back(dimension_);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
const Token *dim = mNameToken;
|
|
if (!dim) {
|
|
// Argument without name
|
|
dim = mTypeEndToken;
|
|
// back up to start of array dimensions
|
|
while (dim && dim->str() == "]")
|
|
dim = dim->link()->previous();
|
|
}
|
|
if (dim)
|
|
dim = dim->next();
|
|
if (dim && dim->str() == ")")
|
|
dim = dim->next();
|
|
|
|
bool arr = false;
|
|
while (dim && dim->next() && dim->str() == "[") {
|
|
Dimension dimension_;
|
|
dimension_.known = false;
|
|
// check for empty array dimension []
|
|
if (dim->next()->str() != "]") {
|
|
dimension_.tok = dim->astOperand2();
|
|
ValueFlow::valueFlowConstantFoldAST(const_cast<Token *>(dimension_.tok), settings);
|
|
if (dimension_.tok && dimension_.tok->hasKnownIntValue()) {
|
|
dimension_.num = dimension_.tok->getKnownIntValue();
|
|
dimension_.known = true;
|
|
}
|
|
}
|
|
mDimensions.push_back(dimension_);
|
|
dim = dim->link()->next();
|
|
arr = true;
|
|
}
|
|
return arr;
|
|
}
|
|
|
|
static std::ostream & operator << (std::ostream & s, Scope::ScopeType type)
|
|
{
|
|
s << (type == Scope::eGlobal ? "Global" :
|
|
type == Scope::eClass ? "Class" :
|
|
type == Scope::eStruct ? "Struct" :
|
|
type == Scope::eUnion ? "Union" :
|
|
type == Scope::eNamespace ? "Namespace" :
|
|
type == Scope::eFunction ? "Function" :
|
|
type == Scope::eIf ? "If" :
|
|
type == Scope::eElse ? "Else" :
|
|
type == Scope::eFor ? "For" :
|
|
type == Scope::eWhile ? "While" :
|
|
type == Scope::eDo ? "Do" :
|
|
type == Scope::eSwitch ? "Switch" :
|
|
type == Scope::eTry ? "Try" :
|
|
type == Scope::eCatch ? "Catch" :
|
|
type == Scope::eUnconditional ? "Unconditional" :
|
|
type == Scope::eLambda ? "Lambda" :
|
|
type == Scope::eEnum ? "Enum" :
|
|
"Unknown");
|
|
return s;
|
|
}
|
|
|
|
static std::string accessControlToString(const AccessControl& access)
|
|
{
|
|
switch (access) {
|
|
case AccessControl::Public:
|
|
return "Public";
|
|
case AccessControl::Protected:
|
|
return "Protected";
|
|
case AccessControl::Private:
|
|
return "Private";
|
|
case AccessControl::Global:
|
|
return "Global";
|
|
case AccessControl::Namespace:
|
|
return "Namespace";
|
|
case AccessControl::Argument:
|
|
return "Argument";
|
|
case AccessControl::Local:
|
|
return "Local";
|
|
case AccessControl::Throw:
|
|
return "Throw";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
static std::string tokenToString(const Token* tok, const Tokenizer* tokenizer)
|
|
{
|
|
std::ostringstream oss;
|
|
if (tok) {
|
|
oss << tok->str() << " ";
|
|
oss << tokenizer->list.fileLine(tok) << " ";
|
|
}
|
|
oss << tok;
|
|
return oss.str();
|
|
}
|
|
|
|
static std::string scopeToString(const Scope* scope, const Tokenizer* tokenizer)
|
|
{
|
|
std::ostringstream oss;
|
|
if (scope) {
|
|
oss << scope->type << " ";
|
|
if (!scope->className.empty())
|
|
oss << scope->className << " ";
|
|
if (scope->classDef)
|
|
oss << tokenizer->list.fileLine(scope->classDef) << " ";
|
|
}
|
|
oss << scope;
|
|
return oss.str();
|
|
}
|
|
|
|
static std::string tokenType(const Token * tok)
|
|
{
|
|
std::ostringstream oss;
|
|
if (tok) {
|
|
if (tok->isUnsigned())
|
|
oss << "unsigned ";
|
|
else if (tok->isSigned())
|
|
oss << "signed ";
|
|
if (tok->isComplex())
|
|
oss << "_Complex ";
|
|
if (tok->isLong())
|
|
oss << "long ";
|
|
oss << tok->str();
|
|
}
|
|
return oss.str();
|
|
}
|
|
|
|
void SymbolDatabase::printVariable(const Variable *var, const char *indent) const
|
|
{
|
|
std::cout << indent << "mNameToken: " << tokenToString(var->nameToken(), mTokenizer) << std::endl;
|
|
if (var->nameToken()) {
|
|
std::cout << indent << " declarationId: " << var->declarationId() << std::endl;
|
|
}
|
|
std::cout << indent << "mTypeStartToken: " << tokenToString(var->typeStartToken(), mTokenizer) << std::endl;
|
|
std::cout << indent << "mTypeEndToken: " << tokenToString(var->typeEndToken(), mTokenizer) << std::endl;
|
|
|
|
if (var->typeStartToken()) {
|
|
const Token * autoTok = nullptr;
|
|
std::cout << indent << " ";
|
|
for (const Token * tok = var->typeStartToken(); tok != var->typeEndToken()->next(); tok = tok->next()) {
|
|
std::cout << " " << tokenType(tok);
|
|
if (tok->str() == "auto")
|
|
autoTok = tok;
|
|
}
|
|
std::cout << std::endl;
|
|
if (autoTok) {
|
|
const ValueType * valueType = autoTok->valueType();
|
|
std::cout << indent << " auto valueType: " << valueType << std::endl;
|
|
if (var->typeStartToken()->valueType()) {
|
|
std::cout << indent << " " << valueType->str() << std::endl;
|
|
}
|
|
}
|
|
} else if (var->valueType()) {
|
|
std::cout << indent << " " << var->valueType()->str() << std::endl;
|
|
}
|
|
std::cout << indent << "mIndex: " << var->index() << std::endl;
|
|
std::cout << indent << "mAccess: " << accessControlToString(var->accessControl()) << std::endl;
|
|
std::cout << indent << "mFlags: " << std::endl;
|
|
std::cout << indent << " isMutable: " << var->isMutable() << std::endl;
|
|
std::cout << indent << " isStatic: " << var->isStatic() << std::endl;
|
|
std::cout << indent << " isExtern: " << var->isExtern() << std::endl;
|
|
std::cout << indent << " isLocal: " << var->isLocal() << std::endl;
|
|
std::cout << indent << " isConst: " << var->isConst() << std::endl;
|
|
std::cout << indent << " isClass: " << var->isClass() << std::endl;
|
|
std::cout << indent << " isArray: " << var->isArray() << std::endl;
|
|
std::cout << indent << " isPointer: " << var->isPointer() << std::endl;
|
|
std::cout << indent << " isReference: " << var->isReference() << std::endl;
|
|
std::cout << indent << " isRValueRef: " << var->isRValueReference() << std::endl;
|
|
std::cout << indent << " hasDefault: " << var->hasDefault() << std::endl;
|
|
std::cout << indent << " isStlType: " << var->isStlType() << std::endl;
|
|
std::cout << indent << "mType: ";
|
|
if (var->type()) {
|
|
std::cout << var->type()->type() << " " << var->type()->name();
|
|
std::cout << " " << mTokenizer->list.fileLine(var->type()->classDef);
|
|
std::cout << " " << var->type() << std::endl;
|
|
} else
|
|
std::cout << "none" << std::endl;
|
|
|
|
if (var->nameToken()) {
|
|
const ValueType * valueType = var->nameToken()->valueType();
|
|
std::cout << indent << "valueType: " << valueType << std::endl;
|
|
if (valueType) {
|
|
std::cout << indent << " " << valueType->str() << std::endl;
|
|
}
|
|
}
|
|
|
|
std::cout << indent << "mScope: " << scopeToString(var->scope(), mTokenizer) << std::endl;
|
|
|
|
std::cout << indent << "mDimensions:";
|
|
for (std::size_t i = 0; i < var->dimensions().size(); i++) {
|
|
std::cout << " " << var->dimension(i);
|
|
if (!var->dimensions()[i].known)
|
|
std::cout << "?";
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
void SymbolDatabase::printOut(const char *title) const
|
|
{
|
|
std::cout << std::setiosflags(std::ios::boolalpha);
|
|
if (title)
|
|
std::cout << "\n### " << title << " ###\n";
|
|
|
|
for (std::list<Scope>::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) {
|
|
std::cout << "Scope: " << &*scope << " " << scope->type << std::endl;
|
|
std::cout << " className: " << scope->className << std::endl;
|
|
std::cout << " classDef: " << tokenToString(scope->classDef, mTokenizer) << std::endl;
|
|
std::cout << " bodyStart: " << tokenToString(scope->bodyStart, mTokenizer) << std::endl;
|
|
std::cout << " bodyEnd: " << tokenToString(scope->bodyEnd, mTokenizer) << std::endl;
|
|
|
|
std::list<Function>::const_iterator func;
|
|
|
|
// find the function body if not implemented inline
|
|
for (func = scope->functionList.begin(); func != scope->functionList.end(); ++func) {
|
|
std::cout << " Function: " << &*func << std::endl;
|
|
std::cout << " name: " << tokenToString(func->tokenDef, mTokenizer) << std::endl;
|
|
std::cout << " type: " << (func->type == Function::eConstructor? "Constructor" :
|
|
func->type == Function::eCopyConstructor ? "CopyConstructor" :
|
|
func->type == Function::eMoveConstructor ? "MoveConstructor" :
|
|
func->type == Function::eOperatorEqual ? "OperatorEqual" :
|
|
func->type == Function::eDestructor ? "Destructor" :
|
|
func->type == Function::eFunction ? "Function" :
|
|
func->type == Function::eLambda ? "Lambda" :
|
|
"Unknown") << std::endl;
|
|
std::cout << " access: " << accessControlToString(func->access) << std::endl;
|
|
std::cout << " hasBody: " << func->hasBody() << std::endl;
|
|
std::cout << " isInline: " << func->isInline() << std::endl;
|
|
std::cout << " isConst: " << func->isConst() << std::endl;
|
|
std::cout << " hasVirtualSpecifier: " << func->hasVirtualSpecifier() << std::endl;
|
|
std::cout << " isPure: " << func->isPure() << std::endl;
|
|
std::cout << " isStatic: " << func->isStatic() << std::endl;
|
|
std::cout << " isStaticLocal: " << func->isStaticLocal() << std::endl;
|
|
std::cout << " isExtern: " << func->isExtern() << std::endl;
|
|
std::cout << " isFriend: " << func->isFriend() << std::endl;
|
|
std::cout << " isExplicit: " << func->isExplicit() << std::endl;
|
|
std::cout << " isDefault: " << func->isDefault() << std::endl;
|
|
std::cout << " isDelete: " << func->isDelete() << std::endl;
|
|
std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl;
|
|
std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl;
|
|
std::cout << " isNoExcept: " << func->isNoExcept() << std::endl;
|
|
std::cout << " isThrow: " << func->isThrow() << std::endl;
|
|
std::cout << " isOperator: " << func->isOperator() << std::endl;
|
|
std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl;
|
|
std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl;
|
|
std::cout << " isVariadic: " << func->isVariadic() << std::endl;
|
|
std::cout << " isVolatile: " << func->isVolatile() << std::endl;
|
|
std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl;
|
|
std::cout << " attributes:";
|
|
if (func->isAttributeConst())
|
|
std::cout << " const ";
|
|
if (func->isAttributePure())
|
|
std::cout << " pure ";
|
|
if (func->isAttributeNoreturn())
|
|
std::cout << " noreturn ";
|
|
if (func->isAttributeNothrow())
|
|
std::cout << " nothrow ";
|
|
if (func->isAttributeConstructor())
|
|
std::cout << " constructor ";
|
|
if (func->isAttributeDestructor())
|
|
std::cout << " destructor ";
|
|
if (func->isAttributeNodiscard())
|
|
std::cout << " nodiscard ";
|
|
std::cout << std::endl;
|
|
std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl;
|
|
std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl;
|
|
std::cout << " tokenDef: " << tokenToString(func->tokenDef, mTokenizer) << std::endl;
|
|
std::cout << " argDef: " << tokenToString(func->argDef, mTokenizer) << std::endl;
|
|
if (!func->isConstructor() && !func->isDestructor())
|
|
std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl;
|
|
if (func->retDef) {
|
|
std::cout << " ";
|
|
for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next())
|
|
std::cout << " " << tokenType(tok);
|
|
std::cout << std::endl;
|
|
}
|
|
std::cout << " retType: " << func->retType << std::endl;
|
|
|
|
if (func->tokenDef->next()->valueType()) {
|
|
const ValueType * valueType = func->tokenDef->next()->valueType();
|
|
std::cout << " valueType: " << valueType << std::endl;
|
|
if (valueType) {
|
|
std::cout << " " << valueType->str() << std::endl;
|
|
}
|
|
}
|
|
|
|
if (func->hasBody()) {
|
|
std::cout << " token: " << tokenToString(func->token, mTokenizer) << std::endl;
|
|
std::cout << " arg: " << tokenToString(func->arg, mTokenizer) << std::endl;
|
|
}
|
|
std::cout << " nestedIn: " << scopeToString(func->nestedIn, mTokenizer) << std::endl;
|
|
std::cout << " functionScope: " << scopeToString(func->functionScope, mTokenizer) << std::endl;
|
|
|
|
std::list<Variable>::const_iterator var;
|
|
|
|
for (var = func->argumentList.begin(); var != func->argumentList.end(); ++var) {
|
|
std::cout << " Variable: " << &*var << std::endl;
|
|
printVariable(&*var, " ");
|
|
}
|
|
}
|
|
|
|
std::list<Variable>::const_iterator var;
|
|
|
|
for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) {
|
|
std::cout << " Variable: " << &*var << std::endl;
|
|
printVariable(&*var, " ");
|
|
}
|
|
|
|
if (scope->type == Scope::eEnum) {
|
|
std::cout << " enumType: ";
|
|
if (scope->enumType) {
|
|
std::cout << scope->enumType->stringify(false, true, false);
|
|
} else
|
|
std::cout << "int";
|
|
std::cout << std::endl;
|
|
std::cout << " enumClass: " << scope->enumClass << std::endl;
|
|
for (const Enumerator &enumerator : scope->enumeratorList) {
|
|
std::cout << " Enumerator: " << enumerator.name->str() << " = ";
|
|
if (enumerator.value_known)
|
|
std::cout << enumerator.value;
|
|
|
|
if (enumerator.start) {
|
|
const Token * tok = enumerator.start;
|
|
std::cout << (enumerator.value_known ? " " : "") << "[" << tok->str();
|
|
while (tok && tok != enumerator.end) {
|
|
if (tok->next())
|
|
std::cout << " " << tok->next()->str();
|
|
tok = tok->next();
|
|
}
|
|
|
|
std::cout << "]";
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
std::cout << " nestedIn: " << scope->nestedIn;
|
|
if (scope->nestedIn) {
|
|
std::cout << " " << scope->nestedIn->type << " "
|
|
<< scope->nestedIn->className;
|
|
}
|
|
std::cout << std::endl;
|
|
|
|
std::cout << " definedType: " << scope->definedType << std::endl;
|
|
|
|
std::cout << " nestedList[" << scope->nestedList.size() << "] = (";
|
|
|
|
std::list<Scope *>::const_iterator nsi;
|
|
|
|
std::size_t count = scope->nestedList.size();
|
|
for (nsi = scope->nestedList.begin(); nsi != scope->nestedList.end(); ++nsi) {
|
|
std::cout << " " << (*nsi) << " " << (*nsi)->type << " " << (*nsi)->className;
|
|
if (count-- > 1)
|
|
std::cout << ",";
|
|
}
|
|
|
|
std::cout << " )" << std::endl;
|
|
|
|
std::list<Scope::UsingInfo>::const_iterator use;
|
|
|
|
for (use = scope->usingList.begin(); use != scope->usingList.end(); ++use) {
|
|
std::cout << " using: " << use->scope << " " << use->start->strAt(2);
|
|
const Token *tok1 = use->start->tokAt(3);
|
|
while (tok1 && tok1->str() == "::") {
|
|
std::cout << "::" << tok1->strAt(1);
|
|
tok1 = tok1->tokAt(2);
|
|
}
|
|
std::cout << " " << mTokenizer->list.fileLine(use->start) << std::endl;
|
|
}
|
|
|
|
std::cout << " functionOf: " << scopeToString(scope->functionOf, mTokenizer) << std::endl;
|
|
|
|
std::cout << " function: " << scope->function;
|
|
if (scope->function)
|
|
std::cout << " " << scope->function->name();
|
|
std::cout << std::endl;
|
|
}
|
|
|
|
for (std::list<Type>::const_iterator type = typeList.begin(); type != typeList.end(); ++type) {
|
|
std::cout << "Type: " << &(*type) << std::endl;
|
|
std::cout << " name: " << type->name() << std::endl;
|
|
std::cout << " classDef: " << tokenToString(type->classDef, mTokenizer) << std::endl;
|
|
std::cout << " classScope: " << type->classScope << std::endl;
|
|
std::cout << " enclosingScope: " << type->enclosingScope;
|
|
if (type->enclosingScope) {
|
|
std::cout << " " << type->enclosingScope->type << " "
|
|
<< type->enclosingScope->className;
|
|
}
|
|
std::cout << std::endl;
|
|
std::cout << " needInitialization: " << (type->needInitialization == Type::NeedInitialization::Unknown ? "Unknown" :
|
|
type->needInitialization == Type::NeedInitialization::True ? "True" :
|
|
type->needInitialization == Type::NeedInitialization::False ? "False" :
|
|
"Invalid") << std::endl;
|
|
|
|
std::cout << " derivedFrom[" << type->derivedFrom.size() << "] = (";
|
|
std::size_t count = type->derivedFrom.size();
|
|
for (const Type::BaseInfo & i : type->derivedFrom) {
|
|
if (i.isVirtual)
|
|
std::cout << "Virtual ";
|
|
|
|
std::cout << (i.access == AccessControl::Public ? " Public" :
|
|
i.access == AccessControl::Protected ? " Protected" :
|
|
i.access == AccessControl::Private ? " Private" :
|
|
" Unknown");
|
|
|
|
if (i.type)
|
|
std::cout << " " << i.type;
|
|
else
|
|
std::cout << " Unknown";
|
|
|
|
std::cout << " " << i.name;
|
|
if (count-- > 1)
|
|
std::cout << ",";
|
|
}
|
|
|
|
std::cout << " )" << std::endl;
|
|
|
|
std::cout << " friendList[" << type->friendList.size() << "] = (";
|
|
for (size_t i = 0; i < type->friendList.size(); i++) {
|
|
if (type->friendList[i].type)
|
|
std::cout << type->friendList[i].type;
|
|
else
|
|
std::cout << " Unknown";
|
|
|
|
std::cout << ' ';
|
|
if (type->friendList[i].nameEnd)
|
|
std::cout << type->friendList[i].nameEnd->str();
|
|
if (i+1 < type->friendList.size())
|
|
std::cout << ',';
|
|
}
|
|
|
|
std::cout << " )" << std::endl;
|
|
}
|
|
|
|
for (std::size_t i = 1; i < mVariableList.size(); i++) {
|
|
std::cout << "mVariableList[" << i << "]: " << mVariableList[i];
|
|
if (mVariableList[i]) {
|
|
std::cout << " " << mVariableList[i]->name() << " "
|
|
<< mTokenizer->list.fileLine(mVariableList[i]->nameToken());
|
|
}
|
|
std::cout << std::endl;
|
|
}
|
|
std::cout << std::resetiosflags(std::ios::boolalpha);
|
|
}
|
|
|
|
void SymbolDatabase::printXml(std::ostream &out) const
|
|
{
|
|
out << std::setiosflags(std::ios::boolalpha);
|
|
|
|
std::set<const Variable *> variables;
|
|
|
|
// Scopes..
|
|
out << " <scopes>" << std::endl;
|
|
for (std::list<Scope>::const_iterator scope = scopeList.begin(); scope != scopeList.end(); ++scope) {
|
|
out << " <scope";
|
|
out << " id=\"" << &*scope << "\"";
|
|
out << " type=\"" << scope->type << "\"";
|
|
if (!scope->className.empty())
|
|
out << " className=\"" << ErrorLogger::toxml(scope->className) << "\"";
|
|
if (scope->bodyStart)
|
|
out << " bodyStart=\"" << scope->bodyStart << '\"';
|
|
if (scope->bodyEnd)
|
|
out << " bodyEnd=\"" << scope->bodyEnd << '\"';
|
|
if (scope->nestedIn)
|
|
out << " nestedIn=\"" << scope->nestedIn << "\"";
|
|
if (scope->function)
|
|
out << " function=\"" << scope->function << "\"";
|
|
if (scope->functionList.empty() && scope->varlist.empty())
|
|
out << "/>" << std::endl;
|
|
else {
|
|
out << '>' << std::endl;
|
|
if (!scope->functionList.empty()) {
|
|
out << " <functionList>" << std::endl;
|
|
for (std::list<Function>::const_iterator function = scope->functionList.begin(); function != scope->functionList.end(); ++function) {
|
|
out << " <function id=\"" << &*function
|
|
<< "\" token=\"" << function->token
|
|
<< "\" tokenDef=\"" << function->tokenDef
|
|
<< "\" name=\"" << ErrorLogger::toxml(function->name()) << '\"';
|
|
out << " type=\"" << (function->type == Function::eConstructor? "Constructor" :
|
|
function->type == Function::eCopyConstructor ? "CopyConstructor" :
|
|
function->type == Function::eMoveConstructor ? "MoveConstructor" :
|
|
function->type == Function::eOperatorEqual ? "OperatorEqual" :
|
|
function->type == Function::eDestructor ? "Destructor" :
|
|
function->type == Function::eFunction ? "Function" :
|
|
function->type == Function::eLambda ? "Lambda" :
|
|
"Unknown") << '\"';
|
|
if (function->nestedIn->definedType) {
|
|
if (function->hasVirtualSpecifier())
|
|
out << " hasVirtualSpecifier=\"true\"";
|
|
else if (function->isImplicitlyVirtual())
|
|
out << " isImplicitlyVirtual=\"true\"";
|
|
}
|
|
if (function->isStatic())
|
|
out << " isStatic=\"true\"";
|
|
if (function->argCount() == 0U)
|
|
out << "/>" << std::endl;
|
|
else {
|
|
out << ">" << std::endl;
|
|
for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) {
|
|
const Variable *arg = function->getArgumentVar(argnr);
|
|
out << " <arg nr=\"" << argnr+1 << "\" variable=\"" << arg << "\"/>" << std::endl;
|
|
variables.insert(arg);
|
|
}
|
|
out << " </function>" << std::endl;
|
|
}
|
|
}
|
|
out << " </functionList>" << std::endl;
|
|
}
|
|
if (!scope->varlist.empty()) {
|
|
out << " <varlist>" << std::endl;
|
|
for (std::list<Variable>::const_iterator var = scope->varlist.begin(); var != scope->varlist.end(); ++var)
|
|
out << " <var id=\"" << &*var << "\"/>" << std::endl;
|
|
out << " </varlist>" << std::endl;
|
|
}
|
|
out << " </scope>" << std::endl;
|
|
}
|
|
}
|
|
out << " </scopes>" << std::endl;
|
|
|
|
// Variables..
|
|
for (const Variable *var : mVariableList)
|
|
variables.insert(var);
|
|
out << " <variables>" << std::endl;
|
|
for (const Variable *var : variables) {
|
|
if (!var)
|
|
continue;
|
|
out << " <var id=\"" << var << '\"';
|
|
out << " nameToken=\"" << var->nameToken() << '\"';
|
|
out << " typeStartToken=\"" << var->typeStartToken() << '\"';
|
|
out << " typeEndToken=\"" << var->typeEndToken() << '\"';
|
|
out << " access=\"" << accessControlToString(var->mAccess) << '\"';
|
|
out << " scope=\"" << var->scope() << '\"';
|
|
if (var->valueType())
|
|
out << " constness=\"" << var->valueType()->constness << '\"';
|
|
out << " isArgument=\"" << var->isArgument() << '\"';
|
|
out << " isArray=\"" << var->isArray() << '\"';
|
|
out << " isClass=\"" << var->isClass() << '\"';
|
|
out << " isConst=\"" << var->isConst() << '\"';
|
|
out << " isExtern=\"" << var->isExtern() << '\"';
|
|
out << " isLocal=\"" << var->isLocal() << '\"';
|
|
out << " isPointer=\"" << var->isPointer() << '\"';
|
|
out << " isReference=\"" << var->isReference() << '\"';
|
|
out << " isStatic=\"" << var->isStatic() << '\"';
|
|
out << "/>" << std::endl;
|
|
}
|
|
out << " </variables>" << std::endl;
|
|
out << std::resetiosflags(std::ios::boolalpha);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
static const Type* findVariableTypeIncludingUsedNamespaces(const SymbolDatabase* symbolDatabase, const Scope* scope, const Token* typeTok)
|
|
{
|
|
const Type* argType = symbolDatabase->findVariableType(scope, typeTok);
|
|
if (argType)
|
|
return argType;
|
|
|
|
// look for variable type in any using namespace in this scope or above
|
|
while (scope) {
|
|
for (const Scope::UsingInfo &ui : scope->usingList) {
|
|
if (ui.scope) {
|
|
argType = symbolDatabase->findVariableType(ui.scope, typeTok);
|
|
if (argType)
|
|
return argType;
|
|
}
|
|
}
|
|
scope = scope->nestedIn;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope)
|
|
{
|
|
// check for non-empty argument list "( ... )"
|
|
const Token * start = arg ? arg : argDef;
|
|
if (!Token::simpleMatch(start, "("))
|
|
return;
|
|
if (!(start && start->link() != start->next() && !Token::simpleMatch(start, "( void )")))
|
|
return;
|
|
|
|
unsigned int count = 0;
|
|
|
|
for (const Token* tok = start->next(); tok; tok = tok->next()) {
|
|
if (Token::Match(tok, ",|)"))
|
|
return; // Syntax error
|
|
|
|
const Token* startTok = tok;
|
|
const Token* endTok = nullptr;
|
|
const Token* nameTok = nullptr;
|
|
|
|
do {
|
|
if (Token::simpleMatch(tok, "decltype (")) {
|
|
tok = tok->linkAt(1)->next();
|
|
continue;
|
|
}
|
|
if (tok != startTok && !nameTok && Token::Match(tok, "( & %var% ) [")) {
|
|
nameTok = tok->tokAt(2);
|
|
endTok = nameTok->previous();
|
|
tok = tok->link();
|
|
} else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) ( ) [,)]")) {
|
|
nameTok = tok->tokAt(2);
|
|
endTok = nameTok->previous();
|
|
tok = tok->link()->tokAt(2);
|
|
} else if (tok->varId() != 0) {
|
|
nameTok = tok;
|
|
endTok = tok->previous();
|
|
} else if (tok->str() == "[") {
|
|
// skip array dimension(s)
|
|
tok = tok->link();
|
|
while (tok->next()->str() == "[")
|
|
tok = tok->next()->link();
|
|
} else if (tok->str() == "<") {
|
|
tok = tok->link();
|
|
if (!tok) // something is wrong so just bail out
|
|
return;
|
|
}
|
|
|
|
tok = tok->next();
|
|
|
|
if (!tok) // something is wrong so just bail
|
|
return;
|
|
} while (tok->str() != "," && tok->str() != ")" && tok->str() != "=");
|
|
|
|
const Token *typeTok = startTok;
|
|
// skip over stuff to get to type
|
|
while (Token::Match(typeTok, "const|volatile|enum|struct|::"))
|
|
typeTok = typeTok->next();
|
|
if (Token::Match(typeTok, ",|)")) { // #8333
|
|
symbolDatabase->mTokenizer->syntaxError(typeTok);
|
|
}
|
|
// skip over qualification
|
|
while (Token::Match(typeTok, "%type% ::"))
|
|
typeTok = typeTok->tokAt(2);
|
|
|
|
// check for argument with no name or missing varid
|
|
if (!endTok) {
|
|
if (tok->previous()->isName() && !Token::Match(tok->tokAt(-1), "const|volatile")) {
|
|
if (tok->previous() != typeTok) {
|
|
nameTok = tok->previous();
|
|
endTok = nameTok->previous();
|
|
|
|
if (hasBody())
|
|
symbolDatabase->debugMessage(nameTok, "varid0", "Function::addArguments found argument \'" + nameTok->str() + "\' with varid 0.");
|
|
} else
|
|
endTok = typeTok;
|
|
} else
|
|
endTok = tok->previous();
|
|
}
|
|
|
|
const ::Type *argType = nullptr;
|
|
if (!typeTok->isStandardType()) {
|
|
argType = findVariableTypeIncludingUsedNamespaces(symbolDatabase, scope, typeTok);
|
|
|
|
// save type
|
|
const_cast<Token *>(typeTok)->type(argType);
|
|
}
|
|
|
|
// skip default values
|
|
if (tok->str() == "=") {
|
|
do {
|
|
if (tok->link() && Token::Match(tok, "[{[(<]"))
|
|
tok = tok->link();
|
|
tok = tok->next();
|
|
} while (tok->str() != "," && tok->str() != ")");
|
|
}
|
|
|
|
// skip over stuff before type
|
|
while (Token::Match(startTok, "enum|struct|const|volatile"))
|
|
startTok = startTok->next();
|
|
|
|
if (startTok == nameTok)
|
|
break;
|
|
|
|
argumentList.emplace_back(nameTok, startTok, endTok, count++, AccessControl::Argument, argType, functionScope, symbolDatabase->mSettings);
|
|
|
|
if (tok->str() == ")") {
|
|
// check for a variadic function or a variadic template function
|
|
if (Token::simpleMatch(endTok, "..."))
|
|
isVariadic(true);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// count default arguments
|
|
for (const Token* tok = argDef->next(); tok && tok != argDef->link(); tok = tok->next()) {
|
|
if (tok->str() == "=") {
|
|
initArgCount++;
|
|
if (tok->strAt(1) == "[") {
|
|
const Token* lambdaStart = tok->next();
|
|
tok = findLambdaEndToken(lambdaStart);
|
|
if (!tok)
|
|
throw InternalError(lambdaStart, "Analysis failed (lambda not recognized). If the code is valid then please report this failure.", InternalError::INTERNAL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Function::isImplicitlyVirtual(bool defaultVal) const
|
|
{
|
|
if (hasVirtualSpecifier()) //If it has the virtual specifier it's definitely virtual
|
|
return true;
|
|
if (hasOverrideSpecifier()) //If it has the override specifier then it's either virtual or not going to compile
|
|
return true;
|
|
bool foundAllBaseClasses = true;
|
|
if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual
|
|
return true;
|
|
if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual
|
|
return false;
|
|
return defaultVal; //If we can't see all the bases classes then we can't say conclusively
|
|
}
|
|
|
|
const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const
|
|
{
|
|
if (foundAllBaseClasses)
|
|
*foundAllBaseClasses = true;
|
|
if (!nestedIn->isClassOrStruct())
|
|
return nullptr;
|
|
return getOverriddenFunctionRecursive(nestedIn->definedType, foundAllBaseClasses);
|
|
}
|
|
|
|
const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const
|
|
{
|
|
// check each base class
|
|
for (const ::Type::BaseInfo & i : baseType->derivedFrom) {
|
|
const ::Type* derivedFromType = i.type;
|
|
// check if base class exists in database
|
|
if (!derivedFromType || !derivedFromType->classScope) {
|
|
if (foundAllBaseClasses)
|
|
*foundAllBaseClasses = false;
|
|
continue;
|
|
}
|
|
|
|
const Scope *parent = derivedFromType->classScope;
|
|
|
|
// check if function defined in base class
|
|
for (std::multimap<std::string, const Function *>::const_iterator it = parent->functionMap.find(tokenDef->str()); it != parent->functionMap.end() && it->first == tokenDef->str(); ++it) {
|
|
const Function * func = it->second;
|
|
if (func->hasVirtualSpecifier()) { // Base is virtual and of same name
|
|
const Token *temp1 = func->tokenDef->previous();
|
|
const Token *temp2 = tokenDef->previous();
|
|
bool match = true;
|
|
|
|
// check for matching return parameters
|
|
while (temp1->str() != "virtual") {
|
|
if (temp1->str() != temp2->str() &&
|
|
!(temp1->str() == derivedFromType->name() &&
|
|
temp2->str() == baseType->name())) {
|
|
match = false;
|
|
break;
|
|
}
|
|
|
|
temp1 = temp1->previous();
|
|
temp2 = temp2->previous();
|
|
}
|
|
|
|
// check for matching function parameters
|
|
match = match && argsMatch(baseType->classScope, func->argDef, argDef, emptyString, 0);
|
|
|
|
// check for matching cv-ref qualifiers
|
|
match = match
|
|
&& isConst() == func->isConst()
|
|
&& isVolatile() == func->isVolatile()
|
|
&& hasRvalRefQualifier() == func->hasRvalRefQualifier()
|
|
&& hasLvalRefQualifier() == func->hasLvalRefQualifier();
|
|
|
|
// it's a match
|
|
if (match) {
|
|
return func;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!derivedFromType->derivedFrom.empty() && !derivedFromType->hasCircularDependencies()) {
|
|
// avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN and
|
|
// #5590 with a loop within the class hierarchy.
|
|
const Function *func = getOverriddenFunctionRecursive(derivedFromType, foundAllBaseClasses);
|
|
if (func) {
|
|
return func;
|
|
}
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const Variable* Function::getArgumentVar(nonneg int num) const
|
|
{
|
|
for (std::list<Variable>::const_iterator i = argumentList.begin(); i != argumentList.end(); ++i) {
|
|
if (i->index() == num)
|
|
return (&*i);
|
|
else if (i->index() > num)
|
|
return nullptr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_) :
|
|
check(check_),
|
|
classDef(classDef_),
|
|
bodyStart(start_),
|
|
bodyEnd(start_->link()),
|
|
nestedIn(nestedIn_),
|
|
numConstructors(0),
|
|
numCopyOrMoveConstructors(0),
|
|
type(type_),
|
|
definedType(nullptr),
|
|
functionOf(nullptr),
|
|
function(nullptr),
|
|
enumType(nullptr),
|
|
enumClass(false)
|
|
{
|
|
}
|
|
|
|
Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_) :
|
|
check(check_),
|
|
classDef(classDef_),
|
|
bodyStart(nullptr),
|
|
bodyEnd(nullptr),
|
|
nestedIn(nestedIn_),
|
|
numConstructors(0),
|
|
numCopyOrMoveConstructors(0),
|
|
definedType(nullptr),
|
|
functionOf(nullptr),
|
|
function(nullptr),
|
|
enumType(nullptr),
|
|
enumClass(false)
|
|
{
|
|
const Token *nameTok = classDef;
|
|
if (!classDef) {
|
|
type = Scope::eGlobal;
|
|
} else if (classDef->str() == "class" && check && check->isCPP()) {
|
|
type = Scope::eClass;
|
|
nameTok = nameTok->next();
|
|
} else if (classDef->str() == "struct") {
|
|
type = Scope::eStruct;
|
|
nameTok = nameTok->next();
|
|
} else if (classDef->str() == "union") {
|
|
type = Scope::eUnion;
|
|
nameTok = nameTok->next();
|
|
} else if (classDef->str() == "namespace") {
|
|
type = Scope::eNamespace;
|
|
nameTok = nameTok->next();
|
|
} else if (classDef->str() == "enum") {
|
|
type = Scope::eEnum;
|
|
nameTok = nameTok->next();
|
|
if (nameTok->str() == "class") {
|
|
enumClass = true;
|
|
nameTok = nameTok->next();
|
|
}
|
|
} else if (classDef->str() == "[") {
|
|
type = Scope::eLambda;
|
|
} else {
|
|
type = Scope::eFunction;
|
|
}
|
|
// skip over qualification if present
|
|
nameTok = skipScopeIdentifiers(nameTok);
|
|
if (nameTok && ((type == Scope::eEnum && Token::Match(nameTok, ":|{")) || nameTok->str() != "{")) // anonymous and unnamed structs/unions don't have a name
|
|
className = nameTok->str();
|
|
}
|
|
|
|
bool Scope::hasDefaultConstructor() const
|
|
{
|
|
if (numConstructors) {
|
|
std::list<Function>::const_iterator func;
|
|
|
|
for (func = functionList.begin(); func != functionList.end(); ++func) {
|
|
if (func->type == Function::eConstructor && func->argCount() == 0)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AccessControl Scope::defaultAccess() const
|
|
{
|
|
switch (type) {
|
|
case eGlobal:
|
|
return AccessControl::Global;
|
|
case eClass:
|
|
return AccessControl::Private;
|
|
case eStruct:
|
|
return AccessControl::Public;
|
|
case eUnion:
|
|
return AccessControl::Public;
|
|
case eNamespace:
|
|
return AccessControl::Namespace;
|
|
default:
|
|
return AccessControl::Local;
|
|
}
|
|
}
|
|
|
|
void Scope::addVariable(const Token *token_, const Token *start_, const Token *end_,
|
|
AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings)
|
|
{
|
|
// keep possible size_t -> int truncation outside emplace_back() to have a single line
|
|
// C4267 VC++ warning instead of several dozens lines
|
|
const int varIndex = varlist.size();
|
|
varlist.emplace_back(token_, start_, end_, varIndex, access_, type_, scope_, settings);
|
|
}
|
|
|
|
// Get variable list..
|
|
void Scope::getVariableList(const Settings* settings)
|
|
{
|
|
const Token *start;
|
|
|
|
if (bodyStart)
|
|
start = bodyStart->next();
|
|
|
|
// global scope
|
|
else if (className.empty())
|
|
start = check->mTokenizer->tokens();
|
|
|
|
// forward declaration
|
|
else
|
|
return;
|
|
|
|
// Variable declared in condition: if (auto x = bar())
|
|
if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) {
|
|
checkVariable(classDef->tokAt(2), defaultAccess(), settings);
|
|
}
|
|
|
|
AccessControl varaccess = defaultAccess();
|
|
for (const Token *tok = start; tok && tok != bodyEnd; tok = tok->next()) {
|
|
// syntax error?
|
|
if (tok->next() == nullptr)
|
|
break;
|
|
|
|
// Is it a function?
|
|
else if (tok->str() == "{") {
|
|
|
|
tok = tok->link();
|
|
continue;
|
|
}
|
|
|
|
// Is it a nested class or structure?
|
|
else if (tok->isKeyword() && Token::Match(tok, "class|struct|union|namespace %type% :|{")) {
|
|
tok = tok->tokAt(2);
|
|
while (tok && tok->str() != "{")
|
|
tok = tok->next();
|
|
if (tok) {
|
|
// skip implementation
|
|
tok = tok->link();
|
|
continue;
|
|
} else
|
|
break;
|
|
} else if (tok->isKeyword() && Token::Match(tok, "struct|union {")) {
|
|
if (Token::Match(tok->next()->link(), "} %name% ;|[")) {
|
|
tok = tok->next()->link()->tokAt(2);
|
|
continue;
|
|
} else if (Token::simpleMatch(tok->next()->link(), "} ;")) {
|
|
tok = tok->next();
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Borland C++: Skip all variables in the __published section.
|
|
// These are automatically initialized.
|
|
else if (tok->str() == "__published:") {
|
|
for (; tok; tok = tok->next()) {
|
|
if (tok->str() == "{")
|
|
tok = tok->link();
|
|
if (Token::Match(tok->next(), "private:|protected:|public:"))
|
|
break;
|
|
}
|
|
if (tok)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// "private:" "public:" "protected:" etc
|
|
else if (tok->str() == "public:") {
|
|
varaccess = AccessControl::Public;
|
|
continue;
|
|
} else if (tok->str() == "protected:") {
|
|
varaccess = AccessControl::Protected;
|
|
continue;
|
|
} else if (tok->str() == "private:") {
|
|
varaccess = AccessControl::Private;
|
|
continue;
|
|
}
|
|
|
|
// Is it a forward declaration?
|
|
else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;")) {
|
|
tok = tok->tokAt(2);
|
|
continue;
|
|
}
|
|
|
|
// Borland C++: Ignore properties..
|
|
else if (tok->str() == "__property")
|
|
continue;
|
|
|
|
// skip return, goto and delete
|
|
else if (tok->isKeyword() && Token::Match(tok, "return|delete|goto")) {
|
|
while (tok->next() &&
|
|
tok->next()->str() != ";" &&
|
|
tok->next()->str() != "}" /* ticket #4994 */) {
|
|
tok = tok->next();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// skip case/default
|
|
if (tok->isKeyword() && Token::Match(tok, "case|default")) {
|
|
while (tok->next() && !Token::Match(tok->next(), "[:;{}]"))
|
|
tok = tok->next();
|
|
continue;
|
|
}
|
|
|
|
// Search for start of statement..
|
|
else if (tok->previous() && !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:"))
|
|
continue;
|
|
else if (tok->str() == ";")
|
|
continue;
|
|
|
|
tok = checkVariable(tok, varaccess, settings);
|
|
|
|
if (!tok)
|
|
break;
|
|
}
|
|
}
|
|
|
|
const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, const Settings* settings)
|
|
{
|
|
// Is it a throw..?
|
|
if (tok->isKeyword() && Token::Match(tok, "throw %any% (") &&
|
|
Token::simpleMatch(tok->linkAt(2), ") ;")) {
|
|
return tok->linkAt(2);
|
|
}
|
|
|
|
if (tok->isKeyword() && Token::Match(tok, "throw %any% :: %any% (") &&
|
|
Token::simpleMatch(tok->linkAt(4), ") ;")) {
|
|
return tok->linkAt(4);
|
|
}
|
|
|
|
// friend?
|
|
if (tok->isKeyword() && Token::Match(tok, "friend %type%") && tok->next()->varId() == 0) {
|
|
const Token *next = Token::findmatch(tok->tokAt(2), ";|{");
|
|
if (next && next->str() == "{")
|
|
next = next->link();
|
|
return next;
|
|
}
|
|
|
|
// skip const|volatile|static|mutable|extern
|
|
while (tok->isKeyword() && Token::Match(tok, "const|volatile|static|mutable|extern")) {
|
|
tok = tok->next();
|
|
}
|
|
|
|
// the start of the type tokens does not include the above modifiers
|
|
const Token *typestart = tok;
|
|
|
|
// C++17 structured bindings
|
|
if (settings->standards.cpp >= Standards::CPP17 && Token::Match(tok, "auto &|&&| [")) {
|
|
const Token *typeend = Token::findsimplematch(typestart, "[")->previous();
|
|
for (tok = typeend->tokAt(2); Token::Match(tok, "%name%|,"); tok = tok->next()) {
|
|
if (tok->varId())
|
|
addVariable(tok, typestart, typeend, varaccess, nullptr, this, settings);
|
|
}
|
|
return typeend->linkAt(1);
|
|
}
|
|
|
|
if (tok->isKeyword() && Token::Match(tok, "class|struct|union|enum")) {
|
|
tok = tok->next();
|
|
}
|
|
|
|
// This is the start of a statement
|
|
const Token *vartok = nullptr;
|
|
const Token *typetok = nullptr;
|
|
|
|
if (tok && isVariableDeclaration(tok, vartok, typetok)) {
|
|
// If the vartok was set in the if-blocks above, create a entry for this variable..
|
|
tok = vartok->next();
|
|
while (Token::Match(tok, "[|{"))
|
|
tok = tok->link()->next();
|
|
|
|
if (vartok->varId() == 0) {
|
|
if (!vartok->isBoolean())
|
|
check->debugMessage(vartok, "varid0", "Scope::checkVariable found variable \'" + vartok->str() + "\' with varid 0.");
|
|
return tok;
|
|
}
|
|
|
|
const Type *vType = nullptr;
|
|
|
|
if (typetok) {
|
|
vType = findVariableTypeIncludingUsedNamespaces(check, this, typetok);
|
|
|
|
const_cast<Token *>(typetok)->type(vType);
|
|
}
|
|
|
|
// skip "enum" or "struct"
|
|
if (Token::Match(typestart, "enum|struct"))
|
|
typestart = typestart->next();
|
|
|
|
addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this, settings);
|
|
}
|
|
|
|
return tok;
|
|
}
|
|
|
|
const Variable *Scope::getVariable(const std::string &varname) const
|
|
{
|
|
std::list<Variable>::const_iterator iter;
|
|
|
|
for (iter = varlist.begin(); iter != varlist.end(); ++iter) {
|
|
if (iter->name() == varname)
|
|
return &*iter;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static const Token* skipPointers(const Token* tok)
|
|
{
|
|
while (Token::Match(tok, "*|&|&&") || (Token::Match(tok, "( [*&]") && Token::Match(tok->link()->next(), "(|["))) {
|
|
tok = tok->next();
|
|
if (tok->strAt(-1) == "(" && Token::Match(tok, "%type% ::"))
|
|
tok = tok->tokAt(2);
|
|
}
|
|
|
|
if (Token::simpleMatch(tok, "( *") && Token::simpleMatch(tok->link()->previous(), "] ) ;")) {
|
|
const Token *tok2 = skipPointers(tok->next());
|
|
if (Token::Match(tok2, "%name% [") && Token::simpleMatch(tok2->linkAt(1), "] ) ;"))
|
|
return tok2;
|
|
}
|
|
|
|
return tok;
|
|
}
|
|
|
|
static const Token* skipPointersAndQualifiers(const Token* tok)
|
|
{
|
|
tok = skipPointers(tok);
|
|
while (Token::Match(tok, "const|volatile")) {
|
|
tok = tok->next();
|
|
tok = skipPointers(tok);
|
|
}
|
|
|
|
return tok;
|
|
}
|
|
|
|
bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const
|
|
{
|
|
const bool isCPP = check && check->mTokenizer->isCPP();
|
|
|
|
if (isCPP && Token::Match(tok, "throw|new"))
|
|
return false;
|
|
|
|
const bool isCPP11 = isCPP && check->mSettings->standards.cpp >= Standards::CPP11;
|
|
|
|
if (isCPP11 && tok->str() == "using")
|
|
return false;
|
|
|
|
const Token* localTypeTok = skipScopeIdentifiers(tok);
|
|
const Token* localVarTok = nullptr;
|
|
|
|
if (Token::Match(localTypeTok, "%type% <")) {
|
|
if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <"))
|
|
return false;
|
|
|
|
const Token* closeTok = localTypeTok->next()->link();
|
|
if (closeTok) {
|
|
localVarTok = skipPointers(closeTok->next());
|
|
|
|
if (Token::Match(localVarTok, ":: %type% %name% [;=({]")) {
|
|
if (localVarTok->strAt(3) != "(" ||
|
|
Token::Match(localVarTok->linkAt(3), "[)}] ;")) {
|
|
localTypeTok = localVarTok->next();
|
|
localVarTok = localVarTok->tokAt(2);
|
|
}
|
|
}
|
|
}
|
|
} else if (Token::Match(localTypeTok, "%type%")) {
|
|
|
|
if (isCPP11 && Token::simpleMatch(localTypeTok, "decltype (") && Token::Match(localTypeTok->linkAt(1), ") %name%|*|&|&&"))
|
|
localVarTok = skipPointersAndQualifiers(localTypeTok->linkAt(1)->next());
|
|
else {
|
|
localVarTok = skipPointersAndQualifiers(localTypeTok->next());
|
|
if (isCPP11 && Token::simpleMatch(localVarTok, "decltype (") && Token::Match(localVarTok->linkAt(1), ") %name%|*|&|&&"))
|
|
localVarTok = skipPointersAndQualifiers(localVarTok->linkAt(1)->next());
|
|
}
|
|
}
|
|
|
|
if (!localVarTok)
|
|
return false;
|
|
|
|
if (localVarTok->str() == "const")
|
|
localVarTok = localVarTok->next();
|
|
|
|
if (Token::Match(localVarTok, "%name% ;|=") || (localVarTok && localVarTok->varId() && localVarTok->strAt(1) == ":")) {
|
|
vartok = localVarTok;
|
|
typetok = localTypeTok;
|
|
} else if (Token::Match(localVarTok, "%name% )|[") && localVarTok->str() != "operator") {
|
|
vartok = localVarTok;
|
|
typetok = localTypeTok;
|
|
} else if (localVarTok && localVarTok->varId() && Token::Match(localVarTok, "%name% (|{") &&
|
|
Token::Match(localVarTok->next()->link(), ")|} ;")) {
|
|
vartok = localVarTok;
|
|
typetok = localTypeTok;
|
|
} else if (type == eCatch &&
|
|
Token::Match(localVarTok, "%name% )")) {
|
|
vartok = localVarTok;
|
|
typetok = localTypeTok;
|
|
}
|
|
|
|
return nullptr != vartok;
|
|
}
|
|
|
|
const Token * Scope::addEnum(const Token * tok, bool isCpp)
|
|
{
|
|
const Token * tok2 = tok->next();
|
|
|
|
// skip over class if present
|
|
if (isCpp && tok2->str() == "class")
|
|
tok2 = tok2->next();
|
|
|
|
// skip over name
|
|
tok2 = tok2->next();
|
|
|
|
// save type if present
|
|
if (tok2->str() == ":") {
|
|
tok2 = tok2->next();
|
|
|
|
enumType = tok2;
|
|
tok2 = tok2->next();
|
|
}
|
|
|
|
// add enumerators
|
|
if (tok2->str() == "{") {
|
|
const Token * end = tok2->link();
|
|
tok2 = tok2->next();
|
|
|
|
while (Token::Match(tok2, "%name% =|,|}") ||
|
|
(Token::Match(tok2, "%name% (") && Token::Match(tok2->linkAt(1), ") ,|}"))) {
|
|
Enumerator enumerator(this);
|
|
|
|
// save enumerator name
|
|
enumerator.name = tok2;
|
|
|
|
// skip over name
|
|
tok2 = tok2->next();
|
|
|
|
if (tok2->str() == "=") {
|
|
// skip over "="
|
|
tok2 = tok2->next();
|
|
|
|
if (tok2->str() == "}")
|
|
return nullptr;
|
|
|
|
enumerator.start = tok2;
|
|
|
|
while (!Token::Match(tok2, ",|}")) {
|
|
if (tok2->link())
|
|
tok2 = tok2->link();
|
|
enumerator.end = tok2;
|
|
tok2 = tok2->next();
|
|
}
|
|
} else if (tok2->str() == "(") {
|
|
// skip over unknown macro
|
|
tok2 = tok2->link()->next();
|
|
}
|
|
|
|
if (tok2->str() == ",") {
|
|
enumeratorList.push_back(enumerator);
|
|
tok2 = tok2->next();
|
|
} else if (tok2->str() == "}") {
|
|
enumeratorList.push_back(enumerator);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tok2 == end) {
|
|
tok2 = tok2->next();
|
|
|
|
if (tok2 && tok2->str() != ";")
|
|
tok2 = nullptr;
|
|
} else
|
|
tok2 = nullptr;
|
|
} else
|
|
tok2 = nullptr;
|
|
|
|
return tok2;
|
|
}
|
|
|
|
const Enumerator * SymbolDatabase::findEnumerator(const Token * tok) const
|
|
{
|
|
const Scope * scope = tok->scope();
|
|
|
|
const std::string &tokStr = tok->str();
|
|
|
|
if (mTokensThatAreNotEnumeratorValues.find(tokStr) != mTokensThatAreNotEnumeratorValues.end()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// check for qualified name
|
|
if (tok->strAt(-1) == "::") {
|
|
// find first scope
|
|
const Token *tok1 = tok;
|
|
while (Token::Match(tok1->tokAt(-2), "%name% ::"))
|
|
tok1 = tok1->tokAt(-2);
|
|
|
|
if (tok1->strAt(-1) == "::")
|
|
scope = &scopeList.front();
|
|
else {
|
|
// FIXME search base class here
|
|
|
|
// find first scope
|
|
while (scope && scope->nestedIn) {
|
|
const Scope * temp = scope->nestedIn->findRecordInNestedList(tok1->str());
|
|
if (temp) {
|
|
scope = temp;
|
|
break;
|
|
}
|
|
scope = scope->nestedIn;
|
|
}
|
|
}
|
|
|
|
if (scope) {
|
|
tok1 = tok1->tokAt(2);
|
|
while (scope && Token::Match(tok1, "%name% ::")) {
|
|
scope = scope->findRecordInNestedList(tok1->str());
|
|
tok1 = tok1->tokAt(2);
|
|
}
|
|
|
|
if (scope) {
|
|
const Enumerator * enumerator = scope->findEnumerator(tokStr);
|
|
|
|
if (enumerator) // enum class
|
|
return enumerator;
|
|
// enum
|
|
else {
|
|
for (std::list<Scope *>::const_iterator it = scope->nestedList.begin(), end = scope->nestedList.end(); it != end; ++it) {
|
|
enumerator = (*it)->findEnumerator(tokStr);
|
|
|
|
if (enumerator)
|
|
return enumerator;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
const Enumerator * enumerator = scope->findEnumerator(tokStr);
|
|
|
|
if (enumerator)
|
|
return enumerator;
|
|
|
|
for (std::list<Scope *>::const_iterator s = scope->nestedList.begin(); s != scope->nestedList.end(); ++s) {
|
|
enumerator = (*s)->findEnumerator(tokStr);
|
|
|
|
if (enumerator)
|
|
return enumerator;
|
|
}
|
|
|
|
if (scope->definedType) {
|
|
const std::vector<Type::BaseInfo> & derivedFrom = scope->definedType->derivedFrom;
|
|
for (const Type::BaseInfo & i : derivedFrom) {
|
|
const Type *derivedFromType = i.type;
|
|
if (derivedFromType && derivedFromType ->classScope) {
|
|
enumerator = derivedFromType->classScope->findEnumerator(tokStr);
|
|
|
|
if (enumerator)
|
|
return enumerator;
|
|
}
|
|
}
|
|
}
|
|
|
|
while (scope->nestedIn) {
|
|
if (scope->type == Scope::eFunction && scope->functionOf)
|
|
scope = scope->functionOf;
|
|
else
|
|
scope = scope->nestedIn;
|
|
|
|
enumerator = scope->findEnumerator(tokStr);
|
|
|
|
if (enumerator)
|
|
return enumerator;
|
|
|
|
for (std::list<Scope*>::const_iterator s = scope->nestedList.begin(); s != scope->nestedList.end(); ++s) {
|
|
enumerator = (*s)->findEnumerator(tokStr);
|
|
|
|
if (enumerator)
|
|
return enumerator;
|
|
}
|
|
}
|
|
}
|
|
|
|
mTokensThatAreNotEnumeratorValues.insert(tokStr);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Type* SymbolDatabase::findVariableTypeInBase(const Scope* scope, const Token* typeTok) const
|
|
{
|
|
if (scope && scope->definedType && !scope->definedType->derivedFrom.empty()) {
|
|
const std::vector<Type::BaseInfo> &derivedFrom = scope->definedType->derivedFrom;
|
|
for (const Type::BaseInfo & i : derivedFrom) {
|
|
const Type *base = i.type;
|
|
if (base && base->classScope) {
|
|
if (base->classScope == scope)
|
|
return nullptr;
|
|
const Type * type = base->classScope->findType(typeTok->str());
|
|
if (type)
|
|
return type;
|
|
type = findVariableTypeInBase(base->classScope, typeTok);
|
|
if (type)
|
|
return type;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const
|
|
{
|
|
const Scope *scope = start;
|
|
|
|
// check if type does not have a namespace
|
|
if (typeTok->strAt(-1) != "::" && typeTok->strAt(1) != "::") {
|
|
// check if type same as scope
|
|
if (start->isClassOrStruct() && typeTok->str() == start->className)
|
|
return start->definedType;
|
|
|
|
while (scope) {
|
|
// look for type in this scope
|
|
const Type * type = scope->findType(typeTok->str());
|
|
|
|
if (type)
|
|
return type;
|
|
|
|
// look for type in base classes if possible
|
|
if (scope->isClassOrStruct()) {
|
|
type = findVariableTypeInBase(scope, typeTok);
|
|
|
|
if (type)
|
|
return type;
|
|
}
|
|
|
|
// check if in member function class to see if it's present in class
|
|
if (scope->type == Scope::eFunction && scope->functionOf) {
|
|
const Scope *scope1 = scope->functionOf;
|
|
|
|
type = scope1->findType(typeTok->str());
|
|
|
|
if (type)
|
|
return type;
|
|
|
|
type = findVariableTypeInBase(scope1, typeTok);
|
|
|
|
if (type)
|
|
return type;
|
|
}
|
|
|
|
scope = scope->nestedIn;
|
|
}
|
|
}
|
|
|
|
// check for a qualified name and use it when given
|
|
else if (typeTok->strAt(-1) == "::") {
|
|
// check if type is not part of qualification
|
|
if (typeTok->strAt(1) == "::")
|
|
return nullptr;
|
|
|
|
// find start of qualified function name
|
|
const Token *tok1 = typeTok;
|
|
|
|
while (Token::Match(tok1->tokAt(-2), "%type% ::") ||
|
|
(Token::simpleMatch(tok1->tokAt(-2), "> ::") && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->tokAt(-1), "%type%"))) {
|
|
if (tok1->strAt(-1) == "::")
|
|
tok1 = tok1->tokAt(-2);
|
|
else
|
|
tok1 = tok1->linkAt(-2)->tokAt(-1);
|
|
}
|
|
|
|
// check for global scope
|
|
if (tok1->strAt(-1) == "::") {
|
|
scope = &scopeList.front();
|
|
|
|
scope = scope->findRecordInNestedList(tok1->str());
|
|
}
|
|
|
|
// find start of qualification
|
|
else {
|
|
while (scope) {
|
|
if (scope->className == tok1->str())
|
|
break;
|
|
else {
|
|
const Scope *scope1 = scope->findRecordInNestedList(tok1->str());
|
|
|
|
if (scope1) {
|
|
scope = scope1;
|
|
break;
|
|
} else if (scope->type == Scope::eFunction && scope->functionOf)
|
|
scope = scope->functionOf;
|
|
else
|
|
scope = scope->nestedIn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (scope) {
|
|
// follow qualification
|
|
while (scope && (Token::Match(tok1, "%type% ::") ||
|
|
(Token::Match(tok1, "%type% <") && Token::simpleMatch(tok1->linkAt(1), "> ::")))) {
|
|
if (tok1->strAt(1) == "::")
|
|
tok1 = tok1->tokAt(2);
|
|
else
|
|
tok1 = tok1->linkAt(1)->tokAt(2);
|
|
const Scope * temp = scope->findRecordInNestedList(tok1->str());
|
|
if (!temp) {
|
|
// look in base classes
|
|
const Type * type = findVariableTypeInBase(scope, tok1);
|
|
|
|
if (type)
|
|
return type;
|
|
}
|
|
scope = temp;
|
|
}
|
|
|
|
if (scope && scope->definedType)
|
|
return scope->definedType;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
bool Scope::hasInlineOrLambdaFunction() const
|
|
{
|
|
for (const Scope *s : nestedList) {
|
|
// Inline function
|
|
if (s->type == Scope::eUnconditional && Token::simpleMatch(s->bodyStart->previous(), ") {"))
|
|
return true;
|
|
// Lambda function
|
|
if (s->type == Scope::eLambda)
|
|
return true;
|
|
if (s->hasInlineOrLambdaFunction())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::vector<const Function *> & matches) const
|
|
{
|
|
if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) {
|
|
const std::vector<Type::BaseInfo> &derivedFrom = definedType->derivedFrom;
|
|
for (const Type::BaseInfo & i : derivedFrom) {
|
|
const Type *base = i.type;
|
|
if (base && base->classScope) {
|
|
if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already
|
|
continue;
|
|
|
|
for (std::multimap<std::string, const Function *>::const_iterator it = base->classScope->functionMap.find(name); it != base->classScope->functionMap.end() && it->first == name; ++it) {
|
|
const Function *func = it->second;
|
|
if ((func->isVariadic() && args >= (func->argCount() - 1)) ||
|
|
(args == func->argCount() || (args < func->argCount() && args >= func->minArgCount()))) {
|
|
matches.push_back(func);
|
|
}
|
|
}
|
|
|
|
base->classScope->findFunctionInBase(name, args, matches);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const Scope *Scope::findRecordInBase(const std::string & name) const
|
|
{
|
|
if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) {
|
|
const std::vector<Type::BaseInfo> &derivedFrom = definedType->derivedFrom;
|
|
for (const Type::BaseInfo & i : derivedFrom) {
|
|
const Type *base = i.type;
|
|
if (base && base->classScope) {
|
|
if (base->classScope == this) // Recursive class; tok should have been found already
|
|
continue;
|
|
|
|
if (base->name() == name) {
|
|
return base->classScope;
|
|
}
|
|
|
|
const ::Type * t = base->classScope->findType(name);
|
|
if (t)
|
|
return t->classScope;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
static void checkVariableCallMatch(const Variable* callarg, const Variable* funcarg, size_t& same, size_t& fallback1, size_t& fallback2)
|
|
{
|
|
if (callarg) {
|
|
ValueType::MatchResult res = ValueType::matchParameter(callarg->valueType(), callarg, funcarg);
|
|
if (res == ValueType::MatchResult::SAME) {
|
|
same++;
|
|
return;
|
|
}
|
|
if (res == ValueType::MatchResult::FALLBACK1) {
|
|
fallback1++;
|
|
return;
|
|
}
|
|
if (res == ValueType::MatchResult::FALLBACK2) {
|
|
fallback2++;
|
|
return;
|
|
}
|
|
if (res == ValueType::MatchResult::NOMATCH)
|
|
return;
|
|
|
|
bool ptrequals = callarg->isArrayOrPointer() == funcarg->isArrayOrPointer();
|
|
bool constEquals = !callarg->isArrayOrPointer() || ((callarg->typeStartToken()->strAt(-1) == "const") == (funcarg->typeStartToken()->strAt(-1) == "const"));
|
|
if (ptrequals && constEquals &&
|
|
callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() &&
|
|
callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() &&
|
|
callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()) {
|
|
same++;
|
|
} else if (callarg->isArrayOrPointer()) {
|
|
if (ptrequals && constEquals && funcarg->typeStartToken()->str() == "void")
|
|
fallback1++;
|
|
else if (constEquals && funcarg->isStlStringType() && Token::Match(callarg->typeStartToken(), "char|wchar_t"))
|
|
fallback2++;
|
|
} else if (ptrequals) {
|
|
const bool takesInt = Token::Match(funcarg->typeStartToken(), "char|short|int|long");
|
|
const bool takesFloat = Token::Match(funcarg->typeStartToken(), "float|double");
|
|
const bool passesInt = Token::Match(callarg->typeStartToken(), "char|short|int|long");
|
|
const bool passesFloat = Token::Match(callarg->typeStartToken(), "float|double");
|
|
if ((takesInt && passesInt) || (takesFloat && passesFloat))
|
|
fallback1++;
|
|
else if ((takesInt && passesFloat) || (takesFloat && passesInt))
|
|
fallback2++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static std::string getTypeString(const Token *typeToken)
|
|
{
|
|
if (!typeToken)
|
|
return "";
|
|
while (Token::Match(typeToken, "%name%|*|&|::")) {
|
|
if (typeToken->str() == "::") {
|
|
std::string ret;
|
|
while (Token::Match(typeToken, ":: %name%")) {
|
|
ret += "::" + typeToken->strAt(1);
|
|
typeToken = typeToken->tokAt(2);
|
|
if (typeToken->str() == "<") {
|
|
for (const Token *tok = typeToken; tok != typeToken->link(); tok = tok->next())
|
|
ret += tok->str();
|
|
ret += ">";
|
|
typeToken = typeToken->link()->next();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
if (Token::Match(typeToken, "%name% const| %var%|*|&")) {
|
|
return typeToken->str();
|
|
}
|
|
typeToken = typeToken->next();
|
|
}
|
|
return "";
|
|
}
|
|
|
|
const Function* Scope::findFunction(const Token *tok, bool requireConst) const
|
|
{
|
|
const bool isCall = Token::Match(tok->next(), "(|{");
|
|
|
|
const std::vector<const Token *> arguments = getArguments(tok);
|
|
|
|
std::vector<const Function *> matches;
|
|
|
|
// find all the possible functions that could match
|
|
const std::size_t args = arguments.size();
|
|
|
|
auto addMatchingFunctions = [&](const Scope *scope) {
|
|
for (std::multimap<std::string, const Function *>::const_iterator it = scope->functionMap.find(tok->str()); it != scope->functionMap.cend() && it->first == tok->str(); ++it) {
|
|
const Function *func = it->second;
|
|
if (!isCall || args == func->argCount() ||
|
|
(func->isVariadic() && args >= (func->argCount() - 1)) ||
|
|
(args < func->argCount() && args >= func->minArgCount())) {
|
|
matches.push_back(func);
|
|
}
|
|
}
|
|
};
|
|
|
|
addMatchingFunctions(this);
|
|
|
|
// check in anonumous namespaces
|
|
for (const Scope *nestedScope : nestedList) {
|
|
if (nestedScope->type == eNamespace && nestedScope->className.empty())
|
|
addMatchingFunctions(nestedScope);
|
|
}
|
|
|
|
// check in base classes
|
|
findFunctionInBase(tok->str(), args, matches);
|
|
|
|
// Non-call => Do not match parameters
|
|
if (!isCall) {
|
|
return matches.empty() ? nullptr : matches[0];
|
|
}
|
|
|
|
const Function* fallback1Func = nullptr;
|
|
const Function* fallback2Func = nullptr;
|
|
|
|
// check each function against the arguments in the function call for a match
|
|
for (std::size_t i = 0; i < matches.size();) {
|
|
bool constFallback = false;
|
|
const Function * func = matches[i];
|
|
size_t same = 0;
|
|
|
|
if (requireConst && !func->isConst()) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
if (!requireConst || !func->isConst()) {
|
|
// get the function this call is in
|
|
const Scope * scope = tok->scope();
|
|
|
|
// check if this function is a member function
|
|
if (scope && scope->functionOf && scope->functionOf->isClassOrStruct() && scope->function) {
|
|
// check if isConst mismatches
|
|
if (scope->function->isConst() != func->isConst()) {
|
|
if (scope->function->isConst()) {
|
|
++i;
|
|
continue;
|
|
}
|
|
constFallback = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
size_t fallback1 = 0;
|
|
size_t fallback2 = 0;
|
|
bool erased = false;
|
|
for (std::size_t j = 0; j < args; ++j) {
|
|
|
|
// don't check variadic arguments
|
|
if (func->isVariadic() && j > (func->argCount() - 1)) {
|
|
break;
|
|
}
|
|
const Variable *funcarg = func->getArgumentVar(j);
|
|
|
|
if (!arguments[j]->valueType()) {
|
|
const Token *vartok = arguments[j];
|
|
int pointer = 0;
|
|
while (vartok && (vartok->isUnaryOp("&") || vartok->isUnaryOp("*"))) {
|
|
pointer += vartok->isUnaryOp("&") ? 1 : -1;
|
|
vartok = vartok->astOperand1();
|
|
}
|
|
if (vartok && vartok->variable()) {
|
|
const Token *callArgTypeToken = vartok->variable()->typeStartToken();
|
|
const Token *funcArgTypeToken = funcarg->typeStartToken();
|
|
|
|
auto parseDecl = [](const Token *typeToken) -> ValueType {
|
|
ValueType ret;
|
|
while (Token::Match(typeToken->previous(), "%name%"))
|
|
typeToken = typeToken->previous();
|
|
while (Token::Match(typeToken, "%name%|*|&|::|<"))
|
|
{
|
|
if (typeToken->str() == "const")
|
|
ret.constness |= (1 << ret.pointer);
|
|
else if (typeToken->str() == "*")
|
|
ret.pointer++;
|
|
else if (typeToken->str() == "<") {
|
|
if (!typeToken->link())
|
|
break;
|
|
typeToken = typeToken->link();
|
|
}
|
|
typeToken = typeToken->next();
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
const std::string type1 = getTypeString(callArgTypeToken);
|
|
const std::string type2 = getTypeString(funcArgTypeToken);
|
|
if (!type1.empty() && type1 == type2) {
|
|
ValueType callArgType = parseDecl(callArgTypeToken);
|
|
callArgType.pointer += pointer;
|
|
ValueType funcArgType = parseDecl(funcArgTypeToken);
|
|
|
|
callArgType.sign = funcArgType.sign = ValueType::Sign::SIGNED;
|
|
callArgType.type = funcArgType.type = ValueType::Type::INT;
|
|
|
|
ValueType::MatchResult res = ValueType::matchParameter(&callArgType, &funcArgType);
|
|
if (res == ValueType::MatchResult::SAME)
|
|
++same;
|
|
else if (res == ValueType::MatchResult::FALLBACK1)
|
|
++fallback1;
|
|
else if (res == ValueType::MatchResult::FALLBACK2)
|
|
++fallback2;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check for a match with a variable
|
|
if (Token::Match(arguments[j], "%var% ,|)")) {
|
|
const Variable * callarg = arguments[j]->variable();
|
|
checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2);
|
|
}
|
|
|
|
else if (funcarg->isStlStringType() && arguments[j]->valueType() && arguments[j]->valueType()->pointer == 1 && arguments[j]->valueType()->type == ValueType::Type::CHAR)
|
|
fallback2++;
|
|
|
|
// check for a match with nullptr
|
|
else if (funcarg->isPointer() && Token::Match(arguments[j], "nullptr|NULL ,|)"))
|
|
same++;
|
|
|
|
else if (funcarg->isPointer() && MathLib::isNullValue(arguments[j]->str()))
|
|
fallback1++;
|
|
|
|
// Try to evaluate the apparently more complex expression
|
|
else if (check->isCPP()) {
|
|
const Token *vartok = arguments[j];
|
|
while (vartok->isUnaryOp("&") || vartok->isUnaryOp("*"))
|
|
vartok = vartok->astOperand1();
|
|
ValueType::MatchResult res = ValueType::matchParameter(arguments[j]->valueType(), vartok->variable(), funcarg);
|
|
if (res == ValueType::MatchResult::SAME)
|
|
++same;
|
|
else if (res == ValueType::MatchResult::FALLBACK1)
|
|
++fallback1;
|
|
else if (res == ValueType::MatchResult::FALLBACK2)
|
|
++fallback2;
|
|
else if (res == ValueType::MatchResult::NOMATCH) {
|
|
// can't match so remove this function from possible matches
|
|
matches.erase(matches.begin() + i);
|
|
erased = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
else
|
|
// C code: if number of arguments match then do not match types
|
|
fallback1++;
|
|
}
|
|
|
|
const size_t hasToBe = func->isVariadic() ? (func->argCount() - 1) : args;
|
|
|
|
// check if all arguments matched
|
|
if (same == hasToBe) {
|
|
if (constFallback || (!requireConst && func->isConst()))
|
|
fallback1Func = func;
|
|
else
|
|
return func;
|
|
}
|
|
|
|
else if (!fallback1Func) {
|
|
if (same + fallback1 == hasToBe)
|
|
fallback1Func = func;
|
|
else if (!fallback2Func && same + fallback2 + fallback1 == hasToBe)
|
|
fallback2Func = func;
|
|
}
|
|
|
|
if (!erased)
|
|
++i;
|
|
}
|
|
|
|
// Fallback cases
|
|
if (fallback1Func)
|
|
return fallback1Func;
|
|
|
|
if (fallback2Func)
|
|
return fallback2Func;
|
|
|
|
// Only one candidate left
|
|
if (matches.size() == 1)
|
|
return matches[0];
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Function* SymbolDatabase::findFunction(const Token *tok) const
|
|
{
|
|
// find the scope this function is in
|
|
const Scope *currScope = tok->scope();
|
|
while (currScope && currScope->isExecutable()) {
|
|
if (currScope->functionOf)
|
|
currScope = currScope->functionOf;
|
|
else
|
|
currScope = currScope->nestedIn;
|
|
}
|
|
|
|
// check for a qualified name and use it when given
|
|
if (tok->strAt(-1) == "::") {
|
|
// find start of qualified function name
|
|
const Token *tok1 = tok;
|
|
|
|
while (Token::Match(tok1->tokAt(-2), ">|%type% ::")) {
|
|
if (tok1->strAt(-2) == ">") {
|
|
if (tok1->linkAt(-2))
|
|
tok1 = tok1->linkAt(-2)->tokAt(-1);
|
|
else {
|
|
if (mSettings->debugwarnings)
|
|
debugMessage(tok1->tokAt(-2), "debug", "SymbolDatabase::findFunction found '>' without link.");
|
|
return nullptr;
|
|
}
|
|
} else
|
|
tok1 = tok1->tokAt(-2);
|
|
}
|
|
|
|
// check for global scope
|
|
if (tok1->strAt(-1) == "::") {
|
|
currScope = &scopeList.front();
|
|
|
|
if (Token::Match(tok1, "%name% ("))
|
|
return currScope->findFunction(tok);
|
|
|
|
currScope = currScope->findRecordInNestedList(tok1->str());
|
|
}
|
|
|
|
// find start of qualification
|
|
else {
|
|
while (currScope) {
|
|
if (currScope->className == tok1->str())
|
|
break;
|
|
else {
|
|
const Scope *scope = currScope->findRecordInNestedList(tok1->str());
|
|
|
|
if (scope) {
|
|
currScope = scope;
|
|
break;
|
|
} else
|
|
currScope = currScope->nestedIn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currScope) {
|
|
while (currScope && tok1 && !(Token::Match(tok1, "%type% :: %name% [(),>]") ||
|
|
(Token::Match(tok1, "%type% <") && Token::Match(tok1->linkAt(1), "> :: %name% (")))) {
|
|
if (tok1->strAt(1) == "::")
|
|
tok1 = tok1->tokAt(2);
|
|
else if (tok1->strAt(1) == "<")
|
|
tok1 = tok1->linkAt(1)->tokAt(2);
|
|
else
|
|
tok1 = nullptr;
|
|
|
|
if (tok1)
|
|
currScope = currScope->findRecordInNestedList(tok1->str());
|
|
}
|
|
|
|
if (tok1)
|
|
tok1 = tok1->tokAt(2);
|
|
|
|
if (currScope && tok1)
|
|
return currScope->findFunction(tok1);
|
|
}
|
|
}
|
|
|
|
// check for member function
|
|
else if (Token::Match(tok->tokAt(-2), "!!this .")) {
|
|
const Token *tok1 = tok->tokAt(-2);
|
|
if (Token::Match(tok1, "%var% .")) {
|
|
const Variable *var = getVariableFromVarId(tok1->varId());
|
|
if (var && var->typeScope())
|
|
return var->typeScope()->findFunction(tok, var->valueType()->constness == 1);
|
|
if (var && var->smartPointerType() && var->smartPointerType()->classScope && tok1->next()->originalName() == "->")
|
|
return var->smartPointerType()->classScope->findFunction(tok, var->valueType()->constness == 1);
|
|
} else if (Token::simpleMatch(tok->previous()->astOperand1(), "(")) {
|
|
const Token *castTok = tok->previous()->astOperand1();
|
|
if (castTok->isCast()) {
|
|
ValueType vt = ValueType::parseDecl(castTok->next(),mSettings);
|
|
if (vt.typeScope)
|
|
return vt.typeScope->findFunction(tok, vt.constness == 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// check in enclosing scopes
|
|
else {
|
|
while (currScope) {
|
|
const Function *func = currScope->findFunction(tok);
|
|
if (func)
|
|
return func;
|
|
currScope = currScope->nestedIn;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Scope *SymbolDatabase::findScopeByName(const std::string& name) const
|
|
{
|
|
for (std::list<Scope>::const_iterator it = scopeList.begin(); it != scopeList.end(); ++it) {
|
|
if (it->className == name)
|
|
return &*it;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
Scope *Scope::findInNestedList(const std::string & name)
|
|
{
|
|
std::list<Scope *>::iterator it;
|
|
|
|
for (it = nestedList.begin(); it != nestedList.end(); ++it) {
|
|
if ((*it)->className == name)
|
|
return (*it);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Scope *Scope::findRecordInNestedList(const std::string & name) const
|
|
{
|
|
std::list<Scope *>::const_iterator it;
|
|
|
|
for (it = nestedList.begin(); it != nestedList.end(); ++it) {
|
|
if ((*it)->className == name && (*it)->type != eFunction)
|
|
return (*it);
|
|
}
|
|
|
|
const Type * nested_type = findType(name);
|
|
|
|
if (nested_type) {
|
|
if (nested_type->isTypeAlias()) {
|
|
if (nested_type->typeStart == nested_type->typeEnd)
|
|
return findRecordInNestedList(nested_type->typeStart->str());
|
|
} else
|
|
return nested_type->classScope;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Type* Scope::findType(const std::string & name) const
|
|
{
|
|
auto it = definedTypesMap.find(name);
|
|
|
|
// Type was found
|
|
if (definedTypesMap.end() != it)
|
|
return (*it).second;
|
|
|
|
// is type defined in anonymous namespace..
|
|
it = definedTypesMap.find("");
|
|
if (it != definedTypesMap.end()) {
|
|
for (const Scope *scope : nestedList) {
|
|
if (scope->className.empty() && (scope->type == eNamespace || scope->isClassOrStructOrUnion())) {
|
|
const Type *t = scope->findType(name);
|
|
if (t)
|
|
return t;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Type was not found
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
Scope *Scope::findInNestedListRecursive(const std::string & name)
|
|
{
|
|
std::list<Scope *>::iterator it;
|
|
|
|
for (it = nestedList.begin(); it != nestedList.end(); ++it) {
|
|
if ((*it)->className == name)
|
|
return (*it);
|
|
}
|
|
|
|
for (it = nestedList.begin(); it != nestedList.end(); ++it) {
|
|
Scope *child = (*it)->findInNestedListRecursive(name);
|
|
if (child)
|
|
return child;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Function *Scope::getDestructor() const
|
|
{
|
|
std::list<Function>::const_iterator it;
|
|
for (it = functionList.begin(); it != functionList.end(); ++it) {
|
|
if (it->type == Function::eDestructor)
|
|
return &(*it);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
bool SymbolDatabase::isCPP() const
|
|
{
|
|
return mTokenizer->isCPP();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Scope *SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const
|
|
{
|
|
const Scope *scope = nullptr;
|
|
// absolute path
|
|
if (tok->str() == "::") {
|
|
tok = tok->next();
|
|
scope = &scopeList.front();
|
|
}
|
|
// relative path
|
|
else if (tok->isName()) {
|
|
scope = startScope;
|
|
}
|
|
|
|
while (scope && tok && tok->isName()) {
|
|
if (tok->strAt(1) == "::") {
|
|
scope = scope->findRecordInNestedList(tok->str());
|
|
tok = tok->tokAt(2);
|
|
} else if (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::")) {
|
|
scope = scope->findRecordInNestedList(tok->str());
|
|
tok = tok->linkAt(1)->tokAt(2);
|
|
} else
|
|
return scope->findRecordInNestedList(tok->str());
|
|
}
|
|
|
|
// not a valid path
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startScope) const
|
|
{
|
|
// skip over struct or union
|
|
if (Token::Match(startTok, "struct|union"))
|
|
startTok = startTok->next();
|
|
|
|
// type same as scope
|
|
if (startTok->str() == startScope->className && startScope->isClassOrStruct() && startTok->strAt(1) != "::")
|
|
return startScope->definedType;
|
|
|
|
const Scope* start_scope = startScope;
|
|
|
|
// absolute path - directly start in global scope
|
|
if (startTok->str() == "::") {
|
|
startTok = startTok->next();
|
|
start_scope = &scopeList.front();
|
|
}
|
|
|
|
const Token* tok = startTok;
|
|
const Scope* scope = start_scope;
|
|
|
|
while (scope && tok && tok->isName()) {
|
|
if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) {
|
|
scope = scope->findRecordInNestedList(tok->str());
|
|
if (scope) {
|
|
if (tok->strAt(1) == "::")
|
|
tok = tok->tokAt(2);
|
|
else
|
|
tok = tok->linkAt(1)->tokAt(2);
|
|
} else {
|
|
start_scope = start_scope->nestedIn;
|
|
if (!start_scope)
|
|
break;
|
|
scope = start_scope;
|
|
tok = startTok;
|
|
}
|
|
} else {
|
|
const Type * type = scope->findType(tok->str());
|
|
const Scope *scope1;
|
|
if (type)
|
|
return type;
|
|
else if ((scope1 = scope->findRecordInBase(tok->str()))) {
|
|
type = scope1->definedType;
|
|
if (type)
|
|
return type;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check using namespaces
|
|
while (startScope) {
|
|
for (std::list<Scope::UsingInfo>::const_iterator it = startScope->usingList.begin();
|
|
it != startScope->usingList.end(); ++it) {
|
|
tok = startTok;
|
|
scope = it->scope;
|
|
start_scope = startScope;
|
|
|
|
while (scope && tok && tok->isName()) {
|
|
if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) {
|
|
scope = scope->findRecordInNestedList(tok->str());
|
|
if (scope) {
|
|
if (tok->strAt(1) == "::")
|
|
tok = tok->tokAt(2);
|
|
else
|
|
tok = tok->linkAt(1)->tokAt(2);
|
|
} else {
|
|
start_scope = start_scope->nestedIn;
|
|
if (!start_scope)
|
|
break;
|
|
scope = start_scope;
|
|
tok = startTok;
|
|
}
|
|
} else {
|
|
const Type * type = scope->findType(tok->str());
|
|
const Scope *scope1;
|
|
if (type)
|
|
return type;
|
|
else if ((scope1 = scope->findRecordInBase(tok->str()))) {
|
|
type = scope1->definedType;
|
|
if (type)
|
|
return type;
|
|
} else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
startScope = startScope->nestedIn;
|
|
}
|
|
|
|
// not a valid path
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Type* SymbolDatabase::findTypeInNested(const Token *startTok, const Scope *startScope) const
|
|
{
|
|
// skip over struct or union
|
|
if (Token::Match(startTok, "struct|union|enum"))
|
|
startTok = startTok->next();
|
|
|
|
// type same as scope
|
|
if (startTok->str() == startScope->className && startScope->isClassOrStruct())
|
|
return startScope->definedType;
|
|
|
|
bool hasPath = false;
|
|
|
|
// absolute path - directly start in global scope
|
|
if (startTok->str() == "::") {
|
|
hasPath = true;
|
|
startTok = startTok->next();
|
|
startScope = &scopeList.front();
|
|
}
|
|
|
|
const Token* tok = startTok;
|
|
const Scope* scope = startScope;
|
|
|
|
while (scope && tok && tok->isName()) {
|
|
if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) {
|
|
hasPath = true;
|
|
scope = scope->findRecordInNestedList(tok->str());
|
|
if (scope) {
|
|
if (tok->strAt(1) == "::")
|
|
tok = tok->tokAt(2);
|
|
else
|
|
tok = tok->linkAt(1)->tokAt(2);
|
|
} else {
|
|
startScope = startScope->nestedIn;
|
|
if (!startScope)
|
|
break;
|
|
scope = startScope;
|
|
tok = startTok;
|
|
}
|
|
} else {
|
|
const Type * type = scope->findType(tok->str());
|
|
if (hasPath || type)
|
|
return type;
|
|
else {
|
|
scope = scope->nestedIn;
|
|
if (!scope)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// not a valid path
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
const Scope * SymbolDatabase::findNamespace(const Token * tok, const Scope * scope) const
|
|
{
|
|
const Scope * s = findScope(tok, scope);
|
|
|
|
if (s)
|
|
return s;
|
|
else if (scope->nestedIn)
|
|
return findNamespace(tok, scope->nestedIn);
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length)
|
|
{
|
|
const Function * function = nullptr;
|
|
const bool destructor = func->strAt(-1) == "~";
|
|
|
|
for (std::multimap<std::string, const Function *>::const_iterator it = ns->functionMap.find(func->str());
|
|
it != ns->functionMap.end() && it->first == func->str(); ++it) {
|
|
|
|
if (it->second->argsMatch(ns, it->second->argDef, func->next(), path, path_length) &&
|
|
it->second->isDestructor() == destructor) {
|
|
function = it->second;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!function) {
|
|
const Scope * scope = ns->findRecordInNestedList(func->str());
|
|
if (scope && Token::Match(func->tokAt(1), "::|<")) {
|
|
if (func->strAt(1) == "::")
|
|
func = func->tokAt(2);
|
|
else if (func->linkAt(1))
|
|
func = func->linkAt(1)->tokAt(2);
|
|
else
|
|
return nullptr;
|
|
if (func->str() == "~")
|
|
func = func->next();
|
|
function = findFunctionInScope(func, scope, path, path_length);
|
|
}
|
|
}
|
|
|
|
return const_cast<Function *>(function);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
namespace {
|
|
|
|
#define C_KEYWORDS\
|
|
"_Alignas", "_Alignof", "_Atomic", "_Bool", "_Complex", "_Generic", "_Imaginary", "_Noreturn", \
|
|
"_Static_assert", "_Thread_local", "auto", "break", "case", "char", "const", "continue", "default", \
|
|
"do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "inline", "int", "long", \
|
|
"register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", \
|
|
"union", "unsigned", "void", "volatile", "while"
|
|
|
|
const std::unordered_set<std::string> c_keywords = { C_KEYWORDS, "restrict" };
|
|
const std::unordered_set<std::string> cpp_keywords = {
|
|
C_KEYWORDS,
|
|
"alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "bool", "catch", "char8_t", "char16_t",
|
|
"char32_t", "class", "compl", "concept", "consteval", "constexpr", "constinit", "const_cast", "co_await",
|
|
"co_return", "co_yield", "decltype",
|
|
"delete", "dynamic_cast", "explicit", "export", "false", "friend",
|
|
"mutable", "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator",
|
|
"or", "or_eq", "private", "protected", "public", "reinterpret_cast",
|
|
"requires", "static_assert",
|
|
"static_cast", "template", "this", "thread_local", "throw",
|
|
"true", "try", "typeid", "typename", "using",
|
|
"virtual", "wchar_t", "xor", "xor_eq"
|
|
};
|
|
}
|
|
|
|
bool SymbolDatabase::isReservedName(const std::string& iName) const
|
|
{
|
|
if (isCPP())
|
|
return cpp_keywords.find(iName) != cpp_keywords.cend();
|
|
else
|
|
return c_keywords.find(iName) != c_keywords.cend();
|
|
}
|
|
|
|
nonneg int SymbolDatabase::sizeOfType(const Token *type) const
|
|
{
|
|
int size = mTokenizer->sizeOfType(type);
|
|
|
|
if (size == 0 && type->type() && type->type()->isEnumType() && type->type()->classScope) {
|
|
size = mSettings->sizeof_int;
|
|
const Token * enum_type = type->type()->classScope->enumType;
|
|
if (enum_type)
|
|
size = mTokenizer->sizeOfType(enum_type);
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings);
|
|
|
|
void SymbolDatabase::setValueType(Token *tok, const Variable &var)
|
|
{
|
|
ValueType valuetype;
|
|
if (var.nameToken())
|
|
valuetype.bits = var.nameToken()->bits();
|
|
valuetype.pointer = var.dimensions().size();
|
|
valuetype.typeScope = var.typeScope();
|
|
if (var.valueType()) {
|
|
valuetype.container = var.valueType()->container;
|
|
}
|
|
valuetype.smartPointerType = var.smartPointerType();
|
|
if (parsedecl(var.typeStartToken(), &valuetype, mDefaultSignedness, mSettings)) {
|
|
if (tok->str() == "." && tok->astOperand1()) {
|
|
const ValueType * const vt = tok->astOperand1()->valueType();
|
|
if (vt && (vt->constness & 1) != 0)
|
|
valuetype.constness |= 1;
|
|
}
|
|
setValueType(tok, valuetype);
|
|
}
|
|
}
|
|
|
|
void SymbolDatabase::setValueType(Token *tok, const Enumerator &enumerator)
|
|
{
|
|
ValueType valuetype;
|
|
valuetype.typeScope = enumerator.scope;
|
|
const Token * type = enumerator.scope->enumType;
|
|
if (type) {
|
|
valuetype.type = ValueType::typeFromString(type->str(), type->isLong());
|
|
if (valuetype.type == ValueType::Type::UNKNOWN_TYPE && type->isStandardType())
|
|
valuetype.fromLibraryType(type->str(), mSettings);
|
|
|
|
if (valuetype.isIntegral()) {
|
|
if (type->isSigned())
|
|
valuetype.sign = ValueType::Sign::SIGNED;
|
|
else if (type->isUnsigned())
|
|
valuetype.sign = ValueType::Sign::UNSIGNED;
|
|
else if (valuetype.type == ValueType::Type::CHAR)
|
|
valuetype.sign = mDefaultSignedness;
|
|
else
|
|
valuetype.sign = ValueType::Sign::SIGNED;
|
|
}
|
|
|
|
setValueType(tok, valuetype);
|
|
} else {
|
|
valuetype.sign = ValueType::SIGNED;
|
|
valuetype.type = ValueType::INT;
|
|
setValueType(tok, valuetype);
|
|
}
|
|
}
|
|
|
|
static void setAutoTokenProperties(Token * const autoTok)
|
|
{
|
|
const ValueType *valuetype = autoTok->valueType();
|
|
if (valuetype->isIntegral() || valuetype->isFloat())
|
|
autoTok->isStandardType(true);
|
|
}
|
|
|
|
void SymbolDatabase::setValueType(Token *tok, const ValueType &valuetype)
|
|
{
|
|
tok->setValueType(new ValueType(valuetype));
|
|
Token *parent = tok->astParent();
|
|
if (!parent || parent->valueType())
|
|
return;
|
|
if (!parent->astOperand1())
|
|
return;
|
|
|
|
const ValueType *vt1 = parent->astOperand1() ? parent->astOperand1()->valueType() : nullptr;
|
|
const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr;
|
|
|
|
if (vt1 && Token::Match(parent, "<<|>>")) {
|
|
if (!mIsCpp || (vt2 && vt2->isIntegral())) {
|
|
if (vt1->type < ValueType::Type::BOOL || vt1->type >= ValueType::Type::INT) {
|
|
ValueType vt(*vt1);
|
|
vt.reference = Reference::None;
|
|
setValueType(parent, vt);
|
|
} else {
|
|
ValueType vt(*vt1);
|
|
vt.type = ValueType::Type::INT; // Integer promotion
|
|
vt.sign = ValueType::Sign::SIGNED;
|
|
vt.reference = Reference::None;
|
|
setValueType(parent, vt);
|
|
}
|
|
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (vt1 && vt1->container && vt1->containerTypeToken && Token::Match(parent, ". %name% (") && vt1->container->getYield(parent->next()->str()) == Library::Container::Yield::ITEM) {
|
|
ValueType item;
|
|
if (parsedecl(vt1->containerTypeToken, &item, mDefaultSignedness, mSettings))
|
|
setValueType(parent->tokAt(2), item);
|
|
}
|
|
|
|
if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) {
|
|
const Scope *scope = vt1->smartPointerType->classScope;
|
|
const Function *f = scope ? scope->findFunction(parent->next(), false) : nullptr;
|
|
if (f)
|
|
parent->next()->function(f);
|
|
}
|
|
|
|
if (parent->isAssignmentOp()) {
|
|
if (vt1) {
|
|
auto vt = *vt1;
|
|
vt.reference = Reference::None;
|
|
setValueType(parent, vt);
|
|
} else if (mIsCpp && ((Token::Match(parent->tokAt(-3), "%var% ; %var% =") && parent->strAt(-3) == parent->strAt(-1)) ||
|
|
Token::Match(parent->tokAt(-1), "%var% ="))) {
|
|
Token *var1Tok = parent->strAt(-2) == ";" ? parent->tokAt(-3) : parent->tokAt(-1);
|
|
Token *autoTok = nullptr;
|
|
if (Token::Match(var1Tok->tokAt(-2), ";|{|}|(|const auto"))
|
|
autoTok = var1Tok->previous();
|
|
else if (Token::Match(var1Tok->tokAt(-3), ";|{|}|(|const auto *"))
|
|
autoTok = var1Tok->tokAt(-2);
|
|
if (autoTok) {
|
|
ValueType vt(*vt2);
|
|
if (vt.constness & (1 << vt.pointer))
|
|
vt.constness &= ~(1 << vt.pointer);
|
|
if (autoTok->strAt(1) == "*" && vt.pointer)
|
|
vt.pointer--;
|
|
if (autoTok->strAt(-1) == "const")
|
|
vt.constness |= 1;
|
|
setValueType(autoTok, vt);
|
|
setAutoTokenProperties(autoTok);
|
|
if (vt2->pointer > vt.pointer)
|
|
vt.pointer++;
|
|
setValueType(var1Tok, vt);
|
|
if (var1Tok != parent->previous())
|
|
setValueType(parent->previous(), vt);
|
|
Variable *var = const_cast<Variable *>(parent->previous()->variable());
|
|
if (var) {
|
|
ValueType vt2_(*vt2);
|
|
if (vt2_.pointer == 0 && autoTok->strAt(1) == "*")
|
|
vt2_.pointer = 1;
|
|
if ((vt.constness & (1 << vt2->pointer)) != 0)
|
|
vt2_.constness |= (1 << vt2->pointer);
|
|
if (!Token::Match(autoTok->tokAt(1), "*|&"))
|
|
vt2_.constness = vt.constness;
|
|
var->setValueType(vt2_);
|
|
if (vt2->typeScope && vt2->typeScope->definedType) {
|
|
var->type(vt2->typeScope->definedType);
|
|
if (autoTok->valueType()->pointer == 0)
|
|
autoTok->type(vt2->typeScope->definedType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (parent->str() == "[" && (!mIsCpp || parent->astOperand1() == tok) && valuetype.pointer > 0U && !Token::Match(parent->previous(), "[{,]")) {
|
|
const Token *op1 = parent->astOperand1();
|
|
while (op1 && op1->str() == "[")
|
|
op1 = op1->astOperand1();
|
|
|
|
ValueType vt(valuetype);
|
|
// the "[" is a dereference unless this is a variable declaration
|
|
if (!(op1 && op1->variable() && op1->variable()->nameToken() == op1))
|
|
vt.pointer -= 1U;
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
if (Token::Match(parent->previous(), "%name% (") && parent->astOperand1() == tok && valuetype.pointer > 0U) {
|
|
ValueType vt(valuetype);
|
|
vt.pointer -= 1U;
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
// std::move
|
|
if (vt2 && parent->str() == "(" && Token::simpleMatch(parent->tokAt(-3), "std :: move (")) {
|
|
ValueType vt = valuetype;
|
|
vt.reference = Reference::RValue;
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) {
|
|
ValueType vt(valuetype);
|
|
vt.pointer -= 1U;
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) {
|
|
const Token *op1 = parent->astOperand2()->astOperand1();
|
|
while (op1 && op1->str() == "[")
|
|
op1 = op1->astOperand1();
|
|
const ValueType& vt(valuetype);
|
|
if (op1 && op1->variable() && op1->variable()->nameToken() == op1) {
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
}
|
|
if (parent->str() == "&" && !parent->astOperand2()) {
|
|
ValueType vt(valuetype);
|
|
vt.reference = Reference::None; //Given int& x; the type of &x is int* not int&*
|
|
vt.pointer += 1U;
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
|
|
if ((parent->str() == "." || parent->str() == "::") &&
|
|
parent->astOperand2() && parent->astOperand2()->isName()) {
|
|
const Variable* var = parent->astOperand2()->variable();
|
|
if (!var && valuetype.typeScope && vt1) {
|
|
const std::string &name = parent->astOperand2()->str();
|
|
const Scope *typeScope = vt1->typeScope;
|
|
if (!typeScope)
|
|
return;
|
|
for (std::list<Variable>::const_iterator it = typeScope->varlist.begin(); it != typeScope->varlist.end(); ++it) {
|
|
if (it->nameToken()->str() == name) {
|
|
var = &*it;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (var)
|
|
setValueType(parent, *var);
|
|
return;
|
|
}
|
|
|
|
// range for loop, auto
|
|
if (vt2 &&
|
|
parent->str() == ":" &&
|
|
Token::Match(parent->astParent(), "( const| auto *|&| %var% :") &&
|
|
!parent->previous()->valueType() &&
|
|
Token::simpleMatch(parent->astParent()->astOperand1(), "for")) {
|
|
const bool isconst = Token::simpleMatch(parent->astParent()->next(), "const");
|
|
Token * const autoToken = parent->astParent()->tokAt(isconst ? 2 : 1);
|
|
if (vt2->pointer) {
|
|
ValueType autovt(*vt2);
|
|
autovt.pointer--;
|
|
autovt.constness = 0;
|
|
setValueType(autoToken, autovt);
|
|
setAutoTokenProperties(autoToken);
|
|
ValueType varvt(*vt2);
|
|
varvt.pointer--;
|
|
if (isconst)
|
|
varvt.constness |= 1;
|
|
setValueType(parent->previous(), varvt);
|
|
Variable *var = const_cast<Variable *>(parent->previous()->variable());
|
|
if (var) {
|
|
var->setValueType(varvt);
|
|
if (vt2->typeScope && vt2->typeScope->definedType) {
|
|
var->type(vt2->typeScope->definedType);
|
|
autoToken->type(vt2->typeScope->definedType);
|
|
}
|
|
}
|
|
} else if (vt2->container) {
|
|
// TODO: Determine exact type of RHS
|
|
const Token *typeStart = parent->astOperand2();
|
|
while (typeStart) {
|
|
if (typeStart->variable())
|
|
typeStart = typeStart->variable()->typeStartToken();
|
|
else if (typeStart->str() == "(" && typeStart->previous() && typeStart->previous()->function())
|
|
typeStart = typeStart->previous()->function()->retDef;
|
|
else
|
|
break;
|
|
}
|
|
|
|
// Try to determine type of "auto" token.
|
|
// TODO: Get type better
|
|
bool setType = false;
|
|
ValueType autovt;
|
|
const Type *templateArgType = nullptr; // container element type / smart pointer type
|
|
if (vt2->containerTypeToken) {
|
|
if (mSettings->library.isSmartPointer(vt2->containerTypeToken)) {
|
|
const Token *smartPointerTypeTok = vt2->containerTypeToken;
|
|
while (Token::Match(smartPointerTypeTok, "%name%|::"))
|
|
smartPointerTypeTok = smartPointerTypeTok->next();
|
|
if (Token::Match(smartPointerTypeTok, "< %name% > >") && smartPointerTypeTok->next()->type()) {
|
|
setType = true;
|
|
templateArgType = smartPointerTypeTok->next()->type();
|
|
autovt.smartPointerType = templateArgType;
|
|
autovt.type = ValueType::Type::NONSTD;
|
|
}
|
|
} else if (parsedecl(vt2->containerTypeToken, &autovt, mDefaultSignedness, mSettings)) {
|
|
setType = true;
|
|
templateArgType = vt2->containerTypeToken->type();
|
|
}
|
|
|
|
}
|
|
|
|
if (setType) {
|
|
// Type of "auto" has been determined.. set type information for "auto" and variable tokens
|
|
setValueType(autoToken, autovt);
|
|
setAutoTokenProperties(autoToken);
|
|
ValueType varvt(autovt);
|
|
if (isconst)
|
|
varvt.constness |= 1;
|
|
setValueType(parent->previous(), varvt);
|
|
Variable * var = const_cast<Variable *>(parent->previous()->variable());
|
|
if (var) {
|
|
var->setValueType(varvt);
|
|
if (templateArgType && templateArgType->classScope && templateArgType->classScope->definedType) {
|
|
autoToken->type(templateArgType->classScope->definedType);
|
|
var->type(templateArgType->classScope->definedType);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (vt1 && vt1->containerTypeToken && parent->str() == "[") {
|
|
ValueType vtParent;
|
|
if (parsedecl(vt1->containerTypeToken, &vtParent, mDefaultSignedness, mSettings)) {
|
|
setValueType(parent, vtParent);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (mIsCpp && vt2 && Token::simpleMatch(parent->previous(), "decltype (")) {
|
|
setValueType(parent, *vt2);
|
|
return;
|
|
}
|
|
|
|
// c++17 auto type deduction of braced init list
|
|
if (mIsCpp && mSettings->standards.cpp >= Standards::CPP17 && vt2 && Token::Match(parent->tokAt(-2), "auto %var% {")) {
|
|
Token *autoTok = parent->tokAt(-2);
|
|
setValueType(autoTok, *vt2);
|
|
setAutoTokenProperties(autoTok);
|
|
const_cast<Variable *>(parent->previous()->variable())->setValueType(*vt2);
|
|
return;
|
|
}
|
|
|
|
if (!vt1)
|
|
return;
|
|
if (parent->astOperand2() && !vt2)
|
|
return;
|
|
|
|
const bool ternary = parent->str() == ":" && parent->astParent() && parent->astParent()->str() == "?";
|
|
if (ternary) {
|
|
if (vt2 && vt1->pointer == vt2->pointer && vt1->type == vt2->type && vt1->sign == vt2->sign)
|
|
setValueType(parent, *vt2);
|
|
parent = parent->astParent();
|
|
}
|
|
|
|
if (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eIncDecOp) {
|
|
|
|
// CONTAINER + x => CONTAINER
|
|
if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->isIntegral()) {
|
|
setValueType(parent, *vt1);
|
|
return;
|
|
}
|
|
// x + CONTAINER => CONTAINER
|
|
if (parent->str() == "+" && vt1->isIntegral() && vt2 && vt2->type == ValueType::Type::CONTAINER) {
|
|
setValueType(parent, *vt2);
|
|
return;
|
|
}
|
|
|
|
if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) {
|
|
setValueType(parent, *vt1);
|
|
return;
|
|
}
|
|
|
|
if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) {
|
|
setValueType(parent, *vt2);
|
|
return;
|
|
}
|
|
|
|
if (vt1->pointer != 0U) {
|
|
if (ternary || parent->tokType() == Token::eIncDecOp) // result is pointer
|
|
setValueType(parent, *vt1);
|
|
else // result is pointer diff
|
|
setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t"));
|
|
return;
|
|
}
|
|
|
|
if (vt1->type == ValueType::Type::LONGDOUBLE || (vt2 && vt2->type == ValueType::Type::LONGDOUBLE)) {
|
|
setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U));
|
|
return;
|
|
}
|
|
if (vt1->type == ValueType::Type::DOUBLE || (vt2 && vt2->type == ValueType::Type::DOUBLE)) {
|
|
setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U));
|
|
return;
|
|
}
|
|
if (vt1->type == ValueType::Type::FLOAT || (vt2 && vt2->type == ValueType::Type::FLOAT)) {
|
|
setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U));
|
|
return;
|
|
}
|
|
|
|
// iterator +/- integral = iterator
|
|
if (vt1->type == ValueType::Type::ITERATOR && vt2 && vt2->isIntegral() &&
|
|
(parent->str() == "+" || parent->str() == "-")) {
|
|
setValueType(parent, *vt1);
|
|
return;
|
|
}
|
|
|
|
if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->type == ValueType::Type::CONTAINER && vt1->container == vt2->container) {
|
|
setValueType(parent, *vt1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (vt1->isIntegral() && vt1->pointer == 0U &&
|
|
(!vt2 || (vt2->isIntegral() && vt2->pointer == 0U)) &&
|
|
(ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eBitOp || parent->tokType() == Token::eIncDecOp || parent->isAssignmentOp())) {
|
|
|
|
ValueType vt;
|
|
if (!vt2 || vt1->type > vt2->type) {
|
|
vt.type = vt1->type;
|
|
vt.sign = vt1->sign;
|
|
vt.originalTypeName = vt1->originalTypeName;
|
|
} else if (vt1->type == vt2->type) {
|
|
vt.type = vt1->type;
|
|
if (vt1->sign == ValueType::Sign::UNSIGNED || vt2->sign == ValueType::Sign::UNSIGNED)
|
|
vt.sign = ValueType::Sign::UNSIGNED;
|
|
else if (vt1->sign == ValueType::Sign::UNKNOWN_SIGN || vt2->sign == ValueType::Sign::UNKNOWN_SIGN)
|
|
vt.sign = ValueType::Sign::UNKNOWN_SIGN;
|
|
else
|
|
vt.sign = ValueType::Sign::SIGNED;
|
|
vt.originalTypeName = (vt1->originalTypeName.empty() ? vt2 : vt1)->originalTypeName;
|
|
} else {
|
|
vt.type = vt2->type;
|
|
vt.sign = vt2->sign;
|
|
vt.originalTypeName = vt2->originalTypeName;
|
|
}
|
|
if (vt.type < ValueType::Type::INT && !(ternary && vt.type==ValueType::Type::BOOL)) {
|
|
vt.type = ValueType::Type::INT;
|
|
vt.sign = ValueType::Sign::SIGNED;
|
|
vt.originalTypeName.clear();
|
|
}
|
|
|
|
setValueType(parent, vt);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static const Token * parsedecl(const Token *type, ValueType * const valuetype, ValueType::Sign defaultSignedness, const Settings* settings)
|
|
{
|
|
const Token * const previousType = type;
|
|
const unsigned int pointer0 = valuetype->pointer;
|
|
while (Token::Match(type->previous(), "%name%") && !endsWith(type->previous()->str(), ':'))
|
|
type = type->previous();
|
|
valuetype->sign = ValueType::Sign::UNKNOWN_SIGN;
|
|
if (!valuetype->typeScope && !valuetype->smartPointerType)
|
|
valuetype->type = ValueType::Type::UNKNOWN_TYPE;
|
|
else if (valuetype->smartPointerType)
|
|
valuetype->type = ValueType::Type::NONSTD;
|
|
else if (valuetype->typeScope->type == Scope::eEnum) {
|
|
const Token * enum_type = valuetype->typeScope->enumType;
|
|
if (enum_type) {
|
|
if (enum_type->isSigned())
|
|
valuetype->sign = ValueType::Sign::SIGNED;
|
|
else if (enum_type->isUnsigned())
|
|
valuetype->sign = ValueType::Sign::UNSIGNED;
|
|
else
|
|
valuetype->sign = defaultSignedness;
|
|
const ValueType::Type t = ValueType::typeFromString(enum_type->str(), enum_type->isLong());
|
|
if (t != ValueType::Type::UNKNOWN_TYPE)
|
|
valuetype->type = t;
|
|
else if (enum_type->isStandardType())
|
|
valuetype->fromLibraryType(enum_type->str(), settings);
|
|
} else
|
|
valuetype->type = ValueType::Type::INT;
|
|
} else
|
|
valuetype->type = ValueType::Type::RECORD;
|
|
bool par = false;
|
|
while (Token::Match(type, "%name%|*|&|&&|::|(") && !Token::Match(type, "typename|template") && type->varId() == 0 &&
|
|
!type->variable() && !type->function()) {
|
|
if (type->str() == "(") {
|
|
if (Token::Match(type->link(), ") const| {"))
|
|
break;
|
|
if (par)
|
|
break;
|
|
par = true;
|
|
}
|
|
if (Token::simpleMatch(type, "decltype (") && type->next()->valueType()) {
|
|
const ValueType *vt2 = type->next()->valueType();
|
|
if (valuetype->sign == ValueType::Sign::UNKNOWN_SIGN)
|
|
valuetype->sign = vt2->sign;
|
|
if (valuetype->type == ValueType::Type::UNKNOWN_TYPE)
|
|
valuetype->type = vt2->type;
|
|
valuetype->constness += vt2->constness;
|
|
valuetype->pointer += vt2->pointer;
|
|
type = type->linkAt(1)->next();
|
|
continue;
|
|
} else if (type->isSigned())
|
|
valuetype->sign = ValueType::Sign::SIGNED;
|
|
else if (type->isUnsigned())
|
|
valuetype->sign = ValueType::Sign::UNSIGNED;
|
|
if (valuetype->type == ValueType::Type::UNKNOWN_TYPE &&
|
|
type->type() && type->type()->isTypeAlias() && type->type()->typeStart &&
|
|
type->type()->typeStart->str() != type->str() && type->type()->typeStart != previousType)
|
|
parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings);
|
|
else if (type->str() == "const")
|
|
valuetype->constness |= (1 << (valuetype->pointer - pointer0));
|
|
else if (settings->clang && type->str().size() > 2 && type->str().find("::") < type->str().find("<")) {
|
|
TokenList typeTokens(settings);
|
|
std::string::size_type pos1 = 0;
|
|
do {
|
|
std::string::size_type pos2 = type->str().find("::", pos1);
|
|
if (pos2 == std::string::npos) {
|
|
typeTokens.addtoken(type->str().substr(pos1), 0, 0, 0, false);
|
|
break;
|
|
}
|
|
typeTokens.addtoken(type->str().substr(pos1, pos2 - pos1), 0, 0, 0, false);
|
|
typeTokens.addtoken("::", 0, 0, 0, false);
|
|
pos1 = pos2 + 2;
|
|
} while (pos1 < type->str().size());
|
|
const Library::Container *container = settings->library.detectContainer(typeTokens.front());
|
|
if (container) {
|
|
valuetype->type = ValueType::Type::CONTAINER;
|
|
valuetype->container = container;
|
|
} else {
|
|
const Scope *scope = type->scope();
|
|
valuetype->typeScope = scope->check->findScope(typeTokens.front(), scope);
|
|
if (valuetype->typeScope)
|
|
valuetype->type = (scope->type == Scope::ScopeType::eClass) ? ValueType::Type::RECORD : ValueType::Type::NONSTD;
|
|
}
|
|
} else if (const Library::Container *container = settings->library.detectContainer(type)) {
|
|
valuetype->type = ValueType::Type::CONTAINER;
|
|
valuetype->container = container;
|
|
while (Token::Match(type, "%name%|::|<")) {
|
|
if (type->str() == "<" && type->link()) {
|
|
if (container->type_templateArgNo >= 0) {
|
|
const Token *templateType = type->next();
|
|
for (int j = 0; templateType && j < container->type_templateArgNo; j++)
|
|
templateType = templateType->nextTemplateArgument();
|
|
valuetype->containerTypeToken = templateType;
|
|
}
|
|
type = type->link();
|
|
}
|
|
type = type->next();
|
|
}
|
|
if (type && type->str() == "(" && type->previous()->function())
|
|
// we are past the end of the type
|
|
type = type->previous();
|
|
continue;
|
|
} else if (settings->library.isSmartPointer(type)) {
|
|
const Token* argTok = Token::findsimplematch(type, "<");
|
|
if (!argTok)
|
|
continue;
|
|
valuetype->smartPointerTypeToken = argTok->next();
|
|
valuetype->smartPointerType = argTok->next()->type();
|
|
valuetype->type = ValueType::Type::NONSTD;
|
|
type = argTok->link();
|
|
if (type)
|
|
type = type->next();
|
|
continue;
|
|
} else if (Token::Match(type, "%name% :: %name%")) {
|
|
std::string typestr;
|
|
const Token *end = type;
|
|
while (Token::Match(end, "%name% :: %name%")) {
|
|
typestr += end->str() + "::";
|
|
end = end->tokAt(2);
|
|
}
|
|
typestr += end->str();
|
|
if (valuetype->fromLibraryType(typestr, settings))
|
|
type = end;
|
|
} else if (ValueType::Type::UNKNOWN_TYPE != ValueType::typeFromString(type->str(), type->isLong())) {
|
|
ValueType::Type t0 = valuetype->type;
|
|
valuetype->type = ValueType::typeFromString(type->str(), type->isLong());
|
|
if (t0 == ValueType::Type::LONG) {
|
|
if (valuetype->type == ValueType::Type::LONG)
|
|
valuetype->type = ValueType::Type::LONGLONG;
|
|
else if (valuetype->type == ValueType::Type::DOUBLE)
|
|
valuetype->type = ValueType::Type::LONGDOUBLE;
|
|
}
|
|
} else if (type->str() == "auto") {
|
|
const ValueType *vt = type->valueType();
|
|
if (!vt)
|
|
return nullptr;
|
|
valuetype->type = vt->type;
|
|
valuetype->pointer = vt->pointer;
|
|
if (vt->sign != ValueType::Sign::UNKNOWN_SIGN)
|
|
valuetype->sign = vt->sign;
|
|
valuetype->constness = vt->constness;
|
|
valuetype->originalTypeName = vt->originalTypeName;
|
|
while (Token::Match(type, "%name%|*|&|::") && !type->variable()) {
|
|
if (type->str() == "*")
|
|
valuetype->pointer++;
|
|
if (type->str() == "const")
|
|
valuetype->constness |= (1 << valuetype->pointer);
|
|
type = type->next();
|
|
}
|
|
break;
|
|
} else if (!valuetype->typeScope && (type->str() == "struct" || type->str() == "enum"))
|
|
valuetype->type = type->str() == "struct" ? ValueType::Type::RECORD : ValueType::Type::NONSTD;
|
|
else if (!valuetype->typeScope && type->type() && type->type()->classScope) {
|
|
if (type->type()->classScope->type == Scope::ScopeType::eEnum) {
|
|
valuetype->type = ValueType::Type::INT;
|
|
valuetype->sign = ValueType::Sign::SIGNED;
|
|
} else {
|
|
valuetype->type = ValueType::Type::RECORD;
|
|
}
|
|
valuetype->typeScope = type->type()->classScope;
|
|
} else if (type->isName() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U)
|
|
return nullptr;
|
|
else if (type->str() == "*")
|
|
valuetype->pointer++;
|
|
else if (type->str() == "&")
|
|
valuetype->reference = Reference::LValue;
|
|
else if (type->str() == "&&")
|
|
valuetype->reference = Reference::RValue;
|
|
else if (type->isStandardType())
|
|
valuetype->fromLibraryType(type->str(), settings);
|
|
else if (Token::Match(type->previous(), "!!:: %name% !!::"))
|
|
valuetype->fromLibraryType(type->str(), settings);
|
|
if (!type->originalName().empty())
|
|
valuetype->originalTypeName = type->originalName();
|
|
type = type->next();
|
|
}
|
|
|
|
// Set signedness for integral types..
|
|
if (valuetype->isIntegral() && valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) {
|
|
if (valuetype->type == ValueType::Type::CHAR)
|
|
valuetype->sign = defaultSignedness;
|
|
else if (valuetype->type >= ValueType::Type::SHORT)
|
|
valuetype->sign = ValueType::Sign::SIGNED;
|
|
}
|
|
|
|
return (type && (valuetype->type != ValueType::Type::UNKNOWN_TYPE || valuetype->pointer > 0 || valuetype->reference != Reference::None)) ? type : nullptr;
|
|
}
|
|
|
|
static const Scope *getClassScope(const Token *tok)
|
|
{
|
|
return tok && tok->valueType() && tok->valueType()->typeScope && tok->valueType()->typeScope->isClassOrStruct() ?
|
|
tok->valueType()->typeScope :
|
|
nullptr;
|
|
}
|
|
|
|
static const Function *getOperatorFunction(const Token * const tok)
|
|
{
|
|
const std::string functionName("operator" + tok->str());
|
|
std::multimap<std::string, const Function *>::const_iterator it;
|
|
|
|
const Scope *classScope = getClassScope(tok->astOperand1());
|
|
if (classScope) {
|
|
it = classScope->functionMap.find(functionName);
|
|
if (it != classScope->functionMap.end())
|
|
return it->second;
|
|
}
|
|
|
|
classScope = getClassScope(tok->astOperand2());
|
|
if (classScope) {
|
|
it = classScope->functionMap.find(functionName);
|
|
if (it != classScope->functionMap.end())
|
|
return it->second;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens)
|
|
{
|
|
if (!tokens)
|
|
tokens = const_cast<Tokenizer *>(mTokenizer)->list.front();
|
|
|
|
for (Token *tok = tokens; tok; tok = tok->next())
|
|
tok->setValueType(nullptr);
|
|
|
|
for (Token *tok = tokens; tok; tok = tok->next()) {
|
|
if (tok->isNumber()) {
|
|
if (MathLib::isFloat(tok->str())) {
|
|
ValueType::Type type = ValueType::Type::DOUBLE;
|
|
const char suffix = tok->str()[tok->str().size() - 1];
|
|
if (suffix == 'f' || suffix == 'F')
|
|
type = ValueType::Type::FLOAT;
|
|
else if (suffix == 'L' || suffix == 'l')
|
|
type = ValueType::Type::LONGDOUBLE;
|
|
setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U));
|
|
} else if (MathLib::isInt(tok->str())) {
|
|
const std::string tokStr = MathLib::abs(tok->str());
|
|
const bool unsignedSuffix = (tokStr.find_last_of("uU") != std::string::npos);
|
|
ValueType::Sign sign = unsignedSuffix ? ValueType::Sign::UNSIGNED : ValueType::Sign::SIGNED;
|
|
ValueType::Type type = ValueType::Type::INT;
|
|
const MathLib::biguint value = MathLib::toULongNumber(tokStr);
|
|
for (std::size_t pos = tokStr.size() - 1U; pos > 0U; --pos) {
|
|
const char suffix = tokStr[pos];
|
|
if (suffix == 'u' || suffix == 'U')
|
|
sign = ValueType::Sign::UNSIGNED;
|
|
else if (suffix == 'l' || suffix == 'L')
|
|
type = (type == ValueType::Type::INT) ? ValueType::Type::LONG : ValueType::Type::LONGLONG;
|
|
else if (pos > 2U && suffix == '4' && tokStr[pos - 1] == '6' && tokStr[pos - 2] == 'i') {
|
|
type = ValueType::Type::LONGLONG;
|
|
pos -= 2;
|
|
} else break;
|
|
}
|
|
if (mSettings->platformType != cppcheck::Platform::Unspecified) {
|
|
if (type <= ValueType::Type::INT && mSettings->isIntValue(unsignedSuffix ? (value >> 1) : value))
|
|
type = ValueType::Type::INT;
|
|
else if (type <= ValueType::Type::INT && !MathLib::isDec(tokStr) && mSettings->isIntValue(value >> 2)) {
|
|
type = ValueType::Type::INT;
|
|
sign = ValueType::Sign::UNSIGNED;
|
|
} else if (type <= ValueType::Type::LONG && mSettings->isLongValue(unsignedSuffix ? (value >> 1) : value))
|
|
type = ValueType::Type::LONG;
|
|
else if (type <= ValueType::Type::LONG && !MathLib::isDec(tokStr) && mSettings->isLongValue(value >> 2)) {
|
|
type = ValueType::Type::LONG;
|
|
sign = ValueType::Sign::UNSIGNED;
|
|
} else if (mSettings->isLongLongValue(unsignedSuffix ? (value >> 1) : value))
|
|
type = ValueType::Type::LONGLONG;
|
|
else {
|
|
type = ValueType::Type::LONGLONG;
|
|
sign = ValueType::Sign::UNSIGNED;
|
|
}
|
|
}
|
|
|
|
setValueType(tok, ValueType(sign, type, 0U));
|
|
}
|
|
} else if (tok->isComparisonOp() || tok->tokType() == Token::eLogicalOp) {
|
|
if (mIsCpp && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) {
|
|
const Function *function = getOperatorFunction(tok);
|
|
if (function) {
|
|
ValueType vt;
|
|
parsedecl(function->retDef, &vt, mDefaultSignedness, mSettings);
|
|
setValueType(tok, vt);
|
|
continue;
|
|
}
|
|
}
|
|
setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U));
|
|
} else if (tok->isBoolean()) {
|
|
setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U));
|
|
} else if (tok->tokType() == Token::eChar || tok->tokType() == Token::eString) {
|
|
nonneg int pointer = tok->tokType() == Token::eChar ? 0U : 1U;
|
|
nonneg int constness = tok->tokType() == Token::eChar ? 0U : 1U;
|
|
ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, pointer, constness);
|
|
|
|
if (mIsCpp && mSettings->standards.cpp >= Standards::CPP20 && tok->isUtf8()) {
|
|
valuetype.originalTypeName = "char8_t";
|
|
valuetype.fromLibraryType(valuetype.originalTypeName, mSettings);
|
|
} else if (tok->isUtf16()) {
|
|
valuetype.originalTypeName = "char16_t";
|
|
valuetype.fromLibraryType(valuetype.originalTypeName, mSettings);
|
|
} else if (tok->isUtf32()) {
|
|
valuetype.originalTypeName = "char32_t";
|
|
valuetype.fromLibraryType(valuetype.originalTypeName, mSettings);
|
|
} else if (tok->isLong()) {
|
|
valuetype.originalTypeName = "wchar_t";
|
|
valuetype.type = ValueType::Type::WCHAR_T;
|
|
} else if ((tok->tokType() == Token::eChar) && ((tok->isCChar() && !mIsCpp) || (tok->isCMultiChar()))) {
|
|
valuetype.type = ValueType::Type::INT;
|
|
valuetype.sign = ValueType::Sign::SIGNED;
|
|
}
|
|
setValueType(tok, valuetype);
|
|
} else if (tok->link() && Token::Match(tok, "(|{")) {
|
|
const Token* start = tok->astOperand1() ? tok->astOperand1()->findExpressionStartEndTokens().first : nullptr;
|
|
// cast
|
|
if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) {
|
|
ValueType valuetype;
|
|
if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, mDefaultSignedness, mSettings), ")"))
|
|
setValueType(tok, valuetype);
|
|
}
|
|
|
|
// C++ cast
|
|
else if (tok->astOperand2() && Token::Match(tok->astOperand1(), "static_cast|const_cast|dynamic_cast|reinterpret_cast < %name%") && tok->astOperand1()->linkAt(1)) {
|
|
ValueType valuetype;
|
|
if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, mDefaultSignedness, mSettings), ">"))
|
|
setValueType(tok, valuetype);
|
|
}
|
|
|
|
// Construct smart pointer
|
|
else if (mSettings->library.isSmartPointer(start)) {
|
|
ValueType valuetype;
|
|
if (parsedecl(start, &valuetype, mDefaultSignedness, mSettings)) {
|
|
setValueType(tok, valuetype);
|
|
setValueType(tok->astOperand1(), valuetype);
|
|
}
|
|
|
|
}
|
|
|
|
// function
|
|
else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) {
|
|
ValueType valuetype;
|
|
if (parsedecl(tok->previous()->function()->retDef, &valuetype, mDefaultSignedness, mSettings))
|
|
setValueType(tok, valuetype);
|
|
}
|
|
|
|
else if (Token::simpleMatch(tok->previous(), "sizeof (")) {
|
|
// TODO: use specified size_t type
|
|
ValueType valuetype(ValueType::Sign::UNSIGNED, ValueType::Type::LONG, 0U);
|
|
valuetype.originalTypeName = "size_t";
|
|
setValueType(tok, valuetype);
|
|
|
|
if (Token::Match(tok, "( %type% %type%| *| *| )")) {
|
|
ValueType vt;
|
|
if (parsedecl(tok->next(), &vt, mDefaultSignedness, mSettings)) {
|
|
setValueType(tok->next(), vt);
|
|
}
|
|
}
|
|
}
|
|
|
|
// function style cast
|
|
else if (tok->previous() && tok->previous()->isStandardType()) {
|
|
ValueType valuetype;
|
|
if (tok->astOperand1() && valuetype.fromLibraryType(tok->astOperand1()->expressionString(), mSettings)) {
|
|
setValueType(tok, valuetype);
|
|
continue;
|
|
}
|
|
|
|
valuetype.type = ValueType::typeFromString(tok->previous()->str(), tok->previous()->isLong());
|
|
if (tok->previous()->isUnsigned())
|
|
valuetype.sign = ValueType::Sign::UNSIGNED;
|
|
else if (tok->previous()->isSigned())
|
|
valuetype.sign = ValueType::Sign::SIGNED;
|
|
setValueType(tok, valuetype);
|
|
}
|
|
|
|
// constructor call
|
|
else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->isConstructor()) {
|
|
ValueType valuetype;
|
|
valuetype.type = ValueType::RECORD;
|
|
valuetype.typeScope = tok->previous()->function()->token->scope();
|
|
setValueType(tok, valuetype);
|
|
}
|
|
|
|
// library type/function
|
|
else if (tok->previous()) {
|
|
if (tok->astParent() && Token::Match(tok->astOperand1(), "%name%|::")) {
|
|
const Token *typeStartToken = tok->astOperand1();
|
|
while (typeStartToken && typeStartToken->str() == "::")
|
|
typeStartToken = typeStartToken->astOperand1();
|
|
if (const Library::Container *c = mSettings->library.detectContainer(typeStartToken)) {
|
|
ValueType vt;
|
|
vt.pointer = 0;
|
|
vt.container = c;
|
|
vt.type = ValueType::Type::CONTAINER;
|
|
setValueType(tok, vt);
|
|
continue;
|
|
}
|
|
|
|
const std::string e = tok->astOperand1()->expressionString();
|
|
|
|
if ((e == "std::make_shared" || e == "std::make_unique") && Token::Match(tok->astOperand1(), ":: %name% < %name%")) {
|
|
ValueType vt;
|
|
parsedecl(tok->astOperand1()->tokAt(3), &vt, mDefaultSignedness, mSettings);
|
|
if (vt.typeScope) {
|
|
vt.smartPointerType = vt.typeScope->definedType;
|
|
vt.typeScope = nullptr;
|
|
setValueType(tok, vt);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
ValueType podtype;
|
|
if (podtype.fromLibraryType(e, mSettings)) {
|
|
setValueType(tok, podtype);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
const std::string& typestr(mSettings->library.returnValueType(tok->previous()));
|
|
if (!typestr.empty()) {
|
|
ValueType valuetype;
|
|
TokenList tokenList(mSettings);
|
|
std::istringstream istr(typestr+";");
|
|
tokenList.createTokens(istr);
|
|
tokenList.simplifyStdType();
|
|
if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings)) {
|
|
valuetype.originalTypeName = typestr;
|
|
setValueType(tok, valuetype);
|
|
}
|
|
}
|
|
|
|
if (typestr.empty() || typestr == "iterator") {
|
|
if (Token::simpleMatch(tok->astOperand1(), ".") &&
|
|
tok->astOperand1()->astOperand1() &&
|
|
tok->astOperand1()->astOperand2() &&
|
|
tok->astOperand1()->astOperand1()->valueType() &&
|
|
tok->astOperand1()->astOperand1()->valueType()->container) {
|
|
const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container;
|
|
const std::map<std::string, Library::Container::Function>::const_iterator it = cont->functions.find(tok->astOperand1()->astOperand2()->str());
|
|
if (it != cont->functions.end()) {
|
|
if (it->second.yield == Library::Container::Yield::START_ITERATOR ||
|
|
it->second.yield == Library::Container::Yield::END_ITERATOR ||
|
|
it->second.yield == Library::Container::Yield::ITERATOR) {
|
|
ValueType vt;
|
|
vt.type = ValueType::Type::ITERATOR;
|
|
vt.container = cont;
|
|
setValueType(tok, vt);
|
|
}
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
TokenList tokenList(mSettings);
|
|
std::istringstream istr(typestr+";");
|
|
if (tokenList.createTokens(istr)) {
|
|
ValueType vt;
|
|
tokenList.simplifyPlatformTypes();
|
|
tokenList.simplifyStdType();
|
|
if (parsedecl(tokenList.front(), &vt, mDefaultSignedness, mSettings)) {
|
|
vt.originalTypeName = typestr;
|
|
setValueType(tok, vt);
|
|
}
|
|
}
|
|
}
|
|
} else if (tok->str() == "return") {
|
|
const Scope *functionScope = tok->scope();
|
|
while (functionScope && functionScope->isExecutable() && functionScope->type != Scope::eLambda && functionScope->type != Scope::eFunction)
|
|
functionScope = functionScope->nestedIn;
|
|
if (functionScope && functionScope->type == Scope::eFunction && functionScope->function && functionScope->function->retDef)
|
|
setValueType(tok, ValueType::parseDecl(functionScope->function->retDef, mSettings));
|
|
} else if (tok->variable()) {
|
|
setValueType(tok, *tok->variable());
|
|
if (!tok->variable()->valueType() && tok->valueType())
|
|
const_cast<Variable*>(tok->variable())->setValueType(*tok->valueType());
|
|
} else if (tok->enumerator()) {
|
|
setValueType(tok, *tok->enumerator());
|
|
} else if (tok->isKeyword() && tok->str() == "new") {
|
|
const Token *typeTok = tok->next();
|
|
if (Token::Match(typeTok, "( std| ::| nothrow )"))
|
|
typeTok = typeTok->link()->next();
|
|
if (const Library::Container *c = mSettings->library.detectContainer(typeTok)) {
|
|
ValueType vt;
|
|
vt.pointer = 1;
|
|
vt.container = c;
|
|
vt.type = ValueType::Type::CONTAINER;
|
|
setValueType(tok, vt);
|
|
continue;
|
|
}
|
|
std::string typestr;
|
|
while (Token::Match(typeTok, "%name% :: %name%")) {
|
|
typestr += typeTok->str() + "::";
|
|
typeTok = typeTok->tokAt(2);
|
|
}
|
|
if (!Token::Match(typeTok, "%type% ;|[|("))
|
|
continue;
|
|
typestr += typeTok->str();
|
|
ValueType vt;
|
|
vt.pointer = 1;
|
|
if (typeTok->type() && typeTok->type()->classScope) {
|
|
vt.type = ValueType::Type::RECORD;
|
|
vt.typeScope = typeTok->type()->classScope;
|
|
} else {
|
|
vt.type = ValueType::typeFromString(typestr, typeTok->isLong());
|
|
if (vt.type == ValueType::Type::UNKNOWN_TYPE)
|
|
vt.fromLibraryType(typestr, mSettings);
|
|
if (vt.type == ValueType::Type::UNKNOWN_TYPE)
|
|
continue;
|
|
if (typeTok->isUnsigned())
|
|
vt.sign = ValueType::Sign::UNSIGNED;
|
|
else if (typeTok->isSigned())
|
|
vt.sign = ValueType::Sign::SIGNED;
|
|
if (vt.sign == ValueType::Sign::UNKNOWN_SIGN && vt.isIntegral())
|
|
vt.sign = (vt.type == ValueType::Type::CHAR) ? mDefaultSignedness : ValueType::Sign::SIGNED;
|
|
}
|
|
setValueType(tok, vt);
|
|
if (Token::simpleMatch(tok->astOperand1(), "(")) {
|
|
vt.pointer--;
|
|
setValueType(tok->astOperand1(), vt);
|
|
}
|
|
} else if (tok->isKeyword() && tok->str() == "return" && tok->scope()) {
|
|
const Scope* fscope = tok->scope();
|
|
while (fscope && !fscope->function)
|
|
fscope = fscope->nestedIn;
|
|
if (fscope && fscope->function && fscope->function->retDef) {
|
|
ValueType vt;
|
|
parsedecl(fscope->function->retDef, &vt, mDefaultSignedness, mSettings);
|
|
setValueType(tok, vt);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (reportDebugWarnings && mSettings->debugwarnings) {
|
|
for (Token *tok = tokens; tok; tok = tok->next()) {
|
|
if (tok->str() == "auto" && !tok->valueType())
|
|
debugMessage(tok, "autoNoType", "auto token with no type.");
|
|
}
|
|
}
|
|
|
|
// Update functions with new type information.
|
|
createSymbolDatabaseSetFunctionPointers(false);
|
|
|
|
// Update auto variables with new type information.
|
|
createSymbolDatabaseSetVariablePointers();
|
|
}
|
|
|
|
ValueType ValueType::parseDecl(const Token *type, const Settings *settings)
|
|
{
|
|
ValueType vt;
|
|
parsedecl(type, &vt, settings->defaultSign == 'u' ? Sign::UNSIGNED : Sign::SIGNED, settings);
|
|
return vt;
|
|
}
|
|
|
|
ValueType::Type ValueType::typeFromString(const std::string &typestr, bool longType)
|
|
{
|
|
if (typestr == "void")
|
|
return ValueType::Type::VOID;
|
|
if (typestr == "bool" || typestr == "_Bool")
|
|
return ValueType::Type::BOOL;
|
|
if (typestr== "char")
|
|
return ValueType::Type::CHAR;
|
|
if (typestr == "short")
|
|
return ValueType::Type::SHORT;
|
|
if (typestr == "wchar_t")
|
|
return ValueType::Type::WCHAR_T;
|
|
if (typestr == "int")
|
|
return ValueType::Type::INT;
|
|
if (typestr == "long")
|
|
return longType ? ValueType::Type::LONGLONG : ValueType::Type::LONG;
|
|
if (typestr == "float")
|
|
return ValueType::Type::FLOAT;
|
|
if (typestr == "double")
|
|
return longType ? ValueType::Type::LONGDOUBLE : ValueType::Type::DOUBLE;
|
|
return ValueType::Type::UNKNOWN_TYPE;
|
|
}
|
|
|
|
bool ValueType::fromLibraryType(const std::string &typestr, const Settings *settings)
|
|
{
|
|
const Library::PodType* podtype = settings->library.podtype(typestr);
|
|
if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) {
|
|
if (podtype->size == 1)
|
|
type = ValueType::Type::CHAR;
|
|
else if (podtype->size == settings->sizeof_int)
|
|
type = ValueType::Type::INT;
|
|
else if (podtype->size == settings->sizeof_short)
|
|
type = ValueType::Type::SHORT;
|
|
else if (podtype->size == settings->sizeof_long)
|
|
type = ValueType::Type::LONG;
|
|
else if (podtype->size == settings->sizeof_long_long)
|
|
type = ValueType::Type::LONGLONG;
|
|
else if (podtype->stdtype == Library::PodType::Type::BOOL)
|
|
type = ValueType::Type::BOOL;
|
|
else if (podtype->stdtype == Library::PodType::Type::CHAR)
|
|
type = ValueType::Type::CHAR;
|
|
else if (podtype->stdtype == Library::PodType::Type::SHORT)
|
|
type = ValueType::Type::SHORT;
|
|
else if (podtype->stdtype == Library::PodType::Type::INT)
|
|
type = ValueType::Type::INT;
|
|
else if (podtype->stdtype == Library::PodType::Type::LONG)
|
|
type = ValueType::Type::LONG;
|
|
else if (podtype->stdtype == Library::PodType::Type::LONGLONG)
|
|
type = ValueType::Type::LONGLONG;
|
|
else
|
|
type = ValueType::Type::UNKNOWN_INT;
|
|
sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED;
|
|
return true;
|
|
}
|
|
|
|
const Library::PlatformType *platformType = settings->library.platform_type(typestr, settings->platformString());
|
|
if (platformType) {
|
|
if (platformType->mType == "char")
|
|
type = ValueType::Type::CHAR;
|
|
else if (platformType->mType == "short")
|
|
type = ValueType::Type::SHORT;
|
|
else if (platformType->mType == "wchar_t")
|
|
type = ValueType::Type::WCHAR_T;
|
|
else if (platformType->mType == "int")
|
|
type = platformType->mLong ? ValueType::Type::LONG : ValueType::Type::INT;
|
|
else if (platformType->mType == "long")
|
|
type = platformType->mLong ? ValueType::Type::LONGLONG : ValueType::Type::LONG;
|
|
if (platformType->mSigned)
|
|
sign = ValueType::SIGNED;
|
|
else if (platformType->mUnsigned)
|
|
sign = ValueType::UNSIGNED;
|
|
if (platformType->mPointer)
|
|
pointer = 1;
|
|
if (platformType->mPtrPtr)
|
|
pointer = 2;
|
|
if (platformType->mConstPtr)
|
|
constness = 1;
|
|
return true;
|
|
} else if (!podtype && (typestr == "size_t" || typestr == "std::size_t")) {
|
|
originalTypeName = "size_t";
|
|
sign = ValueType::UNSIGNED;
|
|
if (settings->sizeof_size_t == settings->sizeof_long)
|
|
type = ValueType::Type::LONG;
|
|
else if (settings->sizeof_size_t == settings->sizeof_long_long)
|
|
type = ValueType::Type::LONGLONG;
|
|
else if (settings->sizeof_size_t == settings->sizeof_int)
|
|
type = ValueType::Type::INT;
|
|
else
|
|
type = ValueType::Type::UNKNOWN_INT;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
std::string ValueType::dump() const
|
|
{
|
|
std::ostringstream ret;
|
|
switch (type) {
|
|
case UNKNOWN_TYPE:
|
|
return "";
|
|
case NONSTD:
|
|
ret << "valueType-type=\"nonstd\"";
|
|
break;
|
|
case RECORD:
|
|
ret << "valueType-type=\"record\"";
|
|
break;
|
|
case CONTAINER:
|
|
ret << "valueType-type=\"container\"";
|
|
break;
|
|
case ITERATOR:
|
|
ret << "valueType-type=\"iterator\"";
|
|
break;
|
|
case VOID:
|
|
ret << "valueType-type=\"void\"";
|
|
break;
|
|
case BOOL:
|
|
ret << "valueType-type=\"bool\"";
|
|
break;
|
|
case CHAR:
|
|
ret << "valueType-type=\"char\"";
|
|
break;
|
|
case SHORT:
|
|
ret << "valueType-type=\"short\"";
|
|
break;
|
|
case WCHAR_T:
|
|
ret << "valueType-type=\"wchar_t\"";
|
|
break;
|
|
case INT:
|
|
ret << "valueType-type=\"int\"";
|
|
break;
|
|
case LONG:
|
|
ret << "valueType-type=\"long\"";
|
|
break;
|
|
case LONGLONG:
|
|
ret << "valueType-type=\"long long\"";
|
|
break;
|
|
case UNKNOWN_INT:
|
|
ret << "valueType-type=\"unknown int\"";
|
|
break;
|
|
case FLOAT:
|
|
ret << "valueType-type=\"float\"";
|
|
break;
|
|
case DOUBLE:
|
|
ret << "valueType-type=\"double\"";
|
|
break;
|
|
case LONGDOUBLE:
|
|
ret << "valueType-type=\"long double\"";
|
|
break;
|
|
}
|
|
|
|
switch (sign) {
|
|
case Sign::UNKNOWN_SIGN:
|
|
break;
|
|
case Sign::SIGNED:
|
|
ret << " valueType-sign=\"signed\"";
|
|
break;
|
|
case Sign::UNSIGNED:
|
|
ret << " valueType-sign=\"unsigned\"";
|
|
break;
|
|
}
|
|
|
|
if (bits > 0)
|
|
ret << " valueType-bits=\"" << bits << '\"';
|
|
|
|
if (pointer > 0)
|
|
ret << " valueType-pointer=\"" << pointer << '\"';
|
|
|
|
if (constness > 0)
|
|
ret << " valueType-constness=\"" << constness << '\"';
|
|
|
|
if (reference == Reference::None)
|
|
ret << " valueType-reference=\"None\"";
|
|
else if (reference == Reference::LValue)
|
|
ret << " valueType-reference=\"LValue\"";
|
|
else if (reference == Reference::RValue)
|
|
ret << " valueType-reference=\"RValue\"";
|
|
|
|
if (typeScope)
|
|
ret << " valueType-typeScope=\"" << typeScope << '\"';
|
|
|
|
if (!originalTypeName.empty())
|
|
ret << " valueType-originalTypeName=\"" << ErrorLogger::toxml(originalTypeName) << '\"';
|
|
|
|
return ret.str();
|
|
}
|
|
|
|
MathLib::bigint ValueType::typeSize(const cppcheck::Platform &platform, bool p) const
|
|
{
|
|
if (p && pointer)
|
|
return platform.sizeof_pointer;
|
|
|
|
if (typeScope && typeScope->definedType && typeScope->definedType->sizeOf)
|
|
return typeScope->definedType->sizeOf;
|
|
|
|
switch (type) {
|
|
case ValueType::Type::BOOL:
|
|
return platform.sizeof_bool;
|
|
case ValueType::Type::CHAR:
|
|
return 1;
|
|
case ValueType::Type::SHORT:
|
|
return platform.sizeof_short;
|
|
case ValueType::Type::WCHAR_T:
|
|
return platform.sizeof_wchar_t;
|
|
case ValueType::Type::INT:
|
|
return platform.sizeof_int;
|
|
case ValueType::Type::LONG:
|
|
return platform.sizeof_long;
|
|
case ValueType::Type::LONGLONG:
|
|
return platform.sizeof_long_long;
|
|
case ValueType::Type::FLOAT:
|
|
return platform.sizeof_float;
|
|
case ValueType::Type::DOUBLE:
|
|
return platform.sizeof_double;
|
|
case ValueType::Type::LONGDOUBLE:
|
|
return platform.sizeof_long_double;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Unknown invalid size
|
|
return 0;
|
|
}
|
|
|
|
std::string ValueType::str() const
|
|
{
|
|
std::string ret;
|
|
if (constness & 1)
|
|
ret = " const";
|
|
if (type == VOID)
|
|
ret += " void";
|
|
else if (isIntegral()) {
|
|
if (sign == SIGNED)
|
|
ret += " signed";
|
|
else if (sign == UNSIGNED)
|
|
ret += " unsigned";
|
|
if (type == BOOL)
|
|
ret += " bool";
|
|
else if (type == CHAR)
|
|
ret += " char";
|
|
else if (type == SHORT)
|
|
ret += " short";
|
|
else if (type == WCHAR_T)
|
|
ret += " wchar_t";
|
|
else if (type == INT)
|
|
ret += " int";
|
|
else if (type == LONG)
|
|
ret += " long";
|
|
else if (type == LONGLONG)
|
|
ret += " long long";
|
|
else if (type == UNKNOWN_INT)
|
|
ret += " unknown_int";
|
|
} else if (type == FLOAT)
|
|
ret += " float";
|
|
else if (type == DOUBLE)
|
|
ret += " double";
|
|
else if (type == LONGDOUBLE)
|
|
ret += " long double";
|
|
else if ((type == ValueType::Type::NONSTD || type == ValueType::Type::RECORD) && typeScope) {
|
|
std::string className(typeScope->className);
|
|
const Scope *scope = typeScope->definedType ? typeScope->definedType->enclosingScope : typeScope->nestedIn;
|
|
while (scope && scope->type != Scope::eGlobal) {
|
|
if (scope->type == Scope::eClass || scope->type == Scope::eStruct || scope->type == Scope::eNamespace)
|
|
className = scope->className + "::" + className;
|
|
scope = (scope->definedType && scope->definedType->enclosingScope) ? scope->definedType->enclosingScope : scope->nestedIn;
|
|
}
|
|
ret += ' ' + className;
|
|
} else if (type == ValueType::Type::CONTAINER && container) {
|
|
ret += " container(" + container->startPattern + ')';
|
|
} else if (type == ValueType::Type::ITERATOR && container) {
|
|
ret += " iterator(" + container->startPattern + ')';
|
|
} else if (smartPointerType) {
|
|
ret += " smart-pointer<" + smartPointerType->name() + ">";
|
|
}
|
|
for (unsigned int p = 0; p < pointer; p++) {
|
|
ret += " *";
|
|
if (constness & (2 << p))
|
|
ret += " const";
|
|
}
|
|
if (reference == Reference::LValue)
|
|
ret += " &";
|
|
else if (reference == Reference::RValue)
|
|
ret += " &&";
|
|
return ret.empty() ? ret : ret.substr(1);
|
|
}
|
|
|
|
ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const ValueType *func)
|
|
{
|
|
if (!call || !func)
|
|
return ValueType::MatchResult::UNKNOWN;
|
|
if (call->pointer != func->pointer) {
|
|
if (call->pointer > 1 && func->pointer == 1 && func->type == ValueType::Type::VOID)
|
|
return ValueType::MatchResult::FALLBACK1;
|
|
if (call->pointer == 1 && func->pointer == 0 && func->isIntegral() && func->sign != ValueType::Sign::SIGNED)
|
|
return ValueType::MatchResult::FALLBACK1;
|
|
if (call->pointer == 1 && call->type == ValueType::Type::CHAR && func->pointer == 0 && func->container && func->container->stdStringLike)
|
|
return ValueType::MatchResult::FALLBACK2;
|
|
return ValueType::MatchResult::NOMATCH; // TODO
|
|
}
|
|
if (call->pointer > 0 && ((call->constness | func->constness) != func->constness))
|
|
return ValueType::MatchResult::NOMATCH;
|
|
if (call->type != func->type) {
|
|
if (call->type == ValueType::Type::VOID || func->type == ValueType::Type::VOID)
|
|
return ValueType::MatchResult::FALLBACK1;
|
|
if (call->pointer > 0 && func->pointer > 0)
|
|
return func->type == ValueType::UNKNOWN_TYPE ? ValueType::MatchResult::UNKNOWN : ValueType::MatchResult::NOMATCH;
|
|
if (call->isIntegral() && func->isIntegral())
|
|
return call->type < func->type ?
|
|
ValueType::MatchResult::FALLBACK1 :
|
|
ValueType::MatchResult::FALLBACK2;
|
|
else if (call->isFloat() && func->isFloat())
|
|
return ValueType::MatchResult::FALLBACK1;
|
|
else if (call->isIntegral() && func->isFloat())
|
|
return ValueType::MatchResult::FALLBACK2;
|
|
else if (call->isFloat() && func->isIntegral())
|
|
return ValueType::MatchResult::FALLBACK2;
|
|
return ValueType::MatchResult::UNKNOWN; // TODO
|
|
}
|
|
|
|
if (call->typeScope != nullptr || func->typeScope != nullptr) {
|
|
if (call->typeScope != func->typeScope)
|
|
return ValueType::MatchResult::NOMATCH;
|
|
}
|
|
|
|
if (call->container != nullptr || func->container != nullptr) {
|
|
if (call->container != func->container)
|
|
return ValueType::MatchResult::NOMATCH;
|
|
}
|
|
|
|
if (func->typeScope != nullptr && func->container != nullptr) {
|
|
if (func->type < ValueType::Type::VOID || func->type == ValueType::Type::UNKNOWN_INT)
|
|
return ValueType::MatchResult::UNKNOWN;
|
|
}
|
|
|
|
if (call->isIntegral() && func->isIntegral() && call->sign != ValueType::Sign::UNKNOWN_SIGN && func->sign != ValueType::Sign::UNKNOWN_SIGN && call->sign != func->sign)
|
|
return ValueType::MatchResult::FALLBACK1;
|
|
|
|
if (func->reference != Reference::None && func->constness > call->constness)
|
|
return ValueType::MatchResult::FALLBACK1;
|
|
|
|
return ValueType::MatchResult::SAME;
|
|
}
|
|
|
|
ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar)
|
|
{
|
|
ValueType::MatchResult res = ValueType::matchParameter(call, funcVar->valueType());
|
|
if (callVar && ((res == ValueType::MatchResult::SAME && call->container) || res == ValueType::MatchResult::UNKNOWN)) {
|
|
const std::string type1 = getTypeString(callVar->typeStartToken());
|
|
const std::string type2 = getTypeString(funcVar->typeStartToken());
|
|
const bool templateVar =
|
|
funcVar->scope() && funcVar->scope()->function && funcVar->scope()->function->templateDef;
|
|
if (type1 == type2)
|
|
return ValueType::MatchResult::SAME;
|
|
if (!templateVar && type1.find("auto") == std::string::npos && type2.find("auto") == std::string::npos &&
|
|
type1 != type2)
|
|
return ValueType::MatchResult::NOMATCH;
|
|
}
|
|
return res;
|
|
}
|