File: Analysis.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (131 lines) | stat: -rw-r--r-- 5,462 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//===--- Analysis.cpp -----------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "clang-include-cleaner/Analysis.h"
#include "AnalysisInternal.h"
#include "clang-include-cleaner/IncludeSpeller.h"
#include "clang-include-cleaner/Record.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/Basic/FileEntry.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Format/Format.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Inclusions/StandardLibrary.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorHandling.h"
#include <string>

namespace clang::include_cleaner {

void walkUsed(llvm::ArrayRef<Decl *> ASTRoots,
              llvm::ArrayRef<SymbolReference> MacroRefs,
              const PragmaIncludes *PI, const SourceManager &SM,
              UsedSymbolCB CB) {
  // This is duplicated in writeHTMLReport, changes should be mirrored there.
  tooling::stdlib::Recognizer Recognizer;
  for (auto *Root : ASTRoots) {
    walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) {
      auto FID = SM.getFileID(SM.getSpellingLoc(Loc));
      if (FID != SM.getMainFileID() && FID != SM.getPreambleFileID())
        return;
      // FIXME: Most of the work done here is repetitive. It might be useful to
      // have a cache/batching.
      SymbolReference SymRef{ND, Loc, RT};
      return CB(SymRef, headersForSymbol(ND, SM, PI));
    });
  }
  for (const SymbolReference &MacroRef : MacroRefs) {
    assert(MacroRef.Target.kind() == Symbol::Macro);
    if (!SM.isWrittenInMainFile(SM.getSpellingLoc(MacroRef.RefLocation)))
      continue;
    CB(MacroRef, headersForSymbol(MacroRef.Target, SM, PI));
  }
}

AnalysisResults
analyze(llvm::ArrayRef<Decl *> ASTRoots,
        llvm::ArrayRef<SymbolReference> MacroRefs, const Includes &Inc,
        const PragmaIncludes *PI, const SourceManager &SM,
        const HeaderSearch &HS,
        llvm::function_ref<bool(llvm::StringRef)> HeaderFilter) {
  const FileEntry *MainFile = SM.getFileEntryForID(SM.getMainFileID());
  llvm::DenseSet<const Include *> Used;
  llvm::StringSet<> Missing;
  if (!HeaderFilter)
    HeaderFilter = [](llvm::StringRef) { return false; };
  walkUsed(ASTRoots, MacroRefs, PI, SM,
           [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {
             bool Satisfied = false;
             for (const Header &H : Providers) {
               if (H.kind() == Header::Physical && H.physical() == MainFile)
                 Satisfied = true;
               for (const Include *I : Inc.match(H)) {
                 Used.insert(I);
                 Satisfied = true;
               }
             }
             if (!Satisfied && !Providers.empty() &&
                 Ref.RT == RefType::Explicit &&
                 !HeaderFilter(Providers.front().resolvedPath()))
               Missing.insert(spellHeader({Providers.front(), HS, MainFile}));
           });

  AnalysisResults Results;
  for (const Include &I : Inc.all()) {
    if (Used.contains(&I) || !I.Resolved ||
        HeaderFilter(I.Resolved->tryGetRealPathName()))
      continue;
    if (PI) {
      if (PI->shouldKeep(I.Line))
        continue;
      // Check if main file is the public interface for a private header. If so
      // we shouldn't diagnose it as unused.
      if (auto PHeader = PI->getPublic(I.Resolved); !PHeader.empty()) {
        PHeader = PHeader.trim("<>\"");
        // Since most private -> public mappings happen in a verbatim way, we
        // check textually here. This might go wrong in presence of symlinks or
        // header mappings. But that's not different than rest of the places.
        if (MainFile->tryGetRealPathName().endswith(PHeader))
          continue;
      }
    }
    Results.Unused.push_back(&I);
  }
  for (llvm::StringRef S : Missing.keys())
    Results.Missing.push_back(S.str());
  llvm::sort(Results.Missing);
  return Results;
}

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

} // namespace clang::include_cleaner