diff --git a/lib/check64bit.cpp b/lib/check64bit.cpp index 09beac543..01778b322 100644 --- a/lib/check64bit.cpp +++ b/lib/check64bit.cpp @@ -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())) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index b36759799..d04550cfd 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -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."; diff --git a/lib/checkio.h b/lib/checkio.h index 00297dae2..281315930 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -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); } diff --git a/test/test64bit.cpp b/test/test64bit.cpp index b2cdb612d..a6a161f19 100644 --- a/test/test64bit.cpp +++ b/test/test64bit.cpp @@ -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()); } }; diff --git a/test/testio.cpp b/test/testio.cpp index 0aa286210..a5cb928d8 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -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)