Whole program analysis: Improved handling of nested calls
This commit is contained in:
parent
599e038282
commit
ce60b326f4
|
@ -1303,6 +1303,7 @@ std::string CheckUninitVar::MyFileInfo::toString() const
|
||||||
writeFunctionArgsXml(readData, "readData", ret);
|
writeFunctionArgsXml(readData, "readData", ret);
|
||||||
writeFunctionArgsXml(nullPointer, "nullPointer", ret);
|
writeFunctionArgsXml(nullPointer, "nullPointer", ret);
|
||||||
writeFunctionArgsXml(dereferenced, "dereferenced", ret);
|
writeFunctionArgsXml(dereferenced, "dereferenced", ret);
|
||||||
|
writeFunctionArgsXml(nestedCall, "nestedCall", ret);
|
||||||
return ret.str();
|
return ret.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1319,6 +1320,54 @@ CheckUninitVar::MyFileInfo::FunctionArg::FunctionArg(const Tokenizer *tokenizer,
|
||||||
location.linenr = tok->linenr();
|
location.linenr = tok->linenr();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isUnsafeFunction(const Scope *scope, int argnr, const Token **tok)
|
||||||
|
{
|
||||||
|
const Variable * const argvar = scope->function->getArgumentVar(argnr);
|
||||||
|
if (!argvar->isPointer())
|
||||||
|
return false;
|
||||||
|
for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) {
|
||||||
|
if (tok2->variable() != argvar)
|
||||||
|
continue;
|
||||||
|
if (!Token::Match(tok2->astParent(), "*|["))
|
||||||
|
return false;
|
||||||
|
while (Token::Match(tok2->astParent(), "*|["))
|
||||||
|
tok2 = tok2->astParent();
|
||||||
|
if (!Token::Match(tok2->astParent(),"%cop%"))
|
||||||
|
return false;
|
||||||
|
*tok = tok2;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int isCallFunction(const Scope *scope, int argnr, const Token **tok)
|
||||||
|
{
|
||||||
|
const Variable * const argvar = scope->function->getArgumentVar(argnr);
|
||||||
|
if (!argvar->isPointer())
|
||||||
|
return -1;
|
||||||
|
for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) {
|
||||||
|
if (tok2->variable() != argvar)
|
||||||
|
continue;
|
||||||
|
if (!Token::Match(tok2->previous(), "[(,] %var% [,)]"))
|
||||||
|
break;
|
||||||
|
int argnr2 = 1;
|
||||||
|
const Token *prev = tok2;
|
||||||
|
while (prev && prev->str() != "(") {
|
||||||
|
if (Token::Match(prev,"]|)"))
|
||||||
|
prev = prev->link();
|
||||||
|
else if (prev->str() == ",")
|
||||||
|
++argnr2;
|
||||||
|
prev = prev->previous();
|
||||||
|
}
|
||||||
|
if (!prev || !Token::Match(prev->previous(), "%name% ("))
|
||||||
|
break;
|
||||||
|
if (!prev->astOperand1()->function())
|
||||||
|
break;
|
||||||
|
*tok = prev->previous();
|
||||||
|
return argnr2;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings * /*settings*/) const
|
Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings * /*settings*/) const
|
||||||
{
|
{
|
||||||
|
@ -1373,31 +1422,24 @@ Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const S
|
||||||
if (CheckNullPointer::isUnsafeFunction(&*scope, argnr, &tok))
|
if (CheckNullPointer::isUnsafeFunction(&*scope, argnr, &tok))
|
||||||
fileInfo->dereferenced.push_back(MyFileInfo::FunctionArg(tokenizer, &*scope, argnr+1, tok));
|
fileInfo->dereferenced.push_back(MyFileInfo::FunctionArg(tokenizer, &*scope, argnr+1, tok));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nested function calls
|
||||||
|
for (int argnr = 0; argnr < function->argCount(); ++argnr) {
|
||||||
|
const Token *tok;
|
||||||
|
int argnr2 = isCallFunction(&*scope, argnr, &tok);
|
||||||
|
if (argnr2 > 0) {
|
||||||
|
MyFileInfo::FunctionArg fa(tokenizer, &*scope, argnr+1, tok);
|
||||||
|
fa.id = FUNCTION_ID(function);
|
||||||
|
fa.id2 = FUNCTION_ID(tok->function());
|
||||||
|
fa.argnr2 = argnr2;
|
||||||
|
fileInfo->nestedCall.push_back(fa);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckUninitVar::isUnsafeFunction(const Scope *scope, int argnr, const Token **tok)
|
|
||||||
{
|
|
||||||
const Variable * const argvar = scope->function->getArgumentVar(argnr);
|
|
||||||
if (!argvar->isPointer())
|
|
||||||
return false;
|
|
||||||
for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) {
|
|
||||||
if (tok2->variable() != argvar)
|
|
||||||
continue;
|
|
||||||
if (!Token::Match(tok2->astParent(), "*|["))
|
|
||||||
return false;
|
|
||||||
while (Token::Match(tok2->astParent(), "*|["))
|
|
||||||
tok2 = tok2->astParent();
|
|
||||||
if (!Token::Match(tok2->astParent(),"%cop%"))
|
|
||||||
return false;
|
|
||||||
*tok = tok2;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const
|
Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const
|
||||||
{
|
{
|
||||||
MyFileInfo *fileInfo = nullptr;
|
MyFileInfo *fileInfo = nullptr;
|
||||||
|
@ -1431,12 +1473,29 @@ Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement
|
||||||
fileInfo->nullPointer.push_back(fa);
|
fileInfo->nullPointer.push_back(fa);
|
||||||
else if (std::strcmp(e->Name(), "dereferenced") == 0)
|
else if (std::strcmp(e->Name(), "dereferenced") == 0)
|
||||||
fileInfo->dereferenced.push_back(fa);
|
fileInfo->dereferenced.push_back(fa);
|
||||||
|
else if (std::strcmp(e->Name(), "nestedCall") == 0)
|
||||||
|
fileInfo->nestedCall.push_back(fa);
|
||||||
else
|
else
|
||||||
throw InternalError(nullptr, "Wrong analyze info");
|
throw InternalError(nullptr, "Wrong analyze info");
|
||||||
}
|
}
|
||||||
return fileInfo;
|
return fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool findPath(const CheckUninitVar::MyFileInfo::FunctionArg &from,
|
||||||
|
const CheckUninitVar::MyFileInfo::FunctionArg &to,
|
||||||
|
const std::list<CheckUninitVar::MyFileInfo::FunctionArg> &nestedCalls)
|
||||||
|
{
|
||||||
|
if (from.id == to.id && from.argnr == to.argnr)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (std::list<CheckUninitVar::MyFileInfo::FunctionArg>::const_iterator it = nestedCalls.begin(); it != nestedCalls.end(); ++it) {
|
||||||
|
if (from.id == it->id && from.argnr == it->argnr && it->id2 == to.id && it->argnr2 == to.argnr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CheckUninitVar::analyseWholeProgram(const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger)
|
bool CheckUninitVar::analyseWholeProgram(const std::list<Check::FileInfo*> &fileInfo, const Settings& settings, ErrorLogger &errorLogger)
|
||||||
{
|
{
|
||||||
(void)settings; // This argument is unused
|
(void)settings; // This argument is unused
|
||||||
|
@ -1451,6 +1510,7 @@ bool CheckUninitVar::analyseWholeProgram(const std::list<Check::FileInfo*> &file
|
||||||
all.readData.insert(all.readData.end(), fi->readData.begin(), fi->readData.end());
|
all.readData.insert(all.readData.end(), fi->readData.begin(), fi->readData.end());
|
||||||
all.nullPointer.insert(all.nullPointer.end(), fi->nullPointer.begin(), fi->nullPointer.end());
|
all.nullPointer.insert(all.nullPointer.end(), fi->nullPointer.begin(), fi->nullPointer.end());
|
||||||
all.dereferenced.insert(all.dereferenced.end(), fi->dereferenced.begin(), fi->dereferenced.end());
|
all.dereferenced.insert(all.dereferenced.end(), fi->dereferenced.begin(), fi->dereferenced.end());
|
||||||
|
all.nestedCall.insert(all.nestedCall.end(), fi->nestedCall.begin(), fi->nestedCall.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool foundErrors = false;
|
bool foundErrors = false;
|
||||||
|
@ -1459,7 +1519,8 @@ bool CheckUninitVar::analyseWholeProgram(const std::list<Check::FileInfo*> &file
|
||||||
const CheckUninitVar::MyFileInfo::FunctionArg &uninitialized = *it1;
|
const CheckUninitVar::MyFileInfo::FunctionArg &uninitialized = *it1;
|
||||||
for (std::list<CheckUninitVar::MyFileInfo::FunctionArg>::const_iterator it2 = all.readData.begin(); it2 != all.readData.end(); ++it2) {
|
for (std::list<CheckUninitVar::MyFileInfo::FunctionArg>::const_iterator it2 = all.readData.begin(); it2 != all.readData.end(); ++it2) {
|
||||||
const CheckUninitVar::MyFileInfo::FunctionArg &readData = *it2;
|
const CheckUninitVar::MyFileInfo::FunctionArg &readData = *it2;
|
||||||
if (uninitialized.id != readData.id || uninitialized.argnr != readData.argnr)
|
|
||||||
|
if (!findPath(*it1, *it2, all.nestedCall))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ErrorLogger::ErrorMessage::FileLocation fileLoc1;
|
ErrorLogger::ErrorMessage::FileLocation fileLoc1;
|
||||||
|
@ -1494,7 +1555,8 @@ bool CheckUninitVar::analyseWholeProgram(const std::list<Check::FileInfo*> &file
|
||||||
const CheckUninitVar::MyFileInfo::FunctionArg &nullPointer = *it1;
|
const CheckUninitVar::MyFileInfo::FunctionArg &nullPointer = *it1;
|
||||||
for (std::list<CheckUninitVar::MyFileInfo::FunctionArg>::const_iterator it2 = all.dereferenced.begin(); it2 != all.dereferenced.end(); ++it2) {
|
for (std::list<CheckUninitVar::MyFileInfo::FunctionArg>::const_iterator it2 = all.dereferenced.begin(); it2 != all.dereferenced.end(); ++it2) {
|
||||||
const CheckUninitVar::MyFileInfo::FunctionArg &dereference = *it2;
|
const CheckUninitVar::MyFileInfo::FunctionArg &dereference = *it2;
|
||||||
if (nullPointer.id != dereference.id || nullPointer.argnr != dereference.argnr)
|
|
||||||
|
if (!findPath(*it1, *it2, all.nestedCall))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
ErrorLogger::ErrorMessage::FileLocation fileLoc1;
|
ErrorLogger::ErrorMessage::FileLocation fileLoc1;
|
||||||
|
|
|
@ -96,6 +96,7 @@ public:
|
||||||
: id(id_),
|
: id(id_),
|
||||||
functionName(functionName_),
|
functionName(functionName_),
|
||||||
argnr(argnr_),
|
argnr(argnr_),
|
||||||
|
argnr2(0),
|
||||||
variableName(varname) {
|
variableName(varname) {
|
||||||
location.fileName = fileName;
|
location.fileName = fileName;
|
||||||
location.linenr = linenr;
|
location.linenr = linenr;
|
||||||
|
@ -104,8 +105,10 @@ public:
|
||||||
FunctionArg(const Tokenizer *tokenizer, const Scope *scope, unsigned int argnr_, const Token *tok);
|
FunctionArg(const Tokenizer *tokenizer, const Scope *scope, unsigned int argnr_, const Token *tok);
|
||||||
|
|
||||||
std::string id;
|
std::string id;
|
||||||
|
std::string id2;
|
||||||
std::string functionName;
|
std::string functionName;
|
||||||
unsigned int argnr;
|
unsigned int argnr;
|
||||||
|
unsigned int argnr2;
|
||||||
std::string variableName;
|
std::string variableName;
|
||||||
struct {
|
struct {
|
||||||
std::string fileName;
|
std::string fileName;
|
||||||
|
@ -125,6 +128,9 @@ public:
|
||||||
/** function arguments that are unconditionally dereferenced */
|
/** function arguments that are unconditionally dereferenced */
|
||||||
std::list<FunctionArg> dereferenced;
|
std::list<FunctionArg> dereferenced;
|
||||||
|
|
||||||
|
/** function calls other function.. */
|
||||||
|
std::list<FunctionArg> nestedCall;
|
||||||
|
|
||||||
/** Convert MyFileInfo data into xml string */
|
/** Convert MyFileInfo data into xml string */
|
||||||
std::string toString() const;
|
std::string toString() const;
|
||||||
};
|
};
|
||||||
|
@ -170,9 +176,6 @@ private:
|
||||||
"- using allocated data before it has been initialized\n"
|
"- using allocated data before it has been initialized\n"
|
||||||
"- using dead pointer\n";
|
"- using dead pointer\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isUnsafeFunction(const Scope *scope, int argnr, const Token **tok);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
/// @}
|
/// @}
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -4003,6 +4003,14 @@ private:
|
||||||
" f(&x);\n"
|
" f(&x);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) using argument p that points at uninitialized variable x\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) using argument p that points at uninitialized variable x\n", errout.str());
|
||||||
|
|
||||||
|
ctu("void use(int *p) { a = *p + 3; }\n"
|
||||||
|
"void call(int x, int *p) { x++; use(p); }\n"
|
||||||
|
"int main() {\n"
|
||||||
|
" int x;\n"
|
||||||
|
" call(4,&x);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (error) using argument p that points at uninitialized variable x\n", errout.str());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue