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