mirror of
https://github.com/github/codeql.git
synced 2026-07-24 20:52:03 +02:00
Ports the Ruby regex library to C++/ECMAScript (`std::regex` default dialect): - `cpp/ql/lib/semmle/code/cpp/regex/internal/ParseRegExp.qll`: ECMAScript-adapted parser over `StringLiteral` - `cpp/ql/lib/semmle/code/cpp/regex/RegexTreeView.qll`: tree view implementing `RegexTreeViewSig` - `cpp/ql/lib/qlpack.yml`: add `codeql/regex` dependency (already present) - `cpp/ql/test/library-tests/regex/`: hermetic test corpus (test.cpp with std:: stubs), parse.ql, regexp.ql, RegExpConfig.qll - `cpp/ql/lib/change-notes/released/12.1.0.md`: minorAnalysis change note Note: .expected files are placeholders; run `codeql test run --accept` after database generation to populate them.
134 lines
3.6 KiB
C++
134 lines
3.6 KiB
C++
/**
|
|
* Minimal stubs for std::regex so that the test corpus compiles hermetically
|
|
* without including any external/standard headers.
|
|
*
|
|
* The qualified names (std::basic_regex, std::regex, std::regex_match, ...) match
|
|
* the real C++ standard library names so that future flow-config PRs can identify
|
|
* them without any test rework.
|
|
*/
|
|
namespace std {
|
|
|
|
template <class CharT>
|
|
class basic_regex {
|
|
public:
|
|
basic_regex() {}
|
|
explicit basic_regex(const char* pattern) {}
|
|
explicit basic_regex(const char* pattern, unsigned flags) {}
|
|
basic_regex& assign(const char* pattern) { return *this; }
|
|
};
|
|
|
|
typedef basic_regex<char> regex;
|
|
|
|
// Minimal free-function stubs (subject types use const char* to avoid pulling in <string>)
|
|
template <class CharT>
|
|
bool regex_match(const char* s, const basic_regex<CharT>& re) { return false; }
|
|
|
|
template <class CharT>
|
|
bool regex_search(const char* s, const basic_regex<CharT>& re) { return false; }
|
|
|
|
template <class CharT>
|
|
const char* regex_replace(const char* s, const basic_regex<CharT>& re, const char* fmt) {
|
|
return s;
|
|
}
|
|
|
|
} // namespace std
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Test corpus — each regex is wrapped in a std::regex construction or use.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
void test_empty() {
|
|
// Empty pattern
|
|
std::regex re1("");
|
|
}
|
|
|
|
void test_basic_sequence() {
|
|
// Basic sequence
|
|
std::regex re("abc");
|
|
}
|
|
|
|
void test_repetition() {
|
|
std::regex re1(R"(a*b+c?d)");
|
|
std::regex re2(R"(a{4,8})");
|
|
std::regex re3(R"(a{3,})");
|
|
std::regex re4(R"(a{7})");
|
|
}
|
|
|
|
void test_alternation() {
|
|
std::regex re(R"(foo|bar)");
|
|
}
|
|
|
|
void test_character_classes() {
|
|
std::regex re1(R"([abc])");
|
|
std::regex re2(R"([a-fA-F0-9_])");
|
|
std::regex re3(R"([+-]?\d+)"); // Note: \d is a value-level \d
|
|
std::regex re4(R"([\w]+)");
|
|
std::regex re5(R"(\[\][123])");
|
|
std::regex re6(R"([^A-Z])");
|
|
std::regex re7(R"([]])"); // matches ']'
|
|
std::regex re8(R"([^]])"); // matches anything except ']'
|
|
std::regex re9(R"([^-])");
|
|
std::regex re10(R"([|])");
|
|
}
|
|
|
|
void test_meta_character_classes() {
|
|
std::regex re1(R"(.*)");
|
|
std::regex re2(R"(\w+\W)");
|
|
std::regex re3(R"(\s\S)");
|
|
std::regex re4(R"(\d\D)");
|
|
std::regex re5(R"(\n\r\t)");
|
|
}
|
|
|
|
void test_anchors() {
|
|
std::regex re1(R"(\b!a\B)");
|
|
}
|
|
|
|
void test_groups() {
|
|
std::regex re1(R"((foo)*bar)");
|
|
std::regex re2(R"(fo(o|b)ar)");
|
|
std::regex re3(R"((a|b|cd)e)");
|
|
std::regex re4(R"((?::+)\w)"); // Non-capturing group
|
|
}
|
|
|
|
void test_named_groups() {
|
|
std::regex re1(R"((?<id>\w+))");
|
|
}
|
|
|
|
void test_backreferences() {
|
|
std::regex re1(R"((a+)b+\1)");
|
|
std::regex re2(R"((?<qux>q+)\s+\k<qux>+)");
|
|
}
|
|
|
|
// Two separate character classes, each containing a single POSIX bracket expression
|
|
void test_posix_separate() {
|
|
std::regex re(R"([[:alpha:]][[:digit:]])");
|
|
}
|
|
|
|
// A single character class containing two POSIX bracket expressions
|
|
void test_posix_combined() {
|
|
std::regex re(R"([[:alpha:][:digit:]])");
|
|
}
|
|
|
|
// A single character class containing two ranges and one POSIX bracket expression
|
|
void test_posix_mixed() {
|
|
std::regex re(R"([A-F[:digit:]a-f])");
|
|
}
|
|
|
|
// *Not* a POSIX bracket expression; just a regular character class.
|
|
void test_posix_not_posix() {
|
|
std::regex re(R"([:digit:])");
|
|
}
|
|
|
|
// Unicode escape
|
|
void test_unicode() {
|
|
std::regex re(R"(\u0041)");
|
|
}
|
|
|
|
// Lookahead / lookbehind assertions
|
|
void test_lookaround() {
|
|
std::regex re1(R"((?=\w))"); // positive lookahead
|
|
std::regex re2(R"((?!\n))"); // negative lookahead
|
|
std::regex re3(R"((?<=\.))"); // positive lookbehind
|
|
std::regex re4(R"((?<!\\))"); // negative lookbehind
|
|
}
|