[clang-tools-extra] 031ffc3 - [clangd] Decouple IncludeCleaner implementation from Config

Kadir Cetinkaya via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 12 02:40:11 PDT 2023


Author: Kadir Cetinkaya
Date: 2023-06-12T11:38:58+02:00
New Revision: 031ffc3e064525731914eff2ea0dfab1ff34cce1

URL: https://github.com/llvm/llvm-project/commit/031ffc3e064525731914eff2ea0dfab1ff34cce1
DIFF: https://github.com/llvm/llvm-project/commit/031ffc3e064525731914eff2ea0dfab1ff34cce1.diff

LOG: [clangd] Decouple IncludeCleaner implementation from Config

This should help managing tests as we change defaults in configs.

Differential Revision: https://reviews.llvm.org/D152685

Added: 
    

Modified: 
    clang-tools-extra/clangd/IncludeCleaner.cpp
    clang-tools-extra/clangd/IncludeCleaner.h
    clang-tools-extra/clangd/ParsedAST.cpp
    clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index fa30592e75807..95585a61c023d 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "IncludeCleaner.h"
-#include "Config.h"
 #include "Diagnostics.h"
 #include "Headers.h"
 #include "ParsedAST.h"
@@ -48,7 +47,6 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/StringSet.h"
 #include "llvm/Support/Casting.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
@@ -62,8 +60,7 @@
 #include <utility>
 #include <vector>
 
