diff --git a/lib/checkio.cpp b/lib/checkio.cpp index a0a547966..d005be876 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -439,7 +439,26 @@ void CheckIO::checkWrongPrintfScanfArguments() const Token* argListTok = 0; // Points to first va_list argument std::string formatString; - if (Token::Match(tok, "printf|scanf|wprintf|wscanf ( %str%")) { + if (Token::Match(tok->next(), "( %any%")) { + const Token *arg = tok->tokAt(2); + int argnr = 1; + while (arg) { + if (Token::Match(arg, "%str% [,)]") && _settings->library.isargformatstr(tok->str(),argnr)) { + formatString = arg->str(); + if (arg->strAt(1) == ",") + argListTok = arg->tokAt(2); + else + argListTok = 0; + break; + } + + arg = arg->nextArgument(); + argnr++; + } + } + + if (!formatString.empty()) { /* formatstring found in library */ } + else if (Token::Match(tok, "printf|scanf|wprintf|wscanf ( %str%")) { formatString = tok->strAt(2); if (tok->strAt(3) == ",") { argListTok = tok->tokAt(4); diff --git a/lib/library.cpp b/lib/library.cpp index 904fc9100..9fa038af4 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -121,16 +121,24 @@ bool Library::load(const char exename[], const char path[]) const int nr = atoi(functionnode->Attribute("nr")); bool notnull = false; bool notuninit = false; + bool formatstr = false; + bool strz = false; for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { if (strcmp(argnode->Name(), "not-null") == 0) notnull = true; else if (strcmp(argnode->Name(), "not-uninit") == 0) notuninit = true; + else if (strcmp(argnode->Name(), "formatstr") == 0) + notuninit = true; + else if (strcmp(argnode->Name(), "strz") == 0) + notuninit = true; else return false; } argumentChecks[name][nr].notnull = notnull; argumentChecks[name][nr].notuninit = notuninit; + argumentChecks[name][nr].formatstr = formatstr; + argumentChecks[name][nr].strz = strz; } else return false; } diff --git a/lib/library.h b/lib/library.h index 146aa51d2..68ed7ab68 100644 --- a/lib/library.h +++ b/lib/library.h @@ -81,8 +81,14 @@ public: } struct ArgumentChecks { + ArgumentChecks() { + notnull = notuninit = formatstr = strz = false; + } + bool notnull; bool notuninit; + bool formatstr; + bool strz; }; // function name, argument nr => argument data @@ -98,6 +104,16 @@ public: return arg && arg->notuninit; } + bool isargformatstr(const std::string &functionName, int argnr) const { + const ArgumentChecks *arg = getarg(functionName,argnr); + return arg && arg->formatstr; + } + + bool isargstrz(const std::string &functionName, int argnr) const { + const ArgumentChecks *arg = getarg(functionName,argnr); + return arg && arg->strz; + } + std::set returnuninitdata; private: diff --git a/test/testio.cpp b/test/testio.cpp index 19939f383..254c31958 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -48,9 +48,11 @@ private: TEST_CASE(testPrintfArgument); TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 + + TEST_CASE(testlibrarycfg); // library configuration } - void check(const char code[], bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified) { + void check(const char code[], bool inconclusive = false, bool portability = false, Settings::PlatformType platform = Settings::Unspecified, Library *lib = NULL) { // Clear the error buffer.. errout.str(""); @@ -62,6 +64,9 @@ private: settings.inconclusive = inconclusive; settings.platform(platform); + if (lib) + settings.library = *lib; + // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); @@ -797,6 +802,22 @@ private: "[test.cpp:12]: (warning) %I64u in format string (no. 2) requires an unsigned integer given in the argument list.\n" "[test.cpp:13]: (warning) %I64d in format string (no. 1) requires a signed integer given in the argument list.\n", errout.str()); } + + void testlibrarycfg() { + const char code[] = "void f() {\n" + " format(\"%s\");\n" + "}"; + + // no error if configuration for 'format' is not provided + check(code); + ASSERT_EQUALS("", errout.str()); + + // error if configuration for 'format' is provided + Library lib; + lib.argumentChecks["format"][1].formatstr = true; + check(code, false, false, Settings::Unspecified, &lib); + ASSERT_EQUALS("[test.cpp:2]: (error) format format string has 1 parameters but only 0 are given.\n", errout.str()); + } }; REGISTER_TEST(TestIO) diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 7308a5ae8..d5220d659 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -2116,7 +2116,7 @@ private: // nothing bad.. { Library library; - Library::ArgumentChecks arg = {false, false}; + Library::ArgumentChecks arg; library.argumentChecks["x"][1] = arg; library.argumentChecks["x"][2] = arg; @@ -2130,7 +2130,7 @@ private: // for 1st parameter null pointer is not ok.. { Library library; - struct Library::ArgumentChecks arg = {false, false}; + struct Library::ArgumentChecks arg; library.argumentChecks["x"][1] = arg; library.argumentChecks["x"][2] = arg; library.argumentChecks["x"][1].notnull = true; @@ -2146,7 +2146,7 @@ private: // for 2nd parameter uninit data is not ok.. { Library library; - Library::ArgumentChecks arg = {false, false}; + Library::ArgumentChecks arg; library.argumentChecks["x"][1] = arg; library.argumentChecks["x"][2] = arg; library.argumentChecks["x"][2].notuninit = true;