New check: initialization by itself in initializer list (#3982)

Refactorizations:
- Rearranged code in checkclass.cpp to increase readability
- Several fixes for testclass.cpp tests.
This commit is contained in:
PKEuS 2014-08-05 11:48:53 +02:00
parent 9eb28cb8af
commit 804e055eee
3 changed files with 201 additions and 104 deletions

View File

@ -2039,6 +2039,42 @@ void CheckClass::initializerListError(const Token *tok1, const Token *tok2, cons
"initialization errors.", true);
}
//---------------------------------------------------------------------------
// Check for self initialization in initialization list
//---------------------------------------------------------------------------
void CheckClass::checkSelfInitialization()
{
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
for (std::size_t i = 0; i < symbolDatabase->functionScopes.size(); ++i) {
const Scope* scope = symbolDatabase->functionScopes[i];
const Function* function = scope->function;
if (!function || !function->isConstructor())
continue;
const Token* tok = function->arg->link()->next();
if (tok->str() != ":")
continue;
for (; tok != scope->classStart; tok = tok->next()) {
if (Token::Match(tok, "[:,] %var% ( %var% )") && tok->next()->varId() && tok->next()->varId() == tok->tokAt(3)->varId()) {
selfInitializationError(tok, tok->strAt(1));
}
}
}
}
void CheckClass::selfInitializationError(const Token* tok, const std::string& name)
{
reportError(tok, Severity::error, "selfInitialization", "Member variable '" + name + "' is initialized by itself.\n");
}
//---------------------------------------------------------------------------
// Check for pure virtual function calls
//---------------------------------------------------------------------------
void CheckClass::checkPureVirtualFunctionCall()
{
const std::size_t functions = symbolDatabase->functionScopes.size();
@ -2064,55 +2100,6 @@ void CheckClass::checkPureVirtualFunctionCall()
}
}
void CheckClass::checkDuplInheritedMembers()
{
if (!_settings->isEnabled("warning"))
return;
// Iterate over all classes
for (std::list<Type>::const_iterator classIt = symbolDatabase->typeList.begin();
classIt != symbolDatabase->typeList.end();
++classIt) {
// Iterate over the parent classes
for (std::vector<Type::BaseInfo>::const_iterator parentClassIt = classIt->derivedFrom.begin();
parentClassIt != classIt->derivedFrom.end();
++parentClassIt) {
// Check if there is info about the 'Base' class
if (!parentClassIt->type || !parentClassIt->type->classScope)
continue;
// Check if they have a member variable in common
for (std::list<Variable>::const_iterator classVarIt = classIt->classScope->varlist.begin();
classVarIt != classIt->classScope->varlist.end();
++classVarIt) {
for (std::list<Variable>::const_iterator parentClassVarIt = parentClassIt->type->classScope->varlist.begin();
parentClassVarIt != parentClassIt->type->classScope->varlist.end();
++parentClassVarIt) {
if (classVarIt->name() == parentClassVarIt->name() && !parentClassVarIt->isPrivate()) { // Check if the class and its parent have a common variable
duplInheritedMembersError(classVarIt->nameToken(), parentClassVarIt->nameToken(),
classIt->name(), parentClassIt->type->name(), classVarIt->name(),
classIt->classScope->type == Scope::eStruct,
parentClassIt->type->classScope->type == Scope::eStruct);
}
}
}
}
}
}
void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2,
const std::string &derivedname, const std::string &basename,
const std::string &variablename, bool derivedIsStruct, bool baseIsStruct)
{
std::list<const Token *> toks;
toks.push_back(tok1);
toks.push_back(tok2);
const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedname +
"' defines member variable with name '" + variablename + "' also defined in its parent " +
std::string(baseIsStruct ? "struct" : "class") + " '" + basename + "'.";
reportError(toks, Severity::warning, "duplInheritedMember", message);
}
const std::list<const Token *> & CheckClass::callsPureVirtualFunction(const Function & function,
std::map<const Function *, std::list<const Token *> > & callsPureVirtualFunctionMap)
{
@ -2200,3 +2187,56 @@ void CheckClass::callsPureVirtualFunctionError(
"Call of pure virtual function '" + purefuncname + "' in " + scopeFunctionTypeName + ". The call will fail during runtime.");
}
//---------------------------------------------------------------------------
// Check for members hiding inherited members with the same name
//---------------------------------------------------------------------------
void CheckClass::checkDuplInheritedMembers()
{
if (!_settings->isEnabled("warning"))
return;
// Iterate over all classes
for (std::list<Type>::const_iterator classIt = symbolDatabase->typeList.begin();
classIt != symbolDatabase->typeList.end();
++classIt) {
// Iterate over the parent classes
for (std::vector<Type::BaseInfo>::const_iterator parentClassIt = classIt->derivedFrom.begin();
parentClassIt != classIt->derivedFrom.end();
++parentClassIt) {
// Check if there is info about the 'Base' class
if (!parentClassIt->type || !parentClassIt->type->classScope)
continue;
// Check if they have a member variable in common
for (std::list<Variable>::const_iterator classVarIt = classIt->classScope->varlist.begin();
classVarIt != classIt->classScope->varlist.end();
++classVarIt) {
for (std::list<Variable>::const_iterator parentClassVarIt = parentClassIt->type->classScope->varlist.begin();
parentClassVarIt != parentClassIt->type->classScope->varlist.end();
++parentClassVarIt) {
if (classVarIt->name() == parentClassVarIt->name() && !parentClassVarIt->isPrivate()) { // Check if the class and its parent have a common variable
duplInheritedMembersError(classVarIt->nameToken(), parentClassVarIt->nameToken(),
classIt->name(), parentClassIt->type->name(), classVarIt->name(),
classIt->classScope->type == Scope::eStruct,
parentClassIt->type->classScope->type == Scope::eStruct);
}
}
}
}
}
}
void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2,
const std::string &derivedname, const std::string &basename,
const std::string &variablename, bool derivedIsStruct, bool baseIsStruct)
{
std::list<const Token *> toks;
toks.push_back(tok1);
toks.push_back(tok2);
const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedname +
"' defines member variable with name '" + variablename + "' also defined in its parent " +
std::string(baseIsStruct ? "struct" : "class") + " '" + basename + "'.";
reportError(toks, Severity::warning, "duplInheritedMember", message);
}

