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