diff --git a/nixd/include/nixd/Syntax/Diagnostic.h b/nixd/include/nixd/Syntax/Diagnostic.h new file mode 100644 index 000000000..da1049484 --- /dev/null +++ b/nixd/include/nixd/Syntax/Diagnostic.h @@ -0,0 +1,35 @@ +/// Nixd diagnostic types. Provide structured diagnostic with location, FixIt +/// hints, annotation, and notes +#pragma once + +#include "nixd/Syntax/Range.h" + +namespace nixd::syntax { + +// The note. +struct Note { + /// Where is the error occurred + nixd::RangeIdx Range; + + std::string Msg; +}; + +struct Edit { + nixd::RangeIdx Range; + + std::string NewText; +}; + +struct Diagnostic { + /// Where is the error occurred + nixd::RangeIdx Range; + + std::string Msg; + + enum DiagKind { Warning, Error } Kind; + + std::vector Notes; + std::vector Edits; +}; + +} // namespace nixd::syntax diff --git a/nixd/include/nixd/Syntax/Parser/Require.h b/nixd/include/nixd/Syntax/Parser/Require.h index 4c64d35e4..b46fafe3d 100644 --- a/nixd/include/nixd/Syntax/Parser/Require.h +++ b/nixd/include/nixd/Syntax/Parser/Require.h @@ -1,6 +1,7 @@ #pragma once #include "nixd/Expr/Expr.h" +#include "nixd/Syntax/Diagnostic.h" #include "nixd/Syntax/Nodes.h" #include @@ -24,6 +25,8 @@ struct ParseData { nix::PosTable::Origin Origin; Context Nodes; + + std::vector Diags; }; // Note: copied from diff --git a/nixd/lib/Syntax/Lexer/Lexer.l b/nixd/lib/Syntax/Lexer/Lexer.l index 0efdccab1..2fe5c259c 100644 --- a/nixd/lib/Syntax/Lexer/Lexer.l +++ b/nixd/lib/Syntax/Lexer/Lexer.l @@ -60,32 +60,30 @@ or { return OR_KW; } {ID} { yylval->ID = {yytext, (size_t) yyleng}; return ID; } {INT} { errno = 0; try { - yylval->N = boost::lexical_cast(yytext); + yylval->N = boost::lexical_cast(yytext); } catch (const boost::bad_lexical_cast &) { - // TODO: emit the error - /* - data->error.emplace_back(nix::ErrorInfo{ - .msg = hintfmt("invalid integer '%1%'", yytext), - .errPos = data->state.positions[CUR_POS], - }); - */ - yyterminate(); + auto Range = mkRange(*yylloc, *Data); + Diagnostic Diag; + Diag.Msg = llvm::formatv("invalid integer {0}", yytext); + Diag.Kind = Diagnostic::Error; + Diag.Range = Range; + Data->Diags.emplace_back(std::move(Diag)); + yyterminate(); } return INT; } {FLOAT} { errno = 0; yylval->NF = strtod(yytext, 0); - // TODO: emit the error - /* if (errno != 0) { - data->error.emplace_back(nix::ErrorInfo{ - .msg = hintfmt("invalid float '%1%'", yytext), - .errPos = data->state.positions[CUR_POS], - }); - yyterminate(); + auto Range = mkRange(*yylloc, *Data); + Diagnostic Diag; + Diag.Msg = llvm::formatv("invalid float {0}", yytext); + Diag.Kind = Diagnostic::Error; + Diag.Range = Range; + Data->Diags.emplace_back(std::move(Diag)); + yyterminate(); } - */ return FLOAT; } @@ -211,11 +209,12 @@ or { return OR_KW; } {ANY} | <> { - /* data->error.emplace_back(nix::ErrorInfo{ - .msg = hintfmt("path has a trailing slash"), - .errPos = data->state.positions[CUR_POS], - }); - */ + auto Range = mkRange(*yylloc, *Data); + Diagnostic Diag; + Diag.Msg = llvm::formatv("path has a trailing slash"); + Diag.Kind = Diagnostic::Error; + Diag.Range = Range; + Data->Diags.emplace_back(std::move(Diag)); yyterminate(); } diff --git a/nixd/lib/Syntax/Lexer/Prologue.cpp b/nixd/lib/Syntax/Lexer/Prologue.cpp index 5cfcd0ffd..98ee05420 100644 --- a/nixd/lib/Syntax/Lexer/Prologue.cpp +++ b/nixd/lib/Syntax/Lexer/Prologue.cpp @@ -1,6 +1,7 @@ #include "Parser.tab.h" #include "nixd/Support/Position.h" +#include "nixd/Syntax/Diagnostic.h" #include "nixd/Syntax/Nodes.h" #include "nixd/Syntax/Parser/Require.h" @@ -9,6 +10,8 @@ #include +using nixd::syntax::Diagnostic; + #ifdef __clang__ #pragma clang diagnostic ignored "-Wunneeded-internal-declaration" #endif @@ -21,6 +24,17 @@ static void initLoc(YYLTYPE *loc) { loc->first_column = loc->last_column = 1; } +static nixd::RangeIdx mkRange(YYLTYPE YL, nix::PosTable &T, + const nix::PosTable::Origin &Origin) { + auto Begin = T.add(Origin, YL.first_line, YL.first_column); + auto End = T.add(Origin, YL.last_line, YL.last_column); + return {Begin, End}; +} + +static nixd::RangeIdx mkRange(YYLTYPE YL, nixd::syntax::ParseData &Data) { + return mkRange(YL, Data.State.Positions, Data.Origin); +} + static void adjustLoc(YYLTYPE *loc, const char *s, size_t len) { prev_yylloc = *loc; diff --git a/nixd/lib/Syntax/Parser/Prologue.cpp b/nixd/lib/Syntax/Parser/Prologue.cpp index c26e78a7a..2c80f9bf9 100644 --- a/nixd/lib/Syntax/Parser/Prologue.cpp +++ b/nixd/lib/Syntax/Parser/Prologue.cpp @@ -2,16 +2,16 @@ #include "Lexer.tab.h" +#include "nixd/Syntax/Diagnostic.h" #include "nixd/Syntax/Nodes.h" #include "nixd/Syntax/Parser/Require.h" +#include "llvm/Support/FormatVariadic.h" + using namespace nixd::syntax; YY_DECL; -void yyerror(YYLTYPE *loc, yyscan_t scanner, nixd::syntax::ParseData *data, - const char *error) {} - /// Convert a yacc location (yylloc) to nix::PosIdx /// Store in the pos table static nixd::RangeIdx mkRange(YYLTYPE YL, nix::PosTable &T, @@ -25,6 +25,16 @@ static nixd::RangeIdx mkRange(YYLTYPE YL, nixd::syntax::ParseData &Data) { return mkRange(YL, Data.State.Positions, Data.Origin); } +void yyerror(YYLTYPE *YL, yyscan_t Scanner, nixd::syntax::ParseData *Data, + const char *Error) { + auto Range = mkRange(*YL, *Data); + Diagnostic Diag; + Diag.Msg = llvm::formatv("{0}", Error); + Diag.Kind = Diagnostic::Error; + Diag.Range = Range; + Data->Diags.emplace_back(std::move(Diag)); +} + template T *decorateNode(T *Node, YYLTYPE YL, nixd::syntax::ParseData &Data) { Data.Nodes.record(Node); diff --git a/nixd/test/Syntax/Basic.cpp b/nixd/test/Syntax/Basic.cpp index 9b6cf058d..6ea750b74 100644 --- a/nixd/test/Syntax/Basic.cpp +++ b/nixd/test/Syntax/Basic.cpp @@ -20,7 +20,7 @@ TEST(NixdSyntax, Case1) { .Origin = nix::Pos::Origin(nix::Pos::none_tag())}); Data->Result = nullptr; - char Test[] = "let x = 1; y = 2; in x\n\0\0"; + char Test[] = "{ a = 1; \n\0\0"; parse(Test, strlen(Test) + 2, Data.get()); }