Skip to content

Commit aee2475

Browse files
committed
Regex: added std::regex implementation [skip ci]
1 parent 7828eb5 commit aee2475

File tree

4 files changed

+101
-8
lines changed

4 files changed

+101
-8
lines changed

cli/cmdlineparser.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
398398

399399
bool executorAuto = true;
400400

401+
Regex::Type regexType = Regex::Type::Unknown;
402+
401403
for (int i = 1; i < argc; i++) {
402404
if (argv[i][0] != '-') {
403405
mPathNames.emplace_back(Path::fromNativeSeparators(Path::removeQuotationMarks(argv[i])));
@@ -1207,6 +1209,26 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
12071209
else if (std::strcmp(argv[i], "-q") == 0 || std::strcmp(argv[i], "--quiet") == 0)
12081210
mSettings.quiet = true;
12091211

1212+
// Rule given at command line
1213+
else if (std::strncmp(argv[i], "--regex=", 7) == 0) {
1214+
#ifdef HAVE_RULES
1215+
const std::string type = 7 + argv[i];
1216+
if (type == "pcre") {
1217+
regexType = Regex::Type::Pcre;
1218+
}
1219+
else if (type == "std") {
1220+
regexType = Regex::Type::Std;
1221+
}
1222+
else {
1223+
mLogger.printError("unknown regex type '" + type + "'.");
1224+
return Result::Fail;
1225+
}
1226+
#else
1227+
mLogger.printError("Option --regex cannot be used as Cppcheck has not been built with rules support.");
1228+
return Result::Fail;
1229+
#endif
1230+
}
1231+
12101232
// Output relative paths
12111233
else if (std::strcmp(argv[i], "-rp") == 0 || std::strcmp(argv[i], "--relative-paths") == 0)
12121234
mSettings.relativePaths = true;
@@ -1276,8 +1298,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
12761298
return Result::Fail;
12771299
}
12781300

1301+
// TODO: the type breaks the left-to-right processing
12791302
std::string regex_err;
1280-
auto regex = Regex::create(rule.pattern, regex_err);
1303+
auto regex = Regex::create(rule.pattern, regexType, regex_err);
12811304
if (!regex) {
12821305
mLogger.printError("failed to compile rule pattern '" + rule.pattern + "' (" + regex_err + ").");
12831306
return Result::Fail;
@@ -1360,8 +1383,9 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
13601383
return Result::Fail;
13611384
}
13621385

1386+
// TODO: the type breaks the left-to-right processing
13631387
std::string regex_err;
1364-
auto regex = Regex::create(rule.pattern, regex_err);
1388+
auto regex = Regex::create(rule.pattern, regexType, regex_err);
13651389
if (!regex) {
13661390
mLogger.printError("unable to load rule-file '" + ruleFile + "' - pattern '" + rule.pattern + "' failed to compile (" + regex_err + ").");
13671391
return Result::Fail;

lib/regex.cpp

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "regex.h"
2222

23+
#include <regex>
2324
#include <utility>
2425

2526
#ifdef _WIN32
@@ -188,7 +189,7 @@ namespace {
188189
std::string PcreRegex::compile()
189190
{
190191
if (mRe)
191-
return "pcre_compile failed: regular expression has already been compiled";
192+
return "regular expression has already been compiled";
192193

193194
const char *pcreCompileErrorStr = nullptr;
194195
int erroffset = 0;
@@ -222,7 +223,7 @@ namespace {
222223
std::string PcreRegex::match(const std::string& str, const MatchFn& match) const
223224
{
224225
if (!mRe)
225-
return "pcre_exec failed: regular expression has not been compiled yet";
226+
return "regular expression has not been compiled yet";
226227

227228
int pos = 0;
228229
int ovector[30]= {0};
@@ -246,10 +247,69 @@ namespace {
246247
}
247248
}
248249

249-
std::shared_ptr<Regex> Regex::create(std::string pattern, std::string& err)
250+
namespace {
251+
class StdRegex : public Regex
252+
{
253+
public:
254+
explicit StdRegex(std::string pattern)
255+
: mPattern(std::move(pattern))
256+
{}
257+
258+
std::string compile()
259+
{
260+
if (mCompiled)
261+
return "regular expression has already been compiled";
262+
263+
try {
264+
mRegex = std::regex(mPattern);
265+
} catch(const std::exception& e) {
266+
return e.what();
267+
}
268+
mCompiled = true;
269+
return "";
270+
}
271+
272+
std::string match(const std::string& str, const MatchFn& matchFn) const override
273+
{
274+
if (!mCompiled)
275+
return "regular expression has not been compiled yet";
276+
277+
auto I = std::sregex_iterator(str.cbegin(), str.cend(), mRegex);
278+
const auto E = std::sregex_iterator();
279+
while (I != E)
280+
{
281+
const std::smatch& match = *I;
282+
matchFn(match.position(), match.position() + match.length());
283+
++I;
284+
}
285+
return "";
286+
}
287+
288+
private:
289+
std::string mPattern;
290+
std::regex mRegex;
291+
bool mCompiled{};
292+
};
293+
}
294+
295+
template<typename T>
296+
static T* createAndCompileRegex(std::string pattern, std::string& err)
250297
{
251-
auto* regex = new PcreRegex(std::move(pattern));
298+
T* regex = new T(std::move(pattern));
252299
err = regex->compile();
300+
return regex;
301+
}
302+
303+
std::shared_ptr<Regex> Regex::create(std::string pattern, Type type, std::string& err)
304+
{
305+
Regex* regex = nullptr;
306+
if (type == Type::Pcre)
307+
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
308+
else if (type == Type::Std)
309+
regex = createAndCompileRegex<StdRegex>(std::move(pattern), err);
310+
else {
311+
err = "unknown regular expression type";
312+
}
253313
if (!err.empty()) {
254314
delete regex;
255315
return nullptr;

lib/regex.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,14 @@ class CPPCHECKLIB Regex
3737
using MatchFn = std::function<void (int start, int end)>;
3838
virtual std::string match(const std::string& str, const MatchFn& matchFn) const = 0;
3939

40-
static std::shared_ptr<Regex> create(std::string pattern, std::string& err);
40+
enum class Type
41+
{
42+
Unknown = 0,
43+
Pcre = 1,
44+
Std = 2
45+
};
46+
47+
static std::shared_ptr<Regex> create(std::string pattern, Type type, std::string& err);
4148
};
4249

4350
#endif // HAVE_RULES

test/testregex.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ class TestRegEx : public TestFixture {
4343
#define assertRegex(...) assertRegex_(__FILE__, __LINE__, __VA_ARGS__)
4444
std::shared_ptr<Regex> assertRegex_(const char* file, int line, std::string pattern, const std::string& exp_err = "") const {
4545
std::string regex_err;
46-
auto r = Regex::create(std::move(pattern), regex_err);
46+
auto r = Regex::create(std::move(pattern), Regex::Type::Pcre, regex_err);
4747
if (exp_err.empty())
4848
ASSERT_LOC(!!r.get(), file, line);
4949
else
@@ -80,6 +80,7 @@ class TestRegEx : public TestFixture {
8080

8181
void compileError() const {
8282
(void)assertRegex("[", "pcre_compile failed: missing terminating ] for character class");
83+
(void)assertRegex("[", "Unexpected character within '[...]' in regular expression");
8384
}
8485

8586
void copy() const {
@@ -189,6 +190,7 @@ class TestRegEx : public TestFixture {
189190
#undef assertRegex
190191
};
191192

193+
// TODO: test with Std
192194
REGISTER_TEST(TestRegEx)
193195

194196
#endif // HAVE_RULES

0 commit comments

Comments
 (0)