-namespace clang {
-namespace clangd {
+namespace clang::clangd {
 
 static bool AnalyzeStdlib = false;
 void setIncludeCleanerAnalyzesStdlib(bool B) { AnalyzeStdlib = B; }
@@ -84,20 +81,19 @@ clangd::Range getDiagnosticRange(llvm::StringRef Code, unsigned HashOffset) {
   return Result;
 }
 
-bool isFilteredByConfig(const Config &Cfg, llvm::StringRef HeaderPath) {
+bool isIgnored(llvm::StringRef HeaderPath, HeaderFilter IgnoreHeaders) {
   // Convert the path to Unix slashes and try to match against the filter.
   llvm::SmallString<64> NormalizedPath(HeaderPath);
   llvm::sys::path::native(NormalizedPath, llvm::sys::path::Style::posix);
-  for (auto &Filter : Cfg.Diagnostics.Includes.IgnoreHeader) {
+  for (auto &Filter : IgnoreHeaders) {
     if (Filter(NormalizedPath))
       return true;
   }
   return false;
 }
 
-static bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
-                              const Config &Cfg,
-                              const include_cleaner::PragmaIncludes *PI) {
+bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
+                       const include_cleaner::PragmaIncludes *PI) {
   // FIXME(kirillbobyrev): We currently do not support the umbrella headers.
   // System headers are likely to be standard library headers.
   // Until we have good support for umbrella headers, don't warn about them.
@@ -133,11 +129,6 @@ static bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
          FE->getName());
     return false;
   }
-
-  if (isFilteredByConfig(Cfg, Inc.Resolved)) {
-    dlog("{0} header is filtered out by the configuration", FE->getName());
-    return false;
-  }
   return true;
 }
 
@@ -166,15 +157,8 @@ std::string getSymbolName(const include_cleaner::Symbol &Sym) {
 
 std::vector<Diag> generateMissingIncludeDiagnostics(
     ParsedAST &AST, llvm::ArrayRef<MissingIncludeDiagInfo> MissingIncludes,
-    llvm::StringRef Code) {
+    llvm::StringRef Code, HeaderFilter IgnoreHeaders) {
   std::vector<Diag> Result;
-  const Config &Cfg = Config::current();
-  if (Cfg.Diagnostics.MissingIncludes != Config::IncludesPolicy::Strict ||
-      Cfg.Diagnostics.SuppressAll ||
-      Cfg.Diagnostics.Suppress.contains("missing-includes")) {
-    return Result;
-  }
-
   const SourceManager &SM = AST.getSourceManager();
   const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
 
@@ -191,7 +175,7 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
   for (const auto &SymbolWithMissingInclude : MissingIncludes) {
     llvm::StringRef ResolvedPath =
         getResolvedPath(SymbolWithMissingInclude.Providers.front());
-    if (isFilteredByConfig(Cfg, ResolvedPath)) {
+    if (isIgnored(ResolvedPath, IgnoreHeaders)) {
       dlog("IncludeCleaner: not diagnosing missing include {0}, filtered by "
            "config",
            ResolvedPath);
@@ -252,15 +236,11 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
 
 std::vector<Diag> generateUnusedIncludeDiagnostics(
     PathRef FileName, llvm::ArrayRef<const Inclusion *> UnusedIncludes,
-    llvm::StringRef Code) {
+    llvm::StringRef Code, HeaderFilter IgnoreHeaders) {
   std::vector<Diag> Result;
-  const Config &Cfg = Config::current();
-  if (Cfg.Diagnostics.UnusedIncludes == Config::IncludesPolicy::None ||
-      Cfg.Diagnostics.SuppressAll ||
-      Cfg.Diagnostics.Suppress.contains("unused-includes")) {
-    return Result;
-  }
   for (const auto *Inc : UnusedIncludes) {
+    if (isIgnored(Inc->Resolved, IgnoreHeaders))
+      continue;
     Diag &D = Result.emplace_back();
     D.Message =
         llvm::formatv("included header {0} is not used directly",
@@ -287,6 +267,104 @@ std::vector<Diag> generateUnusedIncludeDiagnostics(
   }
   return Result;
 }
+
+std::optional<Fix>
+removeAllUnusedIncludes(llvm::ArrayRef<Diag> UnusedIncludes) {
+  if (UnusedIncludes.empty())
+    return std::nullopt;
+
+  Fix RemoveAll;
+  RemoveAll.Message = "remove all unused includes";
+  for (const auto &Diag : UnusedIncludes) {
+    assert(Diag.Fixes.size() == 1 && "Expected exactly one fix.");
+    RemoveAll.Edits.insert(RemoveAll.Edits.end(),
+                           Diag.Fixes.front().Edits.begin(),
+                           Diag.Fixes.front().Edits.end());
+  }
+
+  // TODO(hokein): emit a suitable text for the label.
+  ChangeAnnotation Annotation = {/*label=*/"",
+                                 /*needsConfirmation=*/true,
+                                 /*description=*/""};
+  static const ChangeAnnotationIdentifier RemoveAllUnusedID =
+      "RemoveAllUnusedIncludes";
+  for (unsigned I = 0; I < RemoveAll.Edits.size(); ++I) {
+    ChangeAnnotationIdentifier ID = RemoveAllUnusedID + std::to_string(I);
+    RemoveAll.Edits[I].annotationId = ID;
+    RemoveAll.Annotations.push_back({ID, Annotation});
+  }
+  return RemoveAll;
+}
+
+std::optional<Fix>
+addAllMissingIncludes(llvm::ArrayRef<Diag> MissingIncludeDiags) {
+  if (MissingIncludeDiags.empty())
+    return std::nullopt;
+
+  Fix AddAllMissing;
+  AddAllMissing.Message = "add all missing includes";
+  // A map to deduplicate the edits with the same new text.
+  // newText (#include "my_missing_header.h") -> TextEdit.
+  llvm::StringMap<TextEdit> Edits;
+  for (const auto &Diag : MissingIncludeDiags) {
+    assert(Diag.Fixes.size() == 1 && "Expected exactly one fix.");
+    for (const auto &Edit : Diag.Fixes.front().Edits) {
+      Edits.try_emplace(Edit.newText, Edit);
+    }
+  }
+  // FIXME(hokein): emit used symbol reference in the annotation.
+  ChangeAnnotation Annotation = {/*label=*/"",
+                                 /*needsConfirmation=*/true,
+                                 /*description=*/""};
+  static const ChangeAnnotationIdentifier AddAllMissingID =
+      "AddAllMissingIncludes";
+  unsigned I = 0;
+  for (auto &It : Edits) {
+    ChangeAnnotationIdentifier ID = AddAllMissingID + std::to_string(I++);
+    AddAllMissing.Edits.push_back(std::move(It.getValue()));
+    AddAllMissing.Edits.back().annotationId = ID;
+
+    AddAllMissing.Annotations.push_back({ID, Annotation});
+  }
+  return AddAllMissing;
+}
+Fix fixAll(const Fix &RemoveAllUnused, const Fix &AddAllMissing) {
+  Fix FixAll;
+  FixAll.Message = "fix all includes";
+
+  for (const auto &F : RemoveAllUnused.Edits)
+    FixAll.Edits.push_back(F);
+  for (const auto &F : AddAllMissing.Edits)
+    FixAll.Edits.push_back(F);
+
+  for (const auto &A : RemoveAllUnused.Annotations)
+    FixAll.Annotations.push_back(A);
+  for (const auto &A : AddAllMissing.Annotations)
+    FixAll.Annotations.push_back(A);
+  return FixAll;
+}
+
+std::vector<const Inclusion *>
+getUnused(ParsedAST &AST,
+          const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles) {
+  trace::Span Tracer("IncludeCleaner::getUnused");
+  std::vector<const Inclusion *> Unused;
+  for (const Inclusion &MFI : AST.getIncludeStructure().MainFileIncludes) {
+    if (!MFI.HeaderID)
+      continue;
+    auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
+    if (ReferencedFiles.contains(IncludeID))
+      continue;
+    if (!mayConsiderUnused(MFI, AST, AST.getPragmaIncludes())) {
+      dlog("{0} was not used, but is not eligible to be diagnosed as unused",
+           MFI.Written);
+      continue;
+    }
+    Unused.push_back(&MFI);
+  }
+  return Unused;
+}
+
 } // namespace
 
 std::vector<include_cleaner::SymbolReference>
@@ -350,33 +428,10 @@ std::string spellHeader(ParsedAST &AST, const FileEntry *MainFile,
       {Provider, AST.getPreprocessor().getHeaderSearchInfo(), MainFile});
 }
 
-std::vector<const Inclusion *>
-getUnused(ParsedAST &AST,
-          const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles,
-          const llvm::StringSet<> &ReferencedPublicHeaders) {
-  trace::Span Tracer("IncludeCleaner::getUnused");
-  const Config &Cfg = Config::current();
-  std::vector<const Inclusion *> Unused;
-  for (const Inclusion &MFI : AST.getIncludeStructure().MainFileIncludes) {
-    if (!MFI.HeaderID)
-      continue;
-    if (ReferencedPublicHeaders.contains(MFI.Written))
-      continue;
-    auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
-    bool Used = ReferencedFiles.contains(IncludeID);
-    if (!Used && !mayConsiderUnused(MFI, AST, Cfg, AST.getPragmaIncludes())) {
-      dlog("{0} was not used, but is not eligible to be diagnosed as unused",
-           MFI.Written);
-      continue;
-    }
-    if (!Used)
-      Unused.push_back(&MFI);
-    dlog("{0} is {1}", MFI.Written, Used ? "USED" : "UNUSED");
-  }
-  return Unused;
-}
-
 IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
+  // Interaction is only polished for C/CPP.
+  if (AST.getLangOpts().ObjC)
+    return {};
   const auto &SM = AST.getSourceManager();
   const auto &Includes = AST.getIncludeStructure();
   include_cleaner::Includes ConvertedIncludes =
@@ -454,94 +509,21 @@ IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST) {
            MapInfo::getHashValue(RHS.Symbol);
   });
   MissingIncludes.erase(llvm::unique(MissingIncludes), MissingIncludes.end());
-  std::vector<const Inclusion *> UnusedIncludes =
-      getUnused(AST, Used, /*ReferencedPublicHeaders*/ {});
+  std::vector<const Inclusion *> UnusedIncludes = getUnused(AST, Used);
   return {std::move(UnusedIncludes), std::move(MissingIncludes)};
 }
 
-std::optional<Fix> removeAllUnusedIncludes(llvm::ArrayRef<Diag> UnusedIncludes) {
-  if (UnusedIncludes.empty())
-    return std::nullopt;
-
-  Fix RemoveAll;
-  RemoveAll.Message = "remove all unused includes";
-  for (const auto &Diag : UnusedIncludes) {
-    assert(Diag.Fixes.size() == 1 && "Expected exactly one fix.");
-    RemoveAll.Edits.insert(RemoveAll.Edits.end(),
-         Diag.Fixes.front().Edits.begin(),
-         Diag.Fixes.front().Edits.end());
-  }
-
-  // TODO(hokein): emit a suitable text for the label.
-  ChangeAnnotation Annotation = {/*label=*/"",
-                                 /*needsConfirmation=*/true,
-                                 /*description=*/""};
-  static const ChangeAnnotationIdentifier RemoveAllUnusedID =
-      "RemoveAllUnusedIncludes";
-  for (unsigned I = 0; I < RemoveAll.Edits.size(); ++I) {
-    ChangeAnnotationIdentifier ID = RemoveAllUnusedID + std::to_string(I);
-    RemoveAll.Edits[I].annotationId = ID;
-    RemoveAll.Annotations.push_back({ID, Annotation});
-  }
-  return RemoveAll;
-}
-std::optional<Fix>
-addAllMissingIncludes(llvm::ArrayRef<Diag> MissingIncludeDiags) {
-  if (MissingIncludeDiags.empty())
-    return std::nullopt;
-
-  Fix AddAllMissing;
-  AddAllMissing.Message = "add all missing includes";
-  // A map to deduplicate the edits with the same new text.
-  // newText (#include "my_missing_header.h") -> TextEdit.
-  llvm::StringMap<TextEdit> Edits;
-  for (const auto &Diag : MissingIncludeDiags) {
-    assert(Diag.Fixes.size() == 1 && "Expected exactly one fix.");
-    for (const auto& Edit : Diag.Fixes.front().Edits) {
-      Edits.try_emplace(Edit.newText, Edit);
-    }
-  }
-  // FIXME(hokein): emit used symbol reference in the annotation.
-  ChangeAnnotation Annotation = {/*label=*/"",
-                                 /*needsConfirmation=*/true,
-                                 /*description=*/""};
-  static const ChangeAnnotationIdentifier AddAllMissingID =
-      "AddAllMissingIncludes";
-  unsigned I = 0;
-  for (auto &It : Edits) {
-    ChangeAnnotationIdentifier ID = AddAllMissingID + std::to_string(I++);
-    AddAllMissing.Edits.push_back(std::move(It.getValue()));
-    AddAllMissing.Edits.back().annotationId = ID;
-
-    AddAllMissing.Annotations.push_back({ID, Annotation});
-  }
-  return AddAllMissing;
-}
-Fix fixAll(const Fix& RemoveAllUnused, const Fix& AddAllMissing) {
-  Fix FixAll;
-  FixAll.Message = "fix all includes";
-
-  for (const auto &F : RemoveAllUnused.Edits)
-    FixAll.Edits.push_back(F);
-  for (const auto &F : AddAllMissing.Edits)
-    FixAll.Edits.push_back(F);
-
-  for (const auto& A : RemoveAllUnused.Annotations)
-    FixAll.Annotations.push_back(A);
-  for (const auto& A : AddAllMissing.Annotations)
-    FixAll.Annotations.push_back(A);
-  return FixAll;
-}
-
-std::vector<Diag> generateIncludeCleanerDiagnostic(
-    ParsedAST &AST, const IncludeCleanerFindings &Findings,
-    llvm::StringRef Code) {
+std::vector<Diag>
+issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code,
+                               const IncludeCleanerFindings &Findings,
+                               HeaderFilter IgnoreHeaders) {
+  trace::Span Tracer("IncludeCleaner::issueIncludeCleanerDiagnostics");
   std::vector<Diag> UnusedIncludes = generateUnusedIncludeDiagnostics(
-      AST.tuPath(), Findings.UnusedIncludes, Code);
+      AST.tuPath(), Findings.UnusedIncludes, Code, IgnoreHeaders);
   std::optional<Fix> RemoveAllUnused = removeAllUnusedIncludes(UnusedIncludes);
 
   std::vector<Diag> MissingIncludeDiags = generateMissingIncludeDiagnostics(
-      AST, Findings.MissingIncludes, Code);
+      AST, Findings.MissingIncludes, Code, IgnoreHeaders);
   std::optional<Fix> AddAllMissing = addAllMissingIncludes(MissingIncludeDiags);
 
   std::optional<Fix> FixAll;
@@ -549,47 +531,26 @@ std::vector<Diag> generateIncludeCleanerDiagnostic(
     FixAll = fixAll(*RemoveAllUnused, *AddAllMissing);
 
   auto AddBatchFix = [](const std::optional<Fix> &F, clang::clangd::Diag *Out) {
-    if (!F) return;
+    if (!F)
+      return;
     Out->Fixes.push_back(*F);
   };
   for (auto &Diag : MissingIncludeDiags) {
-    AddBatchFix(MissingIncludeDiags.size() > 1
-                    ? AddAllMissing
-                    : std::nullopt,
+    AddBatchFix(MissingIncludeDiags.size() > 1 ? AddAllMissing : std::nullopt,
                 &Diag);
     AddBatchFix(FixAll, &Diag);
   }
   for (auto &Diag : UnusedIncludes) {
-    AddBatchFix(UnusedIncludes.size() > 1 ? RemoveAllUnused
-                                          : std::nullopt,
+    AddBatchFix(UnusedIncludes.size() > 1 ? RemoveAllUnused : std::nullopt,
                 &Diag);
     AddBatchFix(FixAll, &Diag);
   }
 
   auto Result = std::move(MissingIncludeDiags);
-  llvm::move(UnusedIncludes,
-             std::back_inserter(Result));
+  llvm::move(UnusedIncludes, std::back_inserter(Result));
   return Result;
 }
 
-std::vector<Diag> issueIncludeCleanerDiagnostics(ParsedAST &AST,
-                                                 llvm::StringRef Code) {
-  // Interaction is only polished for C/CPP.
-  if (AST.getLangOpts().ObjC)
-    return {};
-
-  trace::Span Tracer("IncludeCleaner::issueIncludeCleanerDiagnostics");
-
-  const Config &Cfg = Config::current();
-  IncludeCleanerFindings Findings;
-  if (Cfg.Diagnostics.MissingIncludes == Config::IncludesPolicy::Strict ||
-      Cfg.Diagnostics.UnusedIncludes == Config::IncludesPolicy::Strict) {
-    // will need include-cleaner results, call it once
-    Findings = computeIncludeCleanerFindings(AST);
-  }
-  return generateIncludeCleanerDiagnostic(AST, Findings, Code);
-}
-
 std::optional<include_cleaner::Header>
 firstMatchedProvider(const include_cleaner::Includes &Includes,
                      llvm::ArrayRef<include_cleaner::Header> Providers) {
@@ -600,5 +561,4 @@ firstMatchedProvider(const include_cleaner::Includes &Includes,
   // No match for this provider in the includes list.
   return std::nullopt;
 }
-} // namespace clangd
-} // namespace clang
+} // namespace clang::clangd

diff  --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h
index 675c05a53d5f8..58849846e0df7 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.h
+++ b/clang-tools-extra/clangd/IncludeCleaner.h
@@ -18,12 +18,17 @@
 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDECLEANER_H
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDECLEANER_H
 
+#include "Diagnostics.h"
 #include "Headers.h"
 #include "ParsedAST.h"
 #include "clang-include-cleaner/Types.h"
+#include "clang/Basic/SourceManager.h"
 #include "clang/Tooling/Syntax/Tokens.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ADT/StringSet.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include <functional>
+#include <optional>
+#include <string>
 #include <tuple>
 #include <vector>
 
@@ -47,17 +52,13 @@ struct IncludeCleanerFindings {
   std::vector<MissingIncludeDiagInfo> MissingIncludes;
 };
 
-/// Retrieves headers that are referenced from the main file but not used.
-/// In unclear cases, headers are not marked as unused.
-std::vector<const Inclusion *>
-getUnused(ParsedAST &AST,
-          const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles,
-          const llvm::StringSet<> &ReferencedPublicHeaders);
-
 IncludeCleanerFindings computeIncludeCleanerFindings(ParsedAST &AST);
 
-std::vector<Diag> issueIncludeCleanerDiagnostics(ParsedAST &AST,
-                                                 llvm::StringRef Code);
+using HeaderFilter = llvm::ArrayRef<std::function<bool(llvm::StringRef)>>;
+std::vector<Diag>
+issueIncludeCleanerDiagnostics(ParsedAST &AST, llvm::StringRef Code,
+                               const IncludeCleanerFindings &Findings,
+                               HeaderFilter IgnoreHeader = {});
 
 /// Affects whether standard library includes should be considered for
 /// removal. This is off by default for now due to implementation limitations:

diff  --git a/clang-tools-extra/clangd/ParsedAST.cpp b/clang-tools-extra/clangd/ParsedAST.cpp
index e5a391a775389..05f0964be3c74 100644
--- a/clang-tools-extra/clangd/ParsedAST.cpp
+++ b/clang-tools-extra/clangd/ParsedAST.cpp
@@ -11,7 +11,9 @@
 #include "../clang-tidy/ClangTidyDiagnosticConsumer.h"
 #include "../clang-tidy/ClangTidyModule.h"
 #include "../clang-tidy/ClangTidyModuleRegistry.h"
+#include "../clang-tidy/ClangTidyOptions.h"
 #include "AST.h"
+#include "CollectMacros.h"
 #include "Compiler.h"
 #include "Config.h"
 #include "Diagnostics.h"
@@ -28,11 +30,18 @@
 #include "index/CanonicalIncludes.h"
 #include "index/Symbol.h"
 #include "support/Logger.h"
+#include "support/Path.h"
 #include "support/Trace.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
+#include "clang/AST/DeclGroup.h"
+#include "clang/AST/ExternalASTSource.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticIDs.h"
 #include "clang/Basic/DiagnosticSema.h"
+#include "clang/Basic/FileEntry.h"
+#include "clang/Basic/LLVM.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
@@ -40,18 +49,32 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/FrontendActions.h"
+#include "clang/Frontend/FrontendOptions.h"
+#include "clang/Frontend/PrecompiledPreamble.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Lex/PPCallbacks.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Serialization/ASTWriter.h"
 #include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/Core/Diagnostic.h"
 #include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include <cassert>
+#include <cstddef>
+#include <iterator>
 #include <memory>
 #include <optional>
+#include <string>
+#include <tuple>
+#include <utility>
 #include <vector>
 
 // Force the linker to link in Clang-tidy modules.
@@ -338,6 +361,27 @@ void applyWarningOptions(llvm::ArrayRef<std::string> ExtraArgs,
   }
 }
 
+std::vector<Diag> getIncludeCleanerDiags(ParsedAST &AST, llvm::StringRef Code) {
+  auto &Cfg = Config::current();
+  if (Cfg.Diagnostics.SuppressAll)
+    return {};
+  bool SuppressMissing =
+      Cfg.Diagnostics.Suppress.contains("missing-includes") ||
+      Cfg.Diagnostics.MissingIncludes == Config::IncludesPolicy::None;
+  bool SuppressUnused =
+      Cfg.Diagnostics.Suppress.contains("unused-includes") ||
+      Cfg.Diagnostics.UnusedIncludes == Config::IncludesPolicy::None;
+  if (SuppressMissing && SuppressUnused)
+    return {};
+  auto Findings = computeIncludeCleanerFindings(AST);
+  if (SuppressMissing)
+    Findings.MissingIncludes.clear();
+  if (SuppressUnused)
+    Findings.UnusedIncludes.clear();
+  return issueIncludeCleanerDiagnostics(AST, Code, Findings,
+                                        Cfg.Diagnostics.Includes.IgnoreHeader);
+}
+
 } // namespace
 
 std::optional<ParsedAST>
@@ -688,7 +732,7 @@ ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs,
                    std::move(Diags), std::move(Includes),
                    std::move(CanonIncludes));
   if (Result.Diags)
-    llvm::move(issueIncludeCleanerDiagnostics(Result, Inputs.Contents),
+    llvm::move(getIncludeCleanerDiags(Result, Inputs.Contents),
                std::back_inserter(*Result.Diags));
   return std::move(Result);
 }

diff  --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
index c0831c2693c97..33e3960689505 100644
--- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
+++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
@@ -7,7 +7,6 @@
 //===----------------------------------------------------------------------===//
 
 #include "Annotations.h"
-#include "Config.h"
 #include "Diagnostics.h"
 #include "IncludeCleaner.h"
 #include "ParsedAST.h"
@@ -45,7 +44,8 @@ using ::testing::Matcher;
 using ::testing::Pointee;
 using ::testing::UnorderedElementsAre;
 
-Matcher<const Diag &> withFix(std::vector<::testing::Matcher<Fix>> FixMatcheres) {
+Matcher<const Diag &>
+withFix(std::vector<::testing::Matcher<Fix>> FixMatcheres) {
   return Field(&Diag::Fixes, testing::UnorderedElementsAreArray(FixMatcheres));
 }
 
@@ -62,7 +62,6 @@ MATCHER_P3(Fix, Range, Replacement, Message,
 }
 MATCHER_P(FixMessage, Message, "") { return arg.Message == Message; }
 
-
 std::string guard(llvm::StringRef Code) {
   return "#pragma once\n" + Code.str();
 }
@@ -174,11 +173,6 @@ TEST(IncludeCleaner, ComputeMissingHeaders) {
 }
 
 TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
-  Config Cfg;
-  Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
-  Cfg.Diagnostics.Includes.IgnoreHeader = {
-      [](llvm::StringRef Header) { return Header.ends_with("buzz.h"); }};
-  WithContextValue Ctx(Config::Key, std::move(Cfg));
   Annotations MainFile(R"cpp(
 #include "a.h"
 #include "all.h"
@@ -250,8 +244,11 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
   TU.Code = MainFile.code();
   ParsedAST AST = TU.build();
 
-  std::vector<clangd::Diag> Diags =
-      issueIncludeCleanerDiagnostics(AST, TU.Code);
+  auto Findings = computeIncludeCleanerFindings(AST);
+  Findings.UnusedIncludes.clear();
+  std::vector<clangd::Diag> Diags = issueIncludeCleanerDiagnostics(
+      AST, TU.Code, Findings,
+      {[](llvm::StringRef Header) { return Header.ends_with("buzz.h"); }});
   EXPECT_THAT(
       Diags,
       UnorderedElementsAre(
@@ -279,9 +276,11 @@ TEST(IncludeCleaner, GenerateMissingHeaderDiags) {
           AllOf(
               Diag(MainFile.range("vector"),
                    "No header providing \"std::vector\" is directly included"),
-              withFix({Fix(MainFile.range("insert_vector"),
-                           "#include <vector>\n", "#include <vector>"),
-                       FixMessage("add all missing includes"),})),
+              withFix({
+                  Fix(MainFile.range("insert_vector"), "#include <vector>\n",
+                      "#include <vector>"),
+                  FixMessage("add all missing includes"),
+              })),
           AllOf(Diag(MainFile.range("FOO"),
                      "No header providing \"FOO\" is directly included"),
                 withFix({Fix(MainFile.range("insert_foo"),
@@ -320,11 +319,7 @@ TEST(IncludeCleaner, IWYUPragmas) {
     // IWYU pragma: private, include "public.h"
     void foo() {}
   )cpp");
-  Config Cfg;
-  Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
-  WithContextValue Ctx(Config::Key, std::move(Cfg));
   ParsedAST AST = TU.build();
-  EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty()));
   IncludeCleanerFindings Findings = computeIncludeCleanerFindings(AST);
   EXPECT_THAT(Findings.UnusedIncludes, IsEmpty());
 }
@@ -347,7 +342,6 @@ TEST(IncludeCleaner, IWYUPragmaExport) {
   )cpp");
   ParsedAST AST = TU.build();
 
-  EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty()));
   IncludeCleanerFindings Findings = computeIncludeCleanerFindings(AST);
   EXPECT_THAT(Findings.UnusedIncludes,
               ElementsAre(Pointee(writtenInclusion("\"foo.h\""))));
@@ -368,13 +362,10 @@ TEST(IncludeCleaner, NoDiagsForObjC) {
   )cpp";
   TU.ExtraArgs.emplace_back("-xobjective-c");
 
-  Config Cfg;
-
-  Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
-  Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
-  WithContextValue Ctx(Config::Key, std::move(Cfg));
   ParsedAST AST = TU.build();
-  EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty()));
+  IncludeCleanerFindings Findings = computeIncludeCleanerFindings(AST);
+  EXPECT_THAT(Findings.MissingIncludes, IsEmpty());
+  EXPECT_THAT(Findings.UnusedIncludes, IsEmpty());
 }
 
 TEST(IncludeCleaner, UmbrellaUsesPrivate) {
@@ -387,11 +378,7 @@ TEST(IncludeCleaner, UmbrellaUsesPrivate) {
     void foo() {}
   )cpp");
   TU.Filename = "public.h";
-  Config Cfg;
-  Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
-  WithContextValue Ctx(Config::Key, std::move(Cfg));
   ParsedAST AST = TU.build();
-  EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty()));
   IncludeCleanerFindings Findings = computeIncludeCleanerFindings(AST);
   EXPECT_THAT(Findings.UnusedIncludes, IsEmpty());
 }
