From ad0f10313b4aac5a913e5b74c47fb87326f913d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 26 Aug 2024 13:53:23 +0200 Subject: [PATCH 1/7] fix #13021: cmdline: validate option --std --- cli/cmdlineparser.cpp | 14 ++++++++++++-- lib/keywords.cpp | 8 ++++++++ lib/standards.h | 12 ++++++++---- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 924009ed018..30885fee1be 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1259,10 +1259,20 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a const std::string std = argv[i] + 6; // TODO: print error when standard is unknown if (std::strncmp(std.c_str(), "c++", 3) == 0) { - mSettings.standards.cpp = Standards::getCPP(std); + const Standards::cppstd_t cppstd = Standards::getCPP(std); + if (cppstd == Standards::CPPInvalid) { + mLogger.printError("unknown --std value '" + std + "'"); + return Result::Fail; + } + mSettings.standards.cpp = cppstd; } else if (std::strncmp(std.c_str(), "c", 1) == 0) { - mSettings.standards.c = Standards::getC(std); + const Standards::cstd_t cstd = Standards::getC(std); + if (cstd == Standards::CInvalid) { + mLogger.printError("unknown --std value '" + std + "'"); + return Result::Fail; + } + mSettings.standards.c = cstd; } else { mLogger.printError("unknown --std value '" + std + "'"); diff --git a/lib/keywords.cpp b/lib/keywords.cpp index a26fc5bd786..8ef2454922f 100644 --- a/lib/keywords.cpp +++ b/lib/keywords.cpp @@ -181,6 +181,8 @@ const std::unordered_set& Keywords::getAll(Standards::cstd_t cStd) return c17_keywords_all; case Standards::cstd_t::C23: return c23_keywords_all; + case Standards::cstd_t::CInvalid: + cppcheck::unreachable(); } cppcheck::unreachable(); } @@ -203,6 +205,8 @@ const std::unordered_set& Keywords::getAll(Standards::cppstd_t cppS return cpp23_keywords_all; case Standards::cppstd_t::CPP26: return cpp26_keywords_all; + case Standards::cppstd_t::CPPInvalid: + cppcheck::unreachable(); } cppcheck::unreachable(); } @@ -222,6 +226,8 @@ const std::unordered_set& Keywords::getOnly(Standards::cstd_t cStd) return c17_keywords; case Standards::cstd_t::C23: return c23_keywords; + case Standards::cstd_t::CInvalid: + cppcheck::unreachable(); } cppcheck::unreachable(); } @@ -245,6 +251,8 @@ const std::unordered_set& Keywords::getOnly(Standards::cppstd_t cpp return cpp23_keywords; case Standards::cppstd_t::CPP26: return cpp26_keywords; + case Standards::cppstd_t::CPPInvalid: + cppcheck::unreachable(); } cppcheck::unreachable(); } diff --git a/lib/standards.h b/lib/standards.h index 4965e432fcd..1efe3ce7056 100644 --- a/lib/standards.h +++ b/lib/standards.h @@ -38,10 +38,10 @@ struct Standards { enum Language : std::uint8_t { None, C, CPP }; /** C code standard */ - enum cstd_t : std::uint8_t { C89, C99, C11, C17, C23, CLatest = C23 } c = CLatest; + enum cstd_t : std::uint8_t { C89, C99, C11, C17, C23, CLatest = C23, CInvalid } c = CLatest; /** C++ code standard */ - enum cppstd_t : std::uint8_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPP26, CPPLatest = CPP26 } cpp = CPPLatest; + enum cppstd_t : std::uint8_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPP26, CPPLatest = CPP26, CPPInvalid } cpp = CPPLatest; /** --std value given on command line */ std::string stdValue; @@ -64,6 +64,8 @@ struct Standards { return "c17"; case C23: return "c23"; + case CInvalid: + return ""; } return ""; } @@ -83,7 +85,7 @@ struct Standards { if (std == "c23") { return Standards::C23; } - return Standards::CLatest; + return Standards::CInvalid; } bool setCPP(std::string str) { stdValue = str; @@ -110,6 +112,8 @@ struct Standards { return "c++23"; case CPP26: return "c++26"; + case CPPInvalid: + return ""; } return ""; } @@ -135,7 +139,7 @@ struct Standards { if (std == "c++26") { return Standards::CPP26; } - return Standards::CPPLatest; + return Standards::CPPInvalid; } }; From 3202520f7ffb6a18cdf6b6ddb98844285dbdddb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 26 Aug 2024 16:37:44 +0200 Subject: [PATCH 2/7] update stdunknown2 test to use ASSERT... instead of TODO_ASSERT... --- cli/cmdlineparser.cpp | 1 - test/testcmdlineparser.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 30885fee1be..6b9eaac0b6c 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1257,7 +1257,6 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // --std else if (std::strncmp(argv[i], "--std=", 6) == 0) { const std::string std = argv[i] + 6; - // TODO: print error when standard is unknown if (std::strncmp(std.c_str(), "c++", 3) == 0) { const Standards::cppstd_t cppstd = Standards::getCPP(std); if (cppstd == Standards::CPPInvalid) { diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 46425aa161f..b7c46dd5a6f 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -1369,8 +1369,8 @@ class TestCmdlineParser : public TestFixture { void stdunknown2() { REDIRECT; const char *const argv[] = {"cppcheck", "--std=cplusplus11", "file.cpp"}; - TODO_ASSERT_EQUALS(static_cast(CmdLineParser::Result::Fail), static_cast(CmdLineParser::Result::Success), static_cast(parser->parseFromArgs(3, argv))); - TODO_ASSERT_EQUALS("cppcheck: error: unknown --std value 'cplusplus11'\n", "", logger->str()); + ASSERT_EQUALS(static_cast(CmdLineParser::Result::Fail), static_cast(parser->parseFromArgs(3, argv))); + ASSERT_EQUALS("cppcheck: error: unknown --std value 'cplusplus11'\n", logger->str()); } void platformWin64() { From 0b2c852a7ce62a9c7a378e0bc68be320c92cff3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 27 Aug 2024 09:48:46 +0200 Subject: [PATCH 3/7] use bool reference instead of extra enum variant to indicate unknown '--std' option --- cli/cmdlineparser.cpp | 18 ++++++------------ lib/importproject.cpp | 13 +++++++++++-- lib/importproject.h | 2 +- lib/keywords.cpp | 8 -------- lib/standards.h | 24 ++++++++++++++++-------- 5 files changed, 34 insertions(+), 31 deletions(-) diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 6b9eaac0b6c..de814f05a37 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1257,23 +1257,17 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // --std else if (std::strncmp(argv[i], "--std=", 6) == 0) { const std::string std = argv[i] + 6; + bool unknown = false; if (std::strncmp(std.c_str(), "c++", 3) == 0) { - const Standards::cppstd_t cppstd = Standards::getCPP(std); - if (cppstd == Standards::CPPInvalid) { - mLogger.printError("unknown --std value '" + std + "'"); - return Result::Fail; - } - mSettings.standards.cpp = cppstd; + mSettings.standards.cpp = Standards::getCPP(std, unknown); } else if (std::strncmp(std.c_str(), "c", 1) == 0) { - const Standards::cstd_t cstd = Standards::getC(std); - if (cstd == Standards::CInvalid) { - mLogger.printError("unknown --std value '" + std + "'"); - return Result::Fail; - } - mSettings.standards.c = cstd; + mSettings.standards.c = Standards::getC(std, unknown); } else { + unknown = true; + } + if (unknown) { mLogger.printError("unknown --std value '" + std + "'"); return Result::Fail; } diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 336e25744c8..31088d80c64 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -268,7 +268,7 @@ static std::string unescape(const std::string &in) return out; } -void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) +bool ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) { std::string defs; @@ -311,6 +311,13 @@ void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) } else if (F=='s' && startsWith(fval,"td")) { ++pos; fs.standard = readUntil(command, &pos, " "); + bool unknown_std = false; + (void)Standards::getCPP(fs.standard, unknown_std); + if (unknown_std) (void)Standards::getC(fs.standard, unknown_std); + if (unknown_std) { + printError("unkown --std value '" + fs.standard + "'"); + return false; + } } else if (F == 'i' && fval == "system") { ++pos; std::string isystem = readUntil(command, &pos, " "); @@ -339,6 +346,7 @@ void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) } } fsSetDefines(fs, std::move(defs)); + return true; } bool ImportProject::importCompileCommands(std::istream &istr) @@ -416,7 +424,8 @@ bool ImportProject::importCompileCommands(std::istream &istr) return false; } FileSettings fs{std::move(path)}; - fsParseCommand(fs, command); // read settings; -D, -I, -U, -std, -m*, -f* + // read settings; -D, -I, -U, -std, -m*, -f* + if (!fsParseCommand(fs, command)) return false; std::map variables; fsSetIncludePaths(fs, directory, fs.includePaths, variables); fileSettings.push_back(std::move(fs)); diff --git a/lib/importproject.h b/lib/importproject.h index 7382b56eb8d..c112a0e068a 100644 --- a/lib/importproject.h +++ b/lib/importproject.h @@ -64,7 +64,7 @@ class CPPCHECKLIB WARN_UNUSED ImportProject { CPPCHECK_GUI }; - static void fsParseCommand(FileSettings& fs, const std::string& command); + static bool fsParseCommand(FileSettings& fs, const std::string& command); static void fsSetDefines(FileSettings& fs, std::string defs); static void fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list &in, std::map &variables); diff --git a/lib/keywords.cpp b/lib/keywords.cpp index 8ef2454922f..a26fc5bd786 100644 --- a/lib/keywords.cpp +++ b/lib/keywords.cpp @@ -181,8 +181,6 @@ const std::unordered_set& Keywords::getAll(Standards::cstd_t cStd) return c17_keywords_all; case Standards::cstd_t::C23: return c23_keywords_all; - case Standards::cstd_t::CInvalid: - cppcheck::unreachable(); } cppcheck::unreachable(); } @@ -205,8 +203,6 @@ const std::unordered_set& Keywords::getAll(Standards::cppstd_t cppS return cpp23_keywords_all; case Standards::cppstd_t::CPP26: return cpp26_keywords_all; - case Standards::cppstd_t::CPPInvalid: - cppcheck::unreachable(); } cppcheck::unreachable(); } @@ -226,8 +222,6 @@ const std::unordered_set& Keywords::getOnly(Standards::cstd_t cStd) return c17_keywords; case Standards::cstd_t::C23: return c23_keywords; - case Standards::cstd_t::CInvalid: - cppcheck::unreachable(); } cppcheck::unreachable(); } @@ -251,8 +245,6 @@ const std::unordered_set& Keywords::getOnly(Standards::cppstd_t cpp return cpp23_keywords; case Standards::cppstd_t::CPP26: return cpp26_keywords; - case Standards::cppstd_t::CPPInvalid: - cppcheck::unreachable(); } cppcheck::unreachable(); } diff --git a/lib/standards.h b/lib/standards.h index 1efe3ce7056..51f46633e18 100644 --- a/lib/standards.h +++ b/lib/standards.h @@ -38,10 +38,10 @@ struct Standards { enum Language : std::uint8_t { None, C, CPP }; /** C code standard */ - enum cstd_t : std::uint8_t { C89, C99, C11, C17, C23, CLatest = C23, CInvalid } c = CLatest; + enum cstd_t : std::uint8_t { C89, C99, C11, C17, C23, CLatest = C23 } c = CLatest; /** C++ code standard */ - enum cppstd_t : std::uint8_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPP26, CPPLatest = CPP26, CPPInvalid } cpp = CPPLatest; + enum cppstd_t : std::uint8_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPP26, CPPLatest = CPP26 } cpp = CPPLatest; /** --std value given on command line */ std::string stdValue; @@ -64,12 +64,15 @@ struct Standards { return "c17"; case C23: return "c23"; - case CInvalid: - return ""; } return ""; } static cstd_t getC(const std::string &std) { + bool _unused; + return getC(std, _unused); + } + static cstd_t getC(const std::string &std, bool &unknown) { + unknown = false; if (std == "c89") { return Standards::C89; } @@ -85,7 +88,8 @@ struct Standards { if (std == "c23") { return Standards::C23; } - return Standards::CInvalid; + unknown = true; + return Standards::CLatest; } bool setCPP(std::string str) { stdValue = str; @@ -112,12 +116,15 @@ struct Standards { return "c++23"; case CPP26: return "c++26"; - case CPPInvalid: - return ""; } return ""; } static cppstd_t getCPP(const std::string &std) { + bool _unused; + return getCPP(std, _unused); + } + static cppstd_t getCPP(const std::string &std, bool &unknown) { + unknown = false; if (std == "c++03") { return Standards::CPP03; } @@ -139,7 +146,8 @@ struct Standards { if (std == "c++26") { return Standards::CPP26; } - return Standards::CPPInvalid; + unknown = true; + return Standards::CPPLatest; } }; From af28aedff3537cd09d259404cbc22ba196cb903d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 27 Aug 2024 10:30:33 +0200 Subject: [PATCH 4/7] fix typo --- lib/importproject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 31088d80c64..f07a9c5b575 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -315,7 +315,7 @@ bool ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) (void)Standards::getCPP(fs.standard, unknown_std); if (unknown_std) (void)Standards::getC(fs.standard, unknown_std); if (unknown_std) { - printError("unkown --std value '" + fs.standard + "'"); + printError("unknown --std value '" + fs.standard + "'"); return false; } } else if (F == 'i' && fval == "system") { From 960a3d34c83a9358a117a0c27423e45e6dfd759e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 27 Aug 2024 11:27:32 +0200 Subject: [PATCH 5/7] fix parsing of msvc /std option --- lib/importproject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/importproject.cpp b/lib/importproject.cpp index f07a9c5b575..31e9e2fef58 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -289,7 +289,7 @@ bool ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) while (pos < command.size() && command[pos] == ' ') ++pos; } - const std::string fval = readUntil(command, &pos, " ="); + const std::string fval = readUntil(command, &pos, " =:"); if (F=='D') { std::string defval = readUntil(command, &pos, " "); defs += fval; From 960be69d7cb77db110e8149c59cc9c516ca376bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 27 Aug 2024 20:57:26 +0200 Subject: [PATCH 6/7] fix parsing of msvc /std option (2) --- lib/importproject.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 31e9e2fef58..f8cb07f2fc9 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -289,7 +289,7 @@ bool ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) while (pos < command.size() && command[pos] == ' ') ++pos; } - const std::string fval = readUntil(command, &pos, " =:"); + const std::string fval = readUntil(command, &pos, " ="); if (F=='D') { std::string defval = readUntil(command, &pos, " "); defs += fval; @@ -309,8 +309,12 @@ bool ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) if (std::find(fs.includePaths.cbegin(), fs.includePaths.cend(), i) == fs.includePaths.cend()) fs.includePaths.push_back(std::move(i)); } else if (F=='s' && startsWith(fval,"td")) { - ++pos; - fs.standard = readUntil(command, &pos, " "); + if (command[pos] == '=') { + ++pos; + fs.standard = readUntil(command, &pos, " "); + } else { + fs.standard = fval.substr(3); + } bool unknown_std = false; (void)Standards::getCPP(fs.standard, unknown_std); if (unknown_std) (void)Standards::getC(fs.standard, unknown_std); From 22de2478b4e12ed5f4982983446236e05136f0eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 27 Aug 2024 21:32:05 +0200 Subject: [PATCH 7/7] treat gnu++XX as c++XX when parsing command files --- lib/importproject.cpp | 8 ++++++-- lib/standards.h | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/importproject.cpp b/lib/importproject.cpp index f8cb07f2fc9..4f3364ae88f 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -319,8 +319,12 @@ bool ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) (void)Standards::getCPP(fs.standard, unknown_std); if (unknown_std) (void)Standards::getC(fs.standard, unknown_std); if (unknown_std) { - printError("unknown --std value '" + fs.standard + "'"); - return false; + const Standards::cppstd_t gnustd = Standards::getGnuCPP(fs.standard, unknown_std); + if (unknown_std) { + printError("unknown --std value '" + fs.standard + "'"); + return false; + } + fs.standard = Standards::getCPP(gnustd); } } else if (F == 'i' && fval == "system") { ++pos; diff --git a/lib/standards.h b/lib/standards.h index 51f46633e18..5563e631791 100644 --- a/lib/standards.h +++ b/lib/standards.h @@ -149,6 +149,33 @@ struct Standards { unknown = true; return Standards::CPPLatest; } + static cppstd_t getGnuCPP(const std::string &std, bool &unknown) { + // treat gnu++XX as c++XX + unknown = false; + if (std == "gnu++03") { + return Standards::CPP03; + } + if (std == "gnu++11") { + return Standards::CPP11; + } + if (std == "gnu++14") { + return Standards::CPP14; + } + if (std == "gnu++17") { + return Standards::CPP17; + } + if (std == "gnu++20") { + return Standards::CPP20; + } + if (std == "gnu++23") { + return Standards::CPP23; + } + if (std == "gnu++26") { + return Standards::CPP26; + } + unknown = true; + return Standards::CPPLatest; + } }; /// @}