Fixed #6460 (Library: better function/type matching)

This commit is contained in:
Daniel Marjamäki 2015-01-27 17:55:18 +01:00
parent bfa16ec258
commit 3285f85ebf
9 changed files with 108 additions and 5 deletions

View File

@ -322,6 +322,8 @@
<arg nr="4">
<not-uninit/>
</arg>
<arg nr="5"/>
<arg nr="6"/>
</function>
<function name="send">
<arg nr="1">
@ -350,6 +352,8 @@
<arg nr="4">
<not-uninit/>
</arg>
<arg nr="5"/>
<arg nr="6"/>
</function>
<!-- void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); -->
<function name="mmap">

View File

@ -5520,5 +5520,6 @@
<arg nr="2">
<not-null/>
</arg>
<arg nr="3"/>
</function>
</def>

22
cfg/test/std.c Normal file
View File

@ -0,0 +1,22 @@
// Test library configuration for std.cfg
//
// Usage:
// $ cppcheck --check-library --enable=information --error-exitcode=1 --inline-suppr cfg/test/std.c
// =>
// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0
//
#include <string.h>
void strcpy_ok(char *a, char *b) {
strcpy(a,b);
}
void strcpy_bad() {
char a[10];
// cppcheck-suppress bufferAccessOutOfBounds
strcpy(a, "hello world!");
}

View File

@ -2890,3 +2890,23 @@ void CheckOther::redundantPointerOpError(const Token* tok, const std::string &va
reportError(tok, Severity::style, "redundantPointerOp",
"Redundant pointer operation on " + varname + " - it's already a pointer.", inconclusive);
}
void CheckOther::checkLibraryMatchFunctions()
{
if (!_settings->checkLibrary || !_settings->isEnabled("information"))
return;
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) {
if (Token::Match(tok, "%var% (") &&
!tok->function() &&
tok->astParent() == tok->next() &&
_settings->library.isNotLibraryFunction(tok)) {
reportError(tok,
Severity::information,
"checkLibraryFunction",
"--check-library: There is no matching configuration for function " + tok->str() + "()");
}
}
}

View File

@ -75,6 +75,9 @@ public:
checkOther.checkCommaSeparatedReturn();
checkOther.checkIgnoredReturnValue();
checkOther.checkRedundantPointerOp();
// --check-library : functions with nonmatching configuration
checkOther.checkLibraryMatchFunctions();
}
/** @brief Run checks against the simplified token list */
@ -237,6 +240,9 @@ public:
/** @brief %Check for redundant pointer operations */
void checkRedundantPointerOp();
/** @brief --check-library: warn for unconfigured function calls */
void checkLibraryMatchFunctions();
private:
// Error messages..
void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, const std::string &varName, const bool result);

View File

@ -678,3 +678,32 @@ const Library::Container* Library::detectContainer(const Token* typeStart) const
}
return nullptr;
}
// returns true if ftok is not a library function
bool Library::isNotLibraryFunction(const Token *ftok) const
{
if (!ftok->astParent())
return false;
if (ftok->astParent()->str() != "(")
return true;
int callargs = 0;
for (const Token *tok = ftok->tokAt(2); tok && tok->str() != ")"; tok = tok->next()) {
if (callargs == 0)
callargs = 1;
if (tok->str() == ",")
callargs++;
else if (tok->link() && Token::Match(tok, "<|(|["))
tok = tok->link();
}
const std::map<std::string, std::map<int, ArgumentChecks> >::const_iterator it = argumentChecks.find(ftok->str());
if (it == argumentChecks.end())
return (callargs != 0);
int args = 0;
for (std::map<int, ArgumentChecks>::const_iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2) {
if (it2->first > args)
args = it2->first;
if (it2->second.formatstr)
return args > callargs;
}
return args != callargs;
}

View File

@ -127,9 +127,7 @@ public:
std::set<std::string> useretval;
// returns true if ftok is not a library function
static bool isNotLibraryFunction(const Token *ftok) {
return ftok->astParent() ? ftok->astParent()->str() != "(" : false;
}
bool isNotLibraryFunction(const Token *ftok) const;
bool isnoreturn(const Token *ftok) const {
if (ftok->function() && ftok->function()->isAttributeNoreturn())

View File

@ -31,6 +31,7 @@ private:
void run() {
TEST_CASE(empty);
TEST_CASE(function);
TEST_CASE(function_match);
TEST_CASE(function_arg);
TEST_CASE(function_arg_any);
TEST_CASE(function_arg_valid);
@ -78,6 +79,26 @@ private:
ASSERT(library.isnotnoreturn(tokenList.front()));
}
void function_match() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
" <function name=\"foo\">\n"
" <arg nr=\"1\"/>"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
doc.Parse(xmldata, sizeof(xmldata));
TokenList tokenList(nullptr);
std::istringstream istr("foo();"); // <- too few arguments, not library function
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
Library library;
library.load(doc);
ASSERT(library.isNotLibraryFunction(tokenList.front()));
}
void function_arg() const {
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
"<def>\n"
@ -134,7 +155,7 @@ private:
library.load(doc);
TokenList tokenList(nullptr);
std::istringstream istr("foo();");
std::istringstream istr("foo(a,b,c,d,e);");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());
@ -177,6 +198,7 @@ private:
" <function name=\"foo\">\n"
" <arg nr=\"1\"><minsize type=\"strlen\" arg=\"2\"/></arg>\n"
" <arg nr=\"2\"><minsize type=\"argvalue\" arg=\"3\"/></arg>\n"
" <arg nr=\"3\"/>\n"
" </function>\n"
"</def>";
tinyxml2::XMLDocument doc;
@ -186,7 +208,7 @@ private:
library.load(doc);
TokenList tokenList(nullptr);
std::istringstream istr("foo();");
std::istringstream istr("foo(a,b,c);");
tokenList.createTokens(istr);
tokenList.front()->next()->astOperand1(tokenList.front());

View File

@ -2865,6 +2865,7 @@ private:
Settings settings;
settings.library.setnoreturn("exit", true);
settings.library.argumentChecks["exit"][1] = Library::ArgumentChecks();
check("void foo() {\n"
" exit(0);\n"
" break;\n"