Fix FP autoVariables (#5107)
* Fix #11732 FP autoVariables when reassigning argv * Fix #11732 FN autoVariables with array and std::string * Add test * Format * Format * Simplify check, fix inconclusive * Fix merge, add test * Fix FPs * Format * Format * Undo move * Format
This commit is contained in:
parent
1c28457d2c
commit
24b0d08753
|
@ -166,26 +166,6 @@ static bool isLocalContainerBuffer(const Token* tok)
|
||||||
return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT;
|
return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verification that we really take the address of a local variable
|
|
||||||
static bool checkRvalueExpression(const Token * const vartok)
|
|
||||||
{
|
|
||||||
const Variable * const var = vartok->variable();
|
|
||||||
if (var == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (Token::Match(vartok->previous(), "& %name% [") && var->isPointer())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const Token * const next = vartok->next();
|
|
||||||
// &a.b[0]
|
|
||||||
if (Token::Match(vartok, "%name% . %var% [") && !var->isPointer()) {
|
|
||||||
const Variable *var2 = next->next()->variable();
|
|
||||||
return var2 && !var2->isPointer();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ((next->str() != "." || (!var->isPointer() && (!var->isClass() || var->type()))) && next->strAt(2) != ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isAddressOfLocalVariable(const Token *expr)
|
static bool isAddressOfLocalVariable(const Token *expr)
|
||||||
{
|
{
|
||||||
if (!expr)
|
if (!expr)
|
||||||
|
@ -259,6 +239,26 @@ static bool isAutoVariableRHS(const Token* tok) {
|
||||||
return isAddressOfLocalVariable(tok) || isAutoVarArray(tok) || isLocalContainerBuffer(tok);
|
return isAddressOfLocalVariable(tok) || isAutoVarArray(tok) || isLocalContainerBuffer(tok);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool hasOverloadedAssignment(const Token* tok, bool c, bool& inconclusive)
|
||||||
|
{
|
||||||
|
inconclusive = false;
|
||||||
|
if (c)
|
||||||
|
return false;
|
||||||
|
if (const ValueType* vt = tok->valueType()) {
|
||||||
|
if (vt->pointer && !Token::simpleMatch(tok->astParent(), "*"))
|
||||||
|
return false;
|
||||||
|
if (vt->container && vt->container->stdStringLike)
|
||||||
|
return true;
|
||||||
|
if (vt->typeScope)
|
||||||
|
return std::any_of(vt->typeScope->functionList.begin(), vt->typeScope->functionList.end(), [](const Function& f) { // TODO: compare argument type
|
||||||
|
return f.name() == "operator=";
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inconclusive = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckAutoVariables::autoVariables()
|
void CheckAutoVariables::autoVariables()
|
||||||
{
|
{
|
||||||
const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
|
const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
|
||||||
|
@ -271,27 +271,20 @@ void CheckAutoVariables::autoVariables()
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Critical assignment
|
// Critical assignment
|
||||||
if (Token::Match(tok, "[;{}] %var% = & %var%") && isRefPtrArg(tok->next()) && isAutoVar(tok->tokAt(4))) {
|
if (Token::Match(tok, "[;{}] %var% =") && isRefPtrArg(tok->next()) && isAutoVariableRHS(tok->tokAt(2)->astOperand2())) {
|
||||||
if (checkRvalueExpression(tok->tokAt(4)))
|
|
||||||
checkAutoVariableAssignment(tok->next(), false);
|
checkAutoVariableAssignment(tok->next(), false);
|
||||||
} else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAddressOfLocalVariable(tok->tokAt(3)->astOperand2())) {
|
} else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAutoVariableRHS(tok->tokAt(3)->astOperand2())) {
|
||||||
checkAutoVariableAssignment(tok->next(), false);
|
const Token* lhs = tok->tokAt(2);
|
||||||
} else if (Token::Match(tok, "[;{}] %var% . %var% =") && isPtrArg(tok->next()) && isAddressOfLocalVariable(tok->tokAt(4)->astOperand2())) {
|
bool inconclusive = false;
|
||||||
checkAutoVariableAssignment(tok->next(), false);
|
if (!hasOverloadedAssignment(lhs, mTokenizer->isC(), inconclusive) || (printInconclusive && inconclusive))
|
||||||
} else if (Token::Match(tok, "[;{}] %var% . %var% = %var% ;")) {
|
checkAutoVariableAssignment(tok->next(), inconclusive);
|
||||||
// TODO: check if the parameter is only changed temporarily (#2969)
|
|
||||||
if (printInconclusive && isPtrArg(tok->next())) {
|
|
||||||
if (isAutoVarArray(tok->tokAt(5)))
|
|
||||||
checkAutoVariableAssignment(tok->next(), true);
|
|
||||||
}
|
|
||||||
tok = tok->tokAt(5);
|
|
||||||
} else if (Token::Match(tok, "[;{}] * %var% = %var% ;")) {
|
|
||||||
const Variable * var1 = tok->tokAt(2)->variable();
|
|
||||||
if (var1 && var1->isArgument() && Token::Match(var1->nameToken()->tokAt(-3), "%type% * *")) {
|
|
||||||
if (isAutoVarArray(tok->tokAt(4)))
|
|
||||||
checkAutoVariableAssignment(tok->next(), false);
|
|
||||||
}
|
|
||||||
tok = tok->tokAt(4);
|
tok = tok->tokAt(4);
|
||||||
|
} else if (Token::Match(tok, "[;{}] %var% . %var% =") && isPtrArg(tok->next()) && isAutoVariableRHS(tok->tokAt(4)->astOperand2())) {
|
||||||
|
const Token* lhs = tok->tokAt(3);
|
||||||
|
bool inconclusive = false;
|
||||||
|
if (!hasOverloadedAssignment(lhs, mTokenizer->isC(), inconclusive) || (printInconclusive && inconclusive))
|
||||||
|
checkAutoVariableAssignment(tok->next(), inconclusive);
|
||||||
|
tok = tok->tokAt(5);
|
||||||
} else if (Token::Match(tok, "[;{}] %var% [") && Token::simpleMatch(tok->linkAt(2), "] =") &&
|
} else if (Token::Match(tok, "[;{}] %var% [") && Token::simpleMatch(tok->linkAt(2), "] =") &&
|
||||||
(isPtrArg(tok->next()) || isArrayArg(tok->next(), mSettings)) &&
|
(isPtrArg(tok->next()) || isArrayArg(tok->next(), mSettings)) &&
|
||||||
isAutoVariableRHS(tok->linkAt(2)->next()->astOperand2())) {
|
isAutoVariableRHS(tok->linkAt(2)->next()->astOperand2())) {
|
||||||
|
|
|
@ -243,7 +243,7 @@ private:
|
||||||
" char a;\n"
|
" char a;\n"
|
||||||
" ab->a = &a;\n"
|
" ab->a = &a;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testautovar6() { // ticket #2931
|
void testautovar6() { // ticket #2931
|
||||||
|
@ -536,6 +536,21 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n"
|
ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n"
|
||||||
"[test.cpp:7]: (error) Address of local auto-variable assigned to a function parameter.\n",
|
"[test.cpp:7]: (error) Address of local auto-variable assigned to a function parameter.\n",
|
||||||
errout.str());
|
errout.str());
|
||||||
|
|
||||||
|
check("struct String {\n"
|
||||||
|
" String& operator=(const char* c) { m = c; return *this; }\n"
|
||||||
|
" std::string m;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct T { String s; };\n"
|
||||||
|
"void f(T* t) {\n"
|
||||||
|
" char a[] = \"abc\";\n"
|
||||||
|
" t->s = &a[0];\n"
|
||||||
|
"}\n"
|
||||||
|
"void g(std::string* s) {\n"
|
||||||
|
" char a[] = \"abc\";\n"
|
||||||
|
" *s = &a[0];\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testautovar_normal() {
|
void testautovar_normal() {
|
||||||
|
@ -544,7 +559,7 @@ private:
|
||||||
" XPoint DropPoint;\n"
|
" XPoint DropPoint;\n"
|
||||||
" ds->location_data = (XtPointer *)&DropPoint;\n"
|
" ds->location_data = (XtPointer *)&DropPoint;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testautovar_ptrptr() { // #6596
|
void testautovar_ptrptr() { // #6596
|
||||||
|
@ -628,7 +643,7 @@ private:
|
||||||
" if (condition) return;\n" // <- not reassigned => error
|
" if (condition) return;\n" // <- not reassigned => error
|
||||||
" pcb->root0 = 0;\n"
|
" pcb->root0 = 0;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
||||||
|
|
||||||
check("void foo(cb* pcb) {\n"
|
check("void foo(cb* pcb) {\n"
|
||||||
" int root0;\n"
|
" int root0;\n"
|
||||||
|
@ -637,7 +652,7 @@ private:
|
||||||
" if (condition)\n"
|
" if (condition)\n"
|
||||||
" pcb->root0 = 0;\n" // <- conditional reassign => error
|
" pcb->root0 = 0;\n" // <- conditional reassign => error
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
||||||
|
|
||||||
check("struct S { int *p; };\n"
|
check("struct S { int *p; };\n"
|
||||||
"void g(struct S* s) {\n"
|
"void g(struct S* s) {\n"
|
||||||
|
@ -649,7 +664,7 @@ private:
|
||||||
" struct S s;\n"
|
" struct S s;\n"
|
||||||
" g(&s);\n"
|
" g(&s);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testinvaliddealloc() {
|
void testinvaliddealloc() {
|
||||||
|
|
Loading…
Reference in New Issue