Merge pull request #154 from simartin/ticket_4900

Fixed #4900 (error wrongPrintfScanfArgNum doesn't support positional parameters)
This commit is contained in:
Daniel Marjamäki 2013-07-28 07:10:15 -07:00
commit 1409461e4f
3 changed files with 65 additions and 2 deletions

View File

@ -502,6 +502,7 @@ void CheckIO::checkWrongPrintfScanfArguments()
unsigned int numFormat = 0; unsigned int numFormat = 0;
bool percent = false; bool percent = false;
const Token* argListTok2 = argListTok; const Token* argListTok2 = argListTok;
std::set<unsigned int> parameterPositionsUsed;
for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) { for (std::string::iterator i = formatString.begin(); i != formatString.end(); ++i) {
if (*i == '%') { if (*i == '%') {
percent = !percent; percent = !percent;
@ -523,6 +524,7 @@ void CheckIO::checkWrongPrintfScanfArguments()
bool _continue = false; bool _continue = false;
std::string width; std::string width;
unsigned int parameterPosition = 0; bool hasParameterPosition = false;
while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) { while (i != formatString.end() && *i != ']' && !std::isalpha(*i)) {
if (*i == '*') { if (*i == '*') {
if (scan) if (scan)
@ -532,8 +534,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
if (argListTok) if (argListTok)
argListTok = argListTok->nextArgument(); argListTok = argListTok->nextArgument();
} }
} else if (std::isdigit(*i)) } else if (std::isdigit(*i)) {
width += *i; width += *i;
} else if (*i == '$') {
parameterPosition = static_cast<unsigned int>(std::atoi(width.c_str()));
hasParameterPosition = true;
width.clear();
}
++i; ++i;
} }
if (i == formatString.end()) if (i == formatString.end())
@ -542,7 +549,15 @@ void CheckIO::checkWrongPrintfScanfArguments()
continue; continue;
if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. 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 // Perform type checks
if (argListTok && Token::Match(argListTok->next(), "[,)]")) { // We can currently only check the type of arguments matching this simple pattern. 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++; numFunction++;
argListTok2 = argListTok2->nextArgument(); // Find next argument argListTok2 = argListTok2->nextArgument(); // Find next argument
} }
// Check that all parameter positions reference an actual parameter
for(std::set<unsigned int>::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 // Mismatching number of parameters => warning
if (numFormat != numFunction) if (numFormat != numFunction)
@ -747,6 +768,21 @@ void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok,
reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str()); 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) void CheckIO::invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat)
{ {
std::ostringstream errmsg; std::ostringstream errmsg;

View File

@ -82,6 +82,8 @@ private:
const std::string &function, const std::string &function,
unsigned int numFormat, unsigned int numFormat,
unsigned int numFunction); 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 invalidScanfArgTypeError(const Token* tok, const std::string &functionName, unsigned int numFormat);
void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_s(const Token* tok, unsigned int numFormat);
void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_n(const Token* tok, unsigned int numFormat);

View File

@ -46,6 +46,7 @@ private:
TEST_CASE(testScanfArgument); TEST_CASE(testScanfArgument);
TEST_CASE(testPrintfArgument); TEST_CASE(testPrintfArgument);
TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900
TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 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() { void testMicrosoftPrintfArgument() {
check("void foo() {\n" check("void foo() {\n"
" size_t s;\n" " size_t s;\n"