sammccall created this revision. sammccall added a reviewer: nridge. Herald added subscribers: usaxena95, kadircet, arphaman. sammccall requested review of this revision. Herald added subscribers: cfe-commits, sstefan1, MaskRay, ilya-biryukov. Herald added a reviewer: jdoerfert. Herald added a project: clang-tools-extra.
Clang doesn't offer these fixes I guess for a couple of reasons: - where to insert includes is a formatting concern, and clang shouldn't depend on clang-format - the way clang prints diagnostics, we'd show a bunch of basically irrelevant context of "this is where we'd want to insert the include" Maybe it's possible to hack around 1, but 2 is still a concern. Meanwhile, bolting this onto include-fixer gets the job done. Fixes https://github.com/clangd/clangd/issues/355 Fixes https://github.com/clangd/clangd/issues/937 Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D114667 Files: clang-tools-extra/clangd/Diagnostics.cpp clang-tools-extra/clangd/IncludeFixer.cpp clang-tools-extra/clangd/IncludeFixer.h clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp
Index: clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -1034,7 +1034,7 @@ "Add include \"b.h\" for symbol na::nb::X"))))); } -TEST(IncludeFixerTest, NoCrashMemebrAccess) { +TEST(IncludeFixerTest, NoCrashMemberAccess) { Annotations Test(R"cpp(// error-ok struct X { int xyz; }; void g() { X x; x.$[[xy]]; } @@ -1206,6 +1206,26 @@ ElementsAre(Diag(Test.range(), "use of undeclared identifier 'a'"))); } +TEST(IncludeFixerTest, HeaderNamedInDiag) { + Annotations Test(R"cpp( + $insert[[]]int main() { + [[printf]](""); + } + )cpp"); + auto TU = TestTU::withCode(Test.code()); + TU.ExtraArgs = {"-xc"}; + auto Index = buildIndexWithSymbol({}); + TU.ExternalIndex = Index.get(); + + EXPECT_THAT( + *TU.build().getDiagnostics(), + ElementsAre(AllOf( + Diag(Test.range(), "implicitly declaring library function 'printf' " + "with type 'int (const char *, ...)'"), + WithFix(Fix(Test.range("insert"), "#include <stdio.h>\n", + "Include <stdio.h>"))))); +} + TEST(DiagsInHeaders, DiagInsideHeader) { Annotations Main(R"cpp( #include [["a.h"]] Index: clang-tools-extra/clangd/IncludeFixer.h =================================================================== --- clang-tools-extra/clangd/IncludeFixer.h +++ clang-tools-extra/clangd/IncludeFixer.h @@ -40,6 +40,7 @@ IndexRequestLimit(IndexRequestLimit) {} /// Returns include insertions that can potentially recover the diagnostic. + /// If Info describes a note, it will be replaced by any returned fixes. std::vector<Fix> fix(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) const; @@ -55,6 +56,8 @@ /// Generates header insertion fixes for all symbols. Fixes are deduplicated. std::vector<Fix> fixesForSymbols(const SymbolSlab &Syms) const; + std::vector<Fix> fixMissingHeader(llvm::StringRef Name) const; + struct UnresolvedName { std::string Name; // E.g. "X" in foo::X. SourceLocation Loc; // Start location of the unresolved name. Index: clang-tools-extra/clangd/IncludeFixer.cpp =================================================================== --- clang-tools-extra/clangd/IncludeFixer.cpp +++ clang-tools-extra/clangd/IncludeFixer.cpp @@ -47,6 +47,19 @@ namespace clang { namespace clangd { +namespace { +llvm::Optional<llvm::StringRef> getArgStr(const clang::Diagnostic &Info, + unsigned I) { + switch (Info.getArgKind(I)) { + case DiagnosticsEngine::ak_c_string: + return llvm::StringRef(Info.getArgCStr(I)); + case DiagnosticsEngine::ak_std_string: + return llvm::StringRef(Info.getArgStdStr(I)); + default: + return llvm::None; + } +} +} // namespace std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) const { @@ -102,7 +115,32 @@ LastUnresolvedName->Loc == Info.getLocation()) return fixUnresolvedName(); } + break; + // Cases where clang explicitly knows which header to include. + // (There's no fix provided for boring formatting reasons). + case diag::err_implied_std_initializer_list_not_found: + return fixMissingHeader("<initializer_list>"); + case diag::err_need_header_before_typeid: + return fixMissingHeader("<typeid>"); + case diag::err_need_header_before_ms_uuidof: + return fixMissingHeader("<guiddef.h>"); + case diag::err_need_header_before_placement_new: + case diag::err_implicit_coroutine_std_nothrow_type_not_found: + return fixMissingHeader("<new>"); + case diag::err_omp_implied_type_not_found: + case diag::err_omp_interop_type_not_found: + return fixMissingHeader("<omp.h>"); + case diag::err_implied_coroutine_type_not_found: + return fixMissingHeader("<coroutine>"); + case diag::err_implied_comparison_category_type_not_found: + return fixMissingHeader("<compare>"); + case diag::note_include_header_or_declare: + if (Info.getNumArgs() > 0) + if (auto Header = getArgStr(Info, 0)) + return fixMissingHeader(("<" + *Header + ">").str()); + break; } + return {}; } @@ -452,5 +490,11 @@ return &E.first->second; } +std::vector<Fix> IncludeFixer::fixMissingHeader(llvm::StringRef Name) const { + if (auto Edit = Inserter->insert(Name)) + return {Fix{llvm::formatv("Include {0}", Name).str(), {std::move(*Edit)}}}; + return {}; +} + } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/Diagnostics.cpp =================================================================== --- clang-tools-extra/clangd/Diagnostics.cpp +++ clang-tools-extra/clangd/Diagnostics.cpp @@ -823,6 +823,16 @@ if (LastDiag->Severity == DiagnosticsEngine::Ignored) return; + // Give include-fixer a chance to replace a note with a fix. + if (Fixer) { + auto ReplacementFixes = Fixer(LastDiag->Severity, Info); + if (!ReplacementFixes.empty()) { + LastDiag->Fixes.insert(LastDiag->Fixes.end(), ReplacementFixes.begin(), + ReplacementFixes.end()); + return; + } + } + if (!Info.getFixItHints().empty()) { // A clang note with fix-it is not a separate diagnostic in clangd. We // attach it as a Fix to the main diagnostic instead.
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits