VitaNuo created this revision. Herald added subscribers: kadircet, arphaman. Herald added a project: All. VitaNuo requested review of this revision. Herald added subscribers: cfe-commits, MaskRay, ilya-biryukov. Herald added a project: clang-tools-extra.
Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D143635 Files: clang-tools-extra/clangd/IncludeCleaner.cpp clang-tools-extra/clangd/IncludeCleaner.h clang-tools-extra/clangd/ParsedAST.cpp clang-tools-extra/clangd/ParsedAST.h clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp
Index: clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -342,7 +342,9 @@ auto AST = TU.build(); EXPECT_THAT(computeUnusedIncludes(AST), ElementsAre(Pointee(writtenInclusion("<queue>")))); - EXPECT_THAT(computeUnusedIncludesExperimental(AST), + std::vector<include_cleaner::SymbolReference> MacroReferences = collectMacroReferences(AST); + CachedIncludeCleaner IncludeCleaner(AST, MacroReferences); + EXPECT_THAT(IncludeCleaner.computeUnusedIncludesExperimental(), ElementsAre(Pointee(writtenInclusion("<queue>")))); } @@ -379,8 +381,10 @@ computeUnusedIncludes(AST), UnorderedElementsAre(Pointee(writtenInclusion("\"unused.h\"")), Pointee(writtenInclusion("\"dir/unused.h\"")))); + std::vector<include_cleaner::SymbolReference> MacroReferences = collectMacroReferences(AST); + CachedIncludeCleaner IncludeCleaner(AST, MacroReferences); EXPECT_THAT( - computeUnusedIncludesExperimental(AST), + IncludeCleaner.computeUnusedIncludesExperimental(), UnorderedElementsAre(Pointee(writtenInclusion("\"unused.h\"")), Pointee(writtenInclusion("\"dir/unused.h\"")))); } @@ -413,8 +417,10 @@ TU.Code = MainFile.str(); ParsedAST AST = TU.build(); + std::vector<include_cleaner::SymbolReference> MacroReferences = collectMacroReferences(AST); + CachedIncludeCleaner IncludeCleaner(AST, MacroReferences); std::vector<std::pair<std::string, unsigned>> MissingIncludes = - computeMissingIncludes(AST); + IncludeCleaner.computeMissingIncludes(); std::pair<std::string, unsigned> b{"\"b.h\"", 86}; std::pair<std::string, unsigned> d{"\"dir/d.h\"", 97}; @@ -591,7 +597,10 @@ ReferencedFiles.User.contains(AST.getSourceManager().getMainFileID())); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); - EXPECT_THAT(computeUnusedIncludesExperimental(AST), IsEmpty()); + + std::vector<include_cleaner::SymbolReference> MacroReferences = collectMacroReferences(AST); + CachedIncludeCleaner IncludeCleaner(AST, MacroReferences); + EXPECT_THAT(IncludeCleaner.computeUnusedIncludesExperimental(), IsEmpty()); } TEST(IncludeCleaner, RecursiveInclusion) { @@ -620,7 +629,10 @@ EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty())); EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); - EXPECT_THAT(computeUnusedIncludesExperimental(AST), IsEmpty()); + + std::vector<include_cleaner::SymbolReference> MacroReferences = clang::clangd::collectMacroReferences(AST); + CachedIncludeCleaner IncludeCleaner(AST, MacroReferences); + EXPECT_THAT(IncludeCleaner.computeUnusedIncludesExperimental(), IsEmpty()); } TEST(IncludeCleaner, IWYUPragmaExport) { @@ -645,7 +657,9 @@ // FIXME: This is not correct: foo.h is unused but is not diagnosed as such // because we ignore headers with IWYU export pragmas for now. EXPECT_THAT(computeUnusedIncludes(AST), IsEmpty()); - EXPECT_THAT(computeUnusedIncludesExperimental(AST), IsEmpty()); + std::vector<include_cleaner::SymbolReference> MacroReferences = collectMacroReferences(AST); + CachedIncludeCleaner IncludeCleaner(AST, MacroReferences); + EXPECT_THAT(IncludeCleaner.computeUnusedIncludesExperimental(), IsEmpty()); } TEST(IncludeCleaner, NoDiagsForObjC) { Index: clang-tools-extra/clangd/ParsedAST.h =================================================================== --- clang-tools-extra/clangd/ParsedAST.h +++ clang-tools-extra/clangd/ParsedAST.h @@ -168,6 +168,9 @@ std::unique_ptr<HeuristicResolver> Resolver; }; +std::vector<include_cleaner::SymbolReference> +collectMacroReferences(ParsedAST &AST); + } // namespace clangd } // namespace clang Index: clang-tools-extra/clangd/ParsedAST.cpp =================================================================== --- clang-tools-extra/clangd/ParsedAST.cpp +++ clang-tools-extra/clangd/ParsedAST.cpp @@ -341,6 +341,27 @@ } // namespace +std::vector<include_cleaner::SymbolReference> +collectMacroReferences(ParsedAST &AST) { + const auto &SM = AST.getSourceManager(); + // FIXME: !!this is a hacky way to collect macro references. + std::vector<include_cleaner::SymbolReference> Macros; + auto &PP = AST.getPreprocessor(); + for (const syntax::Token &Tok : + AST.getTokens().spelledTokens(SM.getMainFileID())) { + auto Macro = locateMacroAt(Tok, PP); + if (!Macro) + continue; + if (auto DefLoc = Macro->Info->getDefinitionLoc(); DefLoc.isValid()) + Macros.push_back( + {Tok.location(), + include_cleaner::Macro{/*Name=*/PP.getIdentifierInfo(Tok.text(SM)), + DefLoc}, + include_cleaner::RefType::Explicit}); + } + return Macros; +} + std::optional<ParsedAST> ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr<clang::CompilerInvocation> CI, @@ -689,10 +710,12 @@ std::move(Diags), std::move(Includes), std::move(CanonIncludes)); if (Result.Diags) { + std::vector<include_cleaner::SymbolReference> MacroReferences = collectMacroReferences(Result); + CachedIncludeCleaner IncludeCleaner(Result, MacroReferences); auto UnusedHeadersDiags = - issueUnusedIncludesDiagnostics(Result, Inputs.Contents); + issueUnusedIncludesDiagnostics(IncludeCleaner, Result, Inputs.Contents); auto MissingHeadersDiags = - issueMissingIncludesDiagnostics(Result, Inputs.Contents); + issueMissingIncludesDiagnostics(IncludeCleaner, Result, Inputs.Contents); Result.Diags->insert(Result.Diags->end(), make_move_iterator(UnusedHeadersDiags.begin()), make_move_iterator(UnusedHeadersDiags.end())); Index: clang-tools-extra/clangd/IncludeCleaner.h =================================================================== --- clang-tools-extra/clangd/IncludeCleaner.h +++ clang-tools-extra/clangd/IncludeCleaner.h @@ -18,6 +18,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDECLEANER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDECLEANER_H +#include "Config.h" #include "Headers.h" #include "ParsedAST.h" #include "index/CanonicalIncludes.h" @@ -37,6 +38,31 @@ llvm::DenseSet<tooling::stdlib::Symbol> Stdlib; }; +class CachedIncludeCleaner { + +public: + CachedIncludeCleaner( + ParsedAST &AST, + const std::vector<include_cleaner::SymbolReference> &MacroReferences) + : AST(AST), MacroReferences(MacroReferences), SM(AST.getSourceManager()) { + } + + void run(); + std::vector<std::pair<std::string, unsigned>> computeMissingIncludes(); + std::vector<const Inclusion *> computeUnusedIncludesExperimental(); + +private: + ParsedAST &AST; + const std::vector<include_cleaner::SymbolReference> &MacroReferences; + llvm::SmallVector<include_cleaner::SymbolReference> Refs; + llvm::SmallVector<llvm::SmallVector<include_cleaner::Header>> ProviderRefs; + SourceManager &SM; + + include_cleaner::Includes + convertIncludes(const SourceManager &SM, + std::vector<Inclusion> MainFileIncludes); +}; + /// Finds locations of all symbols used in the main file. /// /// - RecursiveASTVisitor finds references to symbols and records their @@ -96,18 +122,14 @@ const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles, const llvm::StringSet<> &ReferencedPublicHeaders); -std::vector<std::pair<std::string, unsigned>> -computeMissingIncludes(ParsedAST &AST); std::vector<const Inclusion *> computeUnusedIncludes(ParsedAST &AST); -// The same as computeUnusedIncludes, but it is an experimental and -// include-cleaner-lib-based implementation. -std::vector<const Inclusion *> -computeUnusedIncludesExperimental(ParsedAST &AST); -std::vector<Diag> issueMissingIncludesDiagnostics(ParsedAST &AST, - llvm::StringRef Code); -std::vector<Diag> issueUnusedIncludesDiagnostics(ParsedAST &AST, - llvm::StringRef Code); +std::vector<Diag> +issueMissingIncludesDiagnostics(CachedIncludeCleaner &IncludeCleaner, + ParsedAST &AST, llvm::StringRef Code); +std::vector<Diag> +issueUnusedIncludesDiagnostics(CachedIncludeCleaner &IncludeCleaner, + ParsedAST &AST, llvm::StringRef Code); /// Affects whether standard library includes should be considered for /// removal. This is off by default for now due to implementation limitations: Index: clang-tools-extra/clangd/IncludeCleaner.cpp =================================================================== --- clang-tools-extra/clangd/IncludeCleaner.cpp +++ clang-tools-extra/clangd/IncludeCleaner.cpp @@ -29,6 +29,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/FormatVariadic.h" @@ -44,6 +45,195 @@ static bool AnalyzeStdlib = false; void setIncludeCleanerAnalyzesStdlib(bool B) { AnalyzeStdlib = B; } +static bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST, + const Config &Cfg) { + if (Inc.BehindPragmaKeep) + return false; + + // 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. + if (Inc.Written.front() == '<') { + if (AnalyzeStdlib && tooling::stdlib::Header::named(Inc.Written)) + return true; + return false; + } + assert(Inc.HeaderID); + auto HID = static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID); + // FIXME: Ignore the headers with IWYU export pragmas for now, remove this + // check when we have more precise tracking of exported headers. + if (AST.getIncludeStructure().hasIWYUExport(HID)) + return false; + auto FE = AST.getSourceManager().getFileManager().getFileRef( + AST.getIncludeStructure().getRealPath(HID)); + assert(FE); + // Headers without include guards have side effects and are not + // self-contained, skip them. + if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( + &FE->getFileEntry())) { + dlog("{0} doesn't have header guard and will not be considered unused", + FE->getName()); + return false; + } + for (auto &Filter : Cfg.Diagnostics.Includes.IgnoreHeader) { + // Convert the path to Unix slashes and try to match against the filter. + llvm::SmallString<64> Path(Inc.Resolved); + llvm::sys::path::native(Path, llvm::sys::path::Style::posix); + if (Filter(Inc.Resolved)) { + dlog("{0} header is filtered out by the configuration", FE->getName()); + return false; + } + } + return true; +} + +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)) { + 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; +} + +void CachedIncludeCleaner::run() { + const SourceManager &SM = AST.getSourceManager(); + include_cleaner::walkUsed( + AST.getLocalTopLevelDecls(), MacroReferences, AST.getPragmaIncludes(), SM, + [&](const include_cleaner::SymbolReference &Ref, + llvm::ArrayRef<include_cleaner::Header> Providers) { + assert(Refs.size() == ProviderRefs.size()); + Refs.push_back(Ref); + + llvm::SmallVector<include_cleaner::Header> SymbolProviders; + for (const include_cleaner::Header &H : Providers) { + SymbolProviders.push_back(H); + } + ProviderRefs.push_back(SymbolProviders); + }); +} + +std::vector<std::pair<std::string, unsigned>> +CachedIncludeCleaner::computeMissingIncludes() { + if (Refs.empty()) { + run(); + } + std::vector<Inclusion> MainFileIncludes = + AST.getIncludeStructure().MainFileIncludes; + + include_cleaner::Includes IncludeCleanerIncludes = + convertIncludes(AST.getSourceManager(), MainFileIncludes); + std::string FileName = + SM.getFileEntryRefForID(AST.getSourceManager().getMainFileID()) + ->getName() + .str(); + + std::vector<std::pair<std::string, unsigned>> Missing; + const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID()); + for (unsigned I = 0; I < Refs.size(); ++I) { + include_cleaner::SymbolReference Ref = Refs[I]; + llvm::SmallVector<include_cleaner::Header> Providers = ProviderRefs[I]; + bool Satisfied = false; + for (const include_cleaner::Header &H : Providers) { + if (H.kind() == include_cleaner::Header::Physical && + H.physical() == MainFile) { + Satisfied = true; + } + if (!IncludeCleanerIncludes.match(H).empty()) { + Satisfied = true; + } + } + if (!Satisfied && !Providers.empty() && + Ref.RT == include_cleaner::RefType::Explicit) { + std::string SpelledHeader = include_cleaner::spellHeader( + Providers.front(), AST.getPreprocessor().getHeaderSearchInfo(), + MainFile); + std::pair<FileID, unsigned int> FileIDAndOffset = + AST.getSourceManager().getDecomposedLoc(Ref.RefLocation); + Missing.push_back({SpelledHeader, FileIDAndOffset.second}); + } + } + return Missing; +} + +// Compute unused #includes using the include-cleaner-lib-based +// implementation. +std::vector<const Inclusion *> +CachedIncludeCleaner::computeUnusedIncludesExperimental() { + if (Refs.empty()) { + run(); + } + const auto &Includes = AST.getIncludeStructure(); + // FIXME: this map should probably be in IncludeStructure. + llvm::StringMap<llvm::SmallVector<IncludeStructure::HeaderID>> BySpelling; + for (const auto &Inc : Includes.MainFileIncludes) { + if (Inc.HeaderID) + BySpelling.try_emplace(Inc.Written) + .first->second.push_back( + static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID)); + } + + llvm::DenseSet<IncludeStructure::HeaderID> Used; + for (unsigned I = 0; I < Refs.size(); ++I) { + llvm::SmallVector<include_cleaner::Header> Providers = ProviderRefs[I]; + for (const include_cleaner::Header &H : Providers) { + switch (H.kind()) { + case include_cleaner::Header::Physical: + if (auto HeaderID = Includes.getID(H.physical())) + Used.insert(*HeaderID); + break; + case include_cleaner::Header::Standard: + for (auto HeaderID : Includes.StdlibHeaders.lookup(H.standard())) + Used.insert(HeaderID); + break; + case include_cleaner::Header::Verbatim: + for (auto HeaderID : BySpelling.lookup(H.verbatim())) + Used.insert(HeaderID); + break; + } + } + } + return getUnused(AST, Used, /*ReferencedPublicHeaders*/ {}); +} + +include_cleaner::Includes +CachedIncludeCleaner::convertIncludes(const SourceManager &SM, + std::vector<Inclusion> MainFileIncludes) { + include_cleaner::Includes Includes; + for (const Inclusion &Inc : MainFileIncludes) { + llvm::ErrorOr<const FileEntry *> ResolvedOrError = + SM.getFileManager().getFile(Inc.Resolved); + const FileEntry *Resolved = nullptr; + if (bool(ResolvedOrError)) { + Resolved = ResolvedOrError.get(); + } + SourceLocation HashLocation = + SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset); + llvm::StringRef WrittenRef = llvm::StringRef(Inc.Written); + bool Angled = WrittenRef.starts_with("<") ? true : false; + Includes.add(include_cleaner::Include{WrittenRef.trim("\"<>"), Resolved, + HashLocation, (unsigned)Inc.HashLine, + Angled}); + } + return Includes; +} + namespace { /// Crawler traverses the AST and feeds in the locations of (sometimes @@ -261,48 +451,6 @@ } } -static bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST, - const Config &Cfg) { - if (Inc.BehindPragmaKeep) - return false; - - // 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. - if (Inc.Written.front() == '<') { - if (AnalyzeStdlib && tooling::stdlib::Header::named(Inc.Written)) - return true; - return false; - } - assert(Inc.HeaderID); - auto HID = static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID); - // FIXME: Ignore the headers with IWYU export pragmas for now, remove this - // check when we have more precise tracking of exported headers. - if (AST.getIncludeStructure().hasIWYUExport(HID)) - return false; - auto FE = AST.getSourceManager().getFileManager().getFileRef( - AST.getIncludeStructure().getRealPath(HID)); - assert(FE); - // Headers without include guards have side effects and are not - // self-contained, skip them. - if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded( - &FE->getFileEntry())) { - dlog("{0} doesn't have header guard and will not be considered unused", - FE->getName()); - return false; - } - for (auto &Filter : Cfg.Diagnostics.Includes.IgnoreHeader) { - // Convert the path to Unix slashes and try to match against the filter. - llvm::SmallString<64> Path(Inc.Resolved); - llvm::sys::path::native(Path, llvm::sys::path::Style::posix); - if (Filter(Inc.Resolved)) { - dlog("{0} header is filtered out by the configuration", FE->getName()); - return false; - } - } - return true; -} - // In case symbols are coming from non self-contained header, we need to find // its first includer that is self-contained. This is the header users can // include, so it will be responsible for bringing the symbols from given @@ -406,32 +554,6 @@ }); } -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)) { - 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; -} - #ifndef NDEBUG // Is FID a <built-in>, <scratch space> etc? static bool isSpecialBuffer(FileID FID, const SourceManager &SM) { @@ -463,27 +585,6 @@ return TranslatedHeaderIDs; } -std::vector<include_cleaner::SymbolReference> -collectMacroReferences(ParsedAST &AST) { - const auto &SM = AST.getSourceManager(); - // FIXME: !!this is a hacky way to collect macro references. - std::vector<include_cleaner::SymbolReference> Macros; - auto &PP = AST.getPreprocessor(); - for (const syntax::Token &Tok : - AST.getTokens().spelledTokens(SM.getMainFileID())) { - auto Macro = locateMacroAt(Tok, PP); - if (!Macro) - continue; - if (auto DefLoc = Macro->Info->getDefinitionLoc(); DefLoc.isValid()) - Macros.push_back( - {Tok.location(), - include_cleaner::Macro{/*Name=*/PP.getIdentifierInfo(Tok.text(SM)), - DefLoc}, - include_cleaner::RefType::Explicit}); - } - return Macros; -} - // This is the original clangd-own implementation for computing unused // #includes. Eventually it will be deprecated and replaced by the // include-cleaner-lib-based implementation. @@ -499,122 +600,9 @@ return getUnused(AST, ReferencedHeaders, ReferencedFiles.SpelledUmbrellas); } -// Compute unused #includes using the include-cleaner-lib-based implementation. -std::vector<const Inclusion *> -computeUnusedIncludesExperimental(ParsedAST &AST) { - const auto &SM = AST.getSourceManager(); - const auto &Includes = AST.getIncludeStructure(); - // FIXME: this map should probably be in IncludeStructure. - llvm::StringMap<llvm::SmallVector<IncludeStructure::HeaderID>> BySpelling; - for (const auto &Inc : Includes.MainFileIncludes) { - if (Inc.HeaderID) - BySpelling.try_emplace(Inc.Written) - .first->second.push_back( - static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID)); - } - - std::vector<include_cleaner::SymbolReference> Macros = - collectMacroReferences(AST); - - llvm::DenseSet<IncludeStructure::HeaderID> Used; - include_cleaner::walkUsed( - AST.getLocalTopLevelDecls(), /*MacroRefs=*/Macros, - AST.getPragmaIncludes(), SM, - [&](const include_cleaner::SymbolReference &Ref, - llvm::ArrayRef<include_cleaner::Header> Providers) { - for (const auto &H : Providers) { - switch (H.kind()) { - case include_cleaner::Header::Physical: - if (auto HeaderID = Includes.getID(H.physical())) - Used.insert(*HeaderID); - break; - case include_cleaner::Header::Standard: - for (auto HeaderID : Includes.StdlibHeaders.lookup(H.standard())) - Used.insert(HeaderID); - break; - case include_cleaner::Header::Verbatim: - for (auto HeaderID : BySpelling.lookup(H.verbatim())) - Used.insert(HeaderID); - break; - } - } - }); - return getUnused(AST, Used, /*ReferencedPublicHeaders*/ {}); -} - -include_cleaner::Includes -convertIncludes(const SourceManager &SM, - std::vector<Inclusion> MainFileIncludes) { - include_cleaner::Includes Includes; - for (const Inclusion &Inc : MainFileIncludes) { - llvm::ErrorOr<const FileEntry *> ResolvedOrError = - SM.getFileManager().getFile(Inc.Resolved); - const FileEntry *Resolved = nullptr; - if (bool(ResolvedOrError)) { - Resolved = ResolvedOrError.get(); - } - SourceLocation HashLocation = - SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset); - llvm::StringRef WrittenRef = llvm::StringRef(Inc.Written); - bool Angled = WrittenRef.starts_with("<") ? true : false; - Includes.add(include_cleaner::Include{WrittenRef.trim("\"<>"), Resolved, - HashLocation, (unsigned)Inc.HashLine, - Angled}); - } - return Includes; -} - -// Compute missing #includes. Uses the include-cleaner-lib-based implementation. -std::vector<std::pair<std::string, unsigned>> -computeMissingIncludes(ParsedAST &AST) { - std::vector<include_cleaner::SymbolReference> Macros = - collectMacroReferences(AST); - std::vector<Inclusion> MainFileIncludes = - AST.getIncludeStructure().MainFileIncludes; - - include_cleaner::Includes IncludeCleanerIncludes = - convertIncludes(AST.getSourceManager(), MainFileIncludes); - std::string FileName = - AST.getSourceManager() - .getFileEntryRefForID(AST.getSourceManager().getMainFileID()) - ->getName() - .str(); - if (FileName.find("foo") != std::string::npos) { - vlog("Include cleaner includes: {0}", IncludeCleanerIncludes.all().size()); - } - - std::vector<std::pair<std::string, unsigned>> Missing; - SourceManager &SM = AST.getSourceManager(); - const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID()); - include_cleaner::walkUsed( - AST.getLocalTopLevelDecls(), Macros, AST.getPragmaIncludes(), SM, - [&](const include_cleaner::SymbolReference &Ref, - llvm::ArrayRef<include_cleaner::Header> Providers) { - bool Satisfied = false; - for (const include_cleaner::Header &H : Providers) { - if (H.kind() == include_cleaner::Header::Physical && - H.physical() == MainFile) { - Satisfied = true; - } - if (!IncludeCleanerIncludes.match(H).empty()) { - Satisfied = true; - } - } - if (!Satisfied && !Providers.empty() && - Ref.RT == include_cleaner::RefType::Explicit) { - std::string SpelledHeader = include_cleaner::spellHeader( - Providers.front(), AST.getPreprocessor().getHeaderSearchInfo(), - MainFile); - std::pair<FileID, unsigned int> FileIDAndOffset = - AST.getSourceManager().getDecomposedLoc(Ref.RefLocation); - Missing.push_back({SpelledHeader, FileIDAndOffset.second}); - } - }); - return Missing; -} - -std::vector<Diag> issueMissingIncludesDiagnostics(ParsedAST &AST, - llvm::StringRef Code) { +std::vector<Diag> +issueMissingIncludesDiagnostics(CachedIncludeCleaner &IncludeCleaner, + ParsedAST &AST, llvm::StringRef Code) { const Config &Cfg = Config::current(); if (Cfg.Diagnostics.MissingIncludes == Config::MissingIncludesPolicy::None || Cfg.Diagnostics.SuppressAll || @@ -638,7 +626,7 @@ FixLine = MainFileIncludes[MainFileIncludes.size() - 1].HashLine + 1; std::vector<Diag> Result; - const auto &MissingIncludes = computeMissingIncludes(AST); + const auto &MissingIncludes = IncludeCleaner.computeMissingIncludes(); for (const std::pair<std::string, unsigned> &Missing : MissingIncludes) { Diag D; D.Message = llvm::formatv("header {0} is missing", Missing.first); @@ -669,8 +657,9 @@ return Result; } -std::vector<Diag> issueUnusedIncludesDiagnostics(ParsedAST &AST, - llvm::StringRef Code) { +std::vector<Diag> +issueUnusedIncludesDiagnostics(CachedIncludeCleaner &IncludeCleaner, + ParsedAST &AST, llvm::StringRef Code) { const Config &Cfg = Config::current(); if (Cfg.Diagnostics.UnusedIncludes == Config::UnusedIncludesPolicy::None || Cfg.Diagnostics.SuppressAll || @@ -686,9 +675,10 @@ .getFileEntryRefForID(AST.getSourceManager().getMainFileID()) ->getName() .str(); + const auto &UnusedIncludes = Cfg.Diagnostics.UnusedIncludes == Config::UnusedIncludesPolicy::Experiment - ? computeUnusedIncludesExperimental(AST) + ? IncludeCleaner.computeUnusedIncludesExperimental() : computeUnusedIncludes(AST); for (const auto *Inc : UnusedIncludes) { Diag D;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits