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