View File

@ -68,6 +68,7 @@ public:
checkClass.operatorEqToSelf();
checkClass.initializerListOrder();
checkClass.initializationListUsage();
checkClass.checkSelfInitialization();
checkClass.virtualDestructor();
checkClass.checkConst();
@ -116,8 +117,12 @@ public:
/** @brief Check initializer list order */
void initializerListOrder();
/** @brief Suggest using initialization list */
void initializationListUsage();
/** @brief Check for initialization of a member with itself */
void checkSelfInitialization();
void copyconstructors();
/** @brief call of pure virtual funcion */
@ -150,6 +155,7 @@ private:
void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic);
void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname);
void suggestInitializationList(const Token *tok, const std::string& varname);
void selfInitializationError(const Token* tok, const std::string& name);
void callsPureVirtualFunctionError(const Function & scopeFunction, const std::list<const Token *> & tokStack, const std::string &purefuncname);
void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedname, const std::string &basename, const std::string &variablename, bool derivedIsStruct, bool baseIsStruct);
@ -174,6 +180,7 @@ private:
c.checkConstError(0, "class", "function", true);
c.initializerListError(0, 0, "class", "variable");
c.suggestInitializationList(0, "variable");
c.selfInitializationError(0, "var");
c.duplInheritedMembersError(0, 0, "class", "class", "variable", false, false);
}
@ -196,6 +203,7 @@ private:
"* Constness for member functions\n"
"* Order of initializations\n"
"* Suggest usage of initialization list\n"
"* Initialization of a member with itself\n"
"* Suspicious subtraction from 'this'\n"
"* Call of pure virtual function in constructor/destructor\n"
"* Duplicated inherited data members\n";

