brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.0 KiB · a1781f4 Raw
171 lines · cpp
1//===--- Analysis.cpp -----------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang-include-cleaner/Analysis.h"10#include "AnalysisInternal.h"11#include "clang-include-cleaner/IncludeSpeller.h"12#include "clang-include-cleaner/Record.h"13#include "clang-include-cleaner/Types.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclBase.h"16#include "clang/Basic/DirectoryEntry.h"17#include "clang/Basic/FileEntry.h"18#include "clang/Basic/SourceManager.h"19#include "clang/Format/Format.h"20#include "clang/Lex/HeaderSearch.h"21#include "clang/Lex/Preprocessor.h"22#include "clang/Tooling/Core/Replacement.h"23#include "clang/Tooling/Inclusions/StandardLibrary.h"24#include "llvm/ADT/ArrayRef.h"25#include "llvm/ADT/DenseSet.h"26#include "llvm/ADT/STLExtras.h"27#include "llvm/ADT/STLFunctionalExtras.h"28#include "llvm/ADT/SmallVector.h"29#include "llvm/ADT/StringMap.h"30#include "llvm/ADT/StringRef.h"31#include "llvm/Support/Error.h"32#include "llvm/Support/ErrorHandling.h"33#include <cassert>34#include <climits>35#include <string>36 37namespace clang::include_cleaner {38 39namespace {40bool shouldIgnoreMacroReference(const Preprocessor &PP, const Macro &M) {41  auto *MI = PP.getMacroInfo(M.Name);42  // Macros that expand to themselves are confusing from user's point of view.43  // They usually aspect the usage to be attributed to the underlying decl and44  // not the macro definition. So ignore such macros (e.g. std{in,out,err} are45  // implementation defined macros, that just resolve to themselves in46  // practice).47  return MI && MI->getNumTokens() == 1 && MI->isObjectLike() &&48         MI->getReplacementToken(0).getIdentifierInfo() == M.Name;49}50} // namespace51 52void walkUsed(llvm::ArrayRef<Decl *> ASTRoots,53              llvm::ArrayRef<SymbolReference> MacroRefs,54              const PragmaIncludes *PI, const Preprocessor &PP,55              UsedSymbolCB CB) {56  const auto &SM = PP.getSourceManager();57  // This is duplicated in writeHTMLReport, changes should be mirrored there.58  tooling::stdlib::Recognizer Recognizer;59  for (auto *Root : ASTRoots) {60    walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) {61      auto FID = SM.getFileID(SM.getSpellingLoc(Loc));62      if (FID != SM.getMainFileID() && FID != SM.getPreambleFileID())63        return;64      // FIXME: Most of the work done here is repetitive. It might be useful to65      // have a cache/batching.66      SymbolReference SymRef{ND, Loc, RT};67      return CB(SymRef, headersForSymbol(ND, PP, PI));68    });69  }70  for (const SymbolReference &MacroRef : MacroRefs) {71    assert(MacroRef.Target.kind() == Symbol::Macro);72    if (!SM.isWrittenInMainFile(SM.getSpellingLoc(MacroRef.RefLocation)) ||73        shouldIgnoreMacroReference(PP, MacroRef.Target.macro()))74      continue;75    CB(MacroRef, headersForSymbol(MacroRef.Target, PP, PI));76  }77}78 79AnalysisResults80analyze(llvm::ArrayRef<Decl *> ASTRoots,81        llvm::ArrayRef<SymbolReference> MacroRefs, const Includes &Inc,82        const PragmaIncludes *PI, const Preprocessor &PP,83        llvm::function_ref<bool(llvm::StringRef)> HeaderFilter) {84  auto &SM = PP.getSourceManager();85  const auto MainFile = *SM.getFileEntryRefForID(SM.getMainFileID());86  llvm::DenseSet<const Include *> Used;87  llvm::StringMap<Header> Missing;88  constexpr auto DefaultHeaderFilter = [](llvm::StringRef) { return false; };89  if (!HeaderFilter)90    HeaderFilter = DefaultHeaderFilter;91  OptionalDirectoryEntryRef ResourceDir =92      PP.getHeaderSearchInfo().getModuleMap().getBuiltinDir();93  walkUsed(ASTRoots, MacroRefs, PI, PP,94           [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {95             bool Satisfied = false;96             for (const Header &H : Providers) {97               if (H.kind() == Header::Physical &&98                   (H.physical() == MainFile ||99                    H.physical().getDir() == ResourceDir)) {100                 Satisfied = true;101               }102               for (const Include *I : Inc.match(H)) {103                 Used.insert(I);104                 Satisfied = true;105               }106             }107             // Bail out if we can't (or need not) insert an include.108             if (Satisfied || Providers.empty() || Ref.RT != RefType::Explicit)109               return;110             if (HeaderFilter(Providers.front().resolvedPath()))111               return;112             // Check if we have any headers with the same spelling, in edge113             // cases like `#include_next "foo.h"`, the user can't ever114             // include the physical foo.h, but can have a spelling that115             // refers to it.116             auto Spelling = spellHeader(117                 {Providers.front(), PP.getHeaderSearchInfo(), MainFile});118             for (const Include *I : Inc.match(Header{Spelling})) {119               Used.insert(I);120               Satisfied = true;121             }122             if (!Satisfied)123               Missing.try_emplace(std::move(Spelling), Providers.front());124           });125 126  AnalysisResults Results;127  for (const Include &I : Inc.all()) {128    if (Used.contains(&I) || !I.Resolved ||129        HeaderFilter(I.Resolved->getName()) ||130        I.Resolved->getDir() == ResourceDir)131      continue;132    if (PI) {133      if (PI->shouldKeep(*I.Resolved))134        continue;135      // Check if main file is the public interface for a private header. If so136      // we shouldn't diagnose it as unused.137      if (auto PHeader = PI->getPublic(*I.Resolved); !PHeader.empty()) {138        PHeader = PHeader.trim("<>\"");139        // Since most private -> public mappings happen in a verbatim way, we140        // check textually here. This might go wrong in presence of symlinks or141        // header mappings. But that's not different than rest of the places.142        if (MainFile.getName().ends_with(PHeader))143          continue;144      }145    }146    Results.Unused.push_back(&I);147  }148  for (auto &E : Missing)149    Results.Missing.emplace_back(E.first().str(), E.second);150  llvm::sort(Results.Missing);151  return Results;152}153 154std::string fixIncludes(const AnalysisResults &Results,155                        llvm::StringRef FileName, llvm::StringRef Code,156                        const format::FormatStyle &Style) {157  assert(Style.isCpp() && "Only C++ style supports include insertions!");158  tooling::Replacements R;159  // Encode insertions/deletions in the magic way clang-format understands.160  for (const Include *I : Results.Unused)161    cantFail(R.add(tooling::Replacement(FileName, UINT_MAX, 1, I->quote())));162  for (auto &[Spelled, _] : Results.Missing)163    cantFail(R.add(164        tooling::Replacement(FileName, UINT_MAX, 0, "#include " + Spelled)));165  // "cleanup" actually turns the UINT_MAX replacements into concrete edits.166  auto Positioned = cantFail(format::cleanupAroundReplacements(Code, R, Style));167  return cantFail(tooling::applyAllReplacements(Code, Positioned));168}169 170} // namespace clang::include_cleaner171