diff --git a/clang-tools-extra/include-cleaner/test/tool.cpp b/clang-tools-extra/include-cleaner/test/tool.cpp --- a/clang-tools-extra/include-cleaner/test/tool.cpp +++ b/clang-tools-extra/include-cleaner/test/tool.cpp @@ -14,6 +14,10 @@ // 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 %s -- -I%S/Inputs/ | FileCheck --match-full-lines --check-prefix=PRINT %s // PRINT: #include "foo.h" // PRINT-NOT: {{^}}#include "foobar.h"{{$}} diff --git a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp --- a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp +++ b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp @@ -14,8 +14,10 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Tooling.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" @@ -47,6 +49,13 @@ cl::cat(IncludeCleaner), }; +cl::opt IgnoreHeaders{ + "ignore-headers", + cl::desc("A comma-separated list of headers to ignore."), + cl::init(""), + cl::cat(IncludeCleaner), +}; + enum class PrintStyle { Changes, Final }; cl::opt Print{ "print", @@ -117,6 +126,27 @@ return; } + std::vector HeaderFilters; + llvm::SmallVector 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; + } + HeaderFilters.push_back(std::move(CompiledRegex)); + } + auto Filter = [&HeaderFilters](llvm::StringRef Path) { + for (const auto &F : HeaderFilters) + if (F.match(Path)) + return true; + return false; + }; + if (!HTMLReportPath.empty()) writeHTML(); @@ -137,10 +167,16 @@ if (Print.getNumOccurrences()) { switch (Print) { case PrintStyle::Changes: - for (const Include *I : Results.Unused) + for (const Include *I : Results.Unused) { + if (Filter(I->Resolved->tryGetRealPathName())) + continue; llvm::outs() << "- " << I->quote() << " @Line:" << I->Line << "\n"; - for (const auto &I : Results.Missing) + } + for (const auto &I : Results.Missing) { + if (Filter(llvm::StringRef(I).trim("<>\""))) + continue; llvm::outs() << "+ " << I << "\n"; + } break; case PrintStyle::Final: llvm::outs() << Final;