Skip to content

Commit d818c3e

Browse files
committed
fixed #14261 - Regex: added std::regex implementation [skip ci]
1 parent 376b31e commit d818c3e

9 files changed

Lines changed: 178 additions & 19 deletions

File tree

cli/cmdlineparser.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,7 +1317,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13171317
}
13181318

13191319
std::string regex_err;
1320-
auto regex = Regex::create(rule.pattern, Regex::Engine::Pcre, regex_err);
1320+
auto regex = Regex::create(rule.pattern, Regex::defaultEngine(), regex_err);
13211321
if (!regex) {
13221322
mLogger.printError("failed to compile rule pattern '" + rule.pattern + "' (" + regex_err + ").");
13231323
return Result::Fail;
@@ -1345,7 +1345,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13451345
node = node->FirstChildElement("rule");
13461346
for (; node && strcmp(node->Value(), "rule") == 0; node = node->NextSiblingElement()) {
13471347
Rule rule;
1348-
Regex::Engine regexEngine = Regex::Engine::Pcre;
1348+
Regex::Engine regexEngine = Regex::defaultEngine();
13491349

13501350
for (const tinyxml2::XMLElement *subnode = node->FirstChildElement(); subnode; subnode = subnode->NextSiblingElement()) {
13511351
const char * const subname = subnode->Name();
@@ -1377,10 +1377,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13771377
}
13781378
else if (std::strcmp(subname, "engine") == 0) {
13791379
const char * const engine = empty_if_null(subtext);
1380-
if (std::strcmp(engine, "pcre") == 0) {
1381-
regexEngine = Regex::Engine::Pcre;
1382-
}
1383-
else {
1380+
if (!Regex::stringToEngine(engine, regexEngine))
1381+
{
13841382
mLogger.printError(std::string("unknown regex engine '") + engine + "'.");
13851383
return Result::Fail;
13861384
}

cmake/compilerDefinitions.cmake

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ endif()
3737

3838
if(HAVE_RULES)
3939
add_definitions(-DHAVE_RULES)
40+
if(HAVE_PCRE)
41+
add_definitions(-DHAVE_PCRE)
42+
endif()
43+
if(HAVE_STD_REGEX)
44+
add_definitions(-DHAVE_STD_REGEX)
45+
endif()
4046
endif()
4147

4248
if(Boost_FOUND)

cmake/options.cmake

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,33 @@ if(NOT BUILD_GUI)
9494
endif()
9595
endif()
9696

97-
option(HAVE_RULES "Usage of rules (needs PCRE library and headers)" OFF)
97+
option(HAVE_RULES "Usage of rules" OFF)
98+
option(HAVE_PCRE "Allow usage of PCRE in rules" OFF)
99+
option(HAVE_STD_REGEX "Allow usage of std::regex in rules" OFF)
100+
# TODO: add option to specify default regular expression engine
101+
if(NOT HAVE_RULES)
102+
if(HAVE_PCRE)
103+
message(WARNING "HAVE_PCRE has no effect without HAVE_RULES")
104+
endif()
105+
if(HAVE_STD_REGEX)
106+
message(WARNING "HAVE_STD_REGEX has no effect without HAVE_RULES")
107+
endif()
108+
endif()
109+
if(HAVE_RULES)
110+
# TODO: handle explicit HAVE_PCRE parameter
111+
message(DEPRECATION "HAVE_RULES will no longer explicitly enable HAVE_PCRE with Cppcheck 2.24")
112+
set(HAVE_PCRE ON)
113+
114+
# TODO: unconditionally enable for all other build?
115+
if(MSVC AND HAVE_STD_REGEX)
116+
message(WARNING "Disabling usage of std::regex since its implementation is broken in Visual Studio")
117+
set(HAVE_STD_REGEX OFF)
118+
endif()
119+
120+
if(NOT HAVE_PCRE AND NOT HAVE_STD_REGEX)
121+
message(FATAL_ERROR "Cannot use rules since no regular expression engine is available")
122+
endif()
123+
endif()
98124
option(USE_BUNDLED_TINYXML2 "Usage of bundled TinyXML2 library" ON)
99125
if(BUILD_CORE_DLL AND NOT USE_BUNDLED_TINYXML2)
100126
message(FATAL_ERROR "Cannot use external TinyXML2 library when building lib as DLL")

cmake/printInfo.cmake

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ endif()
7979
message(STATUS)
8080
message(STATUS "HAVE_RULES = ${HAVE_RULES}")
8181
if(HAVE_RULES)
82-
message(STATUS "PCRE_LIBRARY = ${PCRE_LIBRARY}")
82+
message(STATUS "HAVE_PCRE = ${HAVE_PCRE}")
83+
if(HAVE_PCRE)
84+
message(STATUS "PCRE_INCLUDE = ${PCRE_INCLUDE}")
85+
message(STATUS "PCRE_LIBRARY = ${PCRE_LIBRARY}")
86+
endif()
87+
message(STATUS "HAVE_STD_REGEX = ${HAVE_STD_REGEX}")
8388
endif()
8489
message(STATUS)
8590
message(STATUS "DISALLOW_THREAD_EXECUTOR = ${DISALLOW_THREAD_EXECUTOR}")

lib/regex.cpp

Lines changed: 108 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,29 @@
2222

2323
#include <utility>
2424

25+
#if defined(HAVE_STD_REGEX)
26+
#include <regex>
27+
#endif
28+
29+
#if defined(HAVE_PCRE)
2530
#ifdef _WIN32
2631
#define PCRE_STATIC
2732
#endif
2833
#include <pcre.h>
34+
#endif
2935

36+
enum class Regex::Engine : std::uint8_t
37+
{
38+
Unknown = 0,
39+
#if defined(HAVE_PCRE)
40+
Pcre = 1,
41+
#endif
42+
#if defined(HAVE_STD_REGEX)
43+
Std = 2
44+
#endif
45+
};
46+
47+
#if defined(HAVE_PCRE)
3048
namespace {
3149
std::string pcreErrorCodeToString(const int pcreExecRet)
3250
{
@@ -249,6 +267,58 @@ namespace {
249267
return "";
250268
}
251269
}
270+
#endif // HAVE_PCRE
271+
272+
#if defined(HAVE_STD_REGEX)
273+
namespace {
274+
class StdRegex : public Regex
275+
{
276+
public:
277+
explicit StdRegex(std::string pattern)
278+
: mPattern(std::move(pattern))
279+
{}
280+
281+
std::string compile()
282+
{
283+
if (mCompiled)
284+
return "regular expression has already been compiled";
285+
286+
try {
287+
mRegex = std::regex(mPattern);
288+
} catch (const std::exception& e) {
289+
return e.what();
290+
}
291+
mCompiled = true;
292+
return "";
293+
}
294+
295+
std::string match(const std::string& str, const MatchFn& matchFn) const override
296+
{
297+
if (!mCompiled)
298+
return "regular expression has not been compiled yet";
299+
300+
auto I = std::sregex_iterator(str.cbegin(), str.cend(), mRegex);
301+
const auto E = std::sregex_iterator();
302+
while (I != E)
303+
{
304+
const std::smatch& match = *I;
305+
matchFn(match.position(), match.position() + match.length());
306+
++I;
307+
}
308+
return "";
309+
}
310+
311+
Engine engine() const override {
312+
return Engine::Std;
313+
}
314+
315+
private:
316+
std::string mPattern;
317+
std::regex mRegex;
318+
bool mCompiled{};
319+
};
320+
}
321+
#endif // HAVE_STD_REGEX
252322

253323
template<typename T>
254324
static T* createAndCompileRegex(std::string pattern, std::string& err)
@@ -261,10 +331,21 @@ static T* createAndCompileRegex(std::string pattern, std::string& err)
261331
std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::string& err)
262332
{
263333
Regex* regex = nullptr;
264-
if (engine == Engine::Pcre)
265-
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
266-
else {
267-
err = "unknown regular expression engine";
334+
switch(engine)
335+
{
336+
#if defined(HAVE_PCRE)
337+
case Engine::Pcre:
338+
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
339+
break;
340+
#endif
341+
#if defined(HAVE_STD_REGEX)
342+
case Engine::Std:
343+
regex = createAndCompileRegex<StdRegex>(std::move(pattern), err);
344+
break;
345+
#endif
346+
default:
347+
err = "unknown regular expression engine";
348+
break;
268349
}
269350
if (!err.empty()) {
270351
delete regex;
@@ -273,4 +354,27 @@ std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::st
273354
return std::shared_ptr<Regex>(regex);
274355
}
275356

357+
bool Regex::stringToEngine(const char* engine, Regex::Engine& regexEngine)
358+
{
359+
#if defined(HAVE_PCRE)
360+
if (std::strcmp(engine, "pcre") == 0) {
361+
regexEngine = Regex::Engine::Pcre;
362+
return true;
363+
}
364+
#endif
365+
#if defined(HAVE_STD_REGEX)
366+
if (std::strcmp(engine, "std") == 0) {
367+
regexEngine = Regex::Engine::Std;
368+
return true;
369+
}
370+
#endif
371+
regexEngine = Regex::Engine::Unknown;
372+
return false;
373+
}
374+
375+
Regex::Engine Regex::defaultEngine()
376+
{
377+
return Regex::Engine::Pcre;
378+
}
379+
276380
#endif // HAVE_RULES

lib/regex.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,19 @@
3333
class CPPCHECKLIB Regex
3434
{
3535
public:
36+
enum class Engine : std::uint8_t;
37+
3638
virtual ~Regex() = default;
3739

3840
using MatchFn = std::function<void (int start, int end)>;
3941
virtual std::string match(const std::string& str, const MatchFn& matchFn) const = 0;
4042

41-
enum class Engine : std::uint8_t
42-
{
43-
Unknown = 0,
44-
Pcre = 1
45-
};
46-
4743
virtual Engine engine() const = 0;
4844

4945
static std::shared_ptr<Regex> create(std::string pattern, Engine engine, std::string& err);
46+
47+
static bool stringToEngine(const char* engine, Regex::Engine& regexEngine);
48+
static Engine defaultEngine();
5049
};
5150

5251
#endif // HAVE_RULES

releasenotes.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ Infrastructure & dependencies:
2121
-
2222

2323
Other:
24+
- 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.
25+
-
2426
-

test/testcmdlineparser.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2729,6 +2729,7 @@ class TestCmdlineParser : public TestFixture {
27292729
"</message>\n"
27302730
"</rule>\n"
27312731
"<rule>\n"
2732+
"<engine>std</engine>\n"
27322733
"<tokenlist>define</tokenlist>\n"
27332734
"<pattern>.*</pattern>\n"
27342735
"<message>\n"
@@ -2749,7 +2750,7 @@ class TestCmdlineParser : public TestFixture {
27492750
ASSERT_EQUALS("ruleId1", it->id);
27502751
ASSERT_EQUALS("ruleSummary1", it->summary);
27512752
++it;
2752-
ASSERT_EQUALS_ENUM(Regex::Engine::Pcre, it->regex->engine());
2753+
ASSERT_EQUALS_ENUM(Regex::Engine::Std, it->regex->engine());
27532754
ASSERT_EQUALS("define", it->tokenlist);
27542755
ASSERT_EQUALS(".*", it->pattern);
27552756
ASSERT_EQUALS_ENUM(Severity::warning, it->severity);

test/testregex.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ class TestRegExBase : public TestFixture {
8787
std::string exp;
8888
if (mEngine == Regex::Engine::Pcre)
8989
exp = "missing terminating ] for character class";
90+
else if (mEngine == Regex::Engine::Std)
91+
{
92+
#if defined(_MSC_VER)
93+
exp = "regex_error(error_brack): The expression contained mismatched [ and ].";
94+
#elif defined(_LIBCPP_VERSION)
95+
exp = "The expression contained mismatched [ and ].";
96+
#elif defined(__clang__)
97+
exp = "Unexpected character within '[...]' in regular expression";
98+
#else
99+
exp = "Unexpected character in bracket expression.";
100+
#endif
101+
}
90102

91103
(void)assertRegex("[", exp);
92104
}
@@ -203,6 +215,12 @@ class TestRegExPcre : public TestRegExBase {
203215
TestRegExPcre() : TestRegExBase("TestRegExPcre", Regex::Engine::Pcre) {}
204216
};
205217

218+
class TestRegExStd : public TestRegExBase {
219+
public:
220+
TestRegExStd() : TestRegExBase("TestRegExStd", Regex::Engine::Std) {}
221+
};
222+
206223
REGISTER_TEST(TestRegExPcre)
224+
REGISTER_TEST(TestRegExStd)
207225

208226
#endif // HAVE_RULES

0 commit comments

Comments
 (0)