88
99#include " IncludeCleaner.h"
1010#include " Config.h"
11+ #include " Headers.h"
1112#include " ParsedAST.h"
1213#include " Protocol.h"
1314#include " SourceCode.h"
1617#include " clang/AST/ExprCXX.h"
1718#include " clang/AST/RecursiveASTVisitor.h"
1819#include " clang/Basic/SourceLocation.h"
20+ #include " clang/Basic/SourceManager.h"
1921#include " clang/Lex/HeaderSearch.h"
2022#include " clang/Lex/Preprocessor.h"
2123#include " clang/Tooling/Syntax/Tokens.h"
@@ -221,6 +223,31 @@ bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST) {
221223 return true ;
222224}
223225
226+ // In case symbols are coming from non self-contained header, we need to find
227+ // its first includer that is self-contained. This is the header users can
228+ // include, so it will be responsible for bringing the symbols from given
229+ // header into the scope.
230+ FileID headerResponsible (FileID ID, const SourceManager &SM,
231+ const IncludeStructure &Includes) {
232+ // Unroll the chain of non self-contained headers until we find the one that
233+ // can be included.
234+ for (const FileEntry *FE = SM.getFileEntryForID (ID); ID != SM.getMainFileID ();
235+ FE = SM.getFileEntryForID (ID)) {
236+ // If FE is nullptr, we consider it to be the responsible header.
237+ if (!FE)
238+ break ;
239+ auto HID = Includes.getID (FE);
240+ assert (HID && " We're iterating over headers already existing in "
241+ " IncludeStructure" );
242+ if (Includes.isSelfContained (*HID))
243+ break ;
244+ // The header is not self-contained: put the responsibility for its symbols
245+ // on its includer.
246+ ID = SM.getFileID (SM.getIncludeLoc (ID));
247+ }
248+ return ID;
249+ }
250+
224251} // namespace
225252
226253ReferencedLocations findReferencedLocations (ParsedAST &AST) {
@@ -234,20 +261,27 @@ ReferencedLocations findReferencedLocations(ParsedAST &AST) {
234261
235262llvm::DenseSet<FileID>
236263findReferencedFiles (const llvm::DenseSet<SourceLocation> &Locs,
237- const SourceManager &SM) {
264+ const IncludeStructure &Includes, const SourceManager &SM) {
238265 std::vector<SourceLocation> Sorted{Locs.begin (), Locs.end ()};
239266 llvm::sort (Sorted); // Group by FileID.
240- ReferencedFiles Result (SM);
267+ ReferencedFiles Files (SM);
241268 for (auto It = Sorted.begin (); It < Sorted.end ();) {
242269 FileID FID = SM.getFileID (*It);
243- Result .add (FID, *It);
270+ Files .add (FID, *It);
244271 // Cheaply skip over all the other locations from the same FileID.
245272 // This avoids lots of redundant Loc->File lookups for the same file.
246273 do
247274 ++It;
248275 while (It != Sorted.end () && SM.isInFileID (*It, FID));
249276 }
250- return std::move (Result.Files );
277+ // If a header is not self-contained, we consider its symbols a logical part
278+ // of the including file. Therefore, mark the parents of all used
279+ // non-self-contained FileIDs as used. Perform this on FileIDs rather than
280+ // HeaderIDs, as each inclusion of a non-self-contained file is distinct.
281+ llvm::DenseSet<FileID> Result;
282+ for (FileID ID : Files.Files )
283+ Result.insert (headerResponsible (ID, SM, Includes));
284+ return Result;
251285}
252286
253287std::vector<const Inclusion *>
@@ -304,25 +338,21 @@ std::vector<const Inclusion *> computeUnusedIncludes(ParsedAST &AST) {
304338 const auto &SM = AST.getSourceManager ();
305339
306340 auto Refs = findReferencedLocations (AST);
307- // FIXME(kirillbobyrev): Attribute the symbols from non self-contained
308- // headers to their parents while the information about respective
309- // SourceLocations and FileIDs is not lost. It is not possible to do that
310- // later when non-guarded headers included multiple times will get same
311- // HeaderID.
312- auto ReferencedFileIDs = findReferencedFiles (Refs, SM);
341+ auto ReferencedFileIDs = findReferencedFiles (Refs, AST.getIncludeStructure (),
342+ AST.getSourceManager ());
313343 auto ReferencedHeaders =
314344 translateToHeaderIDs (ReferencedFileIDs, AST.getIncludeStructure (), SM);
315345 return getUnused (AST, ReferencedHeaders);
316346}
317347
318348std::vector<Diag> issueUnusedIncludesDiagnostics (ParsedAST &AST,
319349 llvm::StringRef Code) {
320- trace::Span Tracer (" IncludeCleaner::issueUnusedIncludesDiagnostics" );
321350 const Config &Cfg = Config::current ();
322351 if (Cfg.Diagnostics .UnusedIncludes != Config::UnusedIncludesPolicy::Strict ||
323352 Cfg.Diagnostics .SuppressAll ||
324353 Cfg.Diagnostics .Suppress .contains (" unused-includes" ))
325354 return {};
355+ trace::Span Tracer (" IncludeCleaner::issueUnusedIncludesDiagnostics" );
326356 std::vector<Diag> Result;
327357 std::string FileName =
328358 AST.getSourceManager ()
0 commit comments