treewide: wrap std::regex_error

otherwise lix may crash when e.g. nix search receives invalid regex.
we now also give better error messages for regex errors during eval.

fixes #803

Change-Id: Icc7c578ff488ba520efac5d898572ccf4486e9a8
This commit is contained in:
eldritch horrors
2025-04-24 13:48:15 +00:00
parent 7dbdd5bd0c
commit feebecd60b
32 changed files with 160 additions and 89 deletions
+7 -6
View File
@@ -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<Strings>(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);
+9 -8
View File
@@ -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<std::vector<std::string>>(channelsFile, "\n")) {
chomp(line);
if (std::regex_search(line, std::regex("^\\s*\\#")))
if (std::regex_search(line, regex::parse("^\\s*\\#")))
continue;
auto split = tokenizeString<std::vector<std::string>>(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);
}
+2 -1
View File
@@ -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 <regex>
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;
+3 -2
View File
@@ -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<FlakeRef, std::string> 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);
+2 -1
View File
@@ -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 <cstring>
#include <regex>
@@ -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',
+6 -21
View File
@@ -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 <boost/container/small_vector.hpp>
@@ -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<EvalError>("memory limit exceeded by regular expression '%s'", re)
.atPos(pos)
.debugThrow();
} else
state.ctx.errors.make<EvalError>("invalid regular expression '%s'", re)
.atPos(pos)
.debugThrow();
} catch (regex::Error & e) {
state.ctx.errors.make<EvalError>(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<EvalError>("memory limit exceeded by regular expression '%s'", re)
.atPos(pos)
.debugThrow();
} else
state.ctx.errors.make<EvalError>("invalid regular expression '%s'", re)
.atPos(pos)
.debugThrow();
} catch (regex::Error & e) {
state.ctx.errors.make<EvalError>(e.info()).atPos(pos).debugThrow();
}
}
+2 -1
View File
@@ -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 <ctime>
@@ -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
+3 -2
View File
@@ -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))
+2 -1
View File
@@ -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
{
+3 -2
View File
@@ -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<nar_index::Directory>(&s1Inode);
+2 -1
View File
@@ -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);
+3 -2
View File
@@ -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<char *>(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
+2 -1
View File
@@ -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<std::vector<std::string>>(runProgram(LSOF, true, { "-n", "-w", "-F", "n" }), "\n");
for (const auto & line : lsofLines) {
+2 -1
View File
@@ -1,4 +1,5 @@
#include "lix/libstore/names.hh"
#include "lix/libutil/regex.hh"
#include "lix/libutil/strings.hh"
#include <regex>
@@ -47,7 +48,7 @@ bool DrvName::matches(const DrvName & n)
if (name != "*") {
if (!regex) {
regex = std::make_unique<Regex>();
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;
}
+2 -1
View File
@@ -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> 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
+2 -1
View File
@@ -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 <regex>
@@ -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<Result<std::optional<JSON>>>
ParsedDerivation::prepareStructuredAttrs(Store & store, const StorePathSet & inputPaths)
+2 -2
View File
@@ -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();
+2 -1
View File
@@ -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) {
+2 -1
View File
@@ -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<std::string, std::string> getCgroups(const Path & cgroupFile)
std::map<std::string, std::string> cgroups;
for (auto & line : tokenizeString<std::vector<std::string>>(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);
+2 -1
View File
@@ -1,4 +1,5 @@
#include "lix/libutil/git.hh"
#include "regex.hh"
#include <regex>
@@ -7,7 +8,7 @@ namespace git {
std::optional<LsRemoteRefLine> 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<std::string_view::const_iterator> match;
if (!std::regex_match(line.cbegin(), line.cend(), match, line_regex))
return std::nullopt;
+14 -2
View File
@@ -1,3 +1,4 @@
#include "regex.hh"
#include <string>
#include <regex>
@@ -7,13 +8,24 @@ template class std::basic_regex<char>;
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());
}
}
}
+9
View File
@@ -1,11 +1,20 @@
#pragma once
///@file
#include "error.hh"
#include <string>
#include <regex>
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);
}
+2 -1
View File
@@ -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<std::string> 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 };
+7 -6
View File
@@ -1,17 +1,18 @@
#include <regex>
#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<std::string> getNameFromURL(ParsedURL const & url)
{
+7 -6
View File
@@ -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 + "))?"
+2 -1
View File
@@ -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();
+2 -1
View File
@@ -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)});
}
}
+3 -2
View File
@@ -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 <regex>
@@ -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());
@@ -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<CXXConstructExpr>("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");
}
+26 -4
View File
@@ -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"));
}
+2 -1
View File
@@ -1,3 +1,4 @@
#include "lix/libutil/regex.hh"
#include <regex>
#include <gtest/gtest.h>
@@ -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) { \
+9 -8
View File
@@ -1,4 +1,5 @@
#include "lix/libutil/hilite.hh"
#include "lix/libutil/regex.hh"
#include <gtest/gtest.h>
@@ -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<std::smatch> matches;
for(auto regex : regexes)