diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index 783ceca7d62..84ab76db133 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1356,6 +1356,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a if (std::strcmp(engine, "pcre") == 0) { rule.engine = Regex::Engine::Pcre; } + else if (std::strcmp(engine, "std") == 0) { + rule.engine = Regex::Engine::Std; + } else { mLogger.printError(std::string("unknown regex engine '") + engine + "'."); return Result::Fail; diff --git a/lib/regex.cpp b/lib/regex.cpp index bee8acb2a2a..2da2f45e492 100644 --- a/lib/regex.cpp +++ b/lib/regex.cpp @@ -20,6 +20,7 @@ #include "regex.h" +#include #include #ifdef _WIN32 @@ -246,6 +247,51 @@ namespace { } } +namespace { + class StdRegex : public Regex + { + public: + explicit StdRegex(std::string pattern) + : mPattern(std::move(pattern)) + {} + + std::string compile() + { + if (mCompiled) + return "regular expression has already been compiled"; + + try { + mRegex = std::regex(mPattern); + } catch (const std::exception& e) { + return e.what(); + } + mCompiled = true; + return ""; + } + + std::string match(const std::string& str, const MatchFn& matchFn) const override + { + if (!mCompiled) + return "regular expression has not been compiled yet"; + + auto I = std::sregex_iterator(str.cbegin(), str.cend(), mRegex); + const auto E = std::sregex_iterator(); + while (I != E) + { + const std::smatch& match = *I; + matchFn(match.position(), match.position() + match.length()); + ++I; + } + return ""; + } + + private: + std::string mPattern; + std::regex mRegex; + bool mCompiled{}; + }; +} + template static T* createAndCompileRegex(std::string pattern, std::string& err) { @@ -259,6 +305,8 @@ std::shared_ptr Regex::create(std::string pattern, Engine engine, std::st Regex* regex = nullptr; if (engine == Engine::Pcre) regex = createAndCompileRegex(std::move(pattern), err); + else if (engine == Engine::Std) + regex = createAndCompileRegex(std::move(pattern), err); else { err = "unknown regular expression engine"; } diff --git a/lib/regex.h b/lib/regex.h index afe92d92d40..3ac266ccf20 100644 --- a/lib/regex.h +++ b/lib/regex.h @@ -41,7 +41,8 @@ class CPPCHECKLIB Regex enum class Engine : std::uint8_t { Unknown = 0, - Pcre = 1 + Pcre = 1, + Std = 2 }; static std::shared_ptr create(std::string pattern, Engine engine, std::string& err); diff --git a/releasenotes.txt b/releasenotes.txt index 94e79a792e3..3cb1ccb0b2c 100644 --- a/releasenotes.txt +++ b/releasenotes.txt @@ -21,3 +21,5 @@ Infrastructure & dependencies: Other: - Make it possible to specify the regular expression engine using the `engine` element in a rule XML. +- Added support for `std::regex` as the regular engine for rules. It can be specified using `std` in the engine` element in a rule XML. +- \ No newline at end of file diff --git a/test/testcmdlineparser.cpp b/test/testcmdlineparser.cpp index 870e0ac341c..c35d8102af2 100644 --- a/test/testcmdlineparser.cpp +++ b/test/testcmdlineparser.cpp @@ -2674,6 +2674,7 @@ class TestCmdlineParser : public TestFixture { "\n" "\n" "\n" + "std\n" "define\n" ".*\n" "\n" @@ -2694,7 +2695,7 @@ class TestCmdlineParser : public TestFixture { ASSERT_EQUALS("ruleId1", it->id); ASSERT_EQUALS("ruleSummary1", it->summary); ++it; - ASSERT_EQUALS_ENUM(Regex::Engine::Pcre, it->engine); + ASSERT_EQUALS_ENUM(Regex::Engine::Std, it->engine); ASSERT_EQUALS("define", it->tokenlist); ASSERT_EQUALS(".*", it->pattern); ASSERT_EQUALS_ENUM(Severity::warning, it->severity); diff --git a/test/testregex.cpp b/test/testregex.cpp index 4b6b2781cc8..5cfa217312d 100644 --- a/test/testregex.cpp +++ b/test/testregex.cpp @@ -85,6 +85,18 @@ class TestRegExBase : public TestFixture { std::string exp; if (mEngine == Regex::Engine::Pcre) exp = "missing terminating ] for character class"; + else if (mEngine == Regex::Engine::Std) + { +#if defined(_MSC_VER) + exp = "regex_error(error_brack): The expression contained mismatched [ and ]."; +#elif defined(_LIBCPP_VERSION) + exp = "The expression contained mismatched [ and ]."; +#elif defined(__clang__) + exp = "Unexpected character within '[...]' in regular expression"; +#else + exp = "Unexpected character in bracket expression."; +#endif + } (void)assertRegex("[", exp); } @@ -201,6 +213,12 @@ class TestRegExPcre : public TestRegExBase { TestRegExPcre() : TestRegExBase("TestRegExPcre", Regex::Engine::Pcre) {} }; +class TestRegExStd : public TestRegExBase { +public: + TestRegExStd() : TestRegExBase("TestRegExStd", Regex::Engine::Std) {} +}; + REGISTER_TEST(TestRegExPcre) +REGISTER_TEST(TestRegExStd) #endif // HAVE_RULES