Coverage Report

Created: 2022-07-16 07:03

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
Line
Count
Source (jump to first uncovered line)
1
//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
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
/// \file
10
/// Provides an action to find USR for the symbol at <offset>, as well as
11
/// all additional USRs.
12
///
13
//===----------------------------------------------------------------------===//
14
15
#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
16
#include "clang/AST/AST.h"
17
#include "clang/AST/ASTConsumer.h"
18
#include "clang/AST/ASTContext.h"
19
#include "clang/AST/Decl.h"
20
#include "clang/AST/RecursiveASTVisitor.h"
21
#include "clang/Basic/FileManager.h"
22
#include "clang/Frontend/CompilerInstance.h"
23
#include "clang/Frontend/FrontendAction.h"
24
#include "clang/Lex/Lexer.h"
25
#include "clang/Lex/Preprocessor.h"
26
#include "clang/Tooling/CommonOptionsParser.h"
27
#include "clang/Tooling/Refactoring.h"
28
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
29
#include "clang/Tooling/Tooling.h"
30
31
#include <algorithm>
32
#include <set>
33
#include <string>
34
#include <vector>
35
36
using namespace llvm;
37
38
namespace clang {
39
namespace tooling {
40
41
344
const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {
42
344
  if (!FoundDecl)
43
0
    return nullptr;
44
  // If FoundDecl is a constructor or destructor, we want to instead take
45
  // the Decl of the corresponding class.
46
344
  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
47
2
    FoundDecl = CtorDecl->getParent();
48
342
  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
49
0
    FoundDecl = DtorDecl->getParent();
50
  // FIXME: (Alex L): Canonicalize implicit template instantions, just like
51
  // the indexer does it.
52
53
  // Note: please update the declaration's doc comment every time the
54
  // canonicalization rules are changed.
55
344
  return FoundDecl;
56
344
}
57
58
namespace {
59
// NamedDeclFindingConsumer should delegate finding USRs of given Decl to
60
// AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
61
// Decl refers to class and adds USRs of all overridden methods if Decl refers
62
// to virtual method.
63
class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
64
public:
65
  AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
66
345
      : FoundDecl(FoundDecl), Context(Context) {}
67
68
345
  std::vector<std::string> Find() {
69
    // Fill OverriddenMethods and PartialSpecs storages.
70
345
    TraverseAST(Context);
71
345
    if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
72
40
      addUSRsOfOverridenFunctions(MethodDecl);
73
84
      for (const auto &OverriddenMethod : OverriddenMethods) {
74
84
        if (checkIfOverriddenFunctionAscends(OverriddenMethod))
75
30
          USRSet.insert(getUSRForDecl(OverriddenMethod));
76
84
      }
77
40
      addUSRsOfInstantiatedMethods(MethodDecl);
78
305
    } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
79
120
      handleCXXRecordDecl(RecordDecl);
80
185
    } else if (const auto *TemplateDecl =
81
185
                   dyn_cast<ClassTemplateDecl>(FoundDecl)) {
82
23
      handleClassTemplateDecl(TemplateDecl);
83
162
    } else if (const auto *FD = dyn_cast<FunctionDecl>(FoundDecl)) {
84
17
      USRSet.insert(getUSRForDecl(FD));
85
17
      if (const auto *FTD = FD->getPrimaryTemplate())
86
1
        handleFunctionTemplateDecl(FTD);
87
145
    } else if (const auto *FD = dyn_cast<FunctionTemplateDecl>(FoundDecl)) {
88
6
      handleFunctionTemplateDecl(FD);
89
139
    } else if (const auto *VTD = dyn_cast<VarTemplateDecl>(FoundDecl)) {
90
0
      handleVarTemplateDecl(VTD);
91
139
    } else if (const auto *VD =
92
139
                   dyn_cast<VarTemplateSpecializationDecl>(FoundDecl)) {
93
      // FIXME: figure out why FoundDecl can be a VarTemplateSpecializationDecl.
94
5
      handleVarTemplateDecl(VD->getSpecializedTemplate());
95
134
    } else if (const auto *VD = dyn_cast<VarDecl>(FoundDecl)) {
96
13
      USRSet.insert(getUSRForDecl(VD));
97
13
      if (const auto *VTD = VD->getDescribedVarTemplate())
98
0
        handleVarTemplateDecl(VTD);
99
121
    } else {
100
121
      USRSet.insert(getUSRForDecl(FoundDecl));
101
121
    }
102
345
    return std::vector<std::string>(USRSet.begin(), USRSet.end());
103
345
  }
104
105
577
  bool shouldVisitTemplateInstantiations() const { return true; }
106
107
1.35k
  bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
108
1.35k
    if (MethodDecl->isVirtual())
109
114
      OverriddenMethods.push_back(MethodDecl);
110
1.35k
    if (MethodDecl->getInstantiatedFromMemberFunction())
111
101
      InstantiatedMethods.push_back(MethodDecl);
112
1.35k
    return true;
113
1.35k
  }
114
115
private:
116
120
  void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
117
120
    if (!RecordDecl->getDefinition()) {
118
1
      USRSet.insert(getUSRForDecl(RecordDecl));
119
1
      return;
120
1
    }
121
119
    RecordDecl = RecordDecl->getDefinition();
122
119
    if (const auto *ClassTemplateSpecDecl =
123
119
            dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
124
0
      handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
125
119
    addUSRsOfCtorDtors(RecordDecl);
126
119
  }
127
128
23
  void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
129
23
    for (const auto *Specialization : TemplateDecl->specializations())
130
31
      addUSRsOfCtorDtors(Specialization);
131
23
    SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
132
23
    TemplateDecl->getPartialSpecializations(PartialSpecs);
133
23
    for (const auto *Spec : PartialSpecs)
134
0
      addUSRsOfCtorDtors(Spec);
135
23
    addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
136
23
  }
137
138
7
  void handleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) {
139
7
    USRSet.insert(getUSRForDecl(FTD));
140
7
    USRSet.insert(getUSRForDecl(FTD->getTemplatedDecl()));
141
7
    for (const auto *S : FTD->specializations())
142
10
      USRSet.insert(getUSRForDecl(S));
143
7
  }
144
145
5
  void handleVarTemplateDecl(const VarTemplateDecl *VTD) {
146
5
    USRSet.insert(getUSRForDecl(VTD));
147
5
    USRSet.insert(getUSRForDecl(VTD->getTemplatedDecl()));
148
5
    for (const auto *Spec : VTD->specializations())
149
10
      USRSet.insert(getUSRForDecl(Spec));
150
5
    SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
151
5
    VTD->getPartialSpecializations(PartialSpecs);
152
5
    for (const auto *Spec : PartialSpecs)
153
5
      USRSet.insert(getUSRForDecl(Spec));
154
5
  }
155
156
173
  void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {
157
173
    const auto* RecordDecl = RD->getDefinition();
158
159
    // Skip if the CXXRecordDecl doesn't have definition.
160
173
    if (!RecordDecl) {
161
6
      USRSet.insert(getUSRForDecl(RD));
162
6
      return;
163
6
    }
164
165
167
    for (const auto *CtorDecl : RecordDecl->ctors())
166
210
      USRSet.insert(getUSRForDecl(CtorDecl));
167
    // Add template constructor decls, they are not in ctors() unfortunately.
168
167
    if (RecordDecl->hasUserDeclaredConstructor())
169
16
      for (const auto *D : RecordDecl->decls())
170
147
        if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
171
2
          if (const auto *Ctor =
172
2
                  dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
173
2
            USRSet.insert(getUSRForDecl(Ctor));
174
175
167
    USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
176
167
    USRSet.insert(getUSRForDecl(RecordDecl));
177
167
  }
178
179
53
  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
180
53
    USRSet.insert(getUSRForDecl(MethodDecl));
181
    // Recursively visit each OverridenMethod.
182
53
    for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
183
13
      addUSRsOfOverridenFunctions(OverriddenMethod);
184
53
  }
185
186
40
  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
187
    // For renaming a class template method, all references of the instantiated
188
    // member methods should be renamed too, so add USRs of the instantiated
189
    // methods to the USR set.
190
40
    USRSet.insert(getUSRForDecl(MethodDecl));
191
40
    if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
192
3
      USRSet.insert(getUSRForDecl(FT));
193
44
    for (const auto *Method : InstantiatedMethods) {
194
44
      if (USRSet.find(getUSRForDecl(
195
44
              Method->getInstantiatedFromMemberFunction())) != USRSet.end())
196
20
        USRSet.insert(getUSRForDecl(Method));
197
44
    }
