diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 15ba800ea..8ffcfa081 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -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"; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index 14637c23a..e6aa35d17 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -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"); diff --git a/test/testio.cpp b/test/testio.cpp index 11710bf3b..56b2837b8 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -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"