1 //===--- IncludeCleaner.cpp - Unused/Missing Headers Analysis ---*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "IncludeCleaner.h"
10 #include "Config.h"
11 #include "Headers.h"
12 #include "ParsedAST.h"
13 #include "Protocol.h"
14 #include "SourceCode.h"
15 #include "index/CanonicalIncludes.h"
16 #include "support/Logger.h"
17 #include "support/Trace.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/RecursiveASTVisitor.h"
21 #include "clang/Basic/SourceLocation.h"
22 #include "clang/Basic/SourceManager.h"
23 #include "clang/Lex/HeaderSearch.h"
24 #include "clang/Lex/Preprocessor.h"
25 #include "clang/Tooling/Syntax/Tokens.h"
26 #include "llvm/ADT/STLFunctionalExtras.h"
27 #include "llvm/ADT/StringSet.h"
28 #include "llvm/Support/FormatVariadic.h"
29 #include "llvm/Support/Path.h"
30 
31 namespace clang {
32 namespace clangd {
33 
34 static bool AnalyzeStdlib = false;
35 void setIncludeCleanerAnalyzesStdlib(bool B) { AnalyzeStdlib = B; }
36 
37 namespace {
38 
39 /// Crawler traverses the AST and feeds in the locations of (sometimes
40 /// implicitly) used symbols into \p Result.
41 class ReferencedLocationCrawler
42     : public RecursiveASTVisitor<ReferencedLocationCrawler> {
43 public:
44   ReferencedLocationCrawler(ReferencedLocations &Result,
45                             const SourceManager &SM)
46       : Result(Result), SM(SM) {}
47 
48   bool VisitDeclRefExpr(DeclRefExpr *DRE) {
49     add(DRE->getDecl());
50     add(DRE->getFoundDecl());
51     return true;
52   }
53 
54   bool VisitMemberExpr(MemberExpr *ME) {
55     add(ME->getMemberDecl());
56     add(ME->getFoundDecl().getDecl());
57     return true;
58   }
59 
60   bool VisitTagType(TagType *TT) {
61     add(TT->getDecl());
62     return true;
63   }
64 
65   bool VisitFunctionDecl(FunctionDecl *FD) {
66     // Function definition will require redeclarations to be included.
67     if (FD->isThisDeclarationADefinition())
68       add(FD);
69     return true;
70   }
71 
72   bool VisitCXXConstructExpr(CXXConstructExpr *CCE) {
73     add(CCE->getConstructor());
74     return true;
75   }
76 
77   bool VisitTemplateSpecializationType(TemplateSpecializationType *TST) {
78     add(TST->getTemplateName().getAsTemplateDecl()); // Primary template.
79     add(TST->getAsCXXRecordDecl());                  // Specialization
80     return true;
81   }
82 
83   bool VisitUsingType(UsingType *UT) {
84     add(UT->getFoundDecl());
85     return true;
86   }
87 
88   bool VisitTypedefType(TypedefType *TT) {
89     add(TT->getDecl());
90     return true;
91   }
92 
93   // Consider types of any subexpression used, even if the type is not named.
94   // This is helpful in getFoo().bar(), where Foo must be complete.
95   // FIXME(kirillbobyrev): Should we tweak this? It may not be desirable to
96   // consider types "used" when they are not directly spelled in code.
97   bool VisitExpr(Expr *E) {
98     TraverseType(E->getType());
99     return true;
100   }
101 
102   bool TraverseType(QualType T) {
103     if (isNew(T.getTypePtrOrNull())) // don't care about quals
104       Base::TraverseType(T);
105     return true;
106   }
107 
108   bool VisitUsingDecl(UsingDecl *D) {
109     for (const auto *Shadow : D->shadows())
110       add(Shadow->getTargetDecl());
111     return true;
112   }
113 
114   // Enums may be usefully forward-declared as *complete* types by specifying
115   // an underlying type. In this case, the definition should see the declaration
116   // so they can be checked for compatibility.
117   bool VisitEnumDecl(EnumDecl *D) {
118     if (D->isThisDeclarationADefinition() && D->getIntegerTypeSourceInfo())
119       add(D);
120     return true;
121   }
122 
123   // When the overload is not resolved yet, mark all candidates as used.
124   bool VisitOverloadExpr(OverloadExpr *E) {
125     for (const auto *ResolutionDecl : E->decls())
126       add(ResolutionDecl);
127     return true;
128   }
129 
130 private:
131   using Base = RecursiveASTVisitor<ReferencedLocationCrawler>;
132 
133   void add(const Decl *D) {
134     if (!D || !isNew(D->getCanonicalDecl()))
135       return;
136     if (auto SS = StdRecognizer(D)) {
137       Result.Stdlib.insert(*SS);
138       return;
139     }
140     // Special case RecordDecls, as it is common for them to be forward
141     // declared multiple times. The most common cases are:
142     // - Definition available in TU, only mark that one as usage. The rest is
143     //   likely to be unnecessary. This might result in false positives when an
144     //   internal definition is visible.
145     // - There's a forward declaration in the main file, no need for other
146     //   redecls.
147     if (const auto *RD = llvm::dyn_cast<RecordDecl>(D)) {
148       if (const auto *Definition = RD->getDefinition()) {
149         Result.User.insert(Definition->getLocation());
150         return;
151       }
152       if (SM.isInMainFile(RD->getMostRecentDecl()->getLocation()))
153         return;
154     }
155     for (const Decl *Redecl : D->redecls())
156       Result.User.insert(Redecl->getLocation());
157   }
158 
159   bool isNew(const void *P) { return P && Visited.insert(P).second; }
160 
161   ReferencedLocations &Result;
162   llvm::DenseSet<const void *> Visited;
163   const SourceManager &SM;
164   tooling::stdlib::Recognizer StdRecognizer;
165 };
166 
167 // Given a set of referenced FileIDs, determines all the potentially-referenced
168 // files and macros by traversing expansion/spelling locations of macro IDs.
169 // This is used to map the referenced SourceLocations onto real files.
170 struct ReferencedFilesBuilder {
171   ReferencedFilesBuilder(const SourceManager &SM) : SM(SM) {}
172   llvm::DenseSet<FileID> Files;
173   llvm::DenseSet<FileID> Macros;
174   const SourceManager &SM;
175 
176   void add(SourceLocation Loc) { add(SM.getFileID(Loc), Loc); }
177 
178   void add(FileID FID, SourceLocation Loc) {
179     if (FID.isInvalid())
180       return;
181     assert(SM.isInFileID(Loc, FID));
182     if (Loc.isFileID()) {
183       Files.insert(FID);
184       return;
185     }
186     // Don't process the same macro FID twice.
187     if (!Macros.insert(FID).second)
188       return;
189     const auto &Exp = SM.getSLocEntry(FID).getExpansion();
190     add(Exp.getSpellingLoc());
191     add(Exp.getExpansionLocStart());
192     add(Exp.getExpansionLocEnd());
193   }
194 };
195 
196 // Returns the range starting at '#' and ending at EOL. Escaped newlines are not
197 // handled.
198 clangd::Range getDiagnosticRange(llvm::StringRef Code, unsigned HashOffset) {
199   clangd::Range Result;
200   Result.end = Result.start = offsetToPosition(Code, HashOffset);
201 
202   // Span the warning until the EOL or EOF.
203   Result.end.character +=
204       lspLength(Code.drop_front(HashOffset).take_until([](char C) {
205         return C == '\n' || C == '\r';
206       }));
207   return Result;
208 }
209 
210 // Finds locations of macros referenced from within the main file. That includes
211 // references that were not yet expanded, e.g `BAR` in `#define FOO BAR`.
212 void findReferencedMacros(const SourceManager &SM, Preprocessor &PP,
213                           const syntax::TokenBuffer *Tokens,
214                           ReferencedLocations &Result) {
215   trace::Span Tracer("IncludeCleaner::findReferencedMacros");
216   // FIXME(kirillbobyrev): The macros from the main file are collected in
217   // ParsedAST's MainFileMacros. However, we can't use it here because it
218   // doesn't handle macro references that were not expanded, e.g. in macro
219   // definitions or preprocessor-disabled sections.
220   //
221   // Extending MainFileMacros to collect missing references and switching to
222   // this mechanism (as opposed to iterating through all tokens) will improve
223   // the performance of findReferencedMacros and also improve other features
224   // relying on MainFileMacros.
225   for (const syntax::Token &Tok : Tokens->spelledTokens(SM.getMainFileID())) {
226     auto Macro = locateMacroAt(Tok, PP);
227     if (!Macro)
228       continue;
229     auto Loc = Macro->Info->getDefinitionLoc();
230     if (Loc.isValid())
231       Result.User.insert(Loc);
232     // FIXME: support stdlib macros
233   }
234 }
235 
236 static bool mayConsiderUnused(const Inclusion &Inc, ParsedAST &AST) {
237   if (Inc.BehindPragmaKeep)
238     return false;
239 
240   // FIXME(kirillbobyrev): We currently do not support the umbrella headers.
241   // System headers are likely to be standard library headers.
242   // Until we have good support for umbrella headers, don't warn about them.
243   if (Inc.Written.front() == '<') {
244     if (AnalyzeStdlib && tooling::stdlib::Header::named(Inc.Written))
245       return true;
246     return false;
247   }
248   // Headers without include guards have side effects and are not
249   // self-contained, skip them.
250   assert(Inc.HeaderID);
251   auto FE = AST.getSourceManager().getFileManager().getFileRef(
252       AST.getIncludeStructure().getRealPath(
253           static_cast<IncludeStructure::HeaderID>(*Inc.HeaderID)));
254   assert(FE);
255   if (!AST.getPreprocessor().getHeaderSearchInfo().isFileMultipleIncludeGuarded(
256           &FE->getFileEntry())) {
257     dlog("{0} doesn't have header guard and will not be considered unused",
258          FE->getName());
259     return false;
260   }
261   return true;
262 }
263 
264 // In case symbols are coming from non self-contained header, we need to find
265 // its first includer that is self-contained. This is the header users can
266 // include, so it will be responsible for bringing the symbols from given
267 // header into the scope.
268 FileID headerResponsible(FileID ID, const SourceManager &SM,
269                          const IncludeStructure &Includes) {
270   // Unroll the chain of non self-contained headers until we find the one that
271   // can be included.
272   for (const FileEntry *FE = SM.getFileEntryForID(ID); ID != SM.getMainFileID();
273        FE = SM.getFileEntryForID(ID)) {
274     // If FE is nullptr, we consider it to be the responsible header.
275     if (!FE)
276       break;
277     auto HID = Includes.getID(FE);
278     assert(HID && "We're iterating over headers already existing in "
279                   "IncludeStructure");
280     if (Includes.isSelfContained(*HID))
281       break;
282     // The header is not self-contained: put the responsibility for its symbols
283     // on its includer.
284     ID = SM.getFileID(SM.getIncludeLoc(ID));
285   }
286   return ID;
287 }
288 
289 } // namespace
290 
291 ReferencedLocations findReferencedLocations(ASTContext &Ctx, Preprocessor &PP,
292                                             const syntax::TokenBuffer *Tokens) {
293   trace::Span Tracer("IncludeCleaner::findReferencedLocations");
294   ReferencedLocations Result;
295   const auto &SM = Ctx.getSourceManager();
296   ReferencedLocationCrawler Crawler(Result, SM);
297   Crawler.TraverseAST(Ctx);
298   if (Tokens)
299     findReferencedMacros(SM, PP, Tokens, Result);
300   return Result;
301 }
302 
303 ReferencedLocations findReferencedLocations(ParsedAST &AST) {
304   return findReferencedLocations(AST.getASTContext(), AST.getPreprocessor(),
305                                  &AST.getTokens());
306 }
307 
308 ReferencedFiles findReferencedFiles(
309     const ReferencedLocations &Locs, const SourceManager &SM,
310     llvm::function_ref<FileID(FileID)> HeaderResponsible,
311     llvm::function_ref<Optional<StringRef>(FileID)> UmbrellaHeader) {
312   std::vector<SourceLocation> Sorted{Locs.User.begin(), Locs.User.end()};
313   llvm::sort(Sorted); // Group by FileID.
314   ReferencedFilesBuilder Builder(SM);
315   for (auto It = Sorted.begin(); It < Sorted.end();) {
316     FileID FID = SM.getFileID(*It);
317     Builder.add(FID, *It);
318     // Cheaply skip over all the other locations from the same FileID.
319     // This avoids lots of redundant Loc->File lookups for the same file.
320     do
321       ++It;
322     while (It != Sorted.end() && SM.isInFileID(*It, FID));
323   }
324 
325   // If a header is not self-contained, we consider its symbols a logical part
326   // of the including file. Therefore, mark the parents of all used
327   // non-self-contained FileIDs as used. Perform this on FileIDs rather than
328   // HeaderIDs, as each inclusion of a non-self-contained file is distinct.
329   llvm::DenseSet<FileID> UserFiles;
330   llvm::StringSet<> PublicHeaders;
331   for (FileID ID : Builder.Files) {
332     UserFiles.insert(HeaderResponsible(ID));
333     if (auto PublicHeader = UmbrellaHeader(ID)) {
334       PublicHeaders.insert(*PublicHeader);
335     }
336   }
337 
338   llvm::DenseSet<tooling::stdlib::Header> StdlibFiles;
339   for (const auto &Symbol : Locs.Stdlib)
340     for (const auto &Header : Symbol.headers())
341       StdlibFiles.insert(Header);
342 
343   return {std::move(UserFiles), std::move(StdlibFiles),
344           std::move(PublicHeaders)};
345 }
346 
347 ReferencedFiles findReferencedFiles(const ReferencedLocations &Locs,
348                                     const IncludeStructure &Includes,
349                                     const CanonicalIncludes &CanonIncludes,
350                                     const SourceManager &SM) {
351   return findReferencedFiles(
352       Locs, SM,
353       [&SM, &Includes](FileID ID) {
354         return headerResponsible(ID, SM, Includes);
355       },
356       [&SM, &CanonIncludes](FileID ID) -> Optional<StringRef> {
357         auto Entry = SM.getFileEntryRefForID(ID);
358         if (!Entry)
359           return llvm::None;
360         auto PublicHeader = CanonIncludes.mapHeader(*Entry);
361         if (PublicHeader.empty())
362           return llvm::None;
363         return PublicHeader;
364       });
365 }
366 
367 std::vector<const Inclusion *>
368 getUnused(ParsedAST &AST,
369           const llvm::DenseSet<IncludeStructure::HeaderID> &ReferencedFiles,
370           const llvm::StringSet<> &ReferencedPublicHeaders) {
371   trace::Span Tracer("IncludeCleaner::getUnused");
372   std::vector<const Inclusion *> Unused;
373   for (const Inclusion &MFI : AST.getIncludeStructure().MainFileIncludes) {
374     if (!MFI.HeaderID)
375       continue;
376     if (ReferencedPublicHeaders.contains(MFI.Written))
377       continue;
378     auto IncludeID = static_cast<IncludeStructure::HeaderID>(*MFI.HeaderID);
379     bool Used = ReferencedFiles.contains(IncludeID);
380     if (!Used && !mayConsiderUnused(MFI, AST)) {
381       dlog("{0} was not used, but is not eligible to be diagnosed as unused",
382            MFI.Written);
383       continue;
384     }
385     if (!Used)
386       Unused.push_back(&MFI);
387     dlog("{0} is {1}", MFI.Written, Used ? "USED" : "UNUSED");
388   }
389   return Unused;
390 }
391 
392 #ifndef NDEBUG
393 // Is FID a <built-in>, <scratch space> etc?
394 static bool isSpecialBuffer(FileID FID, const SourceManager &SM) {
395   const SrcMgr::FileInfo &FI = SM.getSLocEntry(FID).getFile();
396   return FI.getName().startswith("<");
397 }
398 #endif
399 
400 llvm::DenseSet<IncludeStructure::HeaderID>
401 translateToHeaderIDs(const ReferencedFiles &Files,
402                      const IncludeStructure &Includes,
403                      const SourceManager &SM) {
404   trace::Span Tracer("IncludeCleaner::translateToHeaderIDs");
405   llvm::DenseSet<IncludeStructure::HeaderID> TranslatedHeaderIDs;
406   TranslatedHeaderIDs.reserve(Files.User.size());
407   for (FileID FID : Files.User) {
408     const FileEntry *FE = SM.getFileEntryForID(FID);
409     if (!FE) {
410       assert(isSpecialBuffer(FID, SM));
411       continue;
412     }
413     const auto File = Includes.getID(FE);
414     assert(File);
415     TranslatedHeaderIDs.insert(*File);
416   }
417   for (tooling::stdlib::Header StdlibUsed : Files.Stdlib)
418     for (auto HID : Includes.StdlibHeaders.lookup(StdlibUsed))
419       TranslatedHeaderIDs.insert(HID);
420   return TranslatedHeaderIDs;
421 }
422 
423 std::vector<const Inclusion *> computeUnusedIncludes(ParsedAST &AST) {
424   const auto &SM = AST.getSourceManager();
425 
426   auto Refs = findReferencedLocations(AST);
427   auto ReferencedFiles =
428       findReferencedFiles(Refs, AST.getIncludeStructure(),
429                           AST.getCanonicalIncludes(), AST.getSourceManager());
430   auto ReferencedHeaders =
431       translateToHeaderIDs(ReferencedFiles, AST.getIncludeStructure(), SM);
432   return getUnused(AST, ReferencedHeaders, ReferencedFiles.SpelledUmbrellas);
433 }
434 
435 std::vector<Diag> issueUnusedIncludesDiagnostics(ParsedAST &AST,
436                                                  llvm::StringRef Code) {
437   const Config &Cfg = Config::current();
438   if (Cfg.Diagnostics.UnusedIncludes != Config::UnusedIncludesPolicy::Strict ||
439       Cfg.Diagnostics.SuppressAll ||
440       Cfg.Diagnostics.Suppress.contains("unused-includes"))
441     return {};
442   trace::Span Tracer("IncludeCleaner::issueUnusedIncludesDiagnostics");
443   std::vector<Diag> Result;
444   std::string FileName =
445       AST.getSourceManager()
446           .getFileEntryRefForID(AST.getSourceManager().getMainFileID())
447           ->getName()
448           .str();
449   for (const auto *Inc : computeUnusedIncludes(AST)) {
450     Diag D;
451     D.Message =
452         llvm::formatv("included header {0} is not used",
453                       llvm::sys::path::filename(
454                           Inc->Written.substr(1, Inc->Written.size() - 2),
455                           llvm::sys::path::Style::posix));
456     D.Name = "unused-includes";
457     D.Source = Diag::DiagSource::Clangd;
458     D.File = FileName;
459     D.Severity = DiagnosticsEngine::Warning;
460     D.Tags.push_back(Unnecessary);
461     D.Range = getDiagnosticRange(Code, Inc->HashOffset);
462     // FIXME(kirillbobyrev): Removing inclusion might break the code if the
463     // used headers are only reachable transitively through this one. Suggest
464     // including them directly instead.
465     // FIXME(kirillbobyrev): Add fix suggestion for adding IWYU pragmas
466     // (keep/export) remove the warning once we support IWYU pragmas.
467     D.Fixes.emplace_back();
468     D.Fixes.back().Message = "remove #include directive";
469     D.Fixes.back().Edits.emplace_back();
470     D.Fixes.back().Edits.back().range.start.line = Inc->HashLine;
471     D.Fixes.back().Edits.back().range.end.line = Inc->HashLine + 1;
472     D.InsideMainFile = true;
473     Result.push_back(std::move(D));
474   }
475   return Result;
476 }
477 
478 } // namespace clangd
479 } // namespace clang
480