View File

@ -57,6 +57,7 @@ private:
TEST_CASE(noConstructor7); // ticket #4391
TEST_CASE(noConstructor8); // ticket #4404
TEST_CASE(noConstructor9); // ticket #4419
TEST_CASE(forwardDeclaration); // ticket #4290/#3190
TEST_CASE(operatorEq1);
TEST_CASE(operatorEq2);
@ -178,8 +179,7 @@ private:
TEST_CASE(initializerListOrder);
TEST_CASE(initializerListUsage);
TEST_CASE(forwardDeclaration); // ticket #4290/#3190
TEST_CASE(selfInitialization);
TEST_CASE(pureVirtualFunctionCall);
TEST_CASE(pureVirtualFunctionCallOtherClass);
@ -2019,12 +2019,12 @@ private:
ASSERT_EQUALS("[test.cpp:3]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout.str());
}
void checkNoConstructor(const char code[], const char* level="style") {
void checkNoConstructor(const char code[]) {
// Clear the error log
errout.str("");
Settings settings;
settings.addEnabled(level);
settings.addEnabled("style");
// Tokenize..
Tokenizer tokenizer(&settings, this);
@ -2124,6 +2124,22 @@ private:
ASSERT_EQUALS("", errout.str());
}
// ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor."
// ticket #3190 "SymbolDatabase: Parse of sub class constructor fails"
void forwardDeclaration() {
checkNoConstructor("class foo;\n"
"int bar;\n");
ASSERT_EQUALS("", errout.str());
checkNoConstructor("class foo;\n"
"class foo;\n");
ASSERT_EQUALS("", errout.str());
checkNoConstructor("class foo{};\n"
"class foo;\n");
ASSERT_EQUALS("", errout.str());
}
void checkNoMemset(const char code[], bool load_std_cfg = false) {
// Clear the error log
errout.str("");
@ -5826,19 +5842,52 @@ private:
ASSERT_EQUALS("", errout.str());
}
// ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor."
// ticket #3190 "SymbolDatabase: Parse of sub class constructor fails"
void forwardDeclaration() {
checkConst("class foo;\n"
"int bar;\n");
ASSERT_EQUALS("", errout.str());
checkConst("class foo;\n"
"class foo;\n");
ASSERT_EQUALS("", errout.str());
void checkSelfInitialization(const char code []) {
// Clear the error log
errout.str("");
checkConst("class foo{};\n"
"class foo;\n");
// Check..
Settings settings;
// Tokenize..
Tokenizer tokenizer(&settings, this);
std::istringstream istr(code);
tokenizer.tokenize(istr, "test.cpp");
tokenizer.simplifyTokenList2();
CheckClass checkClass(&tokenizer, &settings, this);
checkClass.checkSelfInitialization();
}
void selfInitialization() {
checkSelfInitialization("class Fred {\n"
" int i;\n"
" Fred() : i(i) {\n"
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout.str());
checkSelfInitialization("class Fred {\n"
" int i;\n"
" Fred();\n"
"};\n"
"Fred::Fred() : i(i) {\n"
"}");
ASSERT_EQUALS("[test.cpp:5]: (error) Member variable 'i' is initialized by itself.\n", errout.str());
checkSelfInitialization("class Fred {\n"
" std::string s;\n"
" Fred() : s(s) {\n"
" }\n"
"};");
ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 's' is initialized by itself.\n", errout.str());
checkSelfInitialization("class Fred {\n"
" std::string s;\n"
" Fred(const std::string& s) : s(s) {\n"
" }\n"
"};");
ASSERT_EQUALS("", errout.str());
}
@ -6052,7 +6101,7 @@ private:
ASSERT_THROW(checkNoConstructor("struct R1 {\n"
" int a;\n"
" R1 () : a { }\n"
"};\n", "warning"), InternalError);
"};\n"), InternalError);
}
};