@@ -511,11 +498,6 @@ TEST(IncludeCleaner, FirstMatchedProvider) {
 }
 
 TEST(IncludeCleaner, BatchFix) {
-  Config Cfg;
-  Cfg.Diagnostics.MissingIncludes = Config::IncludesPolicy::Strict;
-  Cfg.Diagnostics.UnusedIncludes = Config::IncludesPolicy::Strict;
-  WithContextValue Ctx(Config::Key, std::move(Cfg));
-
   TestTU TU;
   TU.Filename = "main.cpp";
   TU.AdditionalFiles["foo.h"] = guard("class Foo;");
@@ -532,7 +514,8 @@ TEST(IncludeCleaner, BatchFix) {
   )cpp";
   auto AST = TU.build();
   EXPECT_THAT(
-      issueIncludeCleanerDiagnostics(AST, TU.Code),
+      issueIncludeCleanerDiagnostics(AST, TU.Code,
+                                     computeIncludeCleanerFindings(AST)),
       UnorderedElementsAre(withFix({FixMessage("#include \"foo.h\""),
                                     FixMessage("fix all includes")}),
                            withFix({FixMessage("remove #include directive"),
@@ -546,14 +529,15 @@ TEST(IncludeCleaner, BatchFix) {
   )cpp";
   AST = TU.build();
   EXPECT_THAT(
-      issueIncludeCleanerDiagnostics(AST, TU.Code),
+      issueIncludeCleanerDiagnostics(AST, TU.Code,
+                                     computeIncludeCleanerFindings(AST)),
       UnorderedElementsAre(withFix({FixMessage("#include \"foo.h\""),
                                     FixMessage("fix all includes")}),
                            withFix({FixMessage("remove #include directive"),
                                     FixMessage("remove all unused includes"),
                                     FixMessage("fix all includes")}),
                            withFix({FixMessage("remove #include directive"),
-                                   FixMessage("remove all unused includes"),
+                                    FixMessage("remove all unused includes"),
                                     FixMessage("fix all includes")})));
 
   TU.Code = R"cpp(
@@ -564,7 +548,8 @@ TEST(IncludeCleaner, BatchFix) {
   )cpp";
   AST = TU.build();
   EXPECT_THAT(
-      issueIncludeCleanerDiagnostics(AST, TU.Code),
+      issueIncludeCleanerDiagnostics(AST, TU.Code,
+                                     computeIncludeCleanerFindings(AST)),
       UnorderedElementsAre(withFix({FixMessage("#include \"foo.h\""),
                                     FixMessage("add all missing includes"),
                                     FixMessage("fix all includes")}),


        


More information about the cfe-commits mailing list