refactor (use ast) and improve CheckOther::checkRedundantAssignment (warn about global variables unless they are volatile, handle arrays in lhs better)
This commit is contained in:
parent
72ead10d93
commit
0ddeac0429
|
@ -407,6 +407,13 @@ static bool nonLocal(const Variable* var)
|
||||||
return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference();
|
return !var || (!var->isLocal() && !var->isArgument()) || var->isStatic() || var->isReference();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool nonLocalVolatile(const Variable* var)
|
||||||
|
{
|
||||||
|
if (var && var->isVolatile())
|
||||||
|
return false;
|
||||||
|
return nonLocal(var);
|
||||||
|
}
|
||||||
|
|
||||||
static void eraseNotLocalArg(std::map<unsigned int, const Token*>& container, const SymbolDatabase* symbolDatabase)
|
static void eraseNotLocalArg(std::map<unsigned int, const Token*>& container, const SymbolDatabase* symbolDatabase)
|
||||||
{
|
{
|
||||||
for (std::map<unsigned int, const Token*>::iterator i = container.begin(); i != container.end();) {
|
for (std::map<unsigned int, const Token*>::iterator i = container.begin(); i != container.end();) {
|
||||||
|
@ -493,6 +500,18 @@ void CheckOther::checkRedundantAssignment()
|
||||||
varAssignments.clear();
|
varAssignments.clear();
|
||||||
memAssignments.clear();
|
memAssignments.clear();
|
||||||
} else if (tok->tokType() == Token::eVariable && !Token::Match(tok, "%name% (")) {
|
} else if (tok->tokType() == Token::eVariable && !Token::Match(tok, "%name% (")) {
|
||||||
|
const Token *eq = nullptr;
|
||||||
|
for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
||||||
|
if (Token::Match(tok2, "[([]"))
|
||||||
|
tok2 = tok2->link();
|
||||||
|
else if (Token::Match(tok2, "[)];,]"))
|
||||||
|
break;
|
||||||
|
else if (tok2->str() == "=") {
|
||||||
|
eq = tok2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set initialization flag
|
// Set initialization flag
|
||||||
if (!Token::Match(tok, "%var% ["))
|
if (!Token::Match(tok, "%var% ["))
|
||||||
initialized.insert(tok->varId());
|
initialized.insert(tok->varId());
|
||||||
|
@ -512,19 +531,48 @@ void CheckOther::checkRedundantAssignment()
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<unsigned int, const Token*>::iterator it = varAssignments.find(tok->varId());
|
std::map<unsigned int, const Token*>::iterator it = varAssignments.find(tok->varId());
|
||||||
if (Token::simpleMatch(tok->next(), "=") && Token::Match(startToken, "[;{}]")) { // Assignment
|
if (eq && Token::Match(startToken, "[;{}]")) { // Assignment
|
||||||
if (it != varAssignments.end()) {
|
if (it != varAssignments.end()) {
|
||||||
bool error = true; // Ensure that variable is not used on right side
|
const Token *oldeq = nullptr;
|
||||||
for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
|
for (const Token *tok2 = it->second; tok2; tok2 = tok2->next()) {
|
||||||
if (tok2->str() == ";")
|
if (Token::Match(tok2, "[([]"))
|
||||||
|
tok2 = tok2->link();
|
||||||
|
else if (Token::Match(tok2, "[)];,]"))
|
||||||
break;
|
break;
|
||||||
else if (tok2->varId() == tok->varId()) {
|
else if (Token::Match(tok2, "++|--|=")) {
|
||||||
|
oldeq = tok2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!oldeq) {
|
||||||
|
const Token *tok2 = it->second;
|
||||||
|
while (Token::Match(tok2, "%name%|.|[|*|("))
|
||||||
|
tok2 = tok2->astParent();
|
||||||
|
if (Token::Match(tok2, "++|--"))
|
||||||
|
oldeq = tok2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that LHS in assignments are the same
|
||||||
|
bool error = oldeq && isSameExpression(_tokenizer->isCPP(), true, eq->astOperand1(), oldeq->astOperand1(), _settings->library.functionpure);
|
||||||
|
|
||||||
|
// Ensure that variable is not used on right side
|
||||||
|
std::stack<const Token *> tokens;
|
||||||
|
tokens.push(eq->astOperand2());
|
||||||
|
while (!tokens.empty()) {
|
||||||
|
const Token *rhs = tokens.top();
|
||||||
|
tokens.pop();
|
||||||
|
if (!rhs)
|
||||||
|
continue;
|
||||||
|
tokens.push(rhs->astOperand1());
|
||||||
|
tokens.push(rhs->astOperand2());
|
||||||
|
if (rhs->varId() == tok->varId()) {
|
||||||
error = false;
|
error = false;
|
||||||
break;
|
break;
|
||||||
} else if (Token::Match(tok2, "%name% (") && nonLocal(tok->variable())) { // Called function might use the variable
|
}
|
||||||
const Function* const func = tok2->function();
|
if (Token::Match(rhs->previous(), "%name% (") && nonLocalVolatile(tok->variable())) { // Called function might use the variable
|
||||||
|
const Function* const func = rhs->function();
|
||||||
const Variable* const var = tok->variable();
|
const Variable* const var = tok->variable();
|
||||||
if (!var || var->isGlobal() || var->isReference() || ((!func || func->nestedIn) && tok2->strAt(-1) != ".")) {// Global variable, or member function
|
if (!var || var->isGlobal() || var->isReference() || ((!func || func->nestedIn) && rhs->strAt(-1) != ".")) {// Global variable, or member function
|
||||||
error = false;
|
error = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -532,13 +580,13 @@ void CheckOther::checkRedundantAssignment()
|
||||||
}
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
if (printWarning && scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok))
|
if (printWarning && scope->type == Scope::eSwitch && Token::findmatch(it->second, "default|case", tok))
|
||||||
redundantAssignmentInSwitchError(it->second, tok, tok->str());
|
redundantAssignmentInSwitchError(it->second, tok, eq->astOperand1()->expressionString());
|
||||||
else if (printPerformance) {
|
else if (printPerformance) {
|
||||||
// See #7133
|
// See #7133
|
||||||
const bool nonlocal = it->second->variable() && nonLocal(it->second->variable());
|
const bool nonlocal = it->second->variable() && nonLocalVolatile(it->second->variable());
|
||||||
if (printInconclusive || !nonlocal) // see #5089 - report inconclusive only when requested
|
if (printInconclusive || !nonlocal) // report inconclusive only when requested
|
||||||
if (_tokenizer->isC() || checkExceptionHandling(tok)) // see #6555 to see how exception handling might have an impact
|
if (_tokenizer->isC() || checkExceptionHandling(tok)) // see #6555 to see how exception handling might have an impact
|
||||||
redundantAssignmentError(it->second, tok, tok->str(), nonlocal); // Inconclusive for non-local variables
|
redundantAssignmentError(it->second, tok, eq->astOperand1()->expressionString(), nonlocal); // Inconclusive for non-local variables
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
it->second = tok;
|
it->second = tok;
|
||||||
|
|
|
@ -1613,6 +1613,8 @@ void Variable::evaluate(const Library* lib)
|
||||||
setFlag(fIsStatic, true);
|
setFlag(fIsStatic, true);
|
||||||
else if (tok->str() == "extern")
|
else if (tok->str() == "extern")
|
||||||
setFlag(fIsExtern, true);
|
setFlag(fIsExtern, true);
|
||||||
|
else if (tok->str() == "volatile")
|
||||||
|
setFlag(fIsVolatile, true);
|
||||||
else if (tok->str() == "mutable")
|
else if (tok->str() == "mutable")
|
||||||
setFlag(fIsMutable, true);
|
setFlag(fIsMutable, true);
|
||||||
else if (tok->str() == "const")
|
else if (tok->str() == "const")
|
||||||
|
|
|
@ -159,7 +159,8 @@ class CPPCHECKLIB Variable {
|
||||||
fHasDefault = (1 << 9), /** @brief function argument with default value */
|
fHasDefault = (1 << 9), /** @brief function argument with default value */
|
||||||
fIsStlType = (1 << 10), /** @brief STL type ('std::') */
|
fIsStlType = (1 << 10), /** @brief STL type ('std::') */
|
||||||
fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string<T>|u16string|u32string */
|
fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string<T>|u16string|u32string */
|
||||||
fIsFloatType = (1 << 12) /** @brief Floating point type */
|
fIsFloatType = (1 << 12), /** @brief Floating point type */
|
||||||
|
fIsVolatile = (1 << 13) /** @brief volatile */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -339,6 +340,14 @@ public:
|
||||||
return getFlag(fIsMutable);
|
return getFlag(fIsMutable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is variable volatile.
|
||||||
|
* @return true if volatile, false if not
|
||||||
|
*/
|
||||||
|
bool isVolatile() const {
|
||||||
|
return getFlag(fIsVolatile);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is variable static.
|
* Is variable static.
|
||||||
* @return true if static, false if not
|
* @return true if static, false if not
|
||||||
|
|
|
@ -4907,6 +4907,13 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Variable 'i' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str());
|
||||||
|
|
||||||
|
check("void f() {\n"
|
||||||
|
" int i[10];\n"
|
||||||
|
" i[2] = 1;\n"
|
||||||
|
" i[2] = 1;\n"
|
||||||
|
"}", nullptr, false, false, false);
|
||||||
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Variable 'i[2]' is reassigned a value before the old one has been used.\n", errout.str());
|
||||||
|
|
||||||
// Testing different types
|
// Testing different types
|
||||||
check("void f() {\n"
|
check("void f() {\n"
|
||||||
" Foo& bar = foo();\n"
|
" Foo& bar = foo();\n"
|
||||||
|
@ -5041,7 +5048,7 @@ private:
|
||||||
" x = 2;\n"
|
" x = 2;\n"
|
||||||
" x = z.g();\n"
|
" x = z.g();\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:9]: (style, inconclusive) Variable 'x' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// from #3103 (avoid a false negative)
|
// from #3103 (avoid a false negative)
|
||||||
check("int foo(){\n"
|
check("int foo(){\n"
|
||||||
|
@ -5097,7 +5104,7 @@ private:
|
||||||
" ab.a = 2;\n"
|
" ab.a = 2;\n"
|
||||||
" return ab.a;\n"
|
" return ab.a;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:6]: (style, inconclusive) Variable 'a' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:6]: (style) Variable 'ab.a' is reassigned a value before the old one has been used.\n", errout.str());
|
||||||
|
|
||||||
check("struct AB { int a; int b; };\n"
|
check("struct AB { int a; int b; };\n"
|
||||||
"\n"
|
"\n"
|
||||||
|
@ -5257,9 +5264,7 @@ private:
|
||||||
" catch (const uno::Exception&) {\n"
|
" catch (const uno::Exception&) {\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}", "test.cpp", false, true);
|
"}", "test.cpp", false, true);
|
||||||
TODO_ASSERT_EQUALS("",
|
ASSERT_EQUALS("", errout.str());
|
||||||
"[test.cpp:6] -> [test.cpp:9]: (style) Variable 'Name' is reassigned a value before the old one has been used.\n",
|
|
||||||
errout.str());
|
|
||||||
|
|
||||||
check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n"
|
check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n"
|
||||||
"BitmapBuffer aSrcBuf;\n"
|
"BitmapBuffer aSrcBuf;\n"
|
||||||
|
@ -5268,7 +5273,7 @@ private:
|
||||||
" aSrcBuf.mnBitCount = nDestBits;\n"
|
" aSrcBuf.mnBitCount = nDestBits;\n"
|
||||||
" bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n"
|
" bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n"
|
||||||
"}", "test.c");
|
"}", "test.c");
|
||||||
ASSERT_EQUALS("[test.c:3] -> [test.c:5]: (style) Variable 'mnBitCount' is reassigned a value before the old one has been used.\n", errout.str());
|
ASSERT_EQUALS("[test.c:3] -> [test.c:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout.str());
|
||||||
check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n"
|
check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n"
|
||||||
"BitmapBuffer aSrcBuf;\n"
|
"BitmapBuffer aSrcBuf;\n"
|
||||||
" aSrcBuf.mnBitCount = nSrcBits;\n"
|
" aSrcBuf.mnBitCount = nSrcBits;\n"
|
||||||
|
@ -5276,8 +5281,8 @@ private:
|
||||||
" aSrcBuf.mnBitCount = nDestBits;\n"
|
" aSrcBuf.mnBitCount = nDestBits;\n"
|
||||||
" bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n"
|
" bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n"
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style, inconclusive) Variable 'mnBitCount' is reassigned a value before the old one has been used.\n",
|
TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style, inconclusive) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n",
|
||||||
"[test.cpp:3] -> [test.cpp:5]: (style) Variable 'mnBitCount' is reassigned a value before the old one has been used.\n",
|
"[test.cpp:3] -> [test.cpp:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue