llvm-project

Форк
0
157 строк · 6.5 Кб
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-exception
6
//
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/StringRef.h"
30
#include "llvm/ADT/StringSet.h"
31
#include "llvm/Support/Error.h"
32
#include "llvm/Support/ErrorHandling.h"
33
#include <cassert>
34
#include <climits>
35
#include <string>
36

37
namespace clang::include_cleaner {
38

39
namespace {
40
bool 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 and
44
  // not the macro definition. So ignore such macros (e.g. std{in,out,err} are
45
  // implementation defined macros, that just resolve to themselves in
46
  // practice).
47
  return MI && MI->getNumTokens() == 1 && MI->isObjectLike() &&
48
         MI->getReplacementToken(0).getIdentifierInfo() == M.Name;
49
}
50
} // namespace
51

52
void 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 to
65
      // have a cache/batching.
66
      SymbolReference SymRef{ND, Loc, RT};
67
      return CB(SymRef, headersForSymbol(ND, SM, 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, SM, PI));
76
  }
77
}
78

79
AnalysisResults
80
analyze(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 FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
86
  llvm::DenseSet<const Include *> Used;
87
  llvm::StringSet<> Missing;
88
  if (!HeaderFilter)
89
    HeaderFilter = [](llvm::StringRef) { return false; };
90
  OptionalDirectoryEntryRef ResourceDir =
91
      PP.getHeaderSearchInfo().getModuleMap().getBuiltinDir();
92
  walkUsed(ASTRoots, MacroRefs, PI, PP,
93
           [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {
94
             bool Satisfied = false;
95
             for (const Header &H : Providers) {
96
               if (H.kind() == Header::Physical &&
97
                   (H.physical() == MainFile ||
98
                    (ResourceDir && H.physical().getDir() == *ResourceDir))) {
99
                 Satisfied = true;
100
               }
101
               for (const Include *I : Inc.match(H)) {
102
                 Used.insert(I);
103
                 Satisfied = true;
104
               }
105
             }
106
             if (!Satisfied && !Providers.empty() &&
107
                 Ref.RT == RefType::Explicit &&
108
                 !HeaderFilter(Providers.front().resolvedPath()))
109
               Missing.insert(spellHeader(
110
                   {Providers.front(), PP.getHeaderSearchInfo(), MainFile}));
111
           });
112

113
  AnalysisResults Results;
114
  for (const Include &I : Inc.all()) {
115
    if (Used.contains(&I) || !I.Resolved ||
116
        HeaderFilter(I.Resolved->getFileEntry().tryGetRealPathName()) ||
117
        (ResourceDir && I.Resolved->getFileEntry().getDir() == *ResourceDir))
118
      continue;
119
    if (PI) {
120
      if (PI->shouldKeep(*I.Resolved))
121
        continue;
122
      // Check if main file is the public interface for a private header. If so
123
      // we shouldn't diagnose it as unused.
124
      if (auto PHeader = PI->getPublic(*I.Resolved); !PHeader.empty()) {
125
        PHeader = PHeader.trim("<>\"");
126
        // Since most private -> public mappings happen in a verbatim way, we
127
        // check textually here. This might go wrong in presence of symlinks or
128
        // header mappings. But that's not different than rest of the places.
129
        if (MainFile->tryGetRealPathName().ends_with(PHeader))
130
          continue;
131
      }
132
    }
133
    Results.Unused.push_back(&I);
134
  }
135
  for (llvm::StringRef S : Missing.keys())
136
    Results.Missing.push_back(S.str());
137
  llvm::sort(Results.Missing);
138
  return Results;
139
}
140

141
std::string fixIncludes(const AnalysisResults &Results,
142
                        llvm::StringRef FileName, llvm::StringRef Code,
143
                        const format::FormatStyle &Style) {
144
  assert(Style.isCpp() && "Only C++ style supports include insertions!");
145
  tooling::Replacements R;
146
  // Encode insertions/deletions in the magic way clang-format understands.
147
  for (const Include *I : Results.Unused)
148
    cantFail(R.add(tooling::Replacement(FileName, UINT_MAX, 1, I->quote())));
149
  for (llvm::StringRef Spelled : Results.Missing)
150
    cantFail(R.add(tooling::Replacement(FileName, UINT_MAX, 0,
151
                                        ("#include " + Spelled).str())));
152
  // "cleanup" actually turns the UINT_MAX replacements into concrete edits.
153
  auto Positioned = cantFail(format::cleanupAroundReplacements(Code, R, Style));
154
  return cantFail(tooling::applyAllReplacements(Code, Positioned));
155
}
156

157
} // namespace clang::include_cleaner
158

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.