[clang-tools-extra] 507d766 - [include-cleaner] Add an IgnoreHeaders flag to the command-line tool.

Haojian Wu via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 6 02:18:53 PDT 2023


Author: Haojian Wu
Date: 2023-07-06T11:18:44+02:00
New Revision: 507d766d76d873aa6e446889f93384745a0e1c0b

URL: https://github.com/llvm/llvm-project/commit/507d766d76d873aa6e446889f93384745a0e1c0b
DIFF: https://github.com/llvm/llvm-project/commit/507d766d76d873aa6e446889f93384745a0e1c0b.diff

LOG: [include-cleaner] Add an IgnoreHeaders flag to the command-line tool.

Reviewed By: kadircet

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

Added: 
    

Modified: 
    clang-tools-extra/clangd/IncludeCleaner.cpp
    clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
    clang-tools-extra/include-cleaner/include/clang-include-cleaner/Types.h
    clang-tools-extra/include-cleaner/lib/Analysis.cpp
    clang-tools-extra/include-cleaner/lib/Types.cpp
    clang-tools-extra/include-cleaner/test/tool.cpp
    clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp
index dd8b5afc2a24f9..2a8499dade84c0 100644
--- a/clang-tools-extra/clangd/IncludeCleaner.cpp
+++ b/clang-tools-extra/clangd/IncludeCleaner.cpp
@@ -125,18 +125,6 @@ bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST,
   return true;
 }
 
-llvm::StringRef getResolvedPath(const include_cleaner::Header &SymProvider) {
-  switch (SymProvider.kind()) {
-  case include_cleaner::Header::Physical:
-    return SymProvider.physical()->tryGetRealPathName();
-  case include_cleaner::Header::Standard:
-    return SymProvider.standard().name().trim("<>\"");
-  case include_cleaner::Header::Verbatim:
-    return SymProvider.verbatim().trim("<>\"");
-  }
-  llvm_unreachable("Unknown header kind");
-}
-
 std::vector<Diag> generateMissingIncludeDiagnostics(
     ParsedAST &AST, llvm::ArrayRef<MissingIncludeDiagInfo> MissingIncludes,
     llvm::StringRef Code, HeaderFilter IgnoreHeaders) {
@@ -156,7 +144,7 @@ std::vector<Diag> generateMissingIncludeDiagnostics(
                                          FileStyle->IncludeStyle);
   for (const auto &SymbolWithMissingInclude : MissingIncludes) {
     llvm::StringRef ResolvedPath =
-        getResolvedPath(SymbolWithMissingInclude.Providers.front());
+        SymbolWithMissingInclude.Providers.front().resolvedPath();
     if (isIgnored(ResolvedPath, IgnoreHeaders)) {
       dlog("IncludeCleaner: not diagnosing missing include {0}, filtered by "
            "config",

diff  --git a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
index 13a1cfa76c7177..547e9dd7261ca3 100644
--- a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
+++ b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h
@@ -65,10 +65,16 @@ struct AnalysisResults {
 
 /// Determine which headers should be inserted or removed from the main file.
 /// This exposes conclusions but not reasons: use lower-level walkUsed for that.
-AnalysisResults analyze(llvm::ArrayRef<Decl *> ASTRoots,
-                        llvm::ArrayRef<SymbolReference> MacroRefs,
-                        const Includes &I, const PragmaIncludes *PI,
-                        const SourceManager &SM, const HeaderSearch &HS);
+///
+/// The HeaderFilter is a predicate that receives absolute path or spelling
+/// without quotes/brackets, when a phyiscal file doesn't exist.
+/// No analysis will be performed for headers that satisfy the predicate.
+AnalysisResults
+analyze(llvm::ArrayRef<Decl *> ASTRoots,
+        llvm::ArrayRef<SymbolReference> MacroRefs, const Includes &I,
+        const PragmaIncludes *PI, const SourceManager &SM,
+        const HeaderSearch &HS,
+        llvm::function_ref<bool(llvm::StringRef)> HeaderFilter = nullptr);
 
 /// Removes unused includes and inserts missing ones in the main file.
 /// Returns the modified main-file code.

diff  --git a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Types.h b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Types.h
index 48b018b382ba54..b16d494e95b5c9 100644
--- a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Types.h
+++ b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Types.h
@@ -29,6 +29,7 @@
 #include "llvm/ADT/DenseMapInfoVariant.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
 #include <memory>
 #include <utility>
 #include <variant>
@@ -133,6 +134,10 @@ struct Header {
   }
   StringRef verbatim() const { return std::get<Verbatim>(Storage); }
 
+  /// Absolute path for the header when it's a physical file. Otherwise just
+  /// the spelling without surrounding quotes/brackets.
+  llvm::StringRef resolvedPath() const;
+
 private:
   // Order must match Kind enum!
   std::variant<const FileEntry *, tooling::stdlib::Header, StringRef> Storage;

diff  --git a/clang-tools-extra/include-cleaner/lib/Analysis.cpp b/clang-tools-extra/include-cleaner/lib/Analysis.cpp
index 3952d10a423340..616eeae43a87ee 100644
--- a/clang-tools-extra/include-cleaner/lib/Analysis.cpp
+++ b/clang-tools-extra/include-cleaner/lib/Analysis.cpp
@@ -57,13 +57,17 @@ void walkUsed(llvm::ArrayRef<Decl *> ASTRoots,
   }
 }
 
-AnalysisResults analyze(llvm::ArrayRef<Decl *> ASTRoots,
-                        llvm::ArrayRef<SymbolReference> MacroRefs,
-                        const Includes &Inc, const PragmaIncludes *PI,
-                        const SourceManager &SM, const HeaderSearch &HS) {
+AnalysisResults
+analyze(llvm::ArrayRef<Decl *> ASTRoots,
+        llvm::ArrayRef<SymbolReference> MacroRefs, const Includes &Inc,
+        const PragmaIncludes *PI, const SourceManager &SM,
+        const HeaderSearch &HS,
+        llvm::function_ref<bool(llvm::StringRef)> HeaderFilter) {
   const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
   llvm::DenseSet<const Include *> Used;
   llvm::StringSet<> Missing;
+  if (!HeaderFilter)
+    HeaderFilter = [](llvm::StringRef) { return false; };
   walkUsed(ASTRoots, MacroRefs, PI, SM,
            [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {
              bool Satisfied = false;
@@ -76,13 +80,15 @@ AnalysisResults analyze(llvm::ArrayRef<Decl *> ASTRoots,
                }
              }
              if (!Satisfied && !Providers.empty() &&
-                 Ref.RT == RefType::Explicit)
+                 Ref.RT == RefType::Explicit &&
+                 !HeaderFilter(Providers.front().resolvedPath()))
                Missing.insert(spellHeader({Providers.front(), HS, MainFile}));
            });
 
   AnalysisResults Results;
   for (const Include &I : Inc.all()) {
-    if (Used.contains(&I) || !I.Resolved)
+    if (Used.contains(&I) || !I.Resolved ||
+        HeaderFilter(I.Resolved->tryGetRealPathName()))
       continue;
     if (PI) {
       if (PI->shouldKeep(I.Line))

diff  --git a/clang-tools-extra/include-cleaner/lib/Types.cpp b/clang-tools-extra/include-cleaner/lib/Types.cpp
index a5ba4a0d47f6c4..271780bc1e76d2 100644
--- a/clang-tools-extra/include-cleaner/lib/Types.cpp
+++ b/clang-tools-extra/include-cleaner/lib/Types.cpp
@@ -38,6 +38,18 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Symbol &S) {
   llvm_unreachable("Unhandled Symbol kind");
 }
 
+llvm::StringRef Header::resolvedPath() const {
+  switch (kind()) {
+  case include_cleaner::Header::Physical:
+    return physical()->tryGetRealPathName();
+  case include_cleaner::Header::Standard:
+    return standard().name().trim("<>\"");
+  case include_cleaner::Header::Verbatim:
+    return verbatim().trim("<>\"");
+  }
+  llvm_unreachable("Unknown header kind");
+}
+
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Header &H) {
   switch (H.kind()) {
   case Header::Physical:

diff  --git a/clang-tools-extra/include-cleaner/test/tool.cpp b/clang-tools-extra/include-cleaner/test/tool.cpp
index 937099a39ce988..bd90679875acb8 100644
--- a/clang-tools-extra/include-cleaner/test/tool.cpp
+++ b/clang-tools-extra/include-cleaner/test/tool.cpp
@@ -14,6 +14,14 @@ int x = foo();
 //      REMOVE: - "foobar.h"
 //  REMOVE-NOT: + "foo.h"
 
+//        RUN: clang-include-cleaner -print=changes %s --ignore-headers="foobar\.h,foo\.h" -- -I%S/Inputs/ | FileCheck --match-full-lines --allow-empty --check-prefix=IGNORE %s
+// IGNORE-NOT: - "foobar.h"
+// IGNORE-NOT: + "foo.h"
+
+//        RUN: clang-include-cleaner -print=changes %s --ignore-headers="foobar.*\.h" -- -I%S/Inputs/ | FileCheck --match-full-lines --allow-empty --check-prefix=IGNORE2 %s
+// IGNORE2-NOT: - "foobar.h"
+//     IGNORE2: + "foo.h"
+
 //        RUN: clang-include-cleaner -print %s -- -I%S/Inputs/ | FileCheck --match-full-lines --check-prefix=PRINT %s
 //      PRINT: #include "foo.h"
 //  PRINT-NOT: {{^}}#include "foobar.h"{{$}}
@@ -23,3 +31,8 @@ int x = foo();
 //        RUN: FileCheck --match-full-lines --check-prefix=EDIT %s < %t.cpp
 //       EDIT: #include "foo.h"
 //   EDIT-NOT: {{^}}#include "foobar.h"{{$}}
+
+//        RUN: cp %s %t.cpp
+//        RUN: clang-include-cleaner -edit --ignore-headers="foobar\.h,foo\.h" %t.cpp -- -I%S/Inputs/ 
+//        RUN: FileCheck --match-full-lines --check-prefix=EDIT2 %s < %t.cpp
+//  EDIT2-NOT: {{^}}#include "foo.h"{{$}}

diff  --git a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
index 62febb488531af..e7ae0c68c88214 100644
--- a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
+++ b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
@@ -14,10 +14,19 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Tooling/CommonOptionsParser.h"
 #include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/Regex.h"
 #include "llvm/Support/Signals.h"
 #include "llvm/Support/raw_ostream.h"
+#include <functional>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
 
 namespace clang {
 namespace include_cleaner {
@@ -47,6 +56,14 @@ cl::opt<std::string> HTMLReportPath{
     cl::cat(IncludeCleaner),
 };
 
+cl::opt<std::string> IgnoreHeaders{
+    "ignore-headers",
+    cl::desc("A comma-separated list of regexes to match against suffix of a "
+             "header, and disable analysis if matched."),
+    cl::init(""),
+    cl::cat(IncludeCleaner),
+};
+
 enum class PrintStyle { Changes, Final };
 cl::opt<PrintStyle> Print{
     "print",
@@ -91,9 +108,15 @@ format::FormatStyle getStyle(llvm::StringRef Filename) {
 }
 
 class Action : public clang::ASTFrontendAction {
+public:
+  Action(llvm::function_ref<bool(llvm::StringRef)> HeaderFilter)
+      : HeaderFilter(HeaderFilter){};
+
+private:
   RecordedAST AST;
   RecordedPP PP;
   PragmaIncludes PI;
+  llvm::function_ref<bool(llvm::StringRef)> HeaderFilter;
 
   bool BeginInvocation(CompilerInstance &CI) override {
     // We only perform include-cleaner analysis. So we disable diagnostics that
@@ -135,8 +158,8 @@ class Action : public clang::ASTFrontendAction {
     assert(!Path.empty() && "Main file path not known?");
     llvm::StringRef Code = SM.getBufferData(SM.getMainFileID());
 
-    auto Results =
-        analyze(AST.Roots, PP.MacroReferences, PP.Includes, &PI, SM, HS);
+    auto Results = analyze(AST.Roots, PP.MacroReferences, PP.Includes, &PI, SM,
+                           HS, HeaderFilter);
     if (!Insert)
       Results.Missing.clear();
     if (!Remove)
@@ -185,6 +208,43 @@ class Action : public clang::ASTFrontendAction {
         getCompilerInstance().getPreprocessor().getHeaderSearchInfo(), &PI, OS);
   }
 };
+class ActionFactory : public tooling::FrontendActionFactory {
+public:
+  ActionFactory(llvm::function_ref<bool(llvm::StringRef)> HeaderFilter)
+      : HeaderFilter(HeaderFilter) {}
+
+  std::unique_ptr<clang::FrontendAction> create() override {
+    return std::make_unique<Action>(HeaderFilter);
+  }
+
+private:
+  llvm::function_ref<bool(llvm::StringRef)> HeaderFilter;
+};
+
+std::function<bool(llvm::StringRef)> headerFilter() {
+  auto FilterRegs = std::make_shared<std::vector<llvm::Regex>>();
+
+  llvm::SmallVector<llvm::StringRef> Headers;
+  llvm::StringRef(IgnoreHeaders).split(Headers, ',', -1, /*KeepEmpty=*/false);
+  for (auto HeaderPattern : Headers) {
+    std::string AnchoredPattern = "(" + HeaderPattern.str() + ")$";
+    llvm::Regex CompiledRegex(AnchoredPattern);
+    std::string RegexError;
+    if (!CompiledRegex.isValid(RegexError)) {
+      llvm::errs() << llvm::formatv("Invalid regular expression '{0}': {1}\n",
+                                    HeaderPattern, RegexError);
+      return nullptr;
+    }
+    FilterRegs->push_back(std::move(CompiledRegex));
+  }
+  return [FilterRegs](llvm::StringRef Path) {
+    for (const auto &F : *FilterRegs) {
+      if (F.match(Path))
+        return true;
+    }
+    return false;
+  };
+}
 
 } // namespace
 } // namespace include_cleaner
@@ -210,9 +270,10 @@ int main(int argc, const char **argv) {
       }
     }
   }
-  auto Factory = clang::tooling::newFrontendActionFactory<Action>();
+  auto HeaderFilter = headerFilter();
+  ActionFactory Factory(HeaderFilter);
   return clang::tooling::ClangTool(OptionsParser->getCompilations(),
                                    OptionsParser->getSourcePathList())
-             .run(Factory.get()) ||
+             .run(&Factory) ||
          Errors != 0;
 }


        


More information about the cfe-commits mailing list