Skip to content

Commit 9e6eeeb

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

10 files changed

Lines changed: 259 additions & 20 deletions

Makefile

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ ifndef HAVE_RULES
88
HAVE_RULES=
99
endif
1010

11+
ifndef HAVE_PCRE
12+
HAVE_PCRE=1
13+
endif
14+
1115
ifndef MATCHCOMPILER
1216
MATCHCOMPILER=
1317
endif
@@ -140,19 +144,26 @@ ifeq (g++, $(findstring g++,$(CXX)))
140144
endif
141145
override CXXFLAGS += -std=c++11
142146
ifeq ($(HAVE_RULES),yes)
147+
override CPPFLAGS += -DHAVE_RULES
148+
# TODO: handle explicit disabling
149+
HAVE_PCRE=1
150+
override CPPFLAGS += -DHAVE_STD_REGEX
151+
else ifneq ($(HAVE_RULES),)
152+
$(error invalid HAVE_RULES value '$(HAVE_RULES)')
153+
endif
154+
155+
ifeq ($(HAVE_PCRE),1)
143156
PCRE_CONFIG = $(shell which pcre-config)
144157
ifeq ($(PCRE_CONFIG),)
145158
$(error Did not find pcre-config)
146159
endif
147160
override CXXFLAGS += $(shell $(PCRE_CONFIG) --cflags)
148-
override CPPFLAGS += -DHAVE_RULES
161+
override CPPFLAGS += -DHAVE_PCRE
149162
ifdef LIBS
150163
LIBS += $(shell $(PCRE_CONFIG) --libs)
151164
else
152165
LIBS=$(shell $(PCRE_CONFIG) --libs)
153166
endif
154-
else ifneq ($(HAVE_RULES),)
155-
$(error invalid HAVE_RULES value '$(HAVE_RULES)')
156167
endif
157168

158169
# older make versions do not support # in $(shell) and newer ones handle the escape sequence literally

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: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,43 @@ 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 disabling
111+
# TODO: enable deprecation warning when PCRE2 support has been added
112+
#message(DEPRECATION "HAVE_RULES will no longer explicitly enable HAVE_PCRE with Cppcheck 2.2x")
113+
#set(HAVE_PCRE ON)
114+
115+
if(HAVE_PCRE)
116+
# TODO: enable deprecation warning when PCRE2 support has been added
117+
#message(DEPRECATION "Support for PCRE has been deprecated and will be removed in a future Cppcheck version")
118+
endif()
119+
120+
if(MSVC)
121+
if(HAVE_STD_REGEX)
122+
message(WARNING "Disabling usage of std::regex since its implementation is broken in Visual Studio")
123+
set(HAVE_STD_REGEX OFF)
124+
endif()
125+
else()
126+
# TODO: handle explicit disabling
127+
set(HAVE_STD_REGEX ON)
128+
endif()
129+
130+
if(NOT HAVE_PCRE AND NOT HAVE_STD_REGEX)
131+
message(FATAL_ERROR "Cannot use rules since no regular expression engine is available")
132+
endif()
133+
endif()
98134
option(USE_BUNDLED_TINYXML2 "Usage of bundled TinyXML2 library" ON)
99135
if(BUILD_CORE_DLL AND NOT USE_BUNDLED_TINYXML2)
100136
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: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,18 @@
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+
#if defined(HAVE_PCRE)
3037
namespace {
3138
std::string pcreErrorCodeToString(const int pcreExecRet)
3239
{
@@ -249,6 +256,58 @@ namespace {
249256
return "";
250257
}
251258
}
259+
#endif // HAVE_PCRE
260+
261+
#if defined(HAVE_STD_REGEX)
262+
namespace {
263+
class StdRegex : public Regex
264+
{
265+
public:
266+
explicit StdRegex(std::string pattern)
267+
: mPattern(std::move(pattern))
268+
{}
269+
270+
std::string compile()
271+
{
272+
if (mCompiled)
273+
return "regular expression has already been compiled";
274+
275+
try {
276+
mRegex = std::regex(mPattern);
277+
} catch (const std::exception& e) {
278+
return e.what();
279+
}
280+
mCompiled = true;
281+
return "";
282+
}
283+
284+
std::string match(const std::string& str, const MatchFn& matchFn) const override
285+
{
286+
if (!mCompiled)
287+
return "regular expression has not been compiled yet";
288+
289+
auto I = std::sregex_iterator(str.cbegin(), str.cend(), mRegex);
290+
const auto E = std::sregex_iterator();
291+
while (I != E)
292+
{
293+
const std::smatch& match = *I;
294+
matchFn(match.position(), match.position() + match.length());
295+
++I;
296+
}
297+
return "";
298+
}
299+
300+
Engine engine() const override {
301+
return Engine::Std;
302+
}
303+
304+
private:
305+
std::string mPattern;
306+
std::regex mRegex;
307+
bool mCompiled{};
308+
};
309+
}
310+
#endif // HAVE_STD_REGEX
252311

253312
template<typename T>
254313
static T* createAndCompileRegex(std::string pattern, std::string& err)
@@ -261,10 +320,21 @@ static T* createAndCompileRegex(std::string pattern, std::string& err)
261320
std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::string& err)
262321
{
263322
Regex* regex = nullptr;
264-
if (engine == Engine::Pcre)
265-
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
266-
else {
267-
err = "unknown regular expression engine";
323+
switch(engine)
324+
{
325+
#if defined(HAVE_PCRE)
326+
case Engine::Pcre:
327+
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
328+
break;
329+
#endif
330+
#if defined(HAVE_STD_REGEX)
331+
case Engine::Std:
332+
regex = createAndCompileRegex<StdRegex>(std::move(pattern), err);
333+
break;
334+
#endif
335+
default:
336+
err = "unknown regular expression engine";
337+
break;
268338
}
269339
if (!err.empty()) {
270340
delete regex;
@@ -273,4 +343,31 @@ std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::st
273343
return std::shared_ptr<Regex>(regex);
274344
}
275345

346+
bool Regex::stringToEngine(const char* engine, Regex::Engine& regexEngine)
347+
{
348+
#if defined(HAVE_PCRE)
349+
if (std::strcmp(engine, "pcre") == 0) {
350+
regexEngine = Regex::Engine::Pcre;
351+
return true;
352+
}
353+
#endif
354+
#if defined(HAVE_STD_REGEX)
355+
if (std::strcmp(engine, "std") == 0) {
356+
regexEngine = Regex::Engine::Std;
357+
return true;
358+
}
359+
#endif
360+
regexEngine = Regex::Engine::Unknown;
361+
return false;
362+
}
363+
364+
Regex::Engine Regex::defaultEngine()
365+
{
366+
#if defined(HAVE_PCRE)
367+
return Regex::Engine::Pcre;
368+
#else
369+
return Regex::Engine::Std;
370+
#endif
371+
}
372+
276373
#endif // HAVE_RULES

lib/regex.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,20 @@ class CPPCHECKLIB Regex
4141
enum class Engine : std::uint8_t
4242
{
4343
Unknown = 0,
44-
Pcre = 1
44+
#if defined(HAVE_PCRE)
45+
Pcre = 1,
46+
#endif
47+
#if defined(HAVE_STD_REGEX)
48+
Std = 2
49+
#endif
4550
};
4651

4752
virtual Engine engine() const = 0;
4853

4954
static std::shared_ptr<Regex> create(std::string pattern, Engine engine, std::string& err);
55+
56+
static bool stringToEngine(const char* engine, Regex::Engine& regexEngine);
57+
static Engine defaultEngine();
5058
};
5159

5260
#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
-

0 commit comments

Comments
 (0)