1ce286eccSKadir Cetinkaya //===--- Analysis.cpp -----------------------------------------------------===// 2ce286eccSKadir Cetinkaya // 3ce286eccSKadir Cetinkaya // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4ce286eccSKadir Cetinkaya // See https://llvm.org/LICENSE.txt for license information. 5ce286eccSKadir Cetinkaya // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6ce286eccSKadir Cetinkaya // 7ce286eccSKadir Cetinkaya //===----------------------------------------------------------------------===// 8ce286eccSKadir Cetinkaya 9ce286eccSKadir Cetinkaya #include "clang-include-cleaner/Analysis.h" 10ce286eccSKadir Cetinkaya #include "AnalysisInternal.h" 1190c5fe98SViktoriia Bakalova #include "clang-include-cleaner/IncludeSpeller.h" 1229a8eec1SKadir Cetinkaya #include "clang-include-cleaner/Record.h" 13d19ba74dSKadir Cetinkaya #include "clang-include-cleaner/Types.h" 1429a8eec1SKadir Cetinkaya #include "clang/AST/Decl.h" 15749c6a70SKadir Cetinkaya #include "clang/AST/DeclBase.h" 1643c20367SViktoriia Bakalova #include "clang/Basic/DirectoryEntry.h" 1790c5fe98SViktoriia Bakalova #include "clang/Basic/FileEntry.h" 18ce286eccSKadir Cetinkaya #include "clang/Basic/SourceManager.h" 191a8dd742SSam McCall #include "clang/Format/Format.h" 201a8dd742SSam McCall #include "clang/Lex/HeaderSearch.h" 2190ecaddeSKadir Cetinkaya #include "clang/Lex/Preprocessor.h" 221a8dd742SSam McCall #include "clang/Tooling/Core/Replacement.h" 23ce286eccSKadir Cetinkaya #include "clang/Tooling/Inclusions/StandardLibrary.h" 24ce286eccSKadir Cetinkaya #include "llvm/ADT/ArrayRef.h" 25f5b6e9b6SKadir Cetinkaya #include "llvm/ADT/DenseSet.h" 26749c6a70SKadir Cetinkaya #include "llvm/ADT/STLExtras.h" 2790c5fe98SViktoriia Bakalova #include "llvm/ADT/STLFunctionalExtras.h" 28ce286eccSKadir Cetinkaya #include "llvm/ADT/SmallVector.h" 294ef77d61Skadir çetinkaya #include "llvm/ADT/StringMap.h" 30749c6a70SKadir Cetinkaya #include "llvm/ADT/StringRef.h" 31f5b6e9b6SKadir Cetinkaya #include "llvm/Support/Error.h" 3290c5fe98SViktoriia Bakalova #include "llvm/Support/ErrorHandling.h" 3390ecaddeSKadir Cetinkaya #include <cassert> 3490ecaddeSKadir Cetinkaya #include <climits> 35f5b6e9b6SKadir Cetinkaya #include <string> 36ce286eccSKadir Cetinkaya 37ce286eccSKadir Cetinkaya namespace clang::include_cleaner { 3838cccb90SSam McCall 3990ecaddeSKadir Cetinkaya namespace { 4090ecaddeSKadir Cetinkaya bool shouldIgnoreMacroReference(const Preprocessor &PP, const Macro &M) { 4190ecaddeSKadir Cetinkaya auto *MI = PP.getMacroInfo(M.Name); 4290ecaddeSKadir Cetinkaya // Macros that expand to themselves are confusing from user's point of view. 4390ecaddeSKadir Cetinkaya // They usually aspect the usage to be attributed to the underlying decl and 4490ecaddeSKadir Cetinkaya // not the macro definition. So ignore such macros (e.g. std{in,out,err} are 4590ecaddeSKadir Cetinkaya // implementation defined macros, that just resolve to themselves in 4690ecaddeSKadir Cetinkaya // practice). 4790ecaddeSKadir Cetinkaya return MI && MI->getNumTokens() == 1 && MI->isObjectLike() && 4890ecaddeSKadir Cetinkaya MI->getReplacementToken(0).getIdentifierInfo() == M.Name; 4990ecaddeSKadir Cetinkaya } 5090ecaddeSKadir Cetinkaya } // namespace 5190ecaddeSKadir Cetinkaya 52c333b92bSSam McCall void walkUsed(llvm::ArrayRef<Decl *> ASTRoots, 53c333b92bSSam McCall llvm::ArrayRef<SymbolReference> MacroRefs, 5490ecaddeSKadir Cetinkaya const PragmaIncludes *PI, const Preprocessor &PP, 5577955480SHaojian Wu UsedSymbolCB CB) { 5690ecaddeSKadir Cetinkaya const auto &SM = PP.getSourceManager(); 5726757732SSam McCall // This is duplicated in writeHTMLReport, changes should be mirrored there. 58ce286eccSKadir Cetinkaya tooling::stdlib::Recognizer Recognizer; 59ce286eccSKadir Cetinkaya for (auto *Root : ASTRoots) { 60d19ba74dSKadir Cetinkaya walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) { 61481f8885SViktoriia Bakalova auto FID = SM.getFileID(SM.getSpellingLoc(Loc)); 62481f8885SViktoriia Bakalova if (FID != SM.getMainFileID() && FID != SM.getPreambleFileID()) 63bf6e6551SHaojian Wu return; 64481f8885SViktoriia Bakalova // FIXME: Most of the work done here is repetitive. It might be useful to 6529a8eec1SKadir Cetinkaya // have a cache/batching. 663b59842aSHaojian Wu SymbolReference SymRef{ND, Loc, RT}; 67*ec6c3448Skadir çetinkaya return CB(SymRef, headersForSymbol(ND, PP, PI)); 68ce286eccSKadir Cetinkaya }); 69ce286eccSKadir Cetinkaya } 7038cccb90SSam McCall for (const SymbolReference &MacroRef : MacroRefs) { 7138cccb90SSam McCall assert(MacroRef.Target.kind() == Symbol::Macro); 7290ecaddeSKadir Cetinkaya if (!SM.isWrittenInMainFile(SM.getSpellingLoc(MacroRef.RefLocation)) || 7390ecaddeSKadir Cetinkaya shouldIgnoreMacroReference(PP, MacroRef.Target.macro())) 74bf6e6551SHaojian Wu continue; 75*ec6c3448Skadir çetinkaya CB(MacroRef, headersForSymbol(MacroRef.Target, PP, PI)); 7638cccb90SSam McCall } 77ce286eccSKadir Cetinkaya } 78ce286eccSKadir Cetinkaya 79507d766dSHaojian Wu AnalysisResults 80507d766dSHaojian Wu analyze(llvm::ArrayRef<Decl *> ASTRoots, 81507d766dSHaojian Wu llvm::ArrayRef<SymbolReference> MacroRefs, const Includes &Inc, 8290ecaddeSKadir Cetinkaya const PragmaIncludes *PI, const Preprocessor &PP, 83507d766dSHaojian Wu llvm::function_ref<bool(llvm::StringRef)> HeaderFilter) { 8490ecaddeSKadir Cetinkaya auto &SM = PP.getSourceManager(); 8564d97136Skadir çetinkaya const auto MainFile = *SM.getFileEntryRefForID(SM.getMainFileID()); 861a8dd742SSam McCall llvm::DenseSet<const Include *> Used; 874ef77d61Skadir çetinkaya llvm::StringMap<Header> Missing; 8835c5e56bSBenjamin Kramer constexpr auto DefaultHeaderFilter = [](llvm::StringRef) { return false; }; 89507d766dSHaojian Wu if (!HeaderFilter) 9035c5e56bSBenjamin Kramer HeaderFilter = DefaultHeaderFilter; 91cb92511cSJan Svoboda OptionalDirectoryEntryRef ResourceDir = 9243c20367SViktoriia Bakalova PP.getHeaderSearchInfo().getModuleMap().getBuiltinDir(); 9390ecaddeSKadir Cetinkaya walkUsed(ASTRoots, MacroRefs, PI, PP, 941a8dd742SSam McCall [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) { 951a8dd742SSam McCall bool Satisfied = false; 961a8dd742SSam McCall for (const Header &H : Providers) { 9743c20367SViktoriia Bakalova if (H.kind() == Header::Physical && 9843c20367SViktoriia Bakalova (H.physical() == MainFile || 9964d97136Skadir çetinkaya H.physical().getDir() == ResourceDir)) { 1001a8dd742SSam McCall Satisfied = true; 10143c20367SViktoriia Bakalova } 1021a8dd742SSam McCall for (const Include *I : Inc.match(H)) { 1031a8dd742SSam McCall Used.insert(I); 1041a8dd742SSam McCall Satisfied = true; 1051a8dd742SSam McCall } 1061a8dd742SSam McCall } 10764d97136Skadir çetinkaya // Bail out if we can't (or need not) insert an include. 10864d97136Skadir çetinkaya if (Satisfied || Providers.empty() || Ref.RT != RefType::Explicit) 10964d97136Skadir çetinkaya return; 11064d97136Skadir çetinkaya if (HeaderFilter(Providers.front().resolvedPath())) 11164d97136Skadir çetinkaya return; 1122f5dc596Skadir çetinkaya // Check if we have any headers with the same spelling, in edge 1132f5dc596Skadir çetinkaya // cases like `#include_next "foo.h"`, the user can't ever 1142f5dc596Skadir çetinkaya // include the physical foo.h, but can have a spelling that 1152f5dc596Skadir çetinkaya // refers to it. 1162f5dc596Skadir çetinkaya auto Spelling = spellHeader( 1172f5dc596Skadir çetinkaya {Providers.front(), PP.getHeaderSearchInfo(), MainFile}); 1182f5dc596Skadir çetinkaya for (const Include *I : Inc.match(Header{Spelling})) { 1192f5dc596Skadir çetinkaya Used.insert(I); 1202f5dc596Skadir çetinkaya Satisfied = true; 1212f5dc596Skadir çetinkaya } 1222f5dc596Skadir çetinkaya if (!Satisfied) 1234ef77d61Skadir çetinkaya Missing.try_emplace(std::move(Spelling), Providers.front()); 1241a8dd742SSam McCall }); 1251a8dd742SSam McCall 1261a8dd742SSam McCall AnalysisResults Results; 12743fcfdb1SKadir Cetinkaya for (const Include &I : Inc.all()) { 128507d766dSHaojian Wu if (Used.contains(&I) || !I.Resolved || 12964d97136Skadir çetinkaya HeaderFilter(I.Resolved->getName()) || 13064d97136Skadir çetinkaya I.Resolved->getDir() == ResourceDir) 13143fcfdb1SKadir Cetinkaya continue; 13243fcfdb1SKadir Cetinkaya if (PI) { 13343974333SKadir Cetinkaya if (PI->shouldKeep(*I.Resolved)) 13443fcfdb1SKadir Cetinkaya continue; 13543fcfdb1SKadir Cetinkaya // Check if main file is the public interface for a private header. If so 13643fcfdb1SKadir Cetinkaya // we shouldn't diagnose it as unused. 137f6307b26SSam McCall if (auto PHeader = PI->getPublic(*I.Resolved); !PHeader.empty()) { 13843fcfdb1SKadir Cetinkaya PHeader = PHeader.trim("<>\""); 13943fcfdb1SKadir Cetinkaya // Since most private -> public mappings happen in a verbatim way, we 14043fcfdb1SKadir Cetinkaya // check textually here. This might go wrong in presence of symlinks or 14143fcfdb1SKadir Cetinkaya // header mappings. But that's not different than rest of the places. 14264d97136Skadir çetinkaya if (MainFile.getName().ends_with(PHeader)) 14343fcfdb1SKadir Cetinkaya continue; 14443fcfdb1SKadir Cetinkaya } 14543fcfdb1SKadir Cetinkaya } 1461a8dd742SSam McCall Results.Unused.push_back(&I); 14743fcfdb1SKadir Cetinkaya } 1484ef77d61Skadir çetinkaya for (auto &E : Missing) 1494ef77d61Skadir çetinkaya Results.Missing.emplace_back(E.first().str(), E.second); 1501a8dd742SSam McCall llvm::sort(Results.Missing); 1511a8dd742SSam McCall return Results; 1521a8dd742SSam McCall } 1531a8dd742SSam McCall 1547f3d2cd7SHaojian Wu std::string fixIncludes(const AnalysisResults &Results, 1557f3d2cd7SHaojian Wu llvm::StringRef FileName, llvm::StringRef Code, 1561a8dd742SSam McCall const format::FormatStyle &Style) { 1571a8dd742SSam McCall assert(Style.isCpp() && "Only C++ style supports include insertions!"); 1581a8dd742SSam McCall tooling::Replacements R; 1591a8dd742SSam McCall // Encode insertions/deletions in the magic way clang-format understands. 1601a8dd742SSam McCall for (const Include *I : Results.Unused) 1617f3d2cd7SHaojian Wu cantFail(R.add(tooling::Replacement(FileName, UINT_MAX, 1, I->quote()))); 1624ef77d61Skadir çetinkaya for (auto &[Spelled, _] : Results.Missing) 1634ef77d61Skadir çetinkaya cantFail(R.add( 1644ef77d61Skadir çetinkaya tooling::Replacement(FileName, UINT_MAX, 0, "#include " + Spelled))); 1651a8dd742SSam McCall // "cleanup" actually turns the UINT_MAX replacements into concrete edits. 1661a8dd742SSam McCall auto Positioned = cantFail(format::cleanupAroundReplacements(Code, R, Style)); 1671a8dd742SSam McCall return cantFail(tooling::applyAllReplacements(Code, Positioned)); 1681a8dd742SSam McCall } 1691a8dd742SSam McCall 170ce286eccSKadir Cetinkaya } // namespace clang::include_cleaner 171