diff --git a/src/libexpr/lexer.l b/src/libexpr/lexer.l index 1c1cdd673..5bc815f00 100644 --- a/src/libexpr/lexer.l +++ b/src/libexpr/lexer.l @@ -32,6 +32,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) @@ -135,20 +141,20 @@ or { return OR_KW; } if (numMay.has_value()) { yylval->n = *numMay; } else { - throw ParseError(ErrorInfo{ + THROW(ParseError(ErrorInfo{ .msg = HintFmt("invalid integer '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); } return INT; } {FLOAT} { errno = 0; yylval->nf = strtod(yytext, 0); if (errno != 0) - throw ParseError(ErrorInfo{ + THROW(ParseError(ErrorInfo{ .msg = HintFmt("invalid float '%1%'", yytext), .pos = state->positions[CUR_POS], - }); + })); return FLOAT; } @@ -274,10 +280,10 @@ or { return OR_KW; } {ANY} | <> { - throw ParseError(ErrorInfo{ + THROW(ParseError(ErrorInfo{ .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 3305e05b6..94f10064f 100644 --- a/src/libexpr/parser-state.hh +++ b/src/libexpr/parser-state.hh @@ -45,34 +45,35 @@ struct ParserState SourcePath basePath; PosTable::Origin origin; 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 @@ -85,10 +86,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.begin(), i + 1}, pos, j->second.pos); + if (!attrs2) return dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); attrs = attrs2; } else - dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); + return dupAttr({attrPath.begin(), i + 1}, pos, j->second.pos); } else { ExprAttrs * nested = new ExprAttrs; attrs->attrs[i->symbol] = ExprAttrs::AttrDef(nested, pos); @@ -117,7 +118,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); @@ -131,7 +132,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. @@ -141,9 +142,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) { @@ -158,18 +161,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 594026028..7783d2c20 100644 --- a/src/libexpr/parser.y +++ b/src/libexpr/parser.y @@ -62,6 +62,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")) { @@ -153,16 +160,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); } @@ -170,10 +180,10 @@ expr_function { $$ = new ExprWith(CUR_POS, $2, $4); } | LET binds IN 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 @@ -260,10 +270,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; } @@ -306,10 +316,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(path); @@ -323,12 +333,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)); @@ -343,7 +357,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( @@ -365,10 +379,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>; } ; @@ -457,6 +471,10 @@ Expr * parseExprFromBuf( yy_scan_buffer(text, length, scanner); yyparse(scanner, &state); + if (state.error) { + delete state.result; + throw *state.error; + } return state.result; }