From c58edbff754a21771ab66e914d6320a28cfae80d Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Wed, 24 Apr 2024 18:30:10 +0800 Subject: [PATCH] libnixf: diagnose empty inherit (#457) --- libnixf/src/Parse/ParseAttrs.cpp | 16 ++++++++++++++- libnixf/test/Parse/ParseAttrs.cpp | 34 +++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/libnixf/src/Parse/ParseAttrs.cpp b/libnixf/src/Parse/ParseAttrs.cpp index 206e8ed75..37afc49b3 100644 --- a/libnixf/src/Parse/ParseAttrs.cpp +++ b/libnixf/src/Parse/ParseAttrs.cpp @@ -133,11 +133,25 @@ std::shared_ptr Parser::parseInherit() { } break; } - if (ExpectResult ER = expect(tok_semi_colon); ER.ok()) + ExpectResult ER = expect(tok_semi_colon); + if (ER.ok()) consume(); else ER.diag().note(Note::NK_ToMachThis, TokInherit.range()) << std::string(tok::spelling(tok_kw_inherit)); + + // If attrnames are emtpy, this is an emtpy "inherit"; + if (AttrNames.empty()) { + Diagnostic &D = + Diags.emplace_back(Diagnostic::DK_EmptyInherit, TokInherit.range()); + D.tag(DiagnosticTag::Faded); + Fix &F = D.fix("remove `inherit` keyword"); + F.edit(TextEdit::mkRemoval(TokInherit.range())); + if (ER.ok()) { + // Remove ";" also. + F.edit(TextEdit::mkRemoval(ER.tok().range())); + } + } return std::make_shared( LexerCursorRange{TokInherit.lCur(), LastToken->rCur()}, std::move(AttrNames), std::move(Expr)); diff --git a/libnixf/test/Parse/ParseAttrs.cpp b/libnixf/test/Parse/ParseAttrs.cpp index 555c02abc..45913e1ac 100644 --- a/libnixf/test/Parse/ParseAttrs.cpp +++ b/libnixf/test/Parse/ParseAttrs.cpp @@ -275,7 +275,7 @@ TEST(Parser, AttrsBindingInherit) { auto AST = nixf::parse(Src, Diags); ASSERT_TRUE(AST); - ASSERT_EQ(Diags.size(), 5); + ASSERT_EQ(Diags.size(), 6); // Check the bindings. const auto &B = static_cast(AST.get())->binds()->bindings(); @@ -421,6 +421,36 @@ TEST(Parser, SyncInherit3) { ASSERT_EQ(N.args()[0], "("); } +TEST(Parser, InheritEmpty) { + auto Src = R"({ inherit; })"sv; + + std::vector Diags; + auto AST = nixf::parse(Src, Diags); + + ASSERT_TRUE(AST); + + ASSERT_EQ(Diags.size(), 1); + auto &D = Diags[0]; + ASSERT_TRUE(D.range().lCur().isAt(0, 2, 2)); + ASSERT_TRUE(D.range().rCur().isAt(0, 9, 9)); + + // Check the fix. + const Fix &F = Diags[0].fixes()[0]; + + ASSERT_EQ(F.edits().size(), 2); + ASSERT_TRUE(F.edits()[0].isRemoval()); + ASSERT_EQ(F.edits()[0].oldRange().lCur().line(), 0); + ASSERT_EQ(F.edits()[0].oldRange().lCur().column(), 2); + ASSERT_EQ(F.edits()[0].oldRange().rCur().line(), 0); + ASSERT_EQ(F.edits()[0].oldRange().rCur().column(), 9); + + // Second, remove the semicolon. + ASSERT_EQ(F.edits()[1].oldRange().lCur().line(), 0); + ASSERT_EQ(F.edits()[1].oldRange().lCur().column(), 9); + ASSERT_EQ(F.edits()[1].oldRange().rCur().line(), 0); + ASSERT_EQ(F.edits()[1].oldRange().rCur().column(), 10); +} + TEST(Parser, InheritMissingSemi) { auto Src = R"( { @@ -433,7 +463,7 @@ TEST(Parser, InheritMissingSemi) { ASSERT_TRUE(AST); - ASSERT_EQ(Diags.size(), 1); + ASSERT_EQ(Diags.size(), 2); auto &D = Diags[0]; ASSERT_TRUE(D.range().lCur().isAt(2, 9, 12)); ASSERT_TRUE(D.range().rCur().isAt(2, 9, 12));