Connect AbstractPos with Pos

This commit is contained in:
Eelco Dolstra 2022-07-05 20:43:20 +02:00
parent 9e9170a92e
commit 72dffd6c6c
13 changed files with 95 additions and 119 deletions

View file

@ -215,9 +215,8 @@ static std::ostream & showDebugTrace(std::ostream & out, const PosTable & positi
: (std::shared_ptr<AbstractPos>) positions[dt.expr.getPos() ? dt.expr.getPos() : noPos];
if (pos) {
pos->print(out);
if (auto loc = pos->getCodeLines()) {;
out << pos;
if (auto loc = pos->getCodeLines()) {
out << "\n";
printCodeLines(out, "", *pos, *loc);
out << "\n";
@ -584,7 +583,9 @@ bool NixRepl::processLine(std::string line)
return {filename, 0};
} else if (v.isLambda()) {
auto pos = state->positions[v.lambda.fun->pos];
return {pos.file, pos.line};
// FIXME
abort();
//return {pos.file, pos.line};
} else {
// assume it's a derivation
return findPackageFilename(*state, v, arg);

View file

@ -1081,9 +1081,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<SourcePath>(&pos.origin)) {
auto attrs = buildBindings(3);
attrs.alloc(sFile).mkString(pos.file);
attrs.alloc(sFile).mkString(path->path.abs());
attrs.alloc(sLine).mkInt(pos.line);
attrs.alloc(sColumn).mkInt(pos.column);
v.mkAttrs(attrs);
@ -1450,7 +1450,8 @@ void ExprSelect::eval(EvalState & state, Env & env, Value & v)
} catch (Error & e) {
auto pos2r = state.positions[pos2];
if (pos2 && pos2r.file != state.derivationNixPath)
// FIXME: use MemoryAccessor
if (pos2 /* && pos2r.origin != Pos(state.derivationNixPath) */)
state.addErrorTrace(e, pos2, "while evaluating the attribute '%1%'",
showAttrPath(state, env, attrPath));
throw;
@ -2465,7 +2466,8 @@ void EvalState::printStats()
else
obj.attr("name", nullptr);
if (auto pos = positions[i.first->pos]) {
obj.attr("file", (const std::string &) pos.file);
// FIXME
//obj.attr("file", (const std::string &) pos.file);
obj.attr("line", pos.line);
obj.attr("column", pos.column);
}
@ -2477,7 +2479,8 @@ void EvalState::printStats()
for (auto & i : attrSelects) {
auto obj = list.object();
if (auto pos = positions[i.first]) {
obj.attr("file", (const std::string &) pos.file);
// FIXME
//obj.attr("file", (const std::string &) pos.file);
obj.attr("line", pos.line);
obj.attr("column", pos.column);
}

View file

@ -453,8 +453,7 @@ private:
Expr * parse(
char * text,
size_t length,
FileOrigin origin,
const PathView path,
Pos::Origin origin,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv);

View file

@ -154,7 +154,7 @@ static Flake readFlake(
Value vInfo;
state.evalFile(flakePath, vInfo, true);
expectType(state, nAttrs, vInfo, state.positions.add({flakePath.to_string(), foFile}, 0, 0));
expectType(state, nAttrs, vInfo, state.positions.add(Pos::Origin(rootDir), 1, 1));
Flake flake {
.originalRef = originalRef,

View file

@ -10,16 +10,27 @@ namespace nix {
struct SourcePathAdapter : AbstractPos
{
std::string file;
SourcePath path;
ref<InputAccessor> hack; // FIXME: remove
SourcePathAdapter(SourcePath path)
: path(std::move(path))
, hack(path.accessor.shared_from_this())
{
}
std::optional<std::string> getSource() const override
{
return std::nullopt;
try {
return path.readFile();
} catch (Error &) {
return std::nullopt;
}
}
void print(std::ostream & out) const override
{
out << fmt(ANSI_BLUE "at " ANSI_WARNING "%s:%s" ANSI_NORMAL ":", file, showErrPos());
out << path;
}
};
@ -27,7 +38,7 @@ struct StringPosAdapter : AbstractPos
{
void print(std::ostream & out) const override
{
out << fmt(ANSI_BLUE "at " ANSI_WARNING "«string»:%s" ANSI_NORMAL ":", showErrPos());
out << "«string»";
}
};
@ -35,35 +46,27 @@ struct StdinPosAdapter : AbstractPos
{
void print(std::ostream & out) const override
{
out << fmt(ANSI_BLUE "at " ANSI_WARNING "«stdin»:%s" ANSI_NORMAL ":", showErrPos());
out << "«stdin»";
}
};
Pos::operator std::shared_ptr<AbstractPos>() const
{
if (!line) return nullptr;
std::shared_ptr<AbstractPos> pos;
switch (origin) {
case foFile: {
auto pos = std::make_shared<SourcePathAdapter>();
if (auto path = std::get_if<SourcePath>(&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;
pos->file = file;
return pos;
}
case foStdin: {
auto pos = std::make_shared<StdinPosAdapter>();
pos->line = line;
pos->column = column;
return pos;
}
case foString:
auto pos = std::make_shared<StringPosAdapter>();
pos->line = line;
pos->column = column;
return pos;
}
assert(false);
return pos;
}
/* Displaying abstract syntax trees. */
@ -306,24 +309,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;
}

View file

@ -21,21 +21,20 @@ MakeError(TypeError, EvalError);
MakeError(UndefinedVarError, Error);
MakeError(MissingArgumentError, EvalError);
// FIXME: change this into a variant?
typedef enum {
foFile,
foStdin,
foString
} FileOrigin;
/* Position objects. */
struct Pos
{
std::string file;
FileOrigin origin;
uint32_t line;
uint32_t column;
struct no_pos_tag {};
struct stdin_tag {};
struct string_tag {};
typedef std::variant<no_pos_tag, stdin_tag, string_tag, SourcePath> Origin;
Origin origin;
explicit operator bool() const { return line > 0; }
operator std::shared_ptr<AbstractPos>() const;
@ -68,13 +67,12 @@ 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{} {}
explicit Origin(uint32_t idx): idx(idx), origin{Pos::no_pos_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 {
@ -114,7 +112,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};
}
};

View file

@ -648,30 +648,16 @@ namespace nix {
Expr * EvalState::parse(
char * text,
size_t length,
FileOrigin origin,
const PathView path,
Pos::Origin origin,
const SourcePath & basePath,
std::shared_ptr<StaticEnv> & staticEnv)
{
yyscan_t scanner;
std::string file;
switch (origin) {
case foFile:
// FIXME: change this to a SourcePath.
file = path;
break;
case foStdin:
case foString:
file = text;
break;
default:
assert(false);
}
ParseData data {
.state = *this,
.symbols = symbols,
.basePath = basePath,
.origin = {file, origin},
.origin = {origin},
};
yylex_init(&scanner);
@ -713,14 +699,14 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path, std::shared_ptr<Sta
// readFile hopefully have left some extra space for terminators
buffer.append("\0\0", 2);
// FIXME: pass SourcePaths
return parse(buffer.data(), buffer.size(), foFile, path.path.abs(), path.parent(), staticEnv);
return parse(buffer.data(), buffer.size(), Pos::Origin(path), path.parent(), staticEnv);
}
Expr * EvalState::parseExprFromString(std::string s, const SourcePath & 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);
}
@ -736,7 +722,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, "", rootPath(absPath(".")), staticBaseEnv);
return parse(buffer.data(), buffer.size(), Pos::stdin_tag(), rootPath(absPath(".")), staticBaseEnv);
}

View file

@ -368,8 +368,8 @@ 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), state.rootPath(base.file));
//auto base = state.positions[pos];
parsed = state.parseExprFromString(std::move(output), state.rootPath("/"));
} catch (Error & e) {
e.addTrace(state.positions[pos], "While parsing the output from '%1%'", program);
throw;
@ -3979,6 +3979,7 @@ void EvalState::createBaseEnv()
/* Add a wrapper around the derivation primop that computes the
`drvPath' and `outPath' attributes lazily. */
// FIXME: use corepkgsFS.
sDerivationNix = symbols.create(derivationNixPath);
auto vDerivation = allocValue();
addConstant("derivation", vDerivation);
@ -3996,7 +3997,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, rootPath("/"), staticBaseEnv), *vDerivation);
eval(parse(code, sizeof(code), Pos::string_tag(), rootPath("/"), staticBaseEnv), *vDerivation);
}

View file

@ -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;
// FIXME
//xmlAttrs["path"] = pos.file;
xmlAttrs["line"] = (format("%1%") % pos.line).str();
xmlAttrs["column"] = (format("%1%") % pos.column).str();
}

View file

@ -35,13 +35,13 @@ std::ostream & operator<<(std::ostream & os, const hintformat & hf)
return os << hf.str();
}
std::string AbstractPos::showErrPos() const
std::ostream & operator << (std::ostream & str, const AbstractPos & pos)
{
if (column > 0) {
return fmt("%d:%d", line, column);
} else {
return fmt("%d", line);
}
pos.print(str);
str << ":" << pos.line;
if (pos.column > 0)
str << ":" << pos.column;
return str;
}
std::optional<LinesOfCode> AbstractPos::getCodeLines() const
@ -202,8 +202,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
auto noSource = ANSI_ITALIC " (source not available)" ANSI_NORMAL "\n";
if (einfo.errPos) {
oss << "\n";
einfo.errPos->print(oss);
oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *einfo.errPos << ANSI_NORMAL << ":";
if (auto loc = einfo.errPos->getCodeLines()) {
oss << "\n";
@ -226,8 +225,7 @@ std::ostream & showErrorInfo(std::ostream & out, const ErrorInfo & einfo, bool s
oss << "\n" << "" << iter->hint.str() << "\n";
if (iter->pos) {
oss << "\n";
iter->pos->print(oss);
oss << "\n" << ANSI_BLUE << "at " ANSI_WARNING << *iter->pos << ANSI_NORMAL << ":";
if (auto loc = iter->pos->getCodeLines()) {
oss << "\n";

View file

@ -73,11 +73,11 @@ struct AbstractPos
virtual void print(std::ostream & out) const = 0;
std::string showErrPos() const;
std::optional<LinesOfCode> getCodeLines() const;
};
std::ostream & operator << (std::ostream & str, const AbstractPos & pos);
void printCodeLines(std::ostream & out,
const std::string & prefix,
const AbstractPos & errPos,

View file

@ -11,7 +11,7 @@ expect_trace() {
--expr "$expr" 2>&1 \
| grep "function-trace" \
| sed -e 's/ [0-9]*$//'
);
)
echo -n "Tracing expression '$expr'"
set +e
@ -32,40 +32,40 @@ expect_trace() {
# failure inside a tryEval
expect_trace 'builtins.tryEval (throw "example")' "
function-trace entered (string):1:1 at
function-trace entered (string):1:19 at
function-trace exited (string):1:19 at
function-trace exited (string):1:1 at
function-trace entered «string»:1:1 at
function-trace entered «string»:1:19 at
function-trace exited «string»:1:19 at
function-trace exited «string»:1:1 at
"
# Missing argument to a formal function
expect_trace '({ x }: x) { }' "
function-trace entered (string):1:1 at
function-trace exited (string):1:1 at
function-trace entered «string»:1:1 at
function-trace exited «string»:1:1 at
"
# Too many arguments to a formal function
expect_trace '({ x }: x) { x = "x"; y = "y"; }' "
function-trace entered (string):1:1 at
function-trace exited (string):1:1 at
function-trace entered «string»:1:1 at
function-trace exited «string»:1:1 at
"
# Not enough arguments to a lambda
expect_trace '(x: y: x + y) 1' "
function-trace entered (string):1:1 at
function-trace exited (string):1:1 at
function-trace entered «string»:1:1 at
function-trace exited «string»:1:1 at
"
# Too many arguments to a lambda
expect_trace '(x: x) 1 2' "
function-trace entered (string):1:1 at
function-trace exited (string):1:1 at
function-trace entered «string»:1:1 at
function-trace exited «string»:1:1 at
"
# Not a function
expect_trace '1 2' "
function-trace entered (string):1:1 at
function-trace exited (string):1:1 at
function-trace entered «string»:1:1 at
function-trace exited «string»:1:1 at
"
set -e

View file

@ -17,10 +17,10 @@ nix-env -q --foo 2>&1 | grep "unknown flag"
# Eval Errors.
eval_arg_res=$(nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 || true)
echo $eval_arg_res | grep "at «string»:1:15:"
echo $eval_arg_res | grep "at «string»:1:15"
echo $eval_arg_res | grep "infinite recursion encountered"
eval_stdin_res=$(echo 'let a = {} // a; in a.foo' | nix-instantiate --eval -E - 2>&1 || true)
echo $eval_stdin_res | grep "at «stdin»:1:15:"
echo $eval_stdin_res | grep "at «stdin»:1:15"
echo $eval_stdin_res | grep "infinite recursion encountered"