CheckIO: This patch adds support for fprintf_s, fscanf_s and %I. Ticket: #5051

This commit is contained in:
Robert Reif 2013-10-03 06:37:40 +02:00 committed by Daniel Marjamäki
parent 022e7a0f0f
commit 59de30823e
3 changed files with 218 additions and 11 deletions

View File

@ -454,7 +454,7 @@ void CheckIO::checkWrongPrintfScanfArguments()
}
} else if (Token::Match(tok, "sprintf|fprintf|sscanf|fscanf|swscanf|fwprintf|fwscanf ( %any%") ||
(Token::simpleMatch(tok, "swprintf (") && Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) ||
(windows && Token::Match(tok, "sscanf_s|swscanf_s ( %any%"))) {
(windows && Token::Match(tok, "sscanf_s|swscanf_s|fscanf_s|fwscanf_s|fprintf_s|fwprintf_s ( %any%"))) {
const Token* formatStringTok = tok->tokAt(2)->nextArgument(); // Find second parameter (format string)
if (Token::Match(formatStringTok, "%str% [,)]")) {
argListTok = formatStringTok->nextArgument(); // Find third parameter (first argument of va_args)
@ -492,7 +492,7 @@ void CheckIO::checkWrongPrintfScanfArguments()
}
// Count format string parameters..
bool scanf_s = windows ? Token::Match(tok, "scanf_s|wscanf_s|sscanf_s|swscanf_s") : false;
bool scanf_s = windows ? Token::Match(tok, "scanf_s|wscanf_s|sscanf_s|swscanf_s|fscanf_s|fwscanf_s") : false;
bool scan = Token::Match(tok, "sscanf|fscanf|scanf|swscanf|fwscanf|wscanf") || scanf_s;
unsigned int numFormat = 0;
unsigned int numSecure = 0;
@ -661,7 +661,14 @@ void CheckIO::checkWrongPrintfScanfArguments()
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
break;
case 'I':
if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
if (specifier.find("I64") != std::string::npos) {
if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
} else if (specifier.find("I32") != std::string::npos) {
if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
} else if (argInfo.typeToken->originalName() != "ptrdiff_t" &&
argInfo.typeToken->originalName() != "size_t")
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
break;
case 'j':
@ -726,7 +733,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
break;
case 'I':
if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
if (specifier.find("I64") != std::string::npos) {
if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
} else if (specifier.find("I32") != std::string::npos) {
if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
} else if (argInfo.typeToken->originalName() != "ptrdiff_t")
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false);
break;
case 'j':
@ -787,7 +800,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
break;
case 'I':
if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
if (specifier.find("I64") != std::string::npos) {
if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong())
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
} else if (specifier.find("I32") != std::string::npos) {
if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong())
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
} else if (argInfo.typeToken->originalName() != "size_t")
invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true);
break;
case 'j':
@ -853,8 +872,10 @@ void CheckIO::checkWrongPrintfScanfArguments()
done = true;
break;
case 'I':
if (i+1 != formatString.end() && *(i+1) == '6' &&
i+2 != formatString.end() && *(i+2) == '4') {
if ((i+1 != formatString.end() && *(i+1) == '6' &&
i+2 != formatString.end() && *(i+2) == '4') ||
(i+1 != formatString.end() && *(i+1) == '3' &&
i+2 != formatString.end() && *(i+2) == '2')) {
specifier += *i++;
specifier += *i++;
if ((i+1) != formatString.end() && !isalpha(*(i+1))) {
@ -865,9 +886,13 @@ void CheckIO::checkWrongPrintfScanfArguments()
specifier += *i++;
}
} else {
specifier += *i;
invalidLengthModifierError(tok, numFormat, specifier);
done = true;
if ((i+1) != formatString.end() && !isalpha(*(i+1))) {
specifier += *i;
invalidLengthModifierError(tok, numFormat, specifier);
done = true;
} else {
specifier += *i++;
}
}
break;
case 'h':
@ -1497,8 +1522,12 @@ void CheckIO::invalidScanfArgTypeError_int(const Token* tok, unsigned int numFor
errmsg << (isUnsigned ? "unsigned " : "") << "long long";
else
errmsg << (isUnsigned ? "unsigned " : "") << "long";
} else if (specifier[0] == 'I') {
} else if (specifier.find("I32") != std::string::npos) {
errmsg << (isUnsigned ? "unsigned " : "") << "__int32";
} else if (specifier.find("I64") != std::string::npos) {
errmsg << (isUnsigned ? "unsigned " : "") << "__int64";
} else if (specifier[0] == 'I') {
errmsg << (isUnsigned ? "size_t" : "ptrdiff_t");
} else if (specifier[0] == 'j') {
if (isUnsigned)
errmsg << "uintmax_t";

View File

@ -9604,6 +9604,9 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
tok->str("strstr");
} else if (Token::simpleMatch(tok, "_tcstok (")) {
tok->str("strtok");
} else if (Token::simpleMatch(tok, "_ftprintf (")) {
tok->str("fprintf");
tok->originalName("_ftprintf");
} else if (Token::simpleMatch(tok, "_tprintf (")) {
tok->str("printf");
tok->originalName("_tprintf");
@ -9613,12 +9616,18 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
} else if (Token::simpleMatch(tok, "_sntprintf (")) {
tok->str("snprintf");
tok->originalName("_sntprintf");
} else if (Token::simpleMatch(tok, "_ftscanf (")) {
tok->str("fscanf");
tok->originalName("_ftscanf");
} else if (Token::simpleMatch(tok, "_tscanf (")) {
tok->str("scanf");
tok->originalName("_tscanf");
} else if (Token::simpleMatch(tok, "_stscanf (")) {
tok->str("sscanf");
tok->originalName("_stscanf");
} else if (Token::simpleMatch(tok, "_ftprintf_s (")) {
tok->str("fprintf_s");
tok->originalName("_ftprintf_s");
} else if (Token::simpleMatch(tok, "_tprintf_s (")) {
tok->str("printf_s");
tok->originalName("_tprintf_s");
@ -9628,6 +9637,9 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
} else if (Token::simpleMatch(tok, "_sntprintf_s (")) {
tok->str("_snprintf_s");
tok->originalName("_sntprintf_s");
} else if (Token::simpleMatch(tok, "_ftscanf_s (")) {
tok->str("fscanf_s");
tok->originalName("_ftscanf_s");
} else if (Token::simpleMatch(tok, "_tscanf_s (")) {
tok->str("scanf_s");
tok->originalName("_tscanf_s");
@ -9674,6 +9686,9 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
tok->str("wcsstr");
} else if (Token::simpleMatch(tok, "_tcstok (")) {
tok->str("wcstok");
} else if (Token::simpleMatch(tok, "_ftprintf (")) {
tok->str("fwprintf");
tok->originalName("_ftprintf");
} else if (Token::simpleMatch(tok, "_tprintf (")) {
tok->str("wprintf");
tok->originalName("_tprintf");
@ -9683,12 +9698,18 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
} else if (Token::simpleMatch(tok, "_sntprintf (")) {
tok->str("snwprintf");
tok->originalName("_sntprintf");
} else if (Token::simpleMatch(tok, "_ftscanf (")) {
tok->str("fwscanf");
tok->originalName("_ftscanf");
} else if (Token::simpleMatch(tok, "_tscanf (")) {
tok->str("wscanf");
tok->originalName("_tscanf");
} else if (Token::simpleMatch(tok, "_stscanf (")) {
tok->str("swscanf");
tok->originalName("_stscanf");
} else if (Token::simpleMatch(tok, "_ftprintf_s (")) {
tok->str("fwprintf_s");
tok->originalName("_ftprintf_s");
} else if (Token::simpleMatch(tok, "_tprintf_s (")) {
tok->str("wprintf_s");
tok->originalName("_tprintf_s");
@ -9698,6 +9719,9 @@ void Tokenizer::simplifyMicrosoftStringFunctions()
} else if (Token::simpleMatch(tok, "_sntprintf_s (")) {
tok->str("_snwprintf_s");
tok->originalName("_sntprintf_s");
} else if (Token::simpleMatch(tok, "_ftscanf_s (")) {
tok->str("fwscanf_s");
tok->originalName("_ftscanf_s");
} else if (Token::simpleMatch(tok, "_tscanf_s (")) {
tok->str("wscanf_s");
tok->originalName("_tscanf_s");

View File

@ -49,6 +49,7 @@ private:
TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900
TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902
TEST_CASE(testMicrosoftScanfArgument);
TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920
TEST_CASE(testMicrosoftSecurePrintfArgument);
TEST_CASE(testMicrosoftSecureScanfArgument);
@ -2146,6 +2147,83 @@ private:
"[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str());
}
void testMicrosoftScanfArgument() {
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"
" scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n"
" scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n"
" scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n"
" scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n"
" scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n"
" scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n"
"}", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n"
"[test.cpp:9]: (warning) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka long *}'.\n"
"[test.cpp:10]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka int *}'.\n"
"[test.cpp:11]: (warning) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n"
"[test.cpp:12]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka long long *}'.\n"
"[test.cpp:13]: (warning) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout.str());
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"
" scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n"
" scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n"
" scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n"
" scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n"
" scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n"
" scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n"
"}", false, false, Settings::Win64);
ASSERT_EQUALS("[test.cpp:8]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n"
"[test.cpp:9]: (warning) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka long long *}'.\n"
"[test.cpp:10]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka int *}'.\n"
"[test.cpp:11]: (warning) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n"
"[test.cpp:12]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka long long *}'.\n"
"[test.cpp:13]: (warning) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout.str());
check("void foo() {\n"
" size_t s;\n"
" int i;\n"
" scanf(\"%I\", &s);\n"
" scanf(\"%I6\", &s);\n"
" scanf(\"%I6x\", &s);\n"
" scanf(\"%I16\", &s);\n"
" scanf(\"%I16x\", &s);\n"
" scanf(\"%I32\", &s);\n"
" scanf(\"%I64\", &s);\n"
" scanf(\"%I%i\", &s, &i);\n"
" scanf(\"%I6%i\", &s, &i);\n"
" scanf(\"%I6x%i\", &s, &i);\n"
" scanf(\"%I16%i\", &s, &i);\n"
" scanf(\"%I16x%i\", &s, &i);\n"
" scanf(\"%I32%i\", &s, &i);\n"
" scanf(\"%I64%i\", &s, &i);\n"
"}");
ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n"
"[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout.str());
}
void testMicrosoftCStringFormatArguments() { // ticket #4920
check("void foo() {\n"
" unsigned __int32 u32;\n"
@ -2289,6 +2367,42 @@ private:
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:5]: (warning) _snwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" fprintf_s(fp, \"%d %u\", u, i, 0);\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) fprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" fwprintf_s(fp, L\"%d %u\", u, i, 0);\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n"
"[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'int'.\n"
"[test.cpp:4]: (warning) fwprintf_s format string requires 2 parameters but 3 are given.\n", errout.str());
}
void testMicrosoftSecureScanfArgument() {
@ -2376,6 +2490,46 @@ private:
"[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:6]: (warning) swscanf_s format string requires 6 parameters but 7 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" TCHAR str[10];\n"
" _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" TCHAR str[10];\n"
" _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" char str[10];\n"
" fscanf_s(fp, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n"
"}\n", false, false, Settings::Win32A);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:5]: (warning) fscanf_s format string requires 6 parameters but 7 are given.\n", errout.str());
check("void foo(FILE * fp) {\n"
" int i;\n"
" unsigned int u;\n"
" wchar_t str[10];\n"
" fwscanf_s(fp, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n"
"}\n", false, false, Settings::Win32W);
ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n"
"[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'int *'.\n"
"[test.cpp:5]: (warning) fwscanf_s format string requires 6 parameters but 7 are given.\n", errout.str());
check("void foo() {\n"
" WCHAR msStr1[5] = {0};\n"
" wscanf_s(L\"%4[^-]\", msStr1, _countof(msStr1));\n"