From d01e3fa17aa4a4f6e9989fc2d85b32141f723372 Mon Sep 17 00:00:00 2001 From: Simon Martin Date: Sun, 28 Jul 2013 15:00:28 +0200 Subject: [PATCH] Handle parameter positions format strings (POSIX extensions). --- lib/checkio.cpp | 40 ++++++++++++++++++++++++++++++++++++++-- lib/checkio.h | 2 ++ test/testio.cpp | 25 +++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 19117d808..e01ac44c8 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -502,6 +502,7 @@ void CheckIO::checkWrongPrintfScanfArguments() unsigned int numFormat = 0; bool percent = false; const Token* argListTok2 = argListTok; + std::set parameterPositionsUsed; for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) { if (*i == '%') { percent = !percent; @@ -523,6 +524,7 @@ void CheckIO::checkWrongPrintfScanfArguments() bool _continue = false; std::string width; + unsigned int parameterPosition = 0; bool hasParameterPosition = false; while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) { if (*i == '*') { if (scan) @@ -532,8 +534,13 @@ void CheckIO::checkWrongPrintfScanfArguments() if (argListTok) argListTok = argListTok->nextArgument(); } - } else if (std::isdigit(*i)) + } else if (std::isdigit(*i)) { width += *i; + } else if (*i == '$') { + parameterPosition = static_cast(std::atoi(width.c_str())); + hasParameterPosition = true; + width.clear(); + } ++i; } if (i == formatString.end()) @@ -542,7 +549,15 @@ void CheckIO::checkWrongPrintfScanfArguments() continue; if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. - numFormat++; + ++numFormat; + + // Handle parameter positions (POSIX extension) - Ticket #4900 + if(hasParameterPosition) { + if(parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end()) + parameterPositionsUsed.insert(parameterPosition); + else // Parameter already referenced, hence don't consider it a new format + --numFormat; + } // Perform type checks if (argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern. @@ -718,6 +733,12 @@ void CheckIO::checkWrongPrintfScanfArguments() numFunction++; argListTok2 = argListTok2->nextArgument(); // Find next argument } + + // Check that all parameter positions reference an actual parameter + for(std::set::const_iterator it = parameterPositionsUsed.begin() ; it != parameterPositionsUsed.end() ; ++it) { + if((*it == 0) || (*it > numFormat)) + wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), *it, numFormat); + } // Mismatching number of parameters => warning if (numFormat != numFunction) @@ -747,6 +768,21 @@ void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str()); } +void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, + unsigned int index, unsigned int numFunction) +{ + if (!_settings->isEnabled("style")) + return; + std::ostringstream errmsg; + errmsg << functionName << ": "; + if(index == 0) { + errmsg << "parameter positions start at 1, not 0"; + } else { + errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given"; + } + reportError(tok, Severity::warning, "wrongPrintfScanfParameterError", errmsg.str()); +} + void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat) { std::ostringstream errmsg; diff --git a/lib/checkio.h b/lib/checkio.h index 281315930..35477e864 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -82,6 +82,8 @@ private: const std::string &function, unsigned int numFormat, unsigned int numFunction); + void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, + unsigned int index, unsigned int numFunction); void invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat); void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat); diff --git a/test/testio.cpp b/test/testio.cpp index 254c31958..e8448bec9 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -46,6 +46,7 @@ private: TEST_CASE(testScanfArgument); TEST_CASE(testPrintfArgument); + TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900 TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 @@ -780,6 +781,30 @@ private: } + void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings + check("void foo() {" + " int bar;" + " printf(\"%1$d\", 1);" + " printf(\"%1$d, %d, %1$d\", 1, 2);" + " scanf(\"%1$d\", &bar);" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void foo() {\n" + " int bar;\n" + " printf(\"%1$d\");\n" + " printf(\"%1$d, %d, %4$d\", 1, 2, 3);\n" + " scanf(\"%2$d\", &bar);\n" + " printf(\"%0$f\", 0.0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) printf format string has 1 parameters but only 0 are given.\n" + "[test.cpp:4]: (warning) printf: referencing parameter 4 while 3 arguments given\n" + "[test.cpp:5]: (warning) scanf: referencing parameter 2 while 1 arguments given\n" + "[test.cpp:6]: (warning) printf: parameter positions start at 1, not 0\n" + "", errout.str()); + } + + void testMicrosoftPrintfArgument() { check("void foo() {\n" " size_t s;\n"