Coverage Report

Created: 2021-09-21 08:58

/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
10
    llvm::for_each(VTD->specializations(), [&](const auto *Spec) {
149
10
      USRSet.insert(getUSRForDecl(Spec));
150
10
    });
151
5
    SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
152
5
    VTD->getPartialSpecializations(PartialSpecs);
153
5
    llvm::for_each(PartialSpecs, [&](const auto *Spec) {
154
5
      USRSet.insert(getUSRForDecl(Spec));
155
5
    });
156
5
  }
157
158
173
  void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {
159
173
    const auto* RecordDecl = RD->getDefinition();
160
161
    // Skip if the CXXRecordDecl doesn't have definition.
162
173
    if (!RecordDecl) {
163
6
      USRSet.insert(getUSRForDecl(RD));
164
6
      return;
165
6
    }
166
167
167
    for (const auto *CtorDecl : RecordDecl->ctors())
168
210
      USRSet.insert(getUSRForDecl(CtorDecl));
169
    // Add template constructor decls, they are not in ctors() unfortunately.
170
167
    if (RecordDecl->hasUserDeclaredConstructor())
171
16
      for (const auto *D : RecordDecl->decls())
172
147
        if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
173
2
          if (const auto *Ctor =
174
2
                  dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
175
2
            USRSet.insert(getUSRForDecl(Ctor));
176
177
167
    USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
178
167
    USRSet.insert(getUSRForDecl(RecordDecl));
179
167
  }
180
181
53
  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
182
53
    USRSet.insert(getUSRForDecl(MethodDecl));
183
    // Recursively visit each OverridenMethod.
184
53
    for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
185
13
      addUSRsOfOverridenFunctions(OverriddenMethod);
186
53
  }
187
188
40
  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
189
    // For renaming a class template method, all references of the instantiated
190
    // member methods should be renamed too, so add USRs of the instantiated
191
    // methods to the USR set.
192
40
    USRSet.insert(getUSRForDecl(MethodDecl));
193
40
    if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
194
3
      USRSet.insert(getUSRForDecl(FT));
195
44
    for (const auto *Method : InstantiatedMethods) {
196
44
      if (USRSet.find(getUSRForDecl(
197
44
              Method->getInstantiatedFromMemberFunction())) != USRSet.end())
198
20
        USRSet.insert(getUSRForDecl(Method));
199
44
    }
200
40
  }
201
202
105
  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
203
105
    for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
204
51
      if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
205
30
        return true;
206
21
      return checkIfOverriddenFunctionAscends(OverriddenMethod);
207
51
    }
208
54
    return false;
209
105
  }
210
211
  const Decl *FoundDecl;
212
  ASTContext &Context;
213
  std::set<std::string> USRSet;
214
  std::vector<const CXXMethodDecl *> OverriddenMethods;
215
  std::vector<const CXXMethodDecl *> InstantiatedMethods;
216
};
217
} // namespace
218
219
std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
220
13
                                               ASTContext &Context) {
221
13
  AdditionalUSRFinder Finder(ND, Context);
222
13
  return Finder.Find();
223
13
}
224
225
class NamedDeclFindingConsumer : public ASTConsumer {
226
public:
227
  NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
228
                           ArrayRef<std::string> QualifiedNames,
229
                           std::vector<std::string> &SpellingNames,
230
                           std::vector<std::vector<std::string>> &USRList,
231
                           bool Force, bool &ErrorOccurred)
232
      : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
233
        SpellingNames(SpellingNames), USRList(USRList), Force(Force),
234
330
        ErrorOccurred(ErrorOccurred) {}
235
236
private:
237
  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
238
335
                  unsigned SymbolOffset, const std::string &QualifiedName) {
239
335
    DiagnosticsEngine &Engine = Context.getDiagnostics();
240
335
    const FileID MainFileID = SourceMgr.getMainFileID();
241
242
335
    if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
243
1
      ErrorOccurred = true;
244
1
      unsigned InvalidOffset = Engine.getCustomDiagID(
245
1
          DiagnosticsEngine::Error,
246
1
          "SourceLocation in file %0 at offset %1 is invalid");
247
1
      Engine.Report(SourceLocation(), InvalidOffset)
248
1
          << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
249
1
      return false;
250
1
    }
251
252
334
    const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
253
334
                                     .getLocWithOffset(SymbolOffset);
254
334
    const NamedDecl *FoundDecl = QualifiedName.empty()
255
334
                                     ? 
getNamedDeclAt(Context, Point)64
256
334
                                     : 
getNamedDeclFor(Context, QualifiedName)270
;
257
258
334
    if (FoundDecl == nullptr) {
259
2
      if (QualifiedName.empty()) {
260
0
        FullSourceLoc FullLoc(Point, SourceMgr);
261
0
        unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
262
0
            DiagnosticsEngine::Error,
263
0
            "clang-rename could not find symbol (offset %0)");
264
0
        Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
265
0
        ErrorOccurred = true;
266
0
        return false;
267
0
      }
268
269
2
      if (Force) {
270
2
        SpellingNames.push_back(std::string());
271
2
        USRList.push_back(std::vector<std::string>());
272
2
        return true;
273
2
      }
274
275
0
      unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
276
0
          DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
277
0
      Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
278
0
      ErrorOccurred = true;
279
0
      return false;
280
2
    }
281
282
332
    FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
283
332
    SpellingNames.push_back(FoundDecl->getNameAsString());
284
332
    AdditionalUSRFinder Finder(FoundDecl, Context);
285
332
    USRList.push_back(Finder.Find());
286
332
    return true;
287
334
  }
288
289
330
  void HandleTranslationUnit(ASTContext &Context) override {
290
330
    const SourceManager &SourceMgr = Context.getSourceManager();
291
330
    for (unsigned Offset : SymbolOffsets) {
292
65
      if (!FindSymbol(Context, SourceMgr, Offset, ""))
293
1
        return;
294
65
    }
295
329
    for (const std::string &QualifiedName : QualifiedNames) {
296
270
      if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
297
0
        return;
298
270
    }
299
329
  }
300
301
  ArrayRef<unsigned> SymbolOffsets;
302
  ArrayRef<std::string> QualifiedNames;
303
  std::vector<std::string> &SpellingNames;
304
  std::vector<std::vector<std::string>> &USRList;
305
  bool Force;
306
  bool &ErrorOccurred;
307
};
308
309
330
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
310
330
  return std::make_unique<NamedDeclFindingConsumer>(
311
330
      SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
312
330
      ErrorOccurred);
313
330
}
314
315
} // end namespace tooling
316
} // end namespace clang