Fixed #6460 (Library: better function/type matching)
This commit is contained in:
parent
bfa16ec258
commit
3285f85ebf
|
@ -322,6 +322,8 @@
|
||||||
<arg nr="4">
|
<arg nr="4">
|
||||||
<not-uninit/>
|
<not-uninit/>
|
||||||
</arg>
|
</arg>
|
||||||
|
<arg nr="5"/>
|
||||||
|
<arg nr="6"/>
|
||||||
</function>
|
</function>
|
||||||
<function name="send">
|
<function name="send">
|
||||||
<arg nr="1">
|
<arg nr="1">
|
||||||
|
@ -350,6 +352,8 @@
|
||||||
<arg nr="4">
|
<arg nr="4">
|
||||||
<not-uninit/>
|
<not-uninit/>
|
||||||
</arg>
|
</arg>
|
||||||
|
<arg nr="5"/>
|
||||||
|
<arg nr="6"/>
|
||||||
</function>
|
</function>
|
||||||
<!-- void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); -->
|
<!-- void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); -->
|
||||||
<function name="mmap">
|
<function name="mmap">
|
||||||
|
|
|
@ -5520,5 +5520,6 @@
|
||||||
<arg nr="2">
|
<arg nr="2">
|
||||||
<not-null/>
|
<not-null/>
|
||||||
</arg>
|
</arg>
|
||||||
|
<arg nr="3"/>
|
||||||
</function>
|
</function>
|
||||||
</def>
|
</def>
|
||||||
|
|
|
@ -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!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -2890,3 +2890,23 @@ void CheckOther::redundantPointerOpError(const Token* tok, const std::string &va
|
||||||
reportError(tok, Severity::style, "redundantPointerOp",
|
reportError(tok, Severity::style, "redundantPointerOp",
|
||||||
"Redundant pointer operation on " + varname + " - it's already a pointer.", inconclusive);
|
"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() + "()");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,9 @@ public:
|
||||||
checkOther.checkCommaSeparatedReturn();
|
checkOther.checkCommaSeparatedReturn();
|
||||||
checkOther.checkIgnoredReturnValue();
|
checkOther.checkIgnoredReturnValue();
|
||||||
checkOther.checkRedundantPointerOp();
|
checkOther.checkRedundantPointerOp();
|
||||||
|
|
||||||
|
// --check-library : functions with nonmatching configuration
|
||||||
|
checkOther.checkLibraryMatchFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Run checks against the simplified token list */
|
/** @brief Run checks against the simplified token list */
|
||||||
|
@ -237,6 +240,9 @@ public:
|
||||||
/** @brief %Check for redundant pointer operations */
|
/** @brief %Check for redundant pointer operations */
|
||||||
void checkRedundantPointerOp();
|
void checkRedundantPointerOp();
|
||||||
|
|
||||||
|
/** @brief --check-library: warn for unconfigured function calls */
|
||||||
|
void checkLibraryMatchFunctions();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Error messages..
|
// Error messages..
|
||||||
void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, const std::string &varName, const bool result);
|
void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &strFunctionName, const std::string &varName, const bool result);
|
||||||
|
|
|
@ -678,3 +678,32 @@ const Library::Container* Library::detectContainer(const Token* typeStart) const
|
||||||
}
|
}
|
||||||
return nullptr;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -127,9 +127,7 @@ public:
|
||||||
std::set<std::string> useretval;
|
std::set<std::string> useretval;
|
||||||
|
|
||||||
// returns true if ftok is not a library function
|
// returns true if ftok is not a library function
|
||||||
static bool isNotLibraryFunction(const Token *ftok) {
|
bool isNotLibraryFunction(const Token *ftok) const;
|
||||||
return ftok->astParent() ? ftok->astParent()->str() != "(" : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isnoreturn(const Token *ftok) const {
|
bool isnoreturn(const Token *ftok) const {
|
||||||
if (ftok->function() && ftok->function()->isAttributeNoreturn())
|
if (ftok->function() && ftok->function()->isAttributeNoreturn())
|
||||||
|
|
|
@ -31,6 +31,7 @@ private:
|
||||||
void run() {
|
void run() {
|
||||||
TEST_CASE(empty);
|
TEST_CASE(empty);
|
||||||
TEST_CASE(function);
|
TEST_CASE(function);
|
||||||
|
TEST_CASE(function_match);
|
||||||
TEST_CASE(function_arg);
|
TEST_CASE(function_arg);
|
||||||
TEST_CASE(function_arg_any);
|
TEST_CASE(function_arg_any);
|
||||||
TEST_CASE(function_arg_valid);
|
TEST_CASE(function_arg_valid);
|
||||||
|
@ -78,6 +79,26 @@ private:
|
||||||
ASSERT(library.isnotnoreturn(tokenList.front()));
|
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 {
|
void function_arg() const {
|
||||||
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
||||||
"<def>\n"
|
"<def>\n"
|
||||||
|
@ -134,7 +155,7 @@ private:
|
||||||
library.load(doc);
|
library.load(doc);
|
||||||
|
|
||||||
TokenList tokenList(nullptr);
|
TokenList tokenList(nullptr);
|
||||||
std::istringstream istr("foo();");
|
std::istringstream istr("foo(a,b,c,d,e);");
|
||||||
tokenList.createTokens(istr);
|
tokenList.createTokens(istr);
|
||||||
tokenList.front()->next()->astOperand1(tokenList.front());
|
tokenList.front()->next()->astOperand1(tokenList.front());
|
||||||
|
|
||||||
|
@ -177,6 +198,7 @@ private:
|
||||||
" <function name=\"foo\">\n"
|
" <function name=\"foo\">\n"
|
||||||
" <arg nr=\"1\"><minsize type=\"strlen\" arg=\"2\"/></arg>\n"
|
" <arg nr=\"1\"><minsize type=\"strlen\" arg=\"2\"/></arg>\n"
|
||||||
" <arg nr=\"2\"><minsize type=\"argvalue\" arg=\"3\"/></arg>\n"
|
" <arg nr=\"2\"><minsize type=\"argvalue\" arg=\"3\"/></arg>\n"
|
||||||
|
" <arg nr=\"3\"/>\n"
|
||||||
" </function>\n"
|
" </function>\n"
|
||||||
"</def>";
|
"</def>";
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
|
@ -186,7 +208,7 @@ private:
|
||||||
library.load(doc);
|
library.load(doc);
|
||||||
|
|
||||||
TokenList tokenList(nullptr);
|
TokenList tokenList(nullptr);
|
||||||
std::istringstream istr("foo();");
|
std::istringstream istr("foo(a,b,c);");
|
||||||
tokenList.createTokens(istr);
|
tokenList.createTokens(istr);
|
||||||
tokenList.front()->next()->astOperand1(tokenList.front());
|
tokenList.front()->next()->astOperand1(tokenList.front());
|
||||||
|
|
||||||
|
|
|
@ -2865,6 +2865,7 @@ private:
|
||||||
|
|
||||||
Settings settings;
|
Settings settings;
|
||||||
settings.library.setnoreturn("exit", true);
|
settings.library.setnoreturn("exit", true);
|
||||||
|
settings.library.argumentChecks["exit"][1] = Library::ArgumentChecks();
|
||||||
check("void foo() {\n"
|
check("void foo() {\n"
|
||||||
" exit(0);\n"
|
" exit(0);\n"
|
||||||
" break;\n"
|
" break;\n"
|
||||||
|
|
Loading…
Reference in New Issue