198
40
  }
199
200
105
  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
201
105
    for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
202
51
      if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
203
30
        return true;
204
21
      return checkIfOverriddenFunctionAscends(OverriddenMethod);
205
51
    }
206
54
    return false;
207
105
  }
208
209
  const Decl *FoundDecl;
210
  ASTContext &Context;
211
  std::set<std::string> USRSet;
212
  std::vector<const CXXMethodDecl *> OverriddenMethods;
213
  std::vector<const CXXMethodDecl *> InstantiatedMethods;
214
};
215
} // namespace
216
217
std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
218
13
                                               ASTContext &Context) {
219
13
  AdditionalUSRFinder Finder(ND, Context);
220
13
  return Finder.Find();
221
13
}
222
223
class NamedDeclFindingConsumer : public ASTConsumer {
224
public:
225
  NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
226
                           ArrayRef<std::string> QualifiedNames,
227
                           std::vector<std::string> &SpellingNames,
228
                           std::vector<std::vector<std::string>> &USRList,
229
                           bool Force, bool &ErrorOccurred)
230
      : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
231
        SpellingNames(SpellingNames), USRList(USRList), Force(Force),
232
330
        ErrorOccurred(ErrorOccurred) {}
233
234
private:
235
  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
236
335
                  unsigned SymbolOffset, const std::string &QualifiedName) {
237
335
    DiagnosticsEngine &Engine = Context.getDiagnostics();
238
335
    const FileID MainFileID = SourceMgr.getMainFileID();
239
240
335
    if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
241
1
      ErrorOccurred = true;
242
1
      unsigned InvalidOffset = Engine.getCustomDiagID(
243
1
          DiagnosticsEngine::Error,
244
1
          "SourceLocation in file %0 at offset %1 is invalid");
245
1
      Engine.Report(SourceLocation(), InvalidOffset)
246
1
          << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
247
1
      return false;
248
1
    }
249
250
334
    const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
251
334
                                     .getLocWithOffset(SymbolOffset);
252
334
    const NamedDecl *FoundDecl = QualifiedName.empty()
253
334
                                     ? 
getNamedDeclAt(Context, Point)64
254
334
                                     : 
getNamedDeclFor(Context, QualifiedName)270
;
255
256
334
    if (FoundDecl == nullptr) {
257
2
      if (QualifiedName.empty()) {
258
0
        FullSourceLoc FullLoc(Point, SourceMgr);
259
0
        unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
260
0
            DiagnosticsEngine::Error,
261
0
            "clang-rename could not find symbol (offset %0)");
262
0
        Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
263
0
        ErrorOccurred = true;
264
0
        return false;
265
0
      }
266
267
2
      if (Force) {
268
2
        SpellingNames.push_back(std::string());
269
2
        USRList.push_back(std::vector<std::string>());
270
2
        return true;
271
2
      }
272
273
0
      unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
274
0
          DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
275
0
      Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
276
0
      ErrorOccurred = true;
277
0
      return false;
278
2
    }
279
280
332
    FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
281
332
    SpellingNames.push_back(FoundDecl->getNameAsString());
282
332
    AdditionalUSRFinder Finder(FoundDecl, Context);
283
332
    USRList.push_back(Finder.Find());
284
332
    return true;
285
334
  }
286
287
330
  void HandleTranslationUnit(ASTContext &Context) override {
288
330
    const SourceManager &SourceMgr = Context.getSourceManager();
289
330
    for (unsigned Offset : SymbolOffsets) {
290
65
      if (!FindSymbol(Context, SourceMgr, Offset, ""))
291
1
        return;
292
65
    }
293
329
    for (const std::string &QualifiedName : QualifiedNames) {
294
270
      if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
295
0
        return;
296
270
    }
297
329
  }
298
299
  ArrayRef<unsigned> SymbolOffsets;
300
  ArrayRef<std::string> QualifiedNames;
301
  std::vector<std::string> &SpellingNames;
302
  std::vector<std::vector<std::string>> &USRList;
303
  bool Force;
304
  bool &ErrorOccurred;
305
};
306
307
330
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
308
330
  return std::make_unique<NamedDeclFindingConsumer>(
309
330
      SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
310
330
      ErrorOccurred);
311
330
}
312
313
} // end namespace tooling
314
} // end namespace clang