diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/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. diff --git a/clang-tools-extra/clangd/IncludeFixer.h b/clang-tools-extra/clangd/IncludeFixer.h --- a/clang-tools-extra/clangd/IncludeFixer.h +++ b/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(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &Info) const; @@ -55,6 +56,8 @@ /// Generates header insertion fixes for all symbols. Fixes are deduplicated. std::vector fixesForSymbols(const SymbolSlab &Syms) const; + std::vector fixMissingHeader(llvm::StringRef Name) const; + struct UnresolvedName { std::string Name; // E.g. "X" in foo::X. SourceLocation Loc; // Start location of the unresolved name. diff --git a/clang-tools-extra/clangd/IncludeFixer.cpp b/clang-tools-extra/clangd/IncludeFixer.cpp --- a/clang-tools-extra/clangd/IncludeFixer.cpp +++ b/clang-tools-extra/clangd/IncludeFixer.cpp @@ -47,6 +47,19 @@ namespace clang { namespace clangd { +namespace { +llvm::Optional 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 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(""); + case diag::err_need_header_before_typeid: + return fixMissingHeader(""); + case diag::err_need_header_before_ms_uuidof: + return fixMissingHeader(""); + case diag::err_need_header_before_placement_new: + case diag::err_implicit_coroutine_std_nothrow_type_not_found: + return fixMissingHeader(""); + case diag::err_omp_implied_type_not_found: + case diag::err_omp_interop_type_not_found: + return fixMissingHeader(""); + case diag::err_implied_coroutine_type_not_found: + return fixMissingHeader(""); + case diag::err_implied_comparison_category_type_not_found: + return fixMissingHeader(""); + 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 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 diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/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 \n", + "Include "))))); +} + TEST(DiagsInHeaders, DiagInsideHeader) { Annotations Main(R"cpp( #include [["a.h"]]