From 66a355589773ca5a7b88ff0ef36a83ec4c48dae6 Mon Sep 17 00:00:00 2001 From: PKEuS Date: Tue, 5 Mar 2013 04:33:38 -0800 Subject: [PATCH] Implement initial support for Types in Symboldatabase: - For each class/struct/union, a Type instance is added to SymbolDatabase::typeList. - A scope is no longer created for declared but not defined types Fixed name detection for classes when they are declared like this: "class ::Foo::Sub {..." --- lib/checkclass.cpp | 37 +++--- lib/checkmemoryleak.cpp | 12 +- lib/checkother.cpp | 2 +- lib/checkuninitvar.cpp | 2 +- lib/checkunusedvar.cpp | 6 +- lib/symboldatabase.cpp | 235 ++++++++++++++++++++---------------- lib/symboldatabase.h | 69 ++++++++--- test/testconstructors.cpp | 6 +- test/testsymboldatabase.cpp | 48 +++++++- 9 files changed, 263 insertions(+), 154 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index b2faa8a27..8a50d52d6 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -59,17 +59,13 @@ void CheckClass::constructors() for (std::size_t i = 0; i < classes; ++i) { const Scope * scope = symbolDatabase->classAndStructScopes[i]; - // skip forward declarations - if (scope->isForwardDeclaration()) - continue; - // There are no constructors. if (scope->numConstructors == 0 && style) { // If there is a private variable, there should be a constructor.. std::list::const_iterator var; for (var = scope->varlist.begin(); var != scope->varlist.end(); ++var) { if (var->isPrivate() && !var->isStatic() && - (!var->isClass() || (var->type() && var->type()->needInitialization == Scope::True))) { + (!var->isClass() || (var->type() && var->type()->needInitialization == Type::True))) { noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct"); break; } @@ -134,18 +130,18 @@ void CheckClass::constructors() // Known type that doesn't need initialization or // known type that has member variables of an unknown type - else if (var->type()->needInitialization != Scope::True) + else if (var->type()->needInitialization != Type::True) continue; } // Check if type can't be copied - if (!var->isPointer() && var->type() && canNotCopy(var->type())) + if (!var->isPointer() && var->typeScope() && canNotCopy(var->typeScope())) continue; // Don't warn about unknown types in copy constructors since we // don't know if they can be copied or not.. if (!var->isPointer() && - !(var->type() && var->type()->needInitialization != Scope::True) && + !(var->type() && var->type()->needInitialization != Type::True) && (func->type == Function::eCopyConstructor || func->type == Function::eOperatorEqual)) { bool stdtype = false; for (const Token *type = var->typeStartToken(); type && type->isName(); type = type->next()) @@ -173,7 +169,7 @@ void CheckClass::constructors() if (classNameUsed) operatorEqVarError(func->token, scope->className, var->name(), inconclusive); } else if (func->access != Private) { - const Scope *varType = var->type(); + const Scope *varType = var->typeScope(); if (!varType || varType->type != Scope::eUnion) uninitVarError(func->token, scope->className, var->name(), inconclusive); } @@ -857,7 +853,7 @@ void CheckClass::noMemset() derefs -= (int)var->dimensions().size(); if (derefs == 0) - type = var->type(); + type = var->typeScope(); } } @@ -868,16 +864,19 @@ void CheckClass::noMemset() if (typeTok && typeTok->str() == "(") typeTok = typeTok->next(); - if (!type) - type = symbolDatabase->findVariableType(&(*scope), typeTok); + if (!type) { + const Type* t = symbolDatabase->findVariableType(&(*scope), typeTok); + if (t) + type = t->classScope; + } if (type) checkMemsetType(&(*scope), tok, type, false); - } else if (tok->variable() && tok->variable()->type() && Token::Match(tok, "%var% = calloc|malloc|realloc|g_malloc|g_try_malloc|g_realloc|g_try_realloc (")) { - checkMemsetType(&(*scope), tok->tokAt(2), tok->variable()->type(), true); + } else if (tok->variable() && tok->variable()->typeScope() && Token::Match(tok, "%var% = calloc|malloc|realloc|g_malloc|g_try_malloc|g_realloc|g_try_realloc (")) { + checkMemsetType(&(*scope), tok->tokAt(2), tok->variable()->typeScope(), true); - if (tok->variable()->type()->numConstructors > 0 && _settings->isEnabled("warning")) - mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->type()->classDef); + if (tok->variable()->typeScope()->numConstructors > 0 && _settings->isEnabled("warning")) + mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef); } } } @@ -919,8 +918,8 @@ void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Sco memsetError(tok, tok->str(), "'std::" + tok1->strAt(2) + "'", type->classDef->str()); // check for known type - else if (var->type()) - checkMemsetType(start, tok, var->type(), allocation); + else if (var->typeScope()) + checkMemsetType(start, tok, var->typeScope(), allocation); } } } @@ -1648,7 +1647,7 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, bool& if (Token::simpleMatch(var->typeStartToken(), "std ::") // assume all std::*::size() and std::*::empty() are const && (Token::Match(end, "size|empty|cend|crend|cbegin|crbegin|max_size|length|count|capacity|get_allocator|c_str|str ( )") || Token::Match(end, "rfind|copy"))) ; - else if (!var->type() || !isConstMemberFunc(var->type(), end)) + else if (!var->typeScope() || !isConstMemberFunc(var->typeScope(), end)) return(false); } diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 62afbe95f..4c95bd24d 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -115,9 +115,9 @@ bool CheckMemoryLeak::isclass(const Token *tok, unsigned int varid) const // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check base class for side effects */ /** @todo false negative: check constructors for side effects */ - if (var && var->type() && var->type()->numConstructors == 0 && - (var->type()->varlist.empty() || var->type()->needInitialization == Scope::True) && - var->type()->derivedFrom.empty()) + if (var && var->typeScope() && var->typeScope()->numConstructors == 0 && + (var->typeScope()->varlist.empty() || var->type()->needInitialization == Type::True) && + var->typeScope()->derivedFrom.empty()) return false; return true; @@ -2279,7 +2279,7 @@ void CheckMemoryLeakInFunction::check() continue; // check for known class without implementation (forward declaration) - if (var->isPointer() && var->type() && var->type()->isForwardDeclaration()) + if (var->isPointer() && var->type() && !var->typeScope()) continue; unsigned int sz = _tokenizer->sizeOfType(var->typeStartToken()); @@ -2349,9 +2349,9 @@ void CheckMemoryLeakInClass::check() } // known class? - else if (var->type()) { + else if (var->typeScope()) { // not derived? - if (var->type()->derivedFrom.empty()) { + if (var->typeScope()->derivedFrom.empty()) { if (var->isPrivate()) checkPublicFunctions(&(*scope), var->nameToken()); diff --git a/lib/checkother.cpp b/lib/checkother.cpp index bf665daba..0d764d564 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -3083,7 +3083,7 @@ namespace { return false; std::list::const_iterator it = std::find_if(constFunctions.begin(), constFunctions.end(), - FuncFilter(v ? v->type(): 0, prev)); + FuncFilter(v ? v->typeScope(): 0, prev)); if (it == constFunctions.end()) return true; } diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index b1e640aba..51666003f 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -1059,7 +1059,7 @@ void CheckUninitVar::check() void CheckUninitVar::checkScope(const Scope* scope) { for (std::list::const_iterator i = scope->varlist.begin(); i != scope->varlist.end(); ++i) { - if ((_tokenizer->isCPP() && i->type() && !i->isPointer() && i->type()->needInitialization != Scope::True) || + if ((_tokenizer->isCPP() && i->type() && !i->isPointer() && i->type()->needInitialization != Type::True) || i->isStatic() || i->isExtern() || i->isConst() || i->isArray() || i->isReference()) continue; if (i->nameToken()->strAt(1) == "(") diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index c65162885..36e86d46c 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -613,7 +613,7 @@ static bool isRecordTypeWithoutSideEffects(const Scope* type) // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check constructors for side effects */ if (type && type->numConstructors == 0 && - (type->varlist.empty() || type->needInitialization == Scope::True)) { + (type->varlist.empty() || type->definedType->needInitialization == Type::True)) { bool yes = true; for (std::vector::const_iterator i = type->derivedFrom.begin(); yes && i != type->derivedFrom.end(); ++i) yes = isRecordTypeWithoutSideEffects(i->scope); @@ -688,7 +688,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const type = Variables::pointer; else if (_tokenizer->isC() || i->typeEndToken()->isStandardType() || - isRecordTypeWithoutSideEffects(i->type()) || + isRecordTypeWithoutSideEffects(i->typeScope()) || (Token::simpleMatch(i->typeStartToken(), "std ::") && i->typeStartToken()->strAt(2) != "lock_guard" && i->typeStartToken()->strAt(2) != "unique_lock")) @@ -893,7 +893,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const // is it a user defined type? if (!type->isStandardType()) { const Variable *variable = start->variable(); - if (!variable || !isRecordTypeWithoutSideEffects(variable->type())) + if (!variable || !isRecordTypeWithoutSideEffects(variable->typeScope())) allocate = false; } } diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 734cfd254..55bdd8f3b 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -134,12 +134,20 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti break; } - // fill the classAndStructTypes set.. - if (new_scope->isClassOrStruct()) - classAndStructTypes.insert(new_scope->className); + // fill typeList... + if (new_scope->isClassOrStruct() || new_scope->type == Scope::eUnion) { + Type* new_type = findType(tok->next(), scope); + if (!new_type) { + typeList.push_back(Type(new_scope->classDef, new_scope, scope)); + new_type = &typeList.back(); + scope->definedTypes.push_back(new_type); + } else + new_type->classScope = new_scope; + new_scope->definedType = new_type; + } // make the new scope the current scope - scope = &scopeList.back(); + scope = new_scope; scope->nestedIn->nestedList.push_back(scope); tok = tok2; @@ -175,18 +183,12 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti } // forward declaration - else if (Token::Match(tok, "class|struct %var% ;") && + else if (Token::Match(tok, "class|struct|union %var% ;") && tok->strAt(-1) != "friend") { - if (!findScope(tok->next(), scope)) { - // fill the classAndStructTypes set.. - classAndStructTypes.insert(tok->next()->str()); - - scopeList.push_back(Scope(this, tok, scope)); - - Scope *new_scope = &scopeList.back(); - - // add scope - scope->nestedList.push_back(new_scope); + if (!findType(tok->next(), scope)) { + // fill typeList.. + typeList.push_back(Type(tok, 0, scope)); + scope->definedTypes.push_back(&typeList.back()); } tok = tok->tokAt(2); } @@ -226,7 +228,11 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti varNameTok = varNameTok->next(); } - scope->addVariable(varNameTok, tok, tok, access[scope], new_scope, scope); + typeList.push_back(Type(tok, new_scope, scope)); + new_scope->definedType = &typeList.back(); + scope->definedTypes.push_back(&typeList.back()); + + scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope); const Token *tok2 = tok->next(); @@ -240,7 +246,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti } // make the new scope the current scope - scope = &scopeList.back(); + scope = new_scope; scope->nestedIn->nestedList.push_back(scope); tok = tok2; @@ -259,6 +265,10 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti new_scope->classStart = tok2; new_scope->classEnd = tok2->link(); + typeList.push_back(Type(tok, new_scope, scope)); + new_scope->definedType = &typeList.back(); + scope->definedTypes.push_back(&typeList.back()); + // make sure we have valid code if (!new_scope->classEnd) { scopeList.pop_back(); @@ -266,7 +276,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti } // make the new scope the current scope - scope = &scopeList.back(); + scope = new_scope; scope->nestedIn->nestedList.push_back(scope); tok = tok2; @@ -782,7 +792,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti for (it = scopeList.begin(); it != scopeList.end(); ++it) { scope = &(*it); - if (scope->isClassOrStruct() && scope->needInitialization == Scope::Unknown) { + if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::Unknown) { // check for default constructor bool hasDefaultConstructor = false; @@ -808,7 +818,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti // We assume the default constructor initializes everything. // Another check will figure out if the constructor actually initializes everything. if (hasDefaultConstructor) - scope->needInitialization = Scope::False; + scope->definedType->needInitialization = Type::False; // check each member variable to see if it needs initialization else { @@ -820,9 +830,9 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti if (var->isClass()) { if (var->type()) { // does this type need initialization? - if (var->type()->needInitialization == Scope::True) + if (var->typeScope()->definedType->needInitialization == Type::True) needInitialization = true; - else if (var->type()->needInitialization == Scope::Unknown) + else if (var->typeScope()->definedType->needInitialization == Type::Unknown) unknown = true; } } else @@ -831,16 +841,16 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti if (!unknown) { if (needInitialization) - scope->needInitialization = Scope::True; + scope->definedType->needInitialization = Type::True; else - scope->needInitialization = Scope::False; + scope->definedType->needInitialization = Type::False; } - if (scope->needInitialization == Scope::Unknown) + if (scope->definedType->needInitialization == Type::Unknown) unknowns++; } - } else if (scope->type == Scope::eUnion && scope->needInitialization == Scope::Unknown) - scope->needInitialization = Scope::True; + } else if (scope->type == Scope::eUnion && scope->definedType->needInitialization == Type::Unknown) + scope->definedType->needInitialization = Type::True; } retry++; @@ -851,7 +861,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti for (it = scopeList.begin(); it != scopeList.end(); ++it) { scope = &(*it); - if (scope->isClassOrStruct() && scope->needInitialization == Scope::Unknown) + if (scope->isClassOrStruct() && scope->definedType->needInitialization == Type::Unknown) debugMessage(scope->classDef, "SymbolDatabase::SymbolDatabase couldn't resolve all user defined types."); } } @@ -901,9 +911,9 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti const Token *tok1 = tok->tokAt(2); if (tok1 && tok1->varId() && _variableList[tok1->varId()] == 0) { const Variable *var = _variableList[tok->varId()]; - if (var && var->type()) { + if (var && var->typeScope()) { // find the member variable of this variable - const Variable *var1 = var->type()->getVariable(tok1->str()); + const Variable *var1 = var->typeScope()->getVariable(tok1->str()); if (var1) { // add this variable to the look up table _variableList[tok1->varId()] = var1; @@ -1587,8 +1597,10 @@ void SymbolDatabase::printVariable(const Variable *var, const char *indent) cons std::cout << indent << " hasDefault: " << (var->hasDefault() ? "true" : "false") << std::endl; std::cout << indent << "_type: "; if (var->type()) { - std::cout << var->type()->className << " " << var->type()->type << " " - << _tokenizer->list.fileLine(var->type()->classDef) << std::endl; + std::cout << var->type()->name(); + if (var->typeScope()) + std::cout << " " << var->typeScope()->type; + std::cout << " " << _tokenizer->list.fileLine(var->type()->classDef) << std::endl; } else std::cout << "none" << std::endl; @@ -1768,9 +1780,9 @@ void SymbolDatabase::printOut(const char *title) const std::cout << " )" << std::endl; - std::cout << " needInitialization: " << (scope->needInitialization == Scope::Unknown ? "Unknown" : - scope->needInitialization == Scope::True ? "True" : - scope->needInitialization == Scope::False ? "False" : + std::cout << " needInitialization: " << (scope->definedType->needInitialization == Type::Unknown ? "Unknown" : + scope->definedType->needInitialization == Type::True ? "True" : + scope->definedType->needInitialization == Type::False ? "False" : "Invalid") << std::endl; std::list::const_iterator use; @@ -1863,7 +1875,7 @@ void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *s endTok = tok->previous(); } - const Scope *argType = NULL; + const ::Type *argType = NULL; if (!typeTok->isStandardType()) { argType = symbolDatabase->findVariableType(scope, typeTok); if (!argType) { @@ -1991,8 +2003,8 @@ Scope::Scope(SymbolDatabase *check_, const Token *classDef_, Scope *nestedIn_, S classEnd(start_->link()), nestedIn(nestedIn_), numConstructors(0), - needInitialization(Scope::Unknown), type(type_), + definedType(NULL), functionOf(NULL), function(NULL) { @@ -2005,36 +2017,35 @@ Scope::Scope(SymbolDatabase *check_, const Token *classDef_, Scope *nestedIn_) : classEnd(NULL), nestedIn(nestedIn_), numConstructors(0), - needInitialization(Scope::Unknown), + definedType(NULL), functionOf(NULL), function(NULL) { + const Token *nameTok = classDef; if (!classDef) { type = Scope::eGlobal; } else if (classDef->str() == "class") { type = Scope::eClass; - // skip over qualification if present - const Token *nameTok = classDef->next(); - while (nameTok && Token::Match(nameTok, "%type% ::")) - nameTok = nameTok->tokAt(2); - className = nameTok->str(); + nameTok = nameTok->next(); } else if (classDef->str() == "struct") { type = Scope::eStruct; - // anonymous and unnamed structs don't have a name - if (classDef->next()->str() != "{") - className = classDef->next()->str(); + nameTok = nameTok->next(); } else if (classDef->str() == "union") { type = Scope::eUnion; - // anonymous and unnamed unions don't have a name - if (classDef->next()->str() != "{") - className = classDef->next()->str(); + nameTok = nameTok->next(); } else if (classDef->str() == "namespace") { type = Scope::eNamespace; - className = classDef->next()->str(); + nameTok = nameTok->next(); } else { type = Scope::eFunction; - className = classDef->str(); } + // skip over qualification if present + if (nameTok && nameTok->str() == "::") + nameTok = nameTok->next(); + while (nameTok && Token::Match(nameTok, "%type% ::")) + nameTok = nameTok->tokAt(2); + if (nameTok && nameTok->str() != "{") // anonymous and unnamed structs/unions don't have a name + className = nameTok->str(); } bool Scope::hasDefaultConstructor() const @@ -2228,19 +2239,19 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess) if (vartok->varId() == 0 && !vartok->isBoolean()) check->debugMessage(vartok, "Scope::checkVariable found variable \'" + vartok->str() + "\' with varid 0."); - const Scope *scope = NULL; + const Type *vType = NULL; if (typetok) { - scope = check->findVariableType(this, typetok); - if (!scope) { + vType = check->findVariableType(this, typetok); + if (!vType) { // look for variable type in any using namespace in this scope or above const Scope *parent = this; while (parent) { for (std::list::const_iterator ui = parent->usingList.begin(); ui != parent->usingList.end(); ++ui) { if (ui->scope) { - scope = check->findVariableType(ui->scope, typetok); - if (scope) + vType = check->findVariableType(ui->scope, typetok); + if (vType) break; } } @@ -2249,7 +2260,7 @@ const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess) } } - addVariable(vartok, typestart, vartok->previous(), varaccess, scope, this); + addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this); } return tok; @@ -2339,19 +2350,15 @@ bool Scope::isVariableDeclaration(const Token* tok, const Token*& vartok, const //--------------------------------------------------------------------------- -const Scope *SymbolDatabase::findVariableType(const Scope *start, const Token *type) const +const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const { - std::list::const_iterator scope; - - for (scope = scopeList.begin(); scope != scopeList.end(); ++scope) { - // skip namespaces, functions, ... - if (scope->type != Scope::eClass && scope->type != Scope::eStruct && scope->type != Scope::eUnion) - continue; + std::list::const_iterator type; + for (type = typeList.begin(); type != typeList.end(); ++type) { // do the names match? - if (scope->className == type->str()) { + if (type->name() == typeTok->str()) { // check if type does not have a namespace - if (type->previous() == NULL || type->previous()->str() != "::") { + if (typeTok->strAt(-1) != "::") { const Scope *parent = start; // check if in same namespace @@ -2359,20 +2366,20 @@ const Scope *SymbolDatabase::findVariableType(const Scope *start, const Token *t // out of line class function belongs to class if (parent->type == Scope::eFunction && parent->functionOf) parent = parent->functionOf; - else if (parent != scope->nestedIn) + else if (parent != type->enclosingScope) parent = parent->nestedIn; else break; } - if (scope->nestedIn == parent) - return &(*scope); + if (type->enclosingScope == parent) + return &(*type); } // type has a namespace else { // FIXME check if namespace path matches supplied path - return &(*scope); + return &(*type); } } } @@ -2481,8 +2488,8 @@ const Function* SymbolDatabase::findFunction(const Token *tok) const if (tok1->varId()) { const Variable *var = getVariableFromVarId(tok1->varId()); - if (var && var->type()) - return var->type()->findFunction(tok); + if (var && var->typeScope()) + return var->typeScope()->findFunction(tok); } } } @@ -2538,6 +2545,19 @@ const Scope *Scope::findRecordInNestedList(const std::string & name) const //--------------------------------------------------------------------------- +const Type* Scope::findType(const std::string & name) const +{ + std::list::const_iterator it; + + for (it = definedTypes.begin(); it != definedTypes.end(); ++it) { + if ((*it)->name() == name) + return (*it); + } + return 0; +} + +//--------------------------------------------------------------------------- + Scope *Scope::findInNestedListRecursive(const std::string & name) { std::list::iterator it; @@ -2616,45 +2636,52 @@ bool SymbolDatabase::isCPP() const const Scope *SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const { + const Scope *scope = 0; // absolute path if (tok->str() == "::") { tok = tok->next(); - const Scope *scope = &scopeList.front(); - - while (scope && tok && tok->isName()) { - scope = scope->findRecordInNestedList(tok->str()); - - if (scope) { - if (tok->strAt(1) == "::") - tok = tok->tokAt(2); - else - break; - } - - } - - return scope; + scope = &scopeList.front(); } - // relative path else if (tok->isName()) { - const Scope *scope = startScope; - - while (scope && tok && tok->isName()) { - scope = scope->findRecordInNestedList(tok->str()); - - if (scope) { - if (tok->strAt(1) == "::") - tok = tok->tokAt(2); - else - break; - } - } - - return scope; + scope = startScope; } + while (scope && tok && tok->isName()) { + if (tok->strAt(1) == "::") { + scope = scope->findRecordInNestedList(tok->str()); + tok = tok->tokAt(2); + } else + return scope->findRecordInNestedList(tok->str()); + } + + // not a valid path - else - return 0; + return 0; +} + +const Type *SymbolDatabase::findType(const Token *tok, const Scope *startScope) const +{ + const Scope *scope = 0; + // 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 + return scope->findType(tok->str()); + } + + + // not a valid path + return 0; } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 9d2a55ed1..3817cab6b 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -54,6 +54,29 @@ struct Dimension { bool known; // Known size }; +/** @brief Information about a class type. */ +class CPPCHECKLIB Type { +public: + const Token* classDef; // Points to "class" token + const Scope* classScope; + const Scope* enclosingScope; + enum NeedInitialization { + Unknown, True, False + } needInitialization; + + Type(const Token* classDef_ = 0, const Scope* classScope_ = 0, const Scope* enclosingScope_ = 0) : + classDef(classDef_), + classScope(classScope_), + enclosingScope(enclosingScope_), + needInitialization(Unknown) { + } + + const std::string& name() const { + static const std::string empty; + return classDef->next()->isName() ? classDef->strAt(1) : empty; + } +}; + /** @brief Information about a member variable. */ class CPPCHECKLIB Variable { /** @brief flags mask used to access specific bit. */ @@ -97,7 +120,7 @@ class CPPCHECKLIB Variable { public: Variable(const Token *name_, const Token *start_, const Token *end_, - std::size_t index_, AccessControl access_, const Scope *type_, + std::size_t index_, AccessControl access_, const Type *type_, const Scope *scope_) : _name(name_), _start(start_), @@ -305,13 +328,21 @@ public: } /** - * Get Scope pointer of known type. + * Get Type pointer of known type. * @return pointer to type if known, NULL if not known */ - const Scope *type() const { + const Type *type() const { return _type; } + /** + * Get Scope pointer of known type. + * @return pointer to type scope if known, NULL if not known + */ + const Scope *typeScope() const { + return _type ? _type->classScope : 0; + } + /** * Get Scope pointer of enclosing scope. * @return pointer to enclosing scope @@ -356,7 +387,7 @@ private: int _flags; /** @brief pointer to user defined type info (for known types) */ - const Scope *_type; + const Type *_type; /** @brief pointer to scope this variable is in */ const Scope *_scope; @@ -468,7 +499,6 @@ public: }; enum ScopeType { eGlobal, eClass, eStruct, eUnion, eNamespace, eFunction, eIf, eElse, eElseIf, eFor, eWhile, eDo, eSwitch, eUnconditional, eTry, eCatch }; - enum NeedInitialization { Unknown, True, False }; Scope(SymbolDatabase *check_, const Token *classDef_, Scope *nestedIn_); Scope(SymbolDatabase *check_, const Token *classDef_, Scope *nestedIn_, ScopeType type_, const Token *start_); @@ -486,8 +516,9 @@ public: std::list nestedList; unsigned int numConstructors; std::list usingList; - NeedInitialization needInitialization; ScopeType type; + Type* definedType; + std::list definedTypes; // function specific fields Scope *functionOf; // scope this function belongs to @@ -508,10 +539,6 @@ public: type == eTry || type == eCatch); } - bool isForwardDeclaration() const { - return isClassOrStruct() && classStart == NULL; - } - /** * @brief find a function * @param tok token of function call @@ -530,6 +557,8 @@ public: return const_cast(static_cast(this)->findRecordInNestedList(name)); } + const Type *findType(const std::string & name) const; + /** * @brief find if name is in nested list * @param name name of nested scope @@ -539,7 +568,7 @@ public: const Scope *findQualifiedScope(const std::string & name) const; void addVariable(const Token *token_, const Token *start_, - const Token *end_, AccessControl access_, const Scope *type_, + const Token *end_, AccessControl access_, const Type *type_, const Scope *scope_) { varlist.push_back(Variable(token_, start_, end_, varlist.size(), access_, @@ -604,13 +633,16 @@ public: /** @brief Fast access to class and struct scopes */ std::vector classAndStructScopes; + /** @brief Fast access to types */ + std::list typeList; + /** * @brief find a variable type if it's a user defined type * @param start scope to start looking in * @param type token containing variable type * @return pointer to type if found or NULL if not found */ - const Scope *findVariableType(const Scope *start, const Token *type) const; + const Type *findVariableType(const Scope *start, const Token *type) const; /** * @brief find a function @@ -621,13 +653,21 @@ public: const Scope *findScopeByName(const std::string& name) const; + const Type *findType(const Token *tok, const Scope *startScope) const; + Type *findType(const Token *tok, Scope *startScope) const { + return const_cast(this->findType(tok, static_cast(startScope))); + } + const Scope *findScope(const Token *tok, const Scope *startScope) const; Scope *findScope(const Token *tok, Scope *startScope) const { return const_cast(this->findScope(tok, static_cast(startScope))); } bool isClassOrStruct(const std::string &type) const { - return bool(classAndStructTypes.find(type) != classAndStructTypes.end()); + for (std::list::const_iterator i = typeList.begin(); i != typeList.end(); ++i) + if (i->name() == type) + return true; + return false; } const Variable *getVariableFromVarId(std::size_t varId) const { @@ -659,9 +699,6 @@ private: void addNewFunction(Scope **info, const Token **tok); static bool isFunction(const Token *tok, const Scope* outerScope, const Token **funcStart, const Token **argStart); - /** class/struct types */ - std::set classAndStructTypes; - const Tokenizer *_tokenizer; const Settings *_settings; ErrorLogger *_errorLogger; diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index ea9586fcd..f96dff624 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -1783,9 +1783,9 @@ private: "class Bar;\n" "class Sub;\n"); - ASSERT_EQUALS("[test.cpp:20]: (warning) Member variable 'Sub::f' is not initialized in the constructor.\n" - "[test.cpp:9]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n" - "[test.cpp:12]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n" + "[test.cpp:12]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n" + "[test.cpp:20]: (warning) Member variable 'Sub::f' is not initialized in the constructor.\n", errout.str()); } void uninitVarArray1() { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 5db0e1f1b..8fff0ad0d 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -187,6 +187,7 @@ private: TEST_CASE(symboldatabase28); TEST_CASE(symboldatabase29); // ticket #4442 (segmentation fault) TEST_CASE(symboldatabase30); + TEST_CASE(symboldatabase31); TEST_CASE(isImplicitlyVirtual); @@ -1371,7 +1372,7 @@ private: void symboldatabase28() { GET_SYMBOL_DB("struct S {};\n" "void foo(struct S s) {}"); - ASSERT(db && db->getVariableFromVarId(1) && db->getVariableFromVarId(1)->type() && db->getVariableFromVarId(1)->type()->className == "S"); + ASSERT(db && db->getVariableFromVarId(1) && db->getVariableFromVarId(1)->typeScope() && db->getVariableFromVarId(1)->typeScope()->className == "S"); } // #ticket #4442 (segmentation fault) @@ -1388,6 +1389,51 @@ private: ASSERT(db && db->functionScopes.size() == 1 && db->functionScopes[0]->functionOf); } + void symboldatabase31() { + GET_SYMBOL_DB("class Foo;\n" + "class Bar;\n" + "class Sub;\n" + "class Foo { class Sub; };\n" + "class Bar { class Sub; };\n" + "class Bar::Sub {\n" + " int b;\n" + "public:\n" + " Sub() { }\n" + " Sub(int);\n" + "};\n" + "Bar::Sub::Sub(int) { };\n" + "class ::Foo::Sub {\n" + " int f;\n" + "public:\n" + " ~Sub();\n" + " Sub();\n" + "};\n" + "::Foo::Sub::~Sub() { }\n" + "::Foo::Sub::Sub() { }\n" + "class Foo;\n" + "class Bar;\n" + "class Sub;\n"); + ASSERT(db && db->typeList.size() == 5); + ASSERT(db && db->isClassOrStruct("Foo")); + ASSERT(db && db->isClassOrStruct("Bar")); + ASSERT(db && db->isClassOrStruct("Sub")); + if (!db || db->typeList.size() < 5) + return; + std::list::const_iterator i = db->typeList.begin(); + const Type* Foo = &(*i++); + const Type* Bar = &(*i++); + const Type* Sub = &(*i++); + const Type* Foo_Sub = &(*i++); + const Type* Bar_Sub = &(*i); + ASSERT(Foo && Foo->classDef && Foo->classScope && Foo->enclosingScope && Foo->name() == "Foo"); + ASSERT(Bar && Bar->classDef && Bar->classScope && Bar->enclosingScope && Bar->name() == "Bar"); + ASSERT(Sub && Sub->classDef && !Sub->classScope && Sub->enclosingScope && Sub->name() == "Sub"); + ASSERT(Foo_Sub && Foo_Sub->classDef && Foo_Sub->classScope && Foo_Sub->enclosingScope == Foo->classScope && Foo_Sub->name() == "Sub"); + ASSERT(Bar_Sub && Bar_Sub->classDef && Bar_Sub->classScope && Bar_Sub->enclosingScope == Bar->classScope && Bar_Sub->name() == "Sub"); + ASSERT(Foo_Sub && Foo_Sub->classScope && Foo_Sub->classScope->numConstructors == 1 && Foo_Sub->classScope->className == "Sub"); + ASSERT(Bar_Sub && Bar_Sub->classScope && Bar_Sub->classScope->numConstructors == 2 && Bar_Sub->classScope->className == "Sub"); + } + void isImplicitlyVirtual() { { GET_SYMBOL_DB("class Base {\n"