diff --git a/lix/legacy/nix-build.cc b/lix/legacy/nix-build.cc index 94aba0c07..9103b3c43 100644 --- a/lix/legacy/nix-build.cc +++ b/lix/legacy/nix-build.cc @@ -18,6 +18,7 @@ #include "lix/libcmd/common-eval-args.hh" #include "lix/libexpr/attr-path.hh" #include "lix/libcmd/legacy.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/shlex.hh" #include "nix-build.hh" #include "lix/libstore/temporary-dir.hh" @@ -31,7 +32,7 @@ using namespace std::string_literals; static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings argv) { auto dryRun = false; - auto runEnv = std::regex_search(programName, std::regex("nix-shell$")); + auto runEnv = std::regex_search(programName, regex::parse("nix-shell$")); auto pure = false; auto fromArgs = false; auto packages = false; @@ -68,7 +69,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a script = argv.front(); try { auto lines = tokenizeString(readFile(script), "\n"); - if (std::regex_search(lines.front(), std::regex("^#!"))) { + if (std::regex_search(lines.front(), regex::parse("^#!"))) { lines.pop_front(); inShebang = true; savedArgs = {std::next(argv.begin()), argv.end()}; @@ -76,7 +77,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a for (auto line : lines) { line = chomp(line); std::smatch match; - if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell\\s+(.*)$"))) + if (std::regex_match(line, match, regex::parse("^#!\\s*nix-shell\\s+(.*)$"))) for (const auto & word : shell_split(match[1].str())) argv.push_back(word); } @@ -148,14 +149,14 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a // executes it unless it contains the string "perl" or "indir", // or (undocumented) argv[0] does not contain "perl". Exploit // the latter by doing "exec -a". - if (std::regex_search(interpreter, std::regex("perl"))) + if (std::regex_search(interpreter, regex::parse("perl"))) execArgs = "-a PERL"; std::ostringstream joined; for (const auto & i : savedArgs) joined << shellEscape(i) << ' '; - if (std::regex_search(interpreter, std::regex("ruby"))) { + if (std::regex_search(interpreter, regex::parse("ruby"))) { // Hack for Ruby. Ruby also examines the shebang. It tries to // read the shebang to understand which packages to read from. Since // this is handled via nix-shell -p, we wrap our ruby script execution @@ -388,7 +389,7 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a const auto & inputDrv = inputDrv0; if (std::all_of(envExclude.cbegin(), envExclude.cend(), [&](const std::string & exclude) { - return !std::regex_search(store->printStorePath(inputDrv), std::regex(exclude)); + return !std::regex_search(store->printStorePath(inputDrv), regex::parse(exclude)); })) { accumDerivedPath(makeConstantStorePathRef(inputDrv), inputNode); diff --git a/lix/legacy/nix-channel.cc b/lix/legacy/nix-channel.cc index 0343dd7bd..e6a2eb3ca 100644 --- a/lix/legacy/nix-channel.cc +++ b/lix/legacy/nix-channel.cc @@ -8,6 +8,7 @@ #include "lix/libexpr/eval-settings.hh" // for defexpr #include "lix/libstore/temporary-dir.hh" #include "lix/libutil/async.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/users.hh" #include "nix-channel.hh" @@ -30,10 +31,10 @@ static void readChannels() for (const auto & line : tokenizeString>(channelsFile, "\n")) { chomp(line); - if (std::regex_search(line, std::regex("^\\s*\\#"))) + if (std::regex_search(line, regex::parse("^\\s*\\#"))) continue; auto split = tokenizeString>(line, " "); - auto url = std::regex_replace(split[0], std::regex("/*$"), ""); + auto url = std::regex_replace(split[0], regex::parse("/*$"), ""); auto name = split.size() > 1 ? split[1] : std::string(baseNameOf(url)); channels[name] = url; } @@ -52,9 +53,9 @@ static void writeChannels() // Adds a channel. static void addChannel(const std::string & url, const std::string & name) { - if (!regex_search(url, std::regex("^(file|http|https)://"))) + if (!regex_search(url, regex::parse("^(file|http|https)://"))) throw Error("invalid channel URL '%1%'", url); - if (!regex_search(name, std::regex("^[a-zA-Z0-9_][a-zA-Z0-9_\\.-]*$"))) + if (!regex_search(name, regex::parse("^[a-zA-Z0-9_][a-zA-Z0-9_\\.-]*$"))) throw Error("invalid channel identifier '%1%'", name); readChannels(); channels[name] = url; @@ -101,7 +102,7 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames) auto cname = name; std::smatch match; auto urlBase = std::string(baseNameOf(url)); - if (std::regex_search(urlBase, match, std::regex("(-\\d.*)$"))) + if (std::regex_search(urlBase, match, regex::parse("(-\\d.*)$"))) cname = cname + match.str(1); std::string extraAttrs; @@ -125,7 +126,7 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames) url = result.effectiveUrl; bool unpacked = false; - if (std::regex_search(filename, std::regex("\\.tar\\.(gz|bz2|xz)$"))) { + if (std::regex_search(filename, regex::parse("\\.tar\\.(gz|bz2|xz)$"))) { runProgram(settings.nixBinDir + "/nix-build", false, { "--no-out-link", "--expr", "import " + unpackChannelPath + "{ name = \"" + cname + "\"; channelName = \"" + name + "\"; src = builtins.storePath \"" + filename + "\"; }" }); unpacked = true; @@ -234,8 +235,8 @@ static int main_nix_channel(AsyncIoRoot & aio, std::string programName, Strings name = args[1]; } else { name = baseNameOf(url); - name = std::regex_replace(name, std::regex("-unstable$"), ""); - name = std::regex_replace(name, std::regex("-stable$"), ""); + name = std::regex_replace(name, regex::parse("-unstable$"), ""); + name = std::regex_replace(name, regex::parse("-stable$"), ""); } addChannel(url, name); } diff --git a/lix/libcmd/common-eval-args.cc b/lix/libcmd/common-eval-args.cc index 39c3a1423..50143d64e 100644 --- a/lix/libcmd/common-eval-args.cc +++ b/lix/libcmd/common-eval-args.cc @@ -9,12 +9,13 @@ #include "lix/libstore/store-api.hh" #include "lix/libcmd/command.hh" #include "lix/libutil/async.hh" +#include "lix/libutil/regex.hh" #include namespace nix { -static std::regex const identifierRegex("^[A-Za-z_][A-Za-z0-9_'-]*$"); +static std::regex const identifierRegex = regex::parse("^[A-Za-z_][A-Za-z0-9_'-]*$"); static void warnInvalidNixIdentifier(const std::string & name) { std::smatch match; diff --git a/lix/libexpr/flake/flakeref.cc b/lix/libexpr/flake/flakeref.cc index 1e9091e91..1e1a789d5 100644 --- a/lix/libexpr/flake/flakeref.cc +++ b/lix/libexpr/flake/flakeref.cc @@ -1,6 +1,7 @@ #include "lix/libexpr/flake/flakeref.hh" #include "lix/libstore/store-api.hh" #include "lix/libutil/async.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/url.hh" #include "lix/libutil/url-parts.hh" #include "lix/libfetchers/fetchers.hh" @@ -84,13 +85,13 @@ std::pair parseFlakeRefWithFragment( static std::string fnRegex = "[0-9a-zA-Z-._~!$&'\"()*+,;=]+"; - static std::regex pathUrlRegex( + static std::regex pathUrlRegex = regex::parse( "(/?" + fnRegex + "(?:/" + fnRegex + ")*/?)" + "(?:\\?(" + queryRegex + "))?" + "(?:#(" + queryRegex + "))?", std::regex::ECMAScript); - static std::regex flakeShorthandRegex( + static std::regex flakeShorthandRegex = regex::parse( flakeShorthandRegexS + "(?:#(" + queryRegex + "))?", std::regex::ECMAScript); diff --git a/lix/libexpr/get-drvs.cc b/lix/libexpr/get-drvs.cc index 4ddc07457..f44a4e292 100644 --- a/lix/libexpr/get-drvs.cc +++ b/lix/libexpr/get-drvs.cc @@ -4,6 +4,7 @@ #include "lix/libstore/path.hh" #include "lix/libstore/store-api.hh" #include "lix/libstore/path-with-outputs.hh" +#include "lix/libutil/regex.hh" #include #include @@ -408,7 +409,7 @@ static std::string addToPath(const std::string & s1, const std::string & s2) } -static std::regex attrRegex("[A-Za-z_][A-Za-z0-9-_+]*"); +static std::regex attrRegex = regex::parse("[A-Za-z_][A-Za-z0-9-_+]*"); /* Evaluate value `v'. If it evaluates to a set of type `derivation', diff --git a/lix/libexpr/primops.cc b/lix/libexpr/primops.cc index 607ebd8fc..3e8292599 100644 --- a/lix/libexpr/primops.cc +++ b/lix/libexpr/primops.cc @@ -17,6 +17,7 @@ #include "lix/libexpr/value-to-xml.hh" #include "lix/libexpr/primops.hh" #include "lix/libfetchers/fetch-to-store.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/types.hh" #include @@ -2570,7 +2571,7 @@ struct RegexCache if (it != cache.end()) return it->second; keys.emplace_back(re); - return cache.emplace(keys.back(), std::regex(keys.back(), std::regex::extended)).first->second; + return cache.emplace(keys.back(), regex::parse(keys.back(), std::regex::extended)).first->second; } }; @@ -2609,16 +2610,8 @@ void prim_match(EvalState & state, const PosIdx pos, Value * * args, Value & v) (v.listElems()[i] = state.ctx.mem.allocValue())->mkString(match[i + 1].str()); } - } catch (std::regex_error & e) { // NOLINT(lix-foreign-exceptions) - if (e.code() == std::regex_constants::error_space) { - // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ - state.ctx.errors.make("memory limit exceeded by regular expression '%s'", re) - .atPos(pos) - .debugThrow(); - } else - state.ctx.errors.make("invalid regular expression '%s'", re) - .atPos(pos) - .debugThrow(); + } catch (regex::Error & e) { + state.ctx.errors.make(e.info()).atPos(pos).debugThrow(); } } @@ -2675,16 +2668,8 @@ void prim_split(EvalState & state, const PosIdx pos, Value * * args, Value & v) assert(idx == 2 * len + 1); - } catch (std::regex_error & e) { // NOLINT(lix-foreign-exceptions) - if (e.code() == std::regex_constants::error_space) { - // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ - state.ctx.errors.make("memory limit exceeded by regular expression '%s'", re) - .atPos(pos) - .debugThrow(); - } else - state.ctx.errors.make("invalid regular expression '%s'", re) - .atPos(pos) - .debugThrow(); + } catch (regex::Error & e) { + state.ctx.errors.make(e.info()).atPos(pos).debugThrow(); } } diff --git a/lix/libexpr/primops/fetchTree.cc b/lix/libexpr/primops/fetchTree.cc index 93b08ecc9..0b771c3bc 100644 --- a/lix/libexpr/primops/fetchTree.cc +++ b/lix/libexpr/primops/fetchTree.cc @@ -5,6 +5,7 @@ #include "lix/libfetchers/fetchers.hh" #include "lix/libstore/filetransfer.hh" #include "lix/libfetchers/registry.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/url.hh" #include @@ -91,7 +92,7 @@ std::string fixURIForGit(std::string uri, EvalState & state) /* Detects scp-style uris (e.g. git@github.com:NixOS/nix) and fixes * them by removing the `:` and assuming a scheme of `ssh://` * */ - static std::regex scp_uri("([^/]*)@(.*):(.*)"); + static std::regex scp_uri = regex::parse("([^/]*)@(.*):(.*)"); if (uri[0] != '/' && std::regex_match(uri, scp_uri)) return fixURI(std::regex_replace(uri, scp_uri, "$1@$2/$3"), state, "ssh"); else diff --git a/lix/libfetchers/github.cc b/lix/libfetchers/github.cc index 2c5c9487d..dcd1d4632 100644 --- a/lix/libfetchers/github.cc +++ b/lix/libfetchers/github.cc @@ -5,6 +5,7 @@ #include "lix/libfetchers/builtin-fetchers.hh" #include "lix/libstore/store-api.hh" #include "lix/libutil/async.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/types.hh" #include "lix/libutil/url-parts.hh" #include "lix/libutil/git.hh" @@ -25,7 +26,7 @@ struct DownloadUrl // A github, gitlab, or sourcehut host const static std::string hostRegexS = "[a-zA-Z0-9.-]*"; // FIXME: check -std::regex hostRegex(hostRegexS, std::regex::ECMAScript); +std::regex hostRegex = regex::parse(hostRegexS, std::regex::ECMAScript); struct GitArchiveInputScheme : InputScheme { @@ -448,7 +449,7 @@ struct SourceHutInputScheme : GitArchiveInputScheme } else { refUri = fmt("refs/(heads|tags)/%s", ref); } - std::regex refRegex(refUri); + std::regex refRegex = regex::parse(refUri); auto file = store->toRealPath( TRY_AWAIT(downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers)) diff --git a/lix/libfetchers/indirect.cc b/lix/libfetchers/indirect.cc index 1aec6c522..342c8974e 100644 --- a/lix/libfetchers/indirect.cc +++ b/lix/libfetchers/indirect.cc @@ -1,11 +1,12 @@ #include "lix/libfetchers/fetchers.hh" #include "lix/libfetchers/builtin-fetchers.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/url-parts.hh" #include "lix/libstore/path.hh" namespace nix::fetchers { -std::regex flakeRegex("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript); +std::regex flakeRegex = regex::parse("[a-zA-Z][a-zA-Z0-9_-]*", std::regex::ECMAScript); struct IndirectInputScheme : InputScheme { diff --git a/lix/libstore/binary-cache-store.cc b/lix/libstore/binary-cache-store.cc index f300a44ba..d9487f5b0 100644 --- a/lix/libstore/binary-cache-store.cc +++ b/lix/libstore/binary-cache-store.cc @@ -6,6 +6,7 @@ #include "lix/libstore/fs-accessor.hh" #include "lix/libstore/nar-info.hh" #include "lix/libutil/json.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/result.hh" #include "lix/libutil/sync.hh" #include "lix/libstore/remote-fs-accessor.hh" @@ -213,8 +214,8 @@ try { upsertFile(key, json.dump(), "application/json"); }; - std::regex regex1("^[0-9a-f]{2}$"); - std::regex regex2("^[0-9a-f]{38}\\.debug$"); + std::regex regex1 = regex::parse("^[0-9a-f]{2}$"); + std::regex regex2 = regex::parse("^[0-9a-f]{38}\\.debug$"); for (auto & [s1, s1Inode] : buildIdDir->contents) { auto dir = std::get_if(&s1Inode); diff --git a/lix/libstore/build/local-derivation-goal.cc b/lix/libstore/build/local-derivation-goal.cc index 4239be3ab..faac8084b 100644 --- a/lix/libstore/build/local-derivation-goal.cc +++ b/lix/libstore/build/local-derivation-goal.cc @@ -12,6 +12,7 @@ #include "lix/libstore/path-references.hh" #include "lix/libutil/archive.hh" #include "lix/libstore/daemon.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/result.hh" #include "lix/libutil/topo-sort.hh" #include "lix/libutil/json.hh" @@ -572,7 +573,7 @@ try { throw BuildError("odd number of tokens in 'exportReferencesGraph': '%1%'", s); for (Strings::iterator i = ss.begin(); i != ss.end(); ) { auto fileName = *i++; - static std::regex regex("[A-Za-z_][A-Za-z0-9_.-]*"); + static std::regex regex = nix::regex::parse("[A-Za-z_][A-Za-z0-9_.-]*"); if (!std::regex_match(fileName, regex)) throw Error("invalid file name '%s' in 'exportReferencesGraph'", fileName); diff --git a/lix/libstore/filetransfer.cc b/lix/libstore/filetransfer.cc index fac8fe12d..68702fd72 100644 --- a/lix/libstore/filetransfer.cc +++ b/lix/libstore/filetransfer.cc @@ -3,6 +3,7 @@ #include "lix/libstore/globals.hh" #include "lix/libstore/store-api.hh" #include "lix/libstore/s3.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/signals.hh" #include "lix/libutil/strings.hh" #include "lix/libutil/thread-name.hh" @@ -294,7 +295,7 @@ struct curlFileTransfer : public FileTransfer std::string line(static_cast(contents), realSize); printMsg(lvlVomit, "got header for '%s': %s", uri, trim(line)); - static std::regex statusLine("HTTP/[^ ]+ +[0-9]+(.*)", std::regex::extended | std::regex::icase); + static std::regex statusLine = regex::parse("HTTP/[^ ]+ +[0-9]+(.*)", std::regex::extended | std::regex::icase); if (std::smatch match; std::regex_match(line, match, statusLine)) { statusMsg = trim(match.str(1)); } else { @@ -311,7 +312,7 @@ struct curlFileTransfer : public FileTransfer else if (name == "link" || name == "x-amz-meta-link") { auto value = trim(line.substr(i + 1)); - static std::regex linkRegex("<([^>]*)>; rel=\"immutable\"", std::regex::extended | std::regex::icase); + static std::regex linkRegex = regex::parse("<([^>]*)>; rel=\"immutable\"", std::regex::extended | std::regex::icase); if (std::smatch match; std::regex_match(value, match, linkRegex)) { result.immutableUrl = match.str(1); } else diff --git a/lix/libstore/gc.cc b/lix/libstore/gc.cc index 6cc78a527..309c30fc6 100644 --- a/lix/libstore/gc.cc +++ b/lix/libstore/gc.cc @@ -9,6 +9,7 @@ #include "lix/libutil/finally.hh" #include "lix/libutil/types.hh" #include "lix/libutil/unix-domain-socket.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/strings.hh" #include "lix/libutil/thread-name.hh" @@ -363,7 +364,7 @@ void LocalStore::findPlatformRoots(UncheckedRoots & unchecked) // non-Darwin, non-Linux platforms. Both major platforms have // platform-specific code in lix/libstore/platform/ try { - std::regex lsofRegex(R"(^n(/.*)$)"); + std::regex lsofRegex = regex::parse(R"(^n(/.*)$)"); auto lsofLines = tokenizeString>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n"); for (const auto & line : lsofLines) { diff --git a/lix/libstore/names.cc b/lix/libstore/names.cc index b4102218d..e7c77bf53 100644 --- a/lix/libstore/names.cc +++ b/lix/libstore/names.cc @@ -1,4 +1,5 @@ #include "lix/libstore/names.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/strings.hh" #include @@ -47,7 +48,7 @@ bool DrvName::matches(const DrvName & n) if (name != "*") { if (!regex) { regex = std::make_unique(); - regex->regex = std::regex(name, std::regex::extended); + regex->regex = nix::regex::parse(name, std::regex::extended); } if (!std::regex_match(n.name, regex->regex)) return false; } diff --git a/lix/libstore/outputs-spec.cc b/lix/libstore/outputs-spec.cc index aadc1420d..2d28cf6e5 100644 --- a/lix/libstore/outputs-spec.cc +++ b/lix/libstore/outputs-spec.cc @@ -5,6 +5,7 @@ #include "lix/libstore/outputs-spec.hh" #include "lix/libstore/path-regex.hh" #include "lix/libutil/json.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/strings.hh" namespace nix { @@ -28,7 +29,7 @@ static std::string outputSpecRegexStr = std::optional OutputsSpec::parseOpt(std::string_view s) { - static std::regex regex(std::string { outputSpecRegexStr }); + static std::regex regex = nix::regex::parse(std::string { outputSpecRegexStr }); std::smatch match; std::string s2 { s }; // until some improves std::regex diff --git a/lix/libstore/parsed-derivations.cc b/lix/libstore/parsed-derivations.cc index 4c5cb92f0..b133a17c0 100644 --- a/lix/libstore/parsed-derivations.cc +++ b/lix/libstore/parsed-derivations.cc @@ -1,6 +1,7 @@ #include "lix/libstore/parsed-derivations.hh" #include "lix/libutil/async.hh" #include "lix/libutil/json.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/strings.hh" #include @@ -132,7 +133,7 @@ bool ParsedDerivation::useUidRange() const return getRequiredSystemFeatures().count("uid-range"); } -static std::regex shVarName("[A-Za-z_][A-Za-z0-9_]*"); +static std::regex shVarName = regex::parse("[A-Za-z_][A-Za-z0-9_]*"); kj::Promise>> ParsedDerivation::prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths) diff --git a/lix/libstore/platform/linux.cc b/lix/libstore/platform/linux.cc index 2c62df2db..d1217ccb4 100644 --- a/lix/libstore/platform/linux.cc +++ b/lix/libstore/platform/linux.cc @@ -66,8 +66,8 @@ void LinuxLocalStore::findPlatformRoots(UncheckedRoots & unchecked) auto procDir = AutoCloseDir{opendir("/proc")}; if (procDir) { struct dirent * ent; - auto digitsRegex = std::regex(R"(^\d+$)"); - auto mapRegex = std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)"); + auto digitsRegex = regex::parse(R"(^\d+$)"); + auto mapRegex = regex::parse(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)"); auto storePathRegex = regex::storePathRegex(config().storeDir); while (errno = 0, ent = readdir(procDir.get())) { checkInterrupt(); diff --git a/lix/libstore/store-api.cc b/lix/libstore/store-api.cc index 043515563..059f5099c 100644 --- a/lix/libstore/store-api.cc +++ b/lix/libstore/store-api.cc @@ -15,6 +15,7 @@ #include "lix/libutil/url.hh" #include "lix/libutil/archive.hh" #include "lix/libstore/uds-remote-store.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/signals.hh" #include "lix/libutil/strings.hh" // FIXME this should not be here, see TODO below on @@ -1568,7 +1569,7 @@ static std::string extractConnStr(const std::string &proto, const std::string &c { if (proto.rfind("ssh") != std::string::npos) { std::smatch result; - std::regex v6AddrRegex("^((.*)@)?\\[(.*)\\]$"); + std::regex v6AddrRegex = regex::parse("^((.*)@)?\\[(.*)\\]$"); if (std::regex_match(connStr, result, v6AddrRegex)) { if (result[1].matched) { diff --git a/lix/libutil/cgroup.cc b/lix/libutil/cgroup.cc index deff8565a..2aa8e4d76 100644 --- a/lix/libutil/cgroup.cc +++ b/lix/libutil/cgroup.cc @@ -1,4 +1,5 @@ #include "lix/libutil/logging.hh" +#include "regex.hh" #if __linux__ #include "lix/libutil/cgroup.hh" @@ -39,7 +40,7 @@ std::map getCgroups(const Path & cgroupFile) std::map cgroups; for (auto & line : tokenizeString>(readFile(cgroupFile), "\n")) { - static std::regex regex("([0-9]+):([^:]*):(.*)"); + static std::regex regex = nix::regex::parse("([0-9]+):([^:]*):(.*)"); std::smatch match; if (!std::regex_match(line, match, regex)) throw Error("invalid line '%s' in '%s'", line, cgroupFile); diff --git a/lix/libutil/git.cc b/lix/libutil/git.cc index 476d41587..8069515c4 100644 --- a/lix/libutil/git.cc +++ b/lix/libutil/git.cc @@ -1,4 +1,5 @@ #include "lix/libutil/git.hh" +#include "regex.hh" #include @@ -7,7 +8,7 @@ namespace git { std::optional parseLsRemoteLine(std::string_view line) { - const static std::regex line_regex("^(ref: *)?([^\\s]+)(?:\\t+(.*))?$"); + const static std::regex line_regex = regex::parse("^(ref: *)?([^\\s]+)(?:\\t+(.*))?$"); std::match_results match; if (!std::regex_match(line.cbegin(), line.cend(), match, line_regex)) return std::nullopt; diff --git a/lix/libutil/regex.cc b/lix/libutil/regex.cc index a12d13550..1ec7baa22 100644 --- a/lix/libutil/regex.cc +++ b/lix/libutil/regex.cc @@ -1,3 +1,4 @@ +#include "regex.hh" #include #include @@ -7,13 +8,24 @@ template class std::basic_regex; namespace nix::regex { std::string quoteRegexChars(const std::string & raw) { - static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])"); + static auto specialRegex = parse(R"([.^$\\*+?()\[\]{}|])"); return std::regex_replace(raw, specialRegex, R"(\$&)"); } std::regex storePathRegex(const std::string & storeDir) { - return std::regex(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)"); + return parse(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)"); } +std::regex parse(std::string_view re, std::regex::flag_type flags) +try { + return std::regex(re.begin(), re.end(), flags); // NOLINT: lix-foreign-exceptions +} catch (std::regex_error & e) { // NOLINT: lix-foreign-exceptions + if (e.code() == std::regex_constants::error_space) { + // limit is _GLIBCXX_REGEX_STATE_LIMIT for libstdc++ + throw Error("memory limit exceeded by regular expression '%s'", re); + } else { + throw Error("invalid regular expression '%s': %s", re, e.what()); + } +} } diff --git a/lix/libutil/regex.hh b/lix/libutil/regex.hh index 744a7d54a..b3d3f2b78 100644 --- a/lix/libutil/regex.hh +++ b/lix/libutil/regex.hh @@ -1,11 +1,20 @@ #pragma once ///@file +#include "error.hh" #include #include namespace nix::regex { +class Error : public nix::Error +{ +public: + using nix::Error::Error; +}; + std::string quoteRegexChars(const std::string & raw); std::regex storePathRegex(const std::string & storeDir); + +std::regex parse(std::string_view re, std::regex::flag_type flags = std::regex::ECMAScript); } diff --git a/lix/libutil/shlex.cc b/lix/libutil/shlex.cc index 150e7414d..d37ccd6f1 100644 --- a/lix/libutil/shlex.cc +++ b/lix/libutil/shlex.cc @@ -1,4 +1,5 @@ #include "lix/libutil/shlex.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/strings.hh" namespace nix { @@ -15,7 +16,7 @@ std::vector shell_split(const std::string & input) return result; } - std::regex whitespace("^\\s+"); + std::regex whitespace = regex::parse("^\\s+"); auto begin = inputTrimmed.cbegin(); std::string currentToken; enum State { sBegin, sSingleQuote, sDoubleQuote }; diff --git a/lix/libutil/url-name.cc b/lix/libutil/url-name.cc index ab4c5e0c6..3f1de75ea 100644 --- a/lix/libutil/url-name.cc +++ b/lix/libutil/url-name.cc @@ -1,17 +1,18 @@ #include #include "lix/libutil/url-name.hh" +#include "regex.hh" namespace nix { static std::string const attributeNamePattern("[a-zA-Z0-9_-]+"); -static std::regex const lastAttributeRegex("(?:" + attributeNamePattern + "\\.)*(?!default)(" + attributeNamePattern +")(\\^.*)?"); +static std::regex const lastAttributeRegex = regex::parse("(?:" + attributeNamePattern + "\\.)*(?!default)(" + attributeNamePattern +")(\\^.*)?"); static std::string const pathSegmentPattern("[a-zA-Z0-9_-]+"); -static std::regex const lastPathSegmentRegex(".*/(" + pathSegmentPattern +")"); -static std::regex const secondPathSegmentRegex("(?:" + pathSegmentPattern + ")/(" + pathSegmentPattern +")(?:/.*)?"); -static std::regex const gitProviderRegex("github|gitlab|sourcehut"); -static std::regex const gitSchemeRegex("git($|\\+.*)"); -static std::regex const defaultOutputRegex(".*\\.default($|\\^.*)"); +static std::regex const lastPathSegmentRegex = regex::parse(".*/(" + pathSegmentPattern +")"); +static std::regex const secondPathSegmentRegex = regex::parse("(?:" + pathSegmentPattern + ")/(" + pathSegmentPattern +")(?:/.*)?"); +static std::regex const gitProviderRegex = regex::parse("github|gitlab|sourcehut"); +static std::regex const gitSchemeRegex = regex::parse("git($|\\+.*)"); +static std::regex const defaultOutputRegex = regex::parse(".*\\.default($|\\^.*)"); std::optional getNameFromURL(ParsedURL const & url) { diff --git a/lix/libutil/url.cc b/lix/libutil/url.cc index 23c25edf5..255dbe5ae 100644 --- a/lix/libutil/url.cc +++ b/lix/libutil/url.cc @@ -2,18 +2,19 @@ #include "lix/libutil/url-parts.hh" #include "lix/libutil/split.hh" #include "lix/libutil/strings.hh" +#include "regex.hh" namespace nix { -std::regex refRegex(refRegexS, std::regex::ECMAScript); -std::regex badGitRefRegex(badGitRefRegexS, std::regex::ECMAScript); -std::regex revRegex(revRegexS, std::regex::ECMAScript); -std::regex flakeIdRegex(flakeIdRegexS, std::regex::ECMAScript); -std::regex flakeShorthandRegex(flakeShorthandRegexS, std::regex::ECMAScript); +std::regex refRegex = regex::parse(refRegexS, std::regex::ECMAScript); +std::regex badGitRefRegex = regex::parse(badGitRefRegexS, std::regex::ECMAScript); +std::regex revRegex = regex::parse(revRegexS, std::regex::ECMAScript); +std::regex flakeIdRegex = regex::parse(flakeIdRegexS, std::regex::ECMAScript); +std::regex flakeShorthandRegex = regex::parse(flakeShorthandRegexS, std::regex::ECMAScript); ParsedURL parseURL(const std::string & url) { - static std::regex uriRegex( + static std::regex uriRegex = regex::parse( "((" + schemeRegex + "):" + "(?:(?://(" + authorityRegex + ")(" + absPathRegex + "))|(/?" + pathRegex + ")))" + "(?:\\?(" + queryRegex + "))?" diff --git a/lix/nix/diff-closures.cc b/lix/nix/diff-closures.cc index fae2742e1..288724287 100644 --- a/lix/nix/diff-closures.cc +++ b/lix/nix/diff-closures.cc @@ -5,6 +5,7 @@ #include "lix/libmain/common-args.hh" #include "lix/libstore/names.hh" #include "lix/libutil/json.hh" +#include "lix/libutil/regex.hh" #include "lix/libutil/result.hh" #include "diff-closures.hh" @@ -66,7 +67,7 @@ try { /* Strip the output name. Unfortunately this is ambiguous (we can't distinguish between output names like "bin" and version suffixes like "unstable"). */ - static std::regex regex("(.*)-([a-z]+|lib32|lib64)"); + static std::regex regex = regex::parse("(.*)-([a-z]+|lib32|lib64)"); std::cmatch match; std::string name{path.name()}; std::string_view const origName = path.name(); diff --git a/lix/nix/profile.cc b/lix/nix/profile.cc index 58bdb80d8..f61e4f74d 100644 --- a/lix/nix/profile.cc +++ b/lix/nix/profile.cc @@ -8,6 +8,7 @@ #include "lix/libutil/archive.hh" #include "lix/libstore/builtins/buildenv.hh" #include "lix/libexpr/flake/flakeref.hh" +#include "lix/libutil/regex.hh" #include "user-env.hh" #include "lix/libstore/profiles.hh" #include "lix/libstore/names.hh" @@ -196,7 +197,7 @@ public: } else if (store->isStorePath(s)) { res.push_back(s); } else { - res.push_back(RegexPattern{s,std::regex(s, std::regex::extended | std::regex::icase)}); + res.push_back(RegexPattern{s, regex::parse(s, std::regex::extended | std::regex::icase)}); } } diff --git a/lix/nix/search.cc b/lix/nix/search.cc index 62fa95d85..f76ba2638 100644 --- a/lix/nix/search.cc +++ b/lix/nix/search.cc @@ -11,6 +11,7 @@ #include "lix/libexpr/attr-path.hh" #include "lix/libutil/hilite.hh" #include "lix/libutil/json.hh" +#include "lix/libutil/regex.hh" #include "search.hh" #include @@ -79,10 +80,10 @@ struct CmdSearch : InstallableCommand, MixJSON excludeRegexes.reserve(excludeRes.size()); for (auto & re : res) - regexes.push_back(std::regex(re, std::regex::extended | std::regex::icase)); + regexes.push_back(nix::regex::parse(re, std::regex::extended | std::regex::icase)); for (auto & re : excludeRes) - excludeRegexes.emplace_back(re, std::regex::extended | std::regex::icase); + excludeRegexes.emplace_back(nix::regex::parse(re, std::regex::extended | std::regex::icase)); auto evaluator = getEvaluator(); auto state = evaluator->begin(aio()); diff --git a/subprojects/lix-clang-tidy/ForeignExceptions.cc b/subprojects/lix-clang-tidy/ForeignExceptions.cc index 367bdf2c0..7d440c87f 100644 --- a/subprojects/lix-clang-tidy/ForeignExceptions.cc +++ b/subprojects/lix-clang-tidy/ForeignExceptions.cc @@ -42,6 +42,17 @@ void ForeignExceptions::registerMatchers(ast_matchers::MatchFinder *Finder) { cxxThrowExpr(unless(anyOf(rethrowsAllowed, throwsAllowed))) .bind("throw"))), this); + + // flag STL constructors/functions that have caused exception problems before. + Finder->addMatcher( + traverse( + clang::TK_AsIs, + cxxConstructExpr( + hasDeclaration(cxxConstructorDecl( + hasAncestor(cxxRecordDecl(hasName("std::basic_regex"))), + unless(anyOf(isDefaultConstructor(), isCopyConstructor(), isMoveConstructor()))))) + .bind("bad-ctor")), + this); } void ForeignExceptions::check( @@ -64,6 +75,12 @@ void ForeignExceptions::check( "provide useful traces for async functions. Throw " "nix::ForeignException instead where possible."); } + } else if (const auto *ctor = Result.Nodes.getNodeAs("bad-ctor")) { + diag( + ctor->getLocation(), + "%0 throws non-Lix exceptions. Ensure that they are caught and wrapped " + "properly, ideally by wrapping the constructor invocation itself.") + << ctor->getConstructor()->getNameAsString(); } else { llvm_unreachable("bad match"); } diff --git a/tests/unit/libexpr/error_traces.cc b/tests/unit/libexpr/error_traces.cc index 2d2b2acc1..673cc56b7 100644 --- a/tests/unit/libexpr/error_traces.cc +++ b/tests/unit/libexpr/error_traces.cc @@ -87,6 +87,26 @@ namespace nix { , type \ ) +#define ASSERT_TRACE1_PREFIX(args, type, message) \ + ASSERT_THROW( \ + std::string expr(args); \ + std::string name = expr.substr(0, expr.find(" ")); \ + try { \ + Value v = eval("builtins." args); \ + state.forceValueDeep(v); \ + } catch (BaseError & e) { \ + ASSERT_PRED2([](auto got, auto want) { return got.starts_with(want); }, \ + PrintToString(e.info().msg), \ + PrintToString(message)); \ + ASSERT_EQ(e.info().traces.size(), 1) << "while testing " args << std::endl << e.what(); \ + auto trace = e.info().traces.rbegin(); \ + ASSERT_EQ(PrintToString(trace->hint), \ + PrintToString(HintFmt("while calling the '%s' builtin", name))); \ + throw; \ + } \ + , type \ + ) + #define ASSERT_TRACE2(args, type, message, context) \ ASSERT_THROW( \ std::string expr(args); \ @@ -1112,9 +1132,10 @@ namespace nix { HintFmt("expected a string but found %s: %s", "a set", Uncolored("{ }")), HintFmt("while evaluating the second argument passed to builtins.match")); - ASSERT_TRACE1("match \"(.*\" \"\"", + // explanation string may be platform dependent + ASSERT_TRACE1_PREFIX("match \"(.*\" \"\"", EvalError, - HintFmt("invalid regular expression '%s'", "(.*")); + HintFmt("invalid regular expression '%s': ", "(.*")); } @@ -1130,9 +1151,10 @@ namespace nix { HintFmt("expected a string but found %s: %s", "a set", Uncolored("{ }")), HintFmt("while evaluating the second argument passed to builtins.split")); - ASSERT_TRACE1("split \"f(o*o\" \"1foo2\"", + // explanation string may be platform dependent + ASSERT_TRACE1_PREFIX("split \"f(o*o\" \"1foo2\"", EvalError, - HintFmt("invalid regular expression '%s'", "f(o*o")); + HintFmt("invalid regular expression '%s': ", "f(o*o")); } diff --git a/tests/unit/libstore/path.cc b/tests/unit/libstore/path.cc index 8bdcb5071..35e6c9318 100644 --- a/tests/unit/libstore/path.cc +++ b/tests/unit/libstore/path.cc @@ -1,3 +1,4 @@ +#include "lix/libutil/regex.hh" #include #include @@ -22,7 +23,7 @@ class StorePathTest : public LibStoreTest { }; -static std::regex nameRegex { std::string { nameRegexStr } }; +static std::regex nameRegex = regex::parse(nameRegexStr); #define TEST_DONT_PARSE(NAME, STR) \ TEST_F(StorePathTest, bad_ ## NAME) { \ diff --git a/tests/unit/libutil/hilite.cc b/tests/unit/libutil/hilite.cc index 0a5c4b186..08d0ab5c6 100644 --- a/tests/unit/libutil/hilite.cc +++ b/tests/unit/libutil/hilite.cc @@ -1,4 +1,5 @@ #include "lix/libutil/hilite.hh" +#include "lix/libutil/regex.hh" #include @@ -11,7 +12,7 @@ namespace nix { TEST(hiliteMatches, simpleHighlight) { std::string str = "Hello, world!"; - std::regex re = std::regex("world"); + std::regex re = regex::parse("world"); auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator()); ASSERT_STREQ( hiliteMatches(str, matches, "(", ")").c_str(), @@ -21,7 +22,7 @@ namespace nix { TEST(hiliteMatches, multipleMatches) { std::string str = "Hello, world, world, world, world, world, world, Hello!"; - std::regex re = std::regex("world"); + std::regex re = regex::parse("world"); auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator()); ASSERT_STREQ( hiliteMatches(str, matches, "(", ")").c_str(), @@ -31,8 +32,8 @@ namespace nix { TEST(hiliteMatches, overlappingMatches) { std::string str = "world, Hello, world, Hello, world, Hello, world, Hello, world!"; - std::regex re = std::regex("Hello, world"); - std::regex re2 = std::regex("world, Hello"); + std::regex re = regex::parse("Hello, world"); + std::regex re2 = regex::parse("world, Hello"); auto v = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator()); for(auto it = std::sregex_iterator(str.begin(), str.end(), re2); it != std::sregex_iterator(); ++it) { v.push_back(*it); @@ -46,10 +47,10 @@ namespace nix { TEST(hiliteMatches, complexOverlappingMatches) { std::string str = "legacyPackages.x86_64-linux.git-crypt"; std::vector regexes = { - std::regex("t-cry"), - std::regex("ux\\.git-cry"), - std::regex("git-c"), - std::regex("pt"), + regex::parse("t-cry"), + regex::parse("ux\\.git-cry"), + regex::parse("git-c"), + regex::parse("pt"), }; std::vector matches; for(auto regex : regexes)