diff --git a/cfg/posix.cfg b/cfg/posix.cfg index 29d83f563..e45eb0fab 100644 --- a/cfg/posix.cfg +++ b/cfg/posix.cfg @@ -322,6 +322,8 @@ + + @@ -350,6 +352,8 @@ + + diff --git a/cfg/std.cfg b/cfg/std.cfg index 199e4f8c1..929e03ee5 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -5520,5 +5520,6 @@ + diff --git a/cfg/test/std.c b/cfg/test/std.c new file mode 100644 index 000000000..a54483274 --- /dev/null +++ b/cfg/test/std.c @@ -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 + +void strcpy_ok(char *a, char *b) { + strcpy(a,b); +} + +void strcpy_bad() { + char a[10]; + // cppcheck-suppress bufferAccessOutOfBounds + strcpy(a, "hello world!"); +} + + diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 7df3690de..3f86553da 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -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() + "()"); + } + } +} + + diff --git a/lib/checkother.h b/lib/checkother.h index 9665f4227..69b25e4ae 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -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); diff --git a/lib/library.cpp b/lib/library.cpp index d7308f30a..67b5e8311 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -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 >::const_iterator it = argumentChecks.find(ftok->str()); + if (it == argumentChecks.end()) + return (callargs != 0); + int args = 0; + for (std::map::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; +} diff --git a/lib/library.h b/lib/library.h index 3948eff31..78aa64079 100644 --- a/lib/library.h +++ b/lib/library.h @@ -127,9 +127,7 @@ public: std::set 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()) diff --git a/test/testlibrary.cpp b/test/testlibrary.cpp index 550a0fcfe..3774c8b48 100644 --- a/test/testlibrary.cpp +++ b/test/testlibrary.cpp @@ -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[] = "\n" + "\n" + " \n" + " " + " \n" + ""; + 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[] = "\n" "\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: " \n" " \n" " \n" + " \n" " \n" ""; 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()); diff --git a/test/testother.cpp b/test/testother.cpp index 8e55a4bed..d3622ce59 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -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"