checkunusedvar: handle initialization list (#2836)
This commit is contained in:
parent
eb2c0bb6ee
commit
d55874ec37
|
@ -1135,6 +1135,12 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (tok->variable() && tok->variable()->isClass() && tok->variable()->type() &&
|
||||||
|
(tok->variable()->type()->needInitialization == Type::NeedInitialization::False) &&
|
||||||
|
tok->next()->str() == ";")
|
||||||
|
{
|
||||||
|
variables.write(tok->varId(), tok);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1471,7 +1477,6 @@ bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
|
||||||
{
|
{
|
||||||
// a type that has no side effects (no constructors and no members with constructors)
|
// a type that has no side effects (no constructors and no members with constructors)
|
||||||
/** @todo false negative: check constructors for side effects */
|
/** @todo false negative: check constructors for side effects */
|
||||||
|
|
||||||
const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert(
|
const std::pair<std::map<const Type *,bool>::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert(
|
||||||
std::pair<const Type *,bool>(type,false)); //Initialize with side effects for possible recursions
|
std::pair<const Type *,bool>(type,false)); //Initialize with side effects for possible recursions
|
||||||
bool & withoutSideEffects = found.first->second;
|
bool & withoutSideEffects = found.first->second;
|
||||||
|
@ -1489,8 +1494,40 @@ bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
|
||||||
if (f.argDef && Token::simpleMatch(f.argDef->link(), ") ="))
|
if (f.argDef && Token::simpleMatch(f.argDef->link(), ") ="))
|
||||||
continue; // ignore default/deleted constructors
|
continue; // ignore default/deleted constructors
|
||||||
const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }"));
|
const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }"));
|
||||||
const bool hasInitList = f.argDef && Token::simpleMatch(f.argDef->link(), ") :");
|
|
||||||
if (!emptyBody || hasInitList)
|
Token* nextToken = f.argDef->link();
|
||||||
|
if (Token::simpleMatch(nextToken, ") :")) {
|
||||||
|
// validating initialization list
|
||||||
|
nextToken = nextToken->next(); // goto ":"
|
||||||
|
|
||||||
|
for (const Token *initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next())
|
||||||
|
{
|
||||||
|
const Token* varToken = initListToken->next();
|
||||||
|
const Variable* variable = varToken->variable();
|
||||||
|
if (variable && !isVariableWithoutSideEffects(*variable))
|
||||||
|
{
|
||||||
|
return withoutSideEffects = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token* valueEnd = initListToken->linkAt(2);
|
||||||
|
for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) {
|
||||||
|
const Variable* initValueVar = valueToken->variable();
|
||||||
|
if (initValueVar && !isVariableWithoutSideEffects(*initValueVar))
|
||||||
|
{
|
||||||
|
return withoutSideEffects = false;
|
||||||
|
}
|
||||||
|
if ((valueToken->tokType() == Token::Type::eFunction) ||
|
||||||
|
(valueToken->tokType() == Token::Type::eName) ||
|
||||||
|
(valueToken->tokType() == Token::Type::eLambda) ||
|
||||||
|
(valueToken->tokType() == Token::Type::eOther))
|
||||||
|
{
|
||||||
|
return withoutSideEffects = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emptyBody)
|
||||||
return (withoutSideEffects = false);
|
return (withoutSideEffects = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1502,17 +1539,10 @@ bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
|
||||||
|
|
||||||
// Is there a member variable with possible side effects
|
// Is there a member variable with possible side effects
|
||||||
for (const Variable& var : type->classScope->varlist) {
|
for (const Variable& var : type->classScope->varlist) {
|
||||||
if (var.isPointer())
|
withoutSideEffects = isVariableWithoutSideEffects(var);
|
||||||
continue;
|
if (!withoutSideEffects)
|
||||||
|
{
|
||||||
const Type* variableType = var.type();
|
return withoutSideEffects;
|
||||||
if (variableType) {
|
|
||||||
if (!isRecordTypeWithoutSideEffects(variableType))
|
|
||||||
return (withoutSideEffects = false);
|
|
||||||
} else {
|
|
||||||
ValueType::Type valueType = var.valueType()->type;
|
|
||||||
if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD))
|
|
||||||
return (withoutSideEffects = false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1520,6 +1550,24 @@ bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type)
|
||||||
return (withoutSideEffects = true);
|
return (withoutSideEffects = true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var)
|
||||||
|
{
|
||||||
|
if (var.isPointer())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const Type* variableType = var.type();
|
||||||
|
if (variableType) {
|
||||||
|
if (!isRecordTypeWithoutSideEffects(variableType))
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ValueType::Type valueType = var.valueType()->type;
|
||||||
|
if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckUnusedVar::isEmptyType(const Type* type)
|
bool CheckUnusedVar::isEmptyType(const Type* type)
|
||||||
{
|
{
|
||||||
// a type that has no variables and no constructor
|
// a type that has no variables and no constructor
|
||||||
|
|
|
@ -34,6 +34,7 @@ class Token;
|
||||||
class Tokenizer;
|
class Tokenizer;
|
||||||
class Type;
|
class Type;
|
||||||
class Variables;
|
class Variables;
|
||||||
|
class Variable;
|
||||||
|
|
||||||
/// @addtogroup Checks
|
/// @addtogroup Checks
|
||||||
/// @{
|
/// @{
|
||||||
|
@ -70,6 +71,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool isRecordTypeWithoutSideEffects(const Type* type);
|
bool isRecordTypeWithoutSideEffects(const Type* type);
|
||||||
|
bool isVariableWithoutSideEffects(const Variable& var);
|
||||||
bool isEmptyType(const Type* type);
|
bool isEmptyType(const Type* type);
|
||||||
|
|
||||||
// Error messages..
|
// Error messages..
|
||||||
|
|
|
@ -286,12 +286,94 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:6]: (style) Unused variable: e\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6]: (style) Unused variable: e\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(0) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x{0} {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"int y = 0;\n"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(y) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:8]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"int y = 0;"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(++y) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"int y = 0;"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(--y) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"int y = 0;"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(y+=1) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"int y = 0;"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(y-=1) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:7]: (style) Unused variable: f\n", errout.str());
|
||||||
|
|
||||||
// non-empty constructor
|
// non-empty constructor
|
||||||
functionVariableUsage(
|
functionVariableUsage(
|
||||||
"class F {\n"
|
"class F {\n"
|
||||||
"public:\n"
|
"public:\n"
|
||||||
" F() {\n"
|
" F() {\n"
|
||||||
" int i =0;\n"
|
" int i = 0;\n"
|
||||||
" (void) i;"
|
" (void) i;"
|
||||||
" }\n"
|
" }\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
|
@ -305,7 +387,7 @@ private:
|
||||||
"class F {\n"
|
"class F {\n"
|
||||||
"public:\n"
|
"public:\n"
|
||||||
" F() {\n"
|
" F() {\n"
|
||||||
" int i =0;\n"
|
" int i = 0;\n"
|
||||||
" (void) i;"
|
" (void) i;"
|
||||||
" }\n"
|
" }\n"
|
||||||
"};\n"
|
"};\n"
|
||||||
|
@ -318,6 +400,25 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// side-effect variable in initialization list
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() {\n"
|
||||||
|
" int i = 0;\n"
|
||||||
|
" (void) i;"
|
||||||
|
" }\n"
|
||||||
|
"};\n"
|
||||||
|
"class G {\n"
|
||||||
|
"public:\n"
|
||||||
|
" G() : f(F()) {}\n"
|
||||||
|
" F f;"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" G g;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// unknown variable type
|
// unknown variable type
|
||||||
functionVariableUsage(
|
functionVariableUsage(
|
||||||
"class H {\n"
|
"class H {\n"
|
||||||
|
@ -329,7 +430,98 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// unknown variable type in initialization list
|
||||||
|
functionVariableUsage(
|
||||||
|
"class H {\n"
|
||||||
|
"public:\n"
|
||||||
|
" H() : x{0}, u(1) {}\n"
|
||||||
|
" int x;"
|
||||||
|
" unknown_type u;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" H h;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
// unknown variable type used for initialization
|
||||||
|
functionVariableUsage(
|
||||||
|
"unknown_type y = 0;\n"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(y) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"int sideEffectFunc();\n"
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(sideEffectFunc()) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(unknownFunc()) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(++unknownValue) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(--unknownValue) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(unknownValue+=1) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
functionVariableUsage(
|
||||||
|
"class F {\n"
|
||||||
|
"public:\n"
|
||||||
|
" F() : x(unknownValue-=1) {}\n"
|
||||||
|
" int x;\n"
|
||||||
|
"};\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" F f;\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// #5355 - False positive: Variable is not assigned a value.
|
// #5355 - False positive: Variable is not assigned a value.
|
||||||
|
@ -4033,7 +4225,7 @@ private:
|
||||||
" std::cout << \"test\" << std::endl;\n"
|
" std::cout << \"test\" << std::endl;\n"
|
||||||
" delete fred;\n"
|
" delete fred;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'fred' is allocated memory that is never used.\n", errout.str());
|
||||||
|
|
||||||
functionVariableUsage("void foo()\n"
|
functionVariableUsage("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
|
|
Loading…
Reference in New Issue