mirror of
https://github.com/NixOS/nix
synced 2025-06-28 09:31:16 +02:00
Introduce AbstractPos
This makes the position object used in exceptions abstract, with a method getSource() to get the source code of the file in which the error originated. This is needed for lazy trees because source files don't necessarily exist in the filesystem, and we don't want to make libutil depend on the InputAccessor type in libfetcher.
This commit is contained in:
parent
edb54c62e6
commit
b3fdab28a2
16 changed files with 231 additions and 279 deletions
|
@ -823,7 +823,7 @@ void EvalState::runDebugRepl(const Error * error, const Env & env, const Expr &
|
|||
? std::make_unique<DebugTraceStacker>(
|
||||
*this,
|
||||
DebugTrace {
|
||||
.pos = error->info().errPos ? *error->info().errPos : positions[expr.getPos()],
|
||||
.pos = error->info().errPos ? error->info().errPos : (std::shared_ptr<AbstractPos>) positions[expr.getPos()],
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = error->info().msg,
|
||||
|
@ -1012,7 +1012,7 @@ void EvalState::throwMissingArgumentError(const PosIdx pos, const char * s, cons
|
|||
|
||||
void EvalState::addErrorTrace(Error & e, const char * s, const std::string & s2) const
|
||||
{
|
||||
e.addTrace(std::nullopt, s, s2);
|
||||
e.addTrace(nullptr, s, s2);
|
||||
}
|
||||
|
||||
void EvalState::addErrorTrace(Error & e, const PosIdx pos, const char * s, const std::string & s2) const
|
||||
|
@ -1024,13 +1024,13 @@ static std::unique_ptr<DebugTraceStacker> makeDebugTraceStacker(
|
|||
EvalState & state,
|
||||
Expr & expr,
|
||||
Env & env,
|
||||
std::optional<ErrPos> pos,
|
||||
std::shared_ptr<AbstractPos> && pos,
|
||||
const char * s,
|
||||
const std::string & s2)
|
||||
{
|
||||
return std::make_unique<DebugTraceStacker>(state,
|
||||
DebugTrace {
|
||||
.pos = pos,
|
||||
.pos = std::move(pos),
|
||||
.expr = expr,
|
||||
.env = env,
|
||||
.hint = hintfmt(s, s2),
|
||||
|
@ -1136,9 +1136,9 @@ void EvalState::mkThunk_(Value & v, Expr * expr)
|
|||
void EvalState::mkPos(Value & v, PosIdx p)
|
||||
{
|
||||
auto pos = positions[p];
|
||||
if (!pos.file.empty()) {
|
||||
if (auto path = std::get_if<Path>(&pos.origin)) {
|
||||
auto attrs = buildBindings(3);
|
||||
attrs.alloc(sFile).mkString(pos.file);
|
||||
attrs.alloc(sFile).mkString(*path);
|
||||
attrs.alloc(sLine).mkInt(pos.line);
|
||||
attrs.alloc(sColumn).mkInt(pos.column);
|
||||
v.mkAttrs(attrs);
|
||||
|
@ -1246,7 +1246,7 @@ void EvalState::cacheFile(
|
|||
*this,
|
||||
*e,
|
||||
this->baseEnv,
|
||||
e->getPos() ? std::optional(ErrPos(positions[e->getPos()])) : std::nullopt,
|
||||
e->getPos() ? (std::shared_ptr<AbstractPos>) positions[e->getPos()] : nullptr,
|
||||
"while evaluating the file '%1%':", resolvedPath)
|
||||
: nullptr;
|
||||
|
||||
|
@ -1517,10 +1517,13 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
|
|||
state.forceValue(*vAttrs, (pos2 ? pos2 : this->pos ) );
|
||||
|
||||
} catch (Error & e) {
|
||||
auto pos2r = state.positions[pos2];
|
||||
if (pos2 && pos2r.file != state.derivationNixPath)
|
||||
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
||||
showAttrPath(state, env, attrPath));
|
||||
if (pos2) {
|
||||
auto pos2r = state.positions[pos2];
|
||||
auto origin = std::get_if<Path>(&pos2r.origin);
|
||||
if (!(origin && *origin == state.derivationNixPath))
|
||||
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
|
||||
showAttrPath(state, env, attrPath));
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
|
@ -2497,7 +2500,8 @@ void EvalState::printStats()
|
|||
else
|
||||
obj["name"] = nullptr;
|
||||
if (auto pos = positions[fun->pos]) {
|
||||
obj["file"] = (std::string_view) pos.file;
|
||||
if (auto path = std::get_if<Path>(&pos.origin))
|
||||
obj["file"] = *path;
|
||||
obj["line"] = pos.line;
|
||||
obj["column"] = pos.column;
|
||||
}
|
||||
|
@ -2511,7 +2515,8 @@ void EvalState::printStats()
|
|||
for (auto & i : attrSelects) {
|
||||
json obj = json::object();
|
||||
if (auto pos = positions[i.first]) {
|
||||
obj["file"] = (const std::string &) pos.file;
|
||||
if (auto path = std::get_if<Path>(&pos.origin))
|
||||
obj["file"] = *path;
|
||||
obj["line"] = pos.line;
|
||||
obj["column"] = pos.column;
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ struct RegexCache;
|
|||
std::shared_ptr<RegexCache> makeRegexCache();
|
||||
|
||||
struct DebugTrace {
|
||||
std::optional<ErrPos> pos;
|
||||
std::shared_ptr<AbstractPos> pos;
|
||||
const Expr & expr;
|
||||
const Env & env;
|
||||
hintformat hint;
|
||||
|
@ -457,8 +457,12 @@ private:
|
|||
friend struct ExprAttrs;
|
||||
friend struct ExprLet;
|
||||
|
||||
Expr * parse(char * text, size_t length, FileOrigin origin, const PathView path,
|
||||
const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv);
|
||||
Expr * parse(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
Path basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv);
|
||||
|
||||
public:
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ static Flake getFlake(
|
|||
Value vInfo;
|
||||
state.evalFile(flakeFile, vInfo, true); // FIXME: symlink attack
|
||||
|
||||
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile, foFile}, 0, 0));
|
||||
expectType(state, nAttrs, vInfo, state.positions.add({flakeFile}, 1, 1));
|
||||
|
||||
if (auto description = vInfo.attrs->get(state.sDescription)) {
|
||||
expectType(state, nString, *description->value, description->pos);
|
||||
|
|
|
@ -8,6 +8,65 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
struct SourcePathAdapter : AbstractPos
|
||||
{
|
||||
Path path;
|
||||
|
||||
SourcePathAdapter(Path path)
|
||||
: path(std::move(path))
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<std::string> getSource() const override
|
||||
{
|
||||
try {
|
||||
return readFile(path);
|
||||
} catch (Error &) {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void print(std::ostream & out) const override
|
||||
{
|
||||
out << path;
|
||||
}
|
||||
};
|
||||
|
||||
struct StringPosAdapter : AbstractPos
|
||||
{
|
||||
void print(std::ostream & out) const override
|
||||
{
|
||||
out << "«string»";
|
||||
}
|
||||
};
|
||||
|
||||
struct StdinPosAdapter : AbstractPos
|
||||
{
|
||||
void print(std::ostream & out) const override
|
||||
{
|
||||
out << "«stdin»";
|
||||
}
|
||||
};
|
||||
|
||||
Pos::operator std::shared_ptr<AbstractPos>() const
|
||||
{
|
||||
std::shared_ptr<AbstractPos> pos;
|
||||
|
||||
if (auto path = std::get_if<Path>(&origin))
|
||||
pos = std::make_shared<SourcePathAdapter>(*path);
|
||||
else if (std::get_if<stdin_tag>(&origin))
|
||||
pos = std::make_shared<StdinPosAdapter>();
|
||||
else if (std::get_if<string_tag>(&origin))
|
||||
pos = std::make_shared<StringPosAdapter>();
|
||||
|
||||
if (pos) {
|
||||
pos->line = line;
|
||||
pos->column = column;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Displaying abstract syntax trees. */
|
||||
|
||||
static void showString(std::ostream & str, std::string_view s)
|
||||
|
@ -248,24 +307,10 @@ void ExprPos::show(const SymbolTable & symbols, std::ostream & str) const
|
|||
|
||||
std::ostream & operator << (std::ostream & str, const Pos & pos)
|
||||
{
|
||||
if (!pos)
|
||||
if (auto pos2 = (std::shared_ptr<AbstractPos>) pos) {
|
||||
str << *pos2;
|
||||
} else
|
||||
str << "undefined position";
|
||||
else
|
||||
{
|
||||
auto f = format(ANSI_BOLD "%1%" ANSI_NORMAL ":%2%:%3%");
|
||||
switch (pos.origin) {
|
||||
case foFile:
|
||||
f % (const std::string &) pos.file;
|
||||
break;
|
||||
case foStdin:
|
||||
case foString:
|
||||
f % "(string)";
|
||||
break;
|
||||
default:
|
||||
throw Error("unhandled Pos origin!");
|
||||
}
|
||||
str << (f % pos.line % pos.column).str();
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -23,15 +23,21 @@ MakeError(MissingArgumentError, EvalError);
|
|||
MakeError(RestrictedPathError, Error);
|
||||
|
||||
/* Position objects. */
|
||||
|
||||
struct Pos
|
||||
{
|
||||
std::string file;
|
||||
FileOrigin origin;
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
|
||||
struct stdin_tag {};
|
||||
struct string_tag {};
|
||||
|
||||
typedef std::variant<stdin_tag, string_tag, Path> Origin;
|
||||
|
||||
Origin origin;
|
||||
|
||||
explicit operator bool() const { return line > 0; }
|
||||
|
||||
operator std::shared_ptr<AbstractPos>() const;
|
||||
};
|
||||
|
||||
class PosIdx {
|
||||
|
@ -47,7 +53,11 @@ public:
|
|||
|
||||
explicit operator bool() const { return id > 0; }
|
||||
|
||||
bool operator<(const PosIdx other) const { return id < other.id; }
|
||||
bool operator <(const PosIdx other) const { return id < other.id; }
|
||||
|
||||
bool operator ==(const PosIdx other) const { return id == other.id; }
|
||||
|
||||
bool operator !=(const PosIdx other) const { return id != other.id; }
|
||||
};
|
||||
|
||||
class PosTable
|
||||
|
@ -61,13 +71,13 @@ public:
|
|||
// current origins.back() can be reused or not.
|
||||
mutable uint32_t idx = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
explicit Origin(uint32_t idx): idx(idx), file{}, origin{} {}
|
||||
// Used for searching in PosTable::[].
|
||||
explicit Origin(uint32_t idx): idx(idx), origin{Pos::stdin_tag()} {}
|
||||
|
||||
public:
|
||||
const std::string file;
|
||||
const FileOrigin origin;
|
||||
const Pos::Origin origin;
|
||||
|
||||
Origin(std::string file, FileOrigin origin): file(std::move(file)), origin(origin) {}
|
||||
Origin(Pos::Origin origin): origin(origin) {}
|
||||
};
|
||||
|
||||
struct Offset {
|
||||
|
@ -107,7 +117,7 @@ public:
|
|||
[] (const auto & a, const auto & b) { return a.idx < b.idx; });
|
||||
const auto origin = *std::prev(pastOrigin);
|
||||
const auto offset = offsets[idx];
|
||||
return {origin.file, origin.origin, offset.line, offset.column};
|
||||
return {offset.line, offset.column, origin.origin};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -34,11 +34,6 @@ namespace nix {
|
|||
Path basePath;
|
||||
PosTable::Origin origin;
|
||||
std::optional<ErrorInfo> error;
|
||||
ParseData(EvalState & state, PosTable::Origin origin)
|
||||
: state(state)
|
||||
, symbols(state.symbols)
|
||||
, origin(std::move(origin))
|
||||
{ };
|
||||
};
|
||||
|
||||
struct ParserFormals {
|
||||
|
@ -649,24 +644,20 @@ formal
|
|||
namespace nix {
|
||||
|
||||
|
||||
Expr * EvalState::parse(char * text, size_t length, FileOrigin origin,
|
||||
const PathView path, const PathView basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||
Expr * EvalState::parse(
|
||||
char * text,
|
||||
size_t length,
|
||||
Pos::Origin origin,
|
||||
Path basePath,
|
||||
std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
std::string file;
|
||||
switch (origin) {
|
||||
case foFile:
|
||||
file = path;
|
||||
break;
|
||||
case foStdin:
|
||||
case foString:
|
||||
file = text;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
ParseData data(*this, {file, origin});
|
||||
data.basePath = basePath;
|
||||
ParseData data {
|
||||
.state = *this,
|
||||
.symbols = symbols,
|
||||
.basePath = std::move(basePath),
|
||||
.origin = {origin},
|
||||
};
|
||||
|
||||
yylex_init(&scanner);
|
||||
yy_scan_buffer(text, length, scanner);
|
||||
|
@ -718,14 +709,14 @@ Expr * EvalState::parseExprFromFile(const Path & path, std::shared_ptr<StaticEnv
|
|||
auto buffer = readFile(path);
|
||||
// readFile should have left some extra space for terminators
|
||||
buffer.append("\0\0", 2);
|
||||
return parse(buffer.data(), buffer.size(), foFile, path, dirOf(path), staticEnv);
|
||||
return parse(buffer.data(), buffer.size(), path, dirOf(path), staticEnv);
|
||||
}
|
||||
|
||||
|
||||
Expr * EvalState::parseExprFromString(std::string s, const Path & basePath, std::shared_ptr<StaticEnv> & staticEnv)
|
||||
{
|
||||
s.append("\0\0", 2);
|
||||
return parse(s.data(), s.size(), foString, "", basePath, staticEnv);
|
||||
return parse(s.data(), s.size(), Pos::string_tag(), basePath, staticEnv);
|
||||
}
|
||||
|
||||
|
||||
|
@ -741,7 +732,7 @@ Expr * EvalState::parseStdin()
|
|||
auto buffer = drainFD(0);
|
||||
// drainFD should have left some extra space for terminators
|
||||
buffer.append("\0\0", 2);
|
||||
return parse(buffer.data(), buffer.size(), foStdin, "", absPath("."), staticBaseEnv);
|
||||
return parse(buffer.data(), buffer.size(), Pos::stdin_tag(), absPath("."), staticBaseEnv);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -368,8 +368,7 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
auto output = runProgram(program, true, commandArgs);
|
||||
Expr * parsed;
|
||||
try {
|
||||
auto base = state.positions[pos];
|
||||
parsed = state.parseExprFromString(std::move(output), base.file);
|
||||
parsed = state.parseExprFromString(std::move(output), "/");
|
||||
} catch (Error & e) {
|
||||
e.addTrace(state.positions[pos], "While parsing the output from '%1%'", program);
|
||||
throw;
|
||||
|
@ -798,7 +797,7 @@ static void prim_addErrorContext(EvalState & state, const PosIdx pos, Value * *
|
|||
v = *args[1];
|
||||
} catch (Error & e) {
|
||||
PathSet context;
|
||||
e.addTrace(std::nullopt, state.coerceToString(pos, *args[0], context).toOwned());
|
||||
e.addTrace(nullptr, state.coerceToString(pos, *args[0], context).toOwned());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
@ -4018,7 +4017,7 @@ void EvalState::createBaseEnv()
|
|||
// the parser needs two NUL bytes as terminators; one of them
|
||||
// is implied by being a C string.
|
||||
"\0";
|
||||
eval(parse(code, sizeof(code), foFile, derivationNixPath, "/", staticBaseEnv), *vDerivation);
|
||||
eval(parse(code, sizeof(code), derivationNixPath, "/", staticBaseEnv), *vDerivation);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ namespace nix {
|
|||
|
||||
MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) {
|
||||
if (arg.type() != nAttrs) {
|
||||
*result_listener << "Expexted set got " << arg.type();
|
||||
*result_listener << "Expected set got " << arg.type();
|
||||
return false;
|
||||
} else if (arg.attrs->size() != (size_t)n) {
|
||||
*result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size();
|
||||
|
|
|
@ -151,20 +151,7 @@ namespace nix {
|
|||
// The `y` attribute is at position
|
||||
const char* expr = "builtins.unsafeGetAttrPos \"y\" { y = \"x\"; }";
|
||||
auto v = eval(expr);
|
||||
ASSERT_THAT(v, IsAttrsOfSize(3));
|
||||
|
||||
auto file = v.attrs->find(createSymbol("file"));
|
||||
ASSERT_NE(file, nullptr);
|
||||
// FIXME: The file when running these tests is the input string?!?
|
||||
ASSERT_THAT(*file->value, IsStringEq(expr));
|
||||
|
||||
auto line = v.attrs->find(createSymbol("line"));
|
||||
ASSERT_NE(line, nullptr);
|
||||
ASSERT_THAT(*line->value, IsIntEq(1));
|
||||
|
||||
auto column = v.attrs->find(createSymbol("column"));
|
||||
ASSERT_NE(column, nullptr);
|
||||
ASSERT_THAT(*column->value, IsIntEq(33));
|
||||
ASSERT_THAT(v, IsNull());
|
||||
}
|
||||
|
||||
TEST_F(PrimOpTest, hasAttr) {
|
||||
|
|
|
@ -24,7 +24,8 @@ static void printValueAsXML(EvalState & state, bool strict, bool location,
|
|||
|
||||
static void posToXML(EvalState & state, XMLAttrs & xmlAttrs, const Pos & pos)
|
||||
{
|
||||
xmlAttrs["path"] = pos.file;
|
||||
if (auto path = std::get_if<Path>(&pos.origin))
|
||||
xmlAttrs["path"] = *path;
|
||||
xmlAttrs["line"] = (format("%1%") % pos.line).str();
|
||||
xmlAttrs["column"] = (format("%1%") % pos.column).str();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue