diff --git a/nixd/lib/Syntax/Parser/Parser.y b/nixd/lib/Syntax/Parser/Parser.y index 5503a3054..e396d5195 100644 --- a/nixd/lib/Syntax/Parser/Parser.y +++ b/nixd/lib/Syntax/Parser/Parser.y @@ -268,6 +268,12 @@ expr_simple auto N = decorateNode(new LegacyLet, *yylocp, *Data); N->AttrBinds = $3; $$ = N; + + Diagnostic Diag; + Diag.Msg = "using deprecated `let' syntactic sugar `let {..., body = ...}' -> (rec {..., body = ...}).body'"; + Diag.Kind = Diagnostic::Warning; + Diag.Range = N->Range; + Data->Diags.emplace_back(std::move(Diag)); } | REC '{' binds '}' { auto N = decorateNode(new AttrSet, *yylocp, *Data); diff --git a/nixd/tools/nixd-lint/nixd-lint.cpp b/nixd/tools/nixd-lint/nixd-lint.cpp index d3ca3528b..d3d91ca7a 100644 --- a/nixd/tools/nixd-lint/nixd-lint.cpp +++ b/nixd/tools/nixd-lint/nixd-lint.cpp @@ -23,6 +23,54 @@ opt Filename(Positional, desc(""), init("-"), const OptionCategory *Cat[] = {&Misc}; +static void printCodeLines(std::ostream &Out, const std::string &Prefix, + const nix::AbstractPos &BeginPos, + const nix::AbstractPos &EndPos, + const nix::LinesOfCode &LOC) { + using namespace nix; + // previous line of code. + if (LOC.prevLineOfCode.has_value()) { + Out << std::endl + << fmt("%1% %|2$5d|| %3%", Prefix, (BeginPos.line - 1), + *LOC.prevLineOfCode); + } + + if (LOC.errLineOfCode.has_value()) { + // line of code containing the error. + Out << std::endl + << fmt("%1% %|2$5d|| %3%", Prefix, (BeginPos.line), *LOC.errLineOfCode); + // error arrows for the column range. + if (BeginPos.column > 0) { + auto Start = BeginPos.column; + std::string Spaces; + for (auto I = 0; I < Start; ++I) { + Spaces.append(" "); + } + + std::string arrows("^"); + + Out << std::endl + << fmt("%1% |%2%" ANSI_RED "%3%" ANSI_NORMAL, Prefix, Spaces, + arrows); + + if (BeginPos.line == EndPos.line) { + Out << ANSI_RED; + for (auto I = BeginPos.column + 1; I < EndPos.column; I++) { + Out << (I == EndPos.column - 1 ? "^" : "~"); + } + Out << ANSI_NORMAL; + } + } + } + + // next line of code. + if (LOC.nextLineOfCode.has_value()) { + Out << std::endl + << fmt("%1% %|2$5d|| %3%", Prefix, (BeginPos.line + 1), + *LOC.nextLineOfCode); + } +} + int main(int argc, char *argv[]) { HideUnrelatedOptions(Cat); ParseCommandLineOptions(argc, argv, "nixd linter", nullptr, @@ -49,20 +97,25 @@ int main(int argc, char *argv[]) { nixd::syntax::parse(Buffer, &Data); for (const auto &Diag : Data.Diags) { - std::shared_ptr Pos = (*PTable)[Diag.Range.Begin]; + std::shared_ptr BeginAPos = (*PTable)[Diag.Range.Begin]; + std::shared_ptr EndAPos = (*PTable)[Diag.Range.End]; switch (Diag.Kind) { case nixd::syntax::Diagnostic::Warning: - std::cout << ANSI_WARNING "warning:" ANSI_NORMAL; + std::cout << ANSI_WARNING "warning: " ANSI_NORMAL; + break; case nixd::syntax::Diagnostic::Error: std::cout << ANSI_RED "error: " ANSI_NORMAL; + break; } std::cout << Diag.Msg << "\n"; - if (Pos) { + if (BeginAPos) { std::cout << "\n" << ANSI_BLUE << "at " ANSI_WARNING << (*PTable)[Diag.Range.Begin] << ANSI_NORMAL << ":"; - if (auto Lines = Pos->getCodeLines()) { - nix::printCodeLines(std::cout, "", *Pos, *Lines); + if (auto Lines = BeginAPos->getCodeLines()) { + std::cout << "\n"; + printCodeLines(std::cout, "", *BeginAPos, *EndAPos, *Lines); + std::cout << "\n"; } } @@ -75,7 +128,7 @@ int main(int argc, char *argv[]) { << ":"; if (auto Lines = std::shared_ptr(NotePos)->getCodeLines()) { - nix::printCodeLines(std::cout, "", *Pos, *Lines); + nix::printCodeLines(std::cout, "", *BeginAPos, *Lines); } } } diff --git a/nixd/tools/nixd-lint/test/legacy-let.nix b/nixd/tools/nixd-lint/test/legacy-let.nix new file mode 100644 index 000000000..84c4fe578 --- /dev/null +++ b/nixd/tools/nixd-lint/test/legacy-let.nix @@ -0,0 +1,5 @@ +# RUN: nixd-lint %s | FileCheck %s + +let { a = 1; } # CHECK: using deprecated `let' + +; # CHECK: syntax error, unexpected ';', expecting end of file