diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 05e754cf1..da1dcdbcf 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -27,6 +27,12 @@ using namespace nix; +#define THROW(...) \ + do { \ + state->error.reset(new auto(__VA_ARGS__)); \ + return YYerror; \ + } while (0) + namespace nix { #define CUR_POS state->at(*yylloc) @@ -129,20 +135,20 @@ or { return OR_KW; } try { yylval->n = boost::lexical_cast(yytext); } catch (const boost::bad_lexical_cast &) { - throw ParseError(ErrorInfo{ + THROW(ParseError({ .msg = HintFmt("invalid integer '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); } return INT_LIT; } {FLOAT} { errno = 0; yylval->nf = strtod(yytext, 0); if (errno != 0) - throw ParseError(ErrorInfo{ + THROW(ParseError({ .msg = HintFmt("invalid float '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); return FLOAT_LIT; } @@ -268,10 +274,10 @@ or { return OR_KW; } {ANY} | <> { - throw ParseError(ErrorInfo{ + THROW(ParseError({ .msg = HintFmt("path has a trailing slash"), .pos = state->positions[CUR_POS], - }); + })); } {SPATH} { yylval->path = {yytext, (size_t) yyleng}; return SPATH; } diff --git a/src/libexpr/parser-state.hh b/src/libexpr/parser-state.hh index 024e79c43..b4b8e08e2 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -46,34 +46,35 @@ struct ParserState PosTable::Origin origin; const ref rootFS; const Expr::AstSymbols & s; + std::unique_ptr error; - void dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); - void dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); - void addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos); - Formals * validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); + [[nodiscard]] ParseError dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos); + [[nodiscard]] ParseError dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos); + [[nodiscard]] std::optional addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos); + [[nodiscard]] std::optional validateFormals(Formals * formals, PosIdx pos = noPos, Symbol arg = {}); Expr * stripIndentation(const PosIdx pos, std::vector>> && es); PosIdx at(const ParserLocation & loc); }; -inline void ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos) +inline ParseError ParserState::dupAttr(const AttrPath & attrPath, const PosIdx pos, const PosIdx prevPos) { - throw ParseError({ + return ParseError({ .msg = HintFmt("attribute '%1%' already defined at %2%", showAttrPath(symbols, attrPath), positions[prevPos]), .pos = positions[pos] }); } -inline void ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos) +inline ParseError ParserState::dupAttr(Symbol attr, const PosIdx pos, const PosIdx prevPos) { - throw ParseError({ + return ParseError({ .msg = HintFmt("attribute '%1%' already defined at %2%", symbols[attr], positions[prevPos]), .pos = positions[pos] }); } -inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos) +inline std::optional ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * e, const PosIdx pos) { AttrPath::iterator i; // All attrpaths have at least one attr @@ -86,10 +87,10 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * if (j != attrs->attrs.end()) { if (j->second.kind != ExprAttrs::AttrDef::Kind::Inherited) { ExprAttrs * attrs2 = dynamic_cast(j->second.e); - if (!attrs2) dupAttr(attrPath, pos, j->second.pos); + if (!attrs2) return dupAttr(attrPath, pos, j->second.pos); attrs = attrs2; } else - dupAttr(attrPath, pos, j->second.pos); + return dupAttr(attrPath, pos, j->second.pos); } else { ExprAttrs * nested = new ExprAttrs; attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); @@ -118,7 +119,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * for (auto & ad : ae->attrs) { auto j2 = jAttrs->attrs.find(ad.first); if (j2 != jAttrs->attrs.end()) // Attr already defined in iAttrs, error. - dupAttr(ad.first, j2->second.pos, ad.second.pos); + return dupAttr(ad.first, j2->second.pos, ad.second.pos); jAttrs->attrs.emplace(ad.first, ad.second); if (ad.second.kind == ExprAttrs::AttrDef::Kind::InheritedFrom) { auto & sel = dynamic_cast(*ad.second.e); @@ -132,7 +133,7 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * ae->inheritFromExprs->begin(), ae->inheritFromExprs->end()); } } else { - dupAttr(attrPath, pos, j->second.pos); + return dupAttr(attrPath, pos, j->second.pos); } } else { // This attr path is not defined. Let's create it. @@ -142,9 +143,11 @@ inline void ParserState::addAttr(ExprAttrs * attrs, AttrPath && attrPath, Expr * } else { attrs->dynamicAttrs.push_back(ExprAttrs::DynamicAttrDef(i->expr, e, pos)); } + + return {}; } -inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) +inline std::optional ParserState::validateFormals(Formals * formals, PosIdx pos, Symbol arg) { std::sort(formals->formals.begin(), formals->formals.end(), [] (const auto & a, const auto & b) { @@ -159,18 +162,18 @@ inline Formals * ParserState::validateFormals(Formals * formals, PosIdx pos, Sym duplicate = std::min(thisDup, duplicate.value_or(thisDup)); } if (duplicate) - throw ParseError({ + return ParseError({ .msg = HintFmt("duplicate formal function argument '%1%'", symbols[duplicate->first]), .pos = positions[duplicate->second] }); if (arg && formals->has(arg)) - throw ParseError({ + return ParseError({ .msg = HintFmt("duplicate formal function argument '%1%'", symbols[arg]), .pos = positions[pos] }); - return formals; + return {}; } inline Expr * ParserState::stripIndentation(const PosIdx pos, diff --git a/src/libexpr/parser.y b/src/libexpr/parser.y index d0f09081e..7c58f0142 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -64,6 +64,13 @@ using namespace nix; // otherwise destructors cause compiler errors #pragma GCC diagnostic ignored "-Wswitch-enum" +#define THROW(err, ...) \ + do { \ + state->error.reset(new auto(err)); \ + [](auto... d) { (delete d, ...); }(__VA_ARGS__); \ + YYABORT; \ + } while (0) + void yyerror(YYLTYPE * loc, yyscan_t scanner, ParserState * state, const char * error) { if (std::string_view(error).starts_with("syntax error, unexpected end of file")) { @@ -155,16 +162,19 @@ expr_function : ID ':' expr_function { $$ = new ExprLambda(CUR_POS, state->symbols.create($1), 0, $3); } | '{' formals '}' ':' expr_function - { $$ = new ExprLambda(CUR_POS, state->validateFormals($2), $5); } + { if (auto e = state->validateFormals($2)) THROW(*e); + $$ = new ExprLambda(CUR_POS, $2, $5); } | '{' formals '}' '@' ID ':' expr_function { auto arg = state->symbols.create($5); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($2, CUR_POS, arg), $7); + if (auto e = state->validateFormals($2, CUR_POS, arg)) THROW(*e, $2, $7); + $$ = new ExprLambda(CUR_POS, arg, $2, $7); } | ID '@' '{' formals '}' ':' expr_function { auto arg = state->symbols.create($1); - $$ = new ExprLambda(CUR_POS, arg, state->validateFormals($4, CUR_POS, arg), $7); + if (auto e = state->validateFormals($4, CUR_POS, arg)) THROW(*e, $4, $7); + $$ = new ExprLambda(CUR_POS, arg, $4, $7); } | ASSERT expr ';' expr_function { $$ = new ExprAssert(CUR_POS, $2, $4); } @@ -172,10 +182,10 @@ expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN_KW expr_function { if (!$2->dynamicAttrs.empty()) - throw ParseError({ + THROW(ParseError({ .msg = HintFmt("dynamic attributes not allowed in let"), .pos = state->positions[CUR_POS] - }); + }), $2, $4); $$ = new ExprLet($2, $4); } | expr_if @@ -262,10 +272,10 @@ expr_simple | URI { static bool noURLLiterals = experimentalFeatureSettings.isEnabled(Xp::NoUrlLiterals); if (noURLLiterals) - throw ParseError({ + THROW(ParseError({ .msg = HintFmt("URL literals are disabled"), .pos = state->positions[CUR_POS] - }); + })); $$ = new ExprString(std::string($1)); } | '(' expr ')' { $$ = $2; } @@ -308,10 +318,10 @@ path_start } | HPATH { if (evalSettings.pureEval) { - throw Error( + THROW(Error( "the path '%s' can not be resolved in pure mode", std::string_view($1.p, $1.l) - ); + )); } Path path(getHome() + std::string($1.p + 1, $1.l - 1)); $$ = new ExprPath(ref(state->rootFS), std::move(path)); @@ -325,12 +335,16 @@ ind_string_parts ; binds - : binds attrpath '=' expr ';' { $$ = $1; state->addAttr($$, std::move(*$2), $4, state->at(@2)); delete $2; } + : binds attrpath '=' expr ';' + { $$ = $1; + if (auto e = state->addAttr($$, std::move(*$2), $4, state->at(@2))) THROW(*e, $1, $2); + delete $2; + } | binds INHERIT attrs ';' { $$ = $1; for (auto & [i, iPos] : *$3) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); + THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1); $$->attrs.emplace( i.symbol, ExprAttrs::AttrDef(new ExprVar(iPos, i.symbol), iPos, ExprAttrs::AttrDef::Kind::Inherited)); @@ -345,7 +359,7 @@ binds auto from = new nix::ExprInheritFrom(state->at(@4), $$->inheritFromExprs->size() - 1); for (auto & [i, iPos] : *$6) { if ($$->attrs.find(i.symbol) != $$->attrs.end()) - state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos); + THROW(state->dupAttr(i.symbol, iPos, $$->attrs[i.symbol].pos), $1); $$->attrs.emplace( i.symbol, ExprAttrs::AttrDef( @@ -367,10 +381,10 @@ attrs $$->emplace_back(AttrName(state->symbols.create(str->s)), state->at(@2)); delete str; } else - throw ParseError({ + THROW(ParseError({ .msg = HintFmt("dynamic attributes not allowed in inherit"), .pos = state->positions[state->at(@2)] - }); + }), $1, $2); } | { $$ = new std::vector>; } ; @@ -461,6 +475,10 @@ Expr * parseExprFromBuf( yy_scan_buffer(text, length, scanner); yyparse(scanner, &state); + if (state.error) { + delete state.result; + throw *state.error; + } return state.result; }