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;
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;
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
if ((type == NO || type == INT) && Token::Match(tok2, "%var% [+;]") && isaddr(tok2->variable()))

View File

@ -543,86 +543,129 @@ void CheckIO::checkWrongPrintfScanfArguments()
}
}
} else if (!scan && warning) {
switch (*i) {
case 's':
if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
invalidPrintfArgTypeError_s(tok, numFormat);
break;
case 'n':
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString)
invalidPrintfArgTypeError_n(tok, numFormat);
break;
case 'c':
case 'x':
case 'X':
case 'o':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
invalidPrintfArgTypeError_int(tok, numFormat, *i);
else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_int(tok, numFormat, *i);
break;
case 'd':
case 'i':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char")
invalidPrintfArgTypeError_sint(tok, numFormat, *i);
} else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_sint(tok, numFormat, *i);
break;
case 'u':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool")
invalidPrintfArgTypeError_uint(tok, numFormat, *i);
} else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_uint(tok, numFormat, *i);
break;
case 'p':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
invalidPrintfArgTypeError_p(tok, numFormat);
else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_p(tok, numFormat);
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray()))
invalidPrintfArgTypeError_float(tok, numFormat, *i);
else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_float(tok, numFormat, *i);
break;
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)
// If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
if (i != formatString.end() && *(i+1) == *i) {
if (i+1 != formatString.end() && !isalpha(*(i+2))) {
std::string modifier;
modifier += *i;
modifier += *(i+1);
invalidLengthModifierError(tok, numFormat, modifier);
std::string specifier;
bool done = false;
while (!done) {
switch (*i) {
case 's':
if (variableInfo && argListTok->type() != Token::eString && isKnownType(variableInfo, varTypeTok) && (!variableInfo->isPointer() && !variableInfo->isArray()))
invalidPrintfArgTypeError_s(tok, numFormat);
done = true;
break;
case 'n':
if ((varTypeTok && isKnownType(variableInfo, varTypeTok) && ((!variableInfo->isPointer() && !variableInfo->isArray()) || varTypeTok->strAt(-1) == "const")) || argListTok->type() == Token::eString)
invalidPrintfArgTypeError_n(tok, numFormat);
done = true;
break;
case 'c':
case 'x':
case 'X':
case 'o':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "bool|short|long|int|char|size_t") && !variableInfo->isPointer() && !variableInfo->isArray()) {
specifier += *i;
invalidPrintfArgTypeError_int(tok, numFormat, specifier);
} else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_int(tok, numFormat, specifier);
}
} else {
done = true;
break;
case 'd':
case 'i':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
if ((varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "bool|short|long|int")) && varTypeTok->str() != "char") {
specifier += *i;
invalidPrintfArgTypeError_sint(tok, numFormat, specifier);
}
} else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_sint(tok, numFormat, specifier);
}
done = true;
break;
case 'u':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !variableInfo->isPointer() && !variableInfo->isArray()) {
if ((!varTypeTok->isUnsigned() || !Token::Match(varTypeTok, "char|short|long|int|size_t")) && varTypeTok->str() != "bool") {
specifier += *i;
invalidPrintfArgTypeError_uint(tok, numFormat, specifier);
}
} else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_uint(tok, numFormat, specifier);
}
done = true;
break;
case 'p':
if (varTypeTok && isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "short|long|int|size_t") && !variableInfo->isPointer() && !variableInfo->isArray())
invalidPrintfArgTypeError_p(tok, numFormat);
else if (argListTok->type() == Token::eString)
invalidPrintfArgTypeError_p(tok, numFormat);
done = true;
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (varTypeTok && ((isKnownType(variableInfo, varTypeTok) && !Token::Match(varTypeTok, "float|double")) || variableInfo->isPointer() || variableInfo->isArray())) {
specifier += *i;
invalidPrintfArgTypeError_float(tok, numFormat, specifier);
} else if (argListTok->type() == Token::eString) {
specifier += *i;
invalidPrintfArgTypeError_float(tok, numFormat, specifier);
}
done = true;
break;
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)
// If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character
if (i != formatString.end() && *(i+1) == *i) {
if (i+1 != formatString.end() && !isalpha(*(i+2))) {
std::string modifier;
modifier += *i;
modifier += *(i+1);
invalidLengthModifierError(tok, numFormat, modifier);
}
} else {
if (i != formatString.end() && !isalpha(*(i+1))) {
std::string modifier;
modifier += *i;
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;
case 'j': // intmax_t or uintmax_t
case 'z': // size_t
case 't': // ptrdiff_t
case 'L': // long double
// Expect an alphabetical character after these specifiers
if (i != formatString.end() && !isalpha(*(i+1))) {
std::string modifier;
modifier += *i;
invalidLengthModifierError(tok, numFormat, modifier);
}
done = true;
break;
default:
done = true;
break;
}
break;
case 'j': // intmax_t or uintmax_t
case 'z': // size_t
case 't': // ptrdiff_t
case 'L': // long double
// Expect an alphabetical character after these specifiers
if (i != formatString.end() && !isalpha(*(i+1))) {
std::string modifier;
modifier += *i;
invalidLengthModifierError(tok, numFormat, modifier);
}
break;
default:
break;
}
}
}
@ -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.";
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;
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());
}
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;
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());
}
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;
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());
}
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;
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());
}
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;
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_n(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_uint(const Token* tok, unsigned int numFormat, char c);
void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, char c);
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, char c);
void invalidLengthModifierError(const Token* tok, unsigned int numFormat, std::string& modifier);
void invalidPrintfArgTypeError_int(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidPrintfArgTypeError_uint(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidPrintfArgTypeError_sint(const Token* tok, unsigned int numFormat, const std::string& specifier);
void invalidPrintfArgTypeError_float(const Token* tok, unsigned int numFormat, const std::string& specifier);
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 getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const {
@ -108,10 +108,10 @@ private:
c.invalidPrintfArgTypeError_s(0, 1);
c.invalidPrintfArgTypeError_n(0, 1);
c.invalidPrintfArgTypeError_p(0, 1);
c.invalidPrintfArgTypeError_int(0, 1, 'X');
c.invalidPrintfArgTypeError_uint(0, 1, 'u');
c.invalidPrintfArgTypeError_sint(0, 1, 'i');
c.invalidPrintfArgTypeError_float(0, 1, 'f');
c.invalidPrintfArgTypeError_int(0, 1, "X");
c.invalidPrintfArgTypeError_uint(0, 1, "u");
c.invalidPrintfArgTypeError_sint(0, 1, "i");
c.invalidPrintfArgTypeError_float(0, 1, "f");
c.invalidScanfFormatWidthError(0, 10, 5, NULL);
}

View File

@ -193,6 +193,12 @@ private:
" return 1 + p->i;\n"
"}");
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(testPrintfArgument);
TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902
}
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: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)