From cb1fc06a8030fd31b1a8e3e25c60b8dec99f80f0 Mon Sep 17 00:00:00 2001 From: Robert Reif Date: Wed, 12 Mar 2014 19:22:44 +0100 Subject: [PATCH] Fixed #5079 (CheckIO::checkFileUsage doesn't support wide char and microsoft functions) --- lib/checkio.cpp | 43 ++++++++++----- lib/tokenize.cpp | 58 ++++++++++++++++++++- test/testio.cpp | 132 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 211 insertions(+), 22 deletions(-) diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 146a58886..acf374688 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -97,9 +97,10 @@ struct Filepointer { void CheckIO::checkFileUsage() { static const char* _whitelist[] = { - "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc" + "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; static const std::set whitelist(_whitelist, _whitelist + sizeof(_whitelist)/sizeof(*_whitelist)); + const bool windows = _settings->isWindowsPlatform(); std::map filepointers; @@ -147,7 +148,9 @@ void CheckIO::checkFileUsage() i->second.op_indent = 0; i->second.lastOperation = Filepointer::UNKNOWN_OP; } - } else if (tok->varId() && Token::Match(tok, "%var% =") && (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile")) { + } else if (tok->varId() && Token::Match(tok, "%var% =") && + (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile" && + (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) { std::map::iterator i = filepointers.find(tok->varId()); if (i != filepointers.end()) { i->second.mode = UNKNOWN; @@ -158,7 +161,9 @@ void CheckIO::checkFileUsage() const Token* fileTok = 0; Filepointer::Operation operation = Filepointer::NONE; - if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile") && tok->strAt(-1) == "=") { + if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" || + (windows && (tok->str() == "_wfopen" || tok->str() == "_wfreopen"))) && + tok->strAt(-1) == "=") { if (tok->str() != "tmpfile") { const Token* modeTok = tok->tokAt(2)->nextArgument(); if (modeTok && modeTok->type() == Token::eString) @@ -167,21 +172,34 @@ void CheckIO::checkFileUsage() mode = "wb+"; fileTok = tok->tokAt(-2); operation = Filepointer::OPEN; - } else if (tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") { + } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %var%")) { + const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); + if (modeTok && modeTok->type() == Token::eString) + mode = modeTok->strValue(); + fileTok = tok->tokAt(3); + operation = Filepointer::OPEN; + } else if ((tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") || + (windows && tok->str() == "_fseeki64")) { if (Token::simpleMatch(tok, "fflush ( stdin )")) fflushOnInputStreamError(tok, tok->strAt(2)); else { fileTok = tok->tokAt(2); operation = Filepointer::POSITIONING; } - } else if (tok->str() == "fgetc" || tok->str() == "fgets" || tok->str() == "fread" || tok->str() == "fscanf" || tok->str() == "getc") { - if (tok->str() == "fscanf") + } else if (tok->str() == "fgetc" || tok->str() == "fgetwc" || + tok->str() == "fgets" || tok->str() == "fgetws" || tok->str() == "fread" || + tok->str() == "fscanf" || tok->str() == "fwscanf" || tok->str() == "getc" || + (windows && (tok->str() == "fscanf_s" || tok->str() == "fwscanf_s"))) { + if (tok->str().find("scanf") != std::string::npos) fileTok = tok->tokAt(2); else fileTok = tok->linkAt(1)->previous(); operation = Filepointer::READ; - } else if (tok->str() == "fputc" || tok->str() == "fputs" || tok->str() == "fwrite" || tok->str() == "fprintf" || tok->str() == "putcc") { - if (tok->str() == "fprintf") + } else if (tok->str() == "fputc" || tok->str() == "fputwc" || + tok->str() == "fputs" || tok->str() == "fputws" || tok->str() == "fwrite" || + tok->str() == "fprintf" || tok->str() == "fwprintf" || tok->str() == "putcc" || + (windows && (tok->str() == "fprintf_s" || tok->str() == "fwprintf_s"))) { + if (tok->str().find("printf") != std::string::npos) fileTok = tok->tokAt(2); else fileTok = tok->linkAt(1)->previous(); @@ -191,7 +209,7 @@ void CheckIO::checkFileUsage() operation = Filepointer::CLOSE; } else if (whitelist.find(tok->str()) != whitelist.end()) { fileTok = tok->tokAt(2); - if (tok->str() == "ungetc" && fileTok) + if ((tok->str() == "ungetc" || tok->str() == "ungetwc") && fileTok) fileTok = fileTok->nextArgument(); operation = Filepointer::UNIMPORTANT; } else if (!Token::Match(tok, "if|for|while|catch|switch")) { @@ -313,6 +331,7 @@ void CheckIO::invalidScanf() if (!_settings->isEnabled("warning") && !_settings->isEnabled("portability")) return; + const bool windows = _settings->isWindowsPlatform(); const SymbolDatabase * const symbolDatabase = _tokenizer->getSymbolDatabase(); std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t j = 0; j < functions; ++j) { @@ -348,7 +367,7 @@ void CheckIO::invalidScanf() else if (std::isalpha(formatstr[i]) || formatstr[i] == '[') { if ((formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) && _settings->isEnabled("warning")) // #3490 - field width limits are only necessary for string input invalidScanfError(tok, false); - else if (formatstr[i] != 'n' && formatstr[i] != 'c' && _settings->platformType != Settings::Win32A && _settings->platformType != Settings::Win32W && _settings->platformType != Settings::Win64 && _settings->isEnabled("portability")) + else if (formatstr[i] != 'n' && formatstr[i] != 'c' && !windows && _settings->isEnabled("portability")) invalidScanfError(tok, true); // Warn about libc bug in versions prior to 2.13-25 format = false; } @@ -444,8 +463,8 @@ static bool findFormat(unsigned int arg, const Token *firstArg, void CheckIO::checkWrongPrintfScanfArguments() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); - bool warning = _settings->isEnabled("warning"); - bool windows = _settings->isWindowsPlatform(); + const bool warning = _settings->isEnabled("warning"); + const bool windows = _settings->isWindowsPlatform(); std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t j = 0; j < functions; ++j) { diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index fd63477a9..ceb66ca8a 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -9535,32 +9535,58 @@ void Tokenizer::simplifyMicrosoftStringFunctions() for (Token *tok = list.front(); tok; tok = tok->next()) { if (Token::simpleMatch(tok, "_topen (")) { tok->str("open"); + tok->originalName("_topen"); + } else if (Token::simpleMatch(tok, "_tsopen_s (")) { + tok->str("_sopen_s"); + tok->originalName("_tsopen_s"); } else if (Token::simpleMatch(tok, "_tfopen (")) { tok->str("fopen"); + tok->originalName("_tfopen"); + } else if (Token::simpleMatch(tok, "_tfopen_s (")) { + tok->str("fopen_s"); + tok->originalName("_tfopen_s"); + } else if (Token::simpleMatch(tok, "_tfreopen (")) { + tok->str("_wfreopen"); + tok->originalName("_tfreopen"); + } else if (Token::simpleMatch(tok, "_tfreopen_s (")) { + tok->str("_wfreopen_s"); + tok->originalName("_tfreopen_s"); } else if (Token::simpleMatch(tok, "_tcscat (")) { tok->str("strcat"); + tok->originalName("_tcscat"); } else if (Token::simpleMatch(tok, "_tcschr (")) { tok->str("strchr"); + tok->originalName("_tcschr"); } else if (Token::simpleMatch(tok, "_tcscmp (")) { tok->str("strcmp"); + tok->originalName("_tcscmp"); } else if (Token::simpleMatch(tok, "_tcsdup (")) { tok->str("strdup"); + tok->originalName("_tcsdup"); } else if (Token::simpleMatch(tok, "_tcscpy (")) { tok->str("strcpy"); + tok->originalName("_tcscpy"); } else if (Token::simpleMatch(tok, "_tcslen (")) { tok->str("strlen"); + tok->originalName("_tcslen"); } else if (Token::simpleMatch(tok, "_tcsncat (")) { tok->str("strncat"); + tok->originalName("_tcscat"); } else if (Token::simpleMatch(tok, "_tcsncpy (")) { tok->str("strncpy"); + tok->originalName("_tcsncpy"); } else if (Token::simpleMatch(tok, "_tcsnlen (")) { tok->str("strnlen"); + tok->originalName("_tcslen"); } else if (Token::simpleMatch(tok, "_tcsrchr (")) { tok->str("strrchr"); + tok->originalName("_tcsrchr"); } else if (Token::simpleMatch(tok, "_tcsstr (")) { tok->str("strstr"); + tok->originalName("_tcsstr"); } else if (Token::simpleMatch(tok, "_tcstok (")) { tok->str("strtok"); + tok->originalName("_tcstok"); } else if (Token::simpleMatch(tok, "_ftprintf (")) { tok->str("fprintf"); tok->originalName("_ftprintf"); @@ -9619,30 +9645,60 @@ void Tokenizer::simplifyMicrosoftStringFunctions() } else if (_settings->platformType == Settings::Win32W || _settings->platformType == Settings::Win64) { for (Token *tok = list.front(); tok; tok = tok->next()) { - if (Token::simpleMatch(tok, "_tcscat (")) { + if (Token::simpleMatch(tok, "_topen (")) { + tok->str("_wopen"); + tok->originalName("_topen"); + } else if (Token::simpleMatch(tok, "_tsfopen_s (")) { + tok->str("_wsopen_s"); + tok->originalName("_tsopen_s"); + } else if (Token::simpleMatch(tok, "_tfopen (")) { + tok->str("_wfopen"); + tok->originalName("_tfopen"); + } else if (Token::simpleMatch(tok, "_tfopen_s (")) { + tok->str("_wfopen_s"); + tok->originalName("_tfopen_s"); + } else if (Token::simpleMatch(tok, "_tfreopen (")) { + tok->str("_wfreopen"); + tok->originalName("_tfreopen"); + } else if (Token::simpleMatch(tok, "_tfreopen_s (")) { + tok->str("_wfreopen_s"); + tok->originalName("_tfreopen_s"); + } else if (Token::simpleMatch(tok, "_tcscat (")) { tok->str("wcscat"); + tok->originalName("_tcscat"); } else if (Token::simpleMatch(tok, "_tcschr (")) { tok->str("wcschr"); + tok->originalName("_tcschr"); } else if (Token::simpleMatch(tok, "_tcscmp (")) { tok->str("wcscmp"); + tok->originalName("_tcscmp"); } else if (Token::simpleMatch(tok, "_tcscpy (")) { tok->str("wcscpy"); + tok->originalName("_tcscpy"); } else if (Token::simpleMatch(tok, "_tcsdup (")) { tok->str("wcsdup"); + tok->originalName("_tcsdup"); } else if (Token::simpleMatch(tok, "_tcslen (")) { tok->str("wcslen"); + tok->originalName("_tcslen"); } else if (Token::simpleMatch(tok, "_tcsncat (")) { tok->str("wcsncat"); + tok->originalName("_tcsncat"); } else if (Token::simpleMatch(tok, "_tcsncpy (")) { tok->str("wcsncpy"); + tok->originalName("_tcsncpy"); } else if (Token::simpleMatch(tok, "_tcsnlen (")) { tok->str("wcsnlen"); + tok->originalName("_tcsnlen"); } else if (Token::simpleMatch(tok, "_tcsrchr (")) { tok->str("wcsrchr"); + tok->originalName("_tcsrchr"); } else if (Token::simpleMatch(tok, "_tcsstr (")) { tok->str("wcsstr"); + tok->originalName("_tcsstr"); } else if (Token::simpleMatch(tok, "_tcstok (")) { tok->str("wcstok"); + tok->originalName("_tcstok"); } else if (Token::simpleMatch(tok, "_ftprintf (")) { tok->str("fwprintf"); tok->originalName("_ftprintf"); diff --git a/test/testio.cpp b/test/testio.cpp index 640629f2c..0298bb8de 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -139,12 +139,96 @@ private: "}"); ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + check("void foo(FILE*& f) {\n" + " f = _wfopen(name, L\"r\");\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfopen(name, _T(\"r\"));\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32A); + ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfopen(name, _T(\"r\"));\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " _wfopen_s(&f, name, L\"r\");\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " _tfopen_s(&f, name, _T(\"r\"));\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32A); + ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " _tfopen_s(&f, name, _T(\"r\"));\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:5]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + check("void foo(FILE*& f) {\n" " f = fopen(name, \"r+\");\n" " fwrite(buffer, 5, 6, f);\n" "}"); ASSERT_EQUALS("", errout.str()); + check("void foo(FILE*& f) {\n" + " f = _wfopen(name, L\"r+\");\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfopen(name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32A); + ASSERT_EQUALS("", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfopen(name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("", errout.str()); + + check("void foo(FILE*& f) {\n" + " _wfopen_s(&f, name, L\"r+\");\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("", errout.str()); + + check("void foo(FILE*& f) {\n" + " _tfopen_s(&f, name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32A); + ASSERT_EQUALS("", errout.str()); + + check("void foo(FILE*& f) {\n" + " _tfopen_s(&f, name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("", errout.str()); + // Write mode check("void foo(FILE*& f) {\n" " f = fopen(name, \"w\");\n" @@ -192,14 +276,6 @@ private: "}"); ASSERT_EQUALS("", errout.str()); - check("void foo(FILE*& f) {\n" - " f = fopen(name, \"a\");\n" - " fwrite(buffer, 5, 6, f);\n" - " clearerr(f);\n" - " fread(buffer, 5, 6, f);\n" - "}"); - ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout.str()); - // freopen and tmpfile check("void foo(FILE*& f) {\n" " f = freopen(name, \"r\", f);\n" @@ -207,6 +283,42 @@ private: "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + check("void foo(FILE*& f) {\n" + " f = _wfreopen(name, L\"r\", f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfreopen(name, _T(\"r\"), f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32A); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfreopen(name, _T(\"r\"), f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _wfreopen_s(&f, name, L\"r\", f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfreopen_s(&f, name, _T(\"r\"), f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32A); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + + check("void foo(FILE*& f) {\n" + " f = _tfreopen_s(&f, name, _T(\"r\"), f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", false, false, Settings::Win32W); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout.str()); + // Crash tests check("void foo(FILE*& f) {\n" " f = fopen(name, mode);\n" // No assertion failure (#3830) @@ -250,11 +362,13 @@ private: " clearerr(f);\n" " fread(buffer, 5, 6, f);\n" " ungetc('a', f);\n" + " ungetwc(L'a', f);\n" "}"); ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n" "[test.cpp:4]: (error) Used file that is not opened.\n" "[test.cpp:5]: (error) Used file that is not opened.\n" - "[test.cpp:6]: (error) Used file that is not opened.\n", errout.str()); + "[test.cpp:6]: (error) Used file that is not opened.\n" + "[test.cpp:7]: (error) Used file that is not opened.\n", errout.str()); check("void foo(FILE*& f) {\n" " if(!ferror(f)) {\n"