Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ ifndef HAVE_RULES
HAVE_RULES=
endif

ifndef HAVE_PCRE
HAVE_PCRE=1
endif

ifndef MATCHCOMPILER
MATCHCOMPILER=
endif
Expand Down Expand Up @@ -140,19 +144,26 @@ ifeq (g++, $(findstring g++,$(CXX)))
endif
override CXXFLAGS += -std=c++11
ifeq ($(HAVE_RULES),yes)
override CPPFLAGS += -DHAVE_RULES
# TODO: handle explicit disabling
HAVE_PCRE=1
override CPPFLAGS += -DHAVE_STD_REGEX
else ifneq ($(HAVE_RULES),)
$(error invalid HAVE_RULES value '$(HAVE_RULES)')
endif

ifeq ($(HAVE_PCRE),1)
PCRE_CONFIG = $(shell which pcre-config)
ifeq ($(PCRE_CONFIG),)
$(error Did not find pcre-config)
endif
override CXXFLAGS += $(shell $(PCRE_CONFIG) --cflags)
override CPPFLAGS += -DHAVE_RULES
override CPPFLAGS += -DHAVE_PCRE
ifdef LIBS
LIBS += $(shell $(PCRE_CONFIG) --libs)
else
LIBS=$(shell $(PCRE_CONFIG) --libs)
endif
else ifneq ($(HAVE_RULES),)
$(error invalid HAVE_RULES value '$(HAVE_RULES)')
endif

# older make versions do not support # in $(shell) and newer ones handle the escape sequence literally
Expand Down
10 changes: 4 additions & 6 deletions cli/cmdlineparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1317,7 +1317,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
}

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

for (const tinyxml2::XMLElement *subnode = node->FirstChildElement(); subnode; subnode = subnode->NextSiblingElement()) {
const char * const subname = subnode->Name();
Expand Down Expand Up @@ -1377,10 +1377,8 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a
}
else if (std::strcmp(subname, "engine") == 0) {
const char * const engine = empty_if_null(subtext);
if (std::strcmp(engine, "pcre") == 0) {
regexEngine = Regex::Engine::Pcre;
}
else {
if (!Regex::stringToEngine(engine, regexEngine))
{
mLogger.printError(std::string("unknown regex engine '") + engine + "'.");
return Result::Fail;
}
Expand Down
6 changes: 6 additions & 0 deletions cmake/compilerDefinitions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ endif()

if(HAVE_RULES)
add_definitions(-DHAVE_RULES)
if(HAVE_PCRE)
add_definitions(-DHAVE_PCRE)
endif()
if(HAVE_STD_REGEX)
add_definitions(-DHAVE_STD_REGEX)
endif()
endif()

if(Boost_FOUND)
Expand Down
38 changes: 37 additions & 1 deletion cmake/options.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,43 @@ if(NOT BUILD_GUI)
endif()
endif()

option(HAVE_RULES "Usage of rules (needs PCRE library and headers)" OFF)
option(HAVE_RULES "Usage of rules" OFF)
option(HAVE_PCRE "Allow usage of PCRE in rules" OFF)
option(HAVE_STD_REGEX "Allow usage of std::regex in rules" OFF)
# TODO: add option to specify default regular expression engine
if(NOT HAVE_RULES)
if(HAVE_PCRE)
message(WARNING "HAVE_PCRE has no effect without HAVE_RULES")
endif()
if(HAVE_STD_REGEX)
message(WARNING "HAVE_STD_REGEX has no effect without HAVE_RULES")
endif()
endif()
if(HAVE_RULES)
# TODO: handle explicit disabling
# TODO: enable deprecation warning when PCRE2 support has been added
#message(DEPRECATION "HAVE_RULES will no longer explicitly enable HAVE_PCRE with Cppcheck 2.2x")
#set(HAVE_PCRE ON)

if(HAVE_PCRE)
# TODO: enable deprecation warning when PCRE2 support has been added
#message(DEPRECATION "Support for PCRE has been deprecated and will be removed in a future Cppcheck version")
endif()

if(MSVC)
if(HAVE_STD_REGEX)
message(WARNING "Disabling usage of std::regex since its implementation is broken in Visual Studio")
set(HAVE_STD_REGEX OFF)
endif()
else()
# TODO: handle explicit disabling
set(HAVE_STD_REGEX ON)
endif()

if(NOT HAVE_PCRE AND NOT HAVE_STD_REGEX)
message(FATAL_ERROR "Cannot use rules since no regular expression engine is available")
endif()
endif()
option(USE_BUNDLED_TINYXML2 "Usage of bundled TinyXML2 library" ON)
if(BUILD_CORE_DLL AND NOT USE_BUNDLED_TINYXML2)
message(FATAL_ERROR "Cannot use external TinyXML2 library when building lib as DLL")
Expand Down
7 changes: 6 additions & 1 deletion cmake/printInfo.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ endif()
message(STATUS)
message(STATUS "HAVE_RULES = ${HAVE_RULES}")
if(HAVE_RULES)
message(STATUS "PCRE_LIBRARY = ${PCRE_LIBRARY}")
message(STATUS "HAVE_PCRE = ${HAVE_PCRE}")
if(HAVE_PCRE)
message(STATUS "PCRE_INCLUDE = ${PCRE_INCLUDE}")
message(STATUS "PCRE_LIBRARY = ${PCRE_LIBRARY}")
endif()
message(STATUS "HAVE_STD_REGEX = ${HAVE_STD_REGEX}")
endif()
message(STATUS)
message(STATUS "DISALLOW_THREAD_EXECUTOR = ${DISALLOW_THREAD_EXECUTOR}")
Expand Down
105 changes: 101 additions & 4 deletions lib/regex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,18 @@

#include <utility>

#if defined(HAVE_STD_REGEX)
#include <regex>
#endif

#if defined(HAVE_PCRE)
#ifdef _WIN32
#define PCRE_STATIC
#endif
#include <pcre.h>
#endif

#if defined(HAVE_PCRE)
namespace {
std::string pcreErrorCodeToString(const int pcreExecRet)
{
Expand Down Expand Up @@ -249,6 +256,58 @@ namespace {
return "";
}
}
#endif // HAVE_PCRE

#if defined(HAVE_STD_REGEX)
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 "";
}

Engine engine() const override {
return Engine::Std;
}

private:
std::string mPattern;
std::regex mRegex;
bool mCompiled{};
};
}
#endif // HAVE_STD_REGEX

template<typename T>
static T* createAndCompileRegex(std::string pattern, std::string& err)
Expand All @@ -261,10 +320,21 @@ static T* createAndCompileRegex(std::string pattern, std::string& err)
std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::string& err)
{
Regex* regex = nullptr;
if (engine == Engine::Pcre)
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
else {
err = "unknown regular expression engine";
switch(engine)
{
#if defined(HAVE_PCRE)
case Engine::Pcre:
regex = createAndCompileRegex<PcreRegex>(std::move(pattern), err);
break;
#endif
#if defined(HAVE_STD_REGEX)
case Engine::Std:
regex = createAndCompileRegex<StdRegex>(std::move(pattern), err);
break;
#endif
default:
err = "unknown regular expression engine";
break;
}
if (!err.empty()) {
delete regex;
Expand All @@ -273,4 +343,31 @@ std::shared_ptr<Regex> Regex::create(std::string pattern, Engine engine, std::st
return std::shared_ptr<Regex>(regex);
}

bool Regex::stringToEngine(const char* engine, Regex::Engine& regexEngine)
{
#if defined(HAVE_PCRE)
if (std::strcmp(engine, "pcre") == 0) {
regexEngine = Regex::Engine::Pcre;
return true;
}
#endif
#if defined(HAVE_STD_REGEX)
if (std::strcmp(engine, "std") == 0) {
regexEngine = Regex::Engine::Std;
return true;
}
#endif
regexEngine = Regex::Engine::Unknown;
return false;
}

Regex::Engine Regex::defaultEngine()
{
#if defined(HAVE_PCRE)
return Regex::Engine::Pcre;
#else
return Regex::Engine::Std;
#endif
}

#endif // HAVE_RULES
10 changes: 9 additions & 1 deletion lib/regex.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,20 @@ class CPPCHECKLIB Regex
enum class Engine : std::uint8_t
{
Unknown = 0,
Pcre = 1
#if defined(HAVE_PCRE)
Pcre = 1,
#endif
#if defined(HAVE_STD_REGEX)
Std = 2
#endif
};

virtual Engine engine() const = 0;

static std::shared_ptr<Regex> create(std::string pattern, Engine engine, std::string& err);

static bool stringToEngine(const char* engine, Regex::Engine& regexEngine);
static Engine defaultEngine();
};

#endif // HAVE_RULES
Expand Down
2 changes: 2 additions & 0 deletions releasenotes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ Infrastructure & dependencies:
-

Other:
- 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.
-
-
Loading