Fixed #4902 (printf and scanf format checks don't support microsoft %I, %I32 and %I64 extensions)
This commit is contained in:
parent
2ab8489e1b
commit
f11e54aa42
|
@ -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()))
|
||||
|
|
207
lib/checkio.cpp
207
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.";
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue