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

Reply via email to