Fixed #4902 (printf and scanf format checks don't support microsoft %I, %I32 and %I64 extensions)

This commit is contained in:
Robert Reif 2013-07-21 08:35:01 +02:00 committed by Daniel Marjamäki
parent 2ab8489e1b
commit f11e54aa42
5 changed files with 167 additions and 92 deletions

View File

@ -65,7 +65,8 @@ void Check64BitPortability::pointerassignment()
continue; continue;
for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) { for (const Token* tok = scope->classStart->next(); tok != scope->classEnd; tok = tok->next()) {
if (Token::Match(tok, "return %var%|%num% [;+]") && !Token::simpleMatch(tok, "return 0 ;")) { if (Token::Match(tok, "return %var%|%num% [;+]") && !Token::simpleMatch(tok, "return 0 ;") &&
(tok->scope() == scope)) { // #4919 - check if the 'return' doesn't belong to a lambda inside this 'scope'
enum { NO, INT, PTR, PTRDIFF } type = NO; enum { NO, INT, PTR, PTRDIFF } type = NO;
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
if ((type == NO || type == INT) && Token::Match(tok2, "%var% [+;]") && isaddr(tok2->variable())) if ((type == NO || type == INT) && Token::Match(tok2, "%var% [+;]") && isaddr(tok2->variable()))

View File

@ -543,54 +543,78 @@ void CheckIO::checkWrongPrintfScanfArguments()
} }
} }
} else if (!scan && warning) { } else if (!scan && warning) {
std::string specifier;
bool done = false;
while (!done) {
switch (*i) { switch (*i) {
case 's': case 's':
if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray())) if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
invalidPrintfArgTypeError_s(tok, numFormat); invalidPrintfArgTypeError_s(tok, numFormat);
done = true;
break; break;
case 'n': case 'n':
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString) if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString)
invalidPrintfArgTypeError_n(tok, numFormat); invalidPrintfArgTypeError_n(tok, numFormat);
done = true;
break; break;
case 'c': case 'c':
case 'x': case 'x':
case 'X': case 'X':
case 'o': case 'o':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) {
invalidPrintfArgTypeError_int(tok, numFormat, *i); specifier += *i;
else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_int(tok, numFormat, specifier);
invalidPrintfArgTypeError_int(tok, numFormat, *i); } else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_int(tok, numFormat, specifier);
}
done = true;
break; break;
case 'd': case 'd':
case 'i': case 'i':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") {
invalidPrintfArgTypeError_sint(tok, numFormat, *i); specifier += *i;
} else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_sint(tok, numFormat, specifier);
invalidPrintfArgTypeError_sint(tok, numFormat, *i); }
} else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_sint(tok, numFormat, specifier);
}
done = true;
break; break;
case 'u': case 'u':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) { if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") {
invalidPrintfArgTypeError_uint(tok, numFormat, *i); specifier += *i;
} else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_uint(tok, numFormat, specifier);
invalidPrintfArgTypeError_uint(tok, numFormat, *i); }
} else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_uint(tok, numFormat, specifier);
}
done = true;
break; break;
case 'p': case 'p':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
invalidPrintfArgTypeError_p(tok, numFormat); invalidPrintfArgTypeError_p(tok, numFormat);
else if (argListTok->type() == Token::eString) else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_p(tok, numFormat); invalidPrintfArgTypeError_p(tok, numFormat);
done = true;
break; break;
case 'e': case 'e':
case 'E': case 'E':
case 'f': case 'f':
case 'g': case 'g':
case 'G': case 'G':
if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) {
invalidPrintfArgTypeError_float(tok, numFormat, *i); specifier += *i;
else if (argListTok->type() == Token::eString) invalidPrintfArgTypeError_float(tok, numFormat, specifier);
invalidPrintfArgTypeError_float(tok, numFormat, *i); } else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_float(tok, numFormat, specifier);
}
done = true;
break; break;
case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int)
case 'l': // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int) case 'l': // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int)
@ -609,6 +633,22 @@ void CheckIO::checkWrongPrintfScanfArguments()
invalidLengthModifierError(tok, numFormat, modifier); invalidLengthModifierError(tok, numFormat, modifier);
} }
} }
done = true;
break;
case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64
if (i != formatString.end()) {
if (isalpha(*(i+1))) {
specifier = *i++;
} else if (*(i+1) == '3' && *(i+2) == '2' && isalpha(*(i+3))) {
specifier = *i++;
specifier += *i++;
specifier += *i++;
} else if (*(i+1) == '6' && *(i+2) == '4' && isalpha(*(i+3))) {
specifier = *i++;
specifier += *i++;
specifier += *i++;
}
}
break; break;
case 'j': // intmax_t or uintmax_t case 'j': // intmax_t or uintmax_t
case 'z': // size_t case 'z': // size_t
@ -620,12 +660,15 @@ void CheckIO::checkWrongPrintfScanfArguments()
modifier += *i; modifier += *i;
invalidLengthModifierError(tok, numFormat, modifier); invalidLengthModifierError(tok, numFormat, modifier);
} }
done = true;
break; break;
default: default:
done = true;
break; break;
} }
} }
} }
}
if (argListTok) if (argListTok)
argListTok = argListTok->nextArgument(); // Find next argument argListTok = argListTok->nextArgument(); // Find next argument
@ -692,31 +735,31 @@ void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, unsigned int numForm
errmsg << "%p in format string (no. " << numFormat << ") requires an address given in the argument list."; errmsg << "%p in format string (no. " << numFormat << ") requires an address given in the argument list.";
reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str()); reportError(tok, Severity::warning, "invalidPrintfArgType_p", errmsg.str());
} }
void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c) void CheckIO::invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier)
{ {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an integer given in the argument list."; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires an integer given in the argument list.";
reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str()); reportError(tok, Severity::warning, "invalidPrintfArgType_int", errmsg.str());
} }
void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c) void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier)
{ {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires an unsigned integer given in the argument list."; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires an unsigned integer given in the argument list.";
reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str()); reportError(tok, Severity::warning, "invalidPrintfArgType_uint", errmsg.str());
} }
void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c) void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier)
{ {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a signed integer given in the argument list."; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a signed integer given in the argument list.";
reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str()); reportError(tok, Severity::warning, "invalidPrintfArgType_sint", errmsg.str());
} }
void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c) void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier)
{ {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "%" << c << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list."; errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a floating point number given in the argument list.";
reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str()); reportError(tok, Severity::warning, "invalidPrintfArgType_float", errmsg.str());
} }
void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier) void CheckIO::invalidLengthModifierError(const Token* tok, unsigned int numFormat, const std::string& modifier)
{ {
std::ostringstream errmsg; std::ostringstream errmsg;
errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier."; errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier.";

View File

@ -86,11 +86,11 @@ private:
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);
void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat); void invalidPrintfArgTypeError_p(const Token* tok, unsigned int numFormat);
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, char c); void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, char c); void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c); void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c); void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier); void invalidLengthModifierError(const Token* tok, unsigned int numFormat,const std::string& modifier);
void invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var); void invalidScanfFormatWidthError(const Token* tok, unsigned int numFormat, int width, const Variable *var);
void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
@ -108,10 +108,10 @@ private:
c.invalidPrintfArgTypeError_s(0, 1); c.invalidPrintfArgTypeError_s(0, 1);
c.invalidPrintfArgTypeError_n(0, 1); c.invalidPrintfArgTypeError_n(0, 1);
c.invalidPrintfArgTypeError_p(0, 1); c.invalidPrintfArgTypeError_p(0, 1);
c.invalidPrintfArgTypeError_int(0, 1, 'X'); c.invalidPrintfArgTypeError_int(0, 1, "X");
c.invalidPrintfArgTypeError_uint(0, 1, 'u'); c.invalidPrintfArgTypeError_uint(0, 1, "u");
c.invalidPrintfArgTypeError_sint(0, 1, 'i'); c.invalidPrintfArgTypeError_sint(0, 1, "i");
c.invalidPrintfArgTypeError_float(0, 1, 'f'); c.invalidPrintfArgTypeError_float(0, 1, "f");
c.invalidScanfFormatWidthError(0, 10, 5, NULL); c.invalidScanfFormatWidthError(0, 10, 5, NULL);
} }

View File

@ -193,6 +193,12 @@ private:
" return 1 + p->i;\n" " return 1 + p->i;\n"
"}"); "}");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// #4919
check("void* foo(int i) {\n"
" [i] { return i; };\n"
"}");
ASSERT_EQUALS("", errout.str());
} }
}; };

View File

@ -46,6 +46,8 @@ private:
TEST_CASE(testScanfArgument); TEST_CASE(testScanfArgument);
TEST_CASE(testPrintfArgument); TEST_CASE(testPrintfArgument);
TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902
} }
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) {
@ -753,6 +755,29 @@ private:
"[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str()); "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str());
} }
void testMicrosoftPrintfArgument() {
check("void foo() {\n"
" size_t s;\n"
" ptrdiff_t p;\n"
" __int32 i32;\n"
" unsigned __int32 u32;\n"
" __int64 i64;\n"
" unsigned __int64 u64;\n"
" printf(\"%Id %Iu %Ix\", s, s, s);\n"
" printf(\"%Id %Iu %Ix\", p, p, p);\n"
" printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n"
" printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n"
" printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n"
" printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n"
"}");
ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires a signed integer given in the argument list.\n"
"[test.cpp:9]: (warning) %Iu in format string (no. 2) requires an unsigned integer given in the argument list.\n"
"[test.cpp:10]: (warning) %I32u in format string (no. 2) requires an unsigned integer given in the argument list.\n"
"[test.cpp:11]: (warning) %I32d in format string (no. 1) requires a signed integer given in the argument list.\n"
"[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());
}
}; };
REGISTER_TEST(TestIO) REGISTER_TEST(TestIO)