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