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 {..."
This commit is contained in:
PKEuS 2013-03-05 04:33:38 -08:00
parent 6eed6b3cf3
commit 66a3555897
9 changed files with 263 additions and 154 deletions

View File

@ -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<Variable>::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);
}

View File

@ -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());

View File

@ -3083,7 +3083,7 @@ namespace {
return false;
std::list<const Function*>::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;
}

View File

@ -1059,7 +1059,7 @@ void CheckUninitVar::check()
void CheckUninitVar::checkScope(const Scope* scope)
{
for (std::list<Variable>::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) == "(")

View File

@ -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<Scope::BaseInfo>::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;
}
}

View File

@ -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<Scope::UsingInfo>::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<Scope::UsingInfo>::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<Scope>::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<Type>::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<Type*>::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<Scope *>::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;
scope = &scopeList.front();
}
}
return scope;
}
// relative path
else if (tok->isName()) {
const Scope *scope = startScope;
scope = startScope;
}
while (scope && tok && tok->isName()) {
if (tok->strAt(1) == "::") {
scope = scope->findRecordInNestedList(tok->str());
if (scope) {
if (tok->strAt(1) == "::")
tok = tok->tokAt(2);
else
break;
}
} else
return scope->findRecordInNestedList(tok->str());
}
// not a valid path
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());
}
return scope;
}
// not a valid path
else
return 0;
}

View File

@ -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<Scope *> nestedList;
unsigned int numConstructors;
std::list<UsingInfo> usingList;
NeedInitialization needInitialization;
ScopeType type;
Type* definedType;
std::list<Type*> 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<Scope *>(static_cast<const Scope *>(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<const Scope *> classAndStructScopes;
/** @brief Fast access to types */
std::list<Type> typeList;
/**
* @brief find a variable type if it's a user defined type
* @param start scope to start looking in
* @param 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<Type *>(this->findType(tok, static_cast<const Scope *>(startScope)));
}
const Scope *findScope(const Token *tok, const Scope *startScope) const;
Scope *findScope(const Token *tok, Scope *startScope) const {
return const_cast<Scope *>(this->findScope(tok, static_cast<const Scope *>(startScope)));
}
bool isClassOrStruct(const std::string &type) const {
return bool(classAndStructTypes.find(type) != classAndStructTypes.end());
for (std::list<Type>::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<std::string> classAndStructTypes;
const Tokenizer *_tokenizer;
const Settings *_settings;
ErrorLogger *_errorLogger;

View File

@ -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() {

View File

@ -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<Type>::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"