diff --git a/clang-tools-extra/clangd/IncludeCleaner.h b/clang-tools-extra/clangd/IncludeCleaner.h --- a/clang-tools-extra/clangd/IncludeCleaner.h +++ b/clang-tools-extra/clangd/IncludeCleaner.h @@ -25,6 +25,7 @@ #include "ParsedAST.h" #include "clang/Basic/SourceLocation.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLFunctionalExtras.h" #include namespace clang { @@ -41,15 +42,17 @@ /// associated locations. These may be macro expansions, and are not resolved /// to their spelling or expansion location. These locations are later used to /// determine which headers should be marked as "used" and "directly used". -/// - We also examine all identifier tokens in the file in case they reference -/// macros. -/// +/// - If \p Tokens is not nullptr, we also examine all identifier tokens in the +/// file in case they reference macros macros. /// We use this to compute unused headers, so we: /// /// - cover the whole file in a single traversal for efficiency /// - don't attempt to describe where symbols were referenced from in /// ambiguous cases (e.g. implicitly used symbols, multiple declarations) /// - err on the side of reporting all possible locations +ReferencedLocations findReferencedLocations(const SourceManager &SM, + ASTContext &Ctx, Preprocessor &PP, + const syntax::TokenBuffer *Tokens); ReferencedLocations findReferencedLocations(ParsedAST &AST); struct ReferencedFiles { @@ -60,6 +63,12 @@ /// Retrieves IDs of all files containing SourceLocations from \p Locs. /// The output only includes things SourceManager sees as files (not macro IDs). /// This can include , etc that are not true files. +/// \p HeaderResponsible returns the public header that should be included given +/// symbols from a file with the given FileID (example: public headers should be +/// preferred to non self-contained and private headers). +ReferencedFiles +findReferencedFiles(const ReferencedLocations &Locs, const SourceManager &SM, + llvm::function_ref HeaderResponsible); ReferencedFiles findReferencedFiles(const ReferencedLocations &Locs, const IncludeStructure &Includes, const SourceManager &SM); @@ -81,8 +90,8 @@ std::vector issueUnusedIncludesDiagnostics(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: +/// Affects whether standard library includes should be considered for +/// removal. This is off by default for now due to implementation limitations: /// - macros are not tracked /// - symbol names without a unique associated header are not tracked /// - references to std-namespaced C types are not properly tracked: diff --git a/clang-tools-extra/clangd/IncludeCleaner.cpp b/clang-tools-extra/clangd/IncludeCleaner.cpp --- a/clang-tools-extra/clangd/IncludeCleaner.cpp +++ b/clang-tools-extra/clangd/IncludeCleaner.cpp @@ -14,6 +14,7 @@ #include "SourceCode.h" #include "support/Logger.h" #include "support/Trace.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/SourceLocation.h" @@ -21,6 +22,7 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Tooling/Syntax/Tokens.h" +#include "llvm/ADT/STLFunctionalExtras.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" @@ -207,10 +209,10 @@ // Finds locations of macros referenced from within the main file. That includes // references that were not yet expanded, e.g `BAR` in `#define FOO BAR`. -void findReferencedMacros(ParsedAST &AST, ReferencedLocations &Result) { +void findReferencedMacros(const SourceManager &SM, Preprocessor &PP, + const syntax::TokenBuffer *Tokens, + ReferencedLocations &Result) { trace::Span Tracer("IncludeCleaner::findReferencedMacros"); - auto &SM = AST.getSourceManager(); - auto &PP = AST.getPreprocessor(); // FIXME(kirillbobyrev): The macros from the main file are collected in // ParsedAST's MainFileMacros. However, we can't use it here because it // doesn't handle macro references that were not expanded, e.g. in macro @@ -220,8 +222,7 @@ // this mechanism (as opposed to iterating through all tokens) will improve // the performance of findReferencedMacros and also improve other features // relying on MainFileMacros. - for (const syntax::Token &Tok : - AST.getTokens().spelledTokens(SM.getMainFileID())) { + for (const syntax::Token &Tok : Tokens->spelledTokens(SM.getMainFileID())) { auto Macro = locateMacroAt(Tok, PP); if (!Macro) continue; @@ -287,18 +288,26 @@ } // namespace -ReferencedLocations findReferencedLocations(ParsedAST &AST) { +ReferencedLocations findReferencedLocations(const SourceManager &SM, + ASTContext &Ctx, Preprocessor &PP, + const syntax::TokenBuffer *Tokens) { trace::Span Tracer("IncludeCleaner::findReferencedLocations"); ReferencedLocations Result; - ReferencedLocationCrawler Crawler(Result, AST.getSourceManager()); - Crawler.TraverseAST(AST.getASTContext()); - findReferencedMacros(AST, Result); + ReferencedLocationCrawler Crawler(Result, SM); + Crawler.TraverseAST(Ctx); + if (Tokens) + findReferencedMacros(SM, PP, Tokens, Result); return Result; } -ReferencedFiles findReferencedFiles(const ReferencedLocations &Locs, - const IncludeStructure &Includes, - const SourceManager &SM) { +ReferencedLocations findReferencedLocations(ParsedAST &AST) { + return findReferencedLocations(AST.getSourceManager(), AST.getASTContext(), + AST.getPreprocessor(), &AST.getTokens()); +} + +ReferencedFiles +findReferencedFiles(const ReferencedLocations &Locs, const SourceManager &SM, + llvm::function_ref HeaderResponsible) { std::vector Sorted{Locs.User.begin(), Locs.User.end()}; llvm::sort(Sorted); // Group by FileID. ReferencedFilesBuilder Builder(SM); @@ -318,7 +327,7 @@ // HeaderIDs, as each inclusion of a non-self-contained file is distinct. llvm::DenseSet UserFiles; for (FileID ID : Builder.Files) - UserFiles.insert(headerResponsible(ID, SM, Includes)); + UserFiles.insert(HeaderResponsible(ID)); llvm::DenseSet StdlibFiles; for (const auto &Symbol : Locs.Stdlib) @@ -328,6 +337,14 @@ return {std::move(UserFiles), std::move(StdlibFiles)}; } +ReferencedFiles findReferencedFiles(const ReferencedLocations &Locs, + const IncludeStructure &Includes, + const SourceManager &SM) { + return findReferencedFiles(Locs, SM, [&SM, &Includes](FileID ID) { + return headerResponsible(ID, SM, Includes); + }); +} + std::vector getUnused(ParsedAST &AST, const llvm::DenseSet &ReferencedFiles) {