Coverage Report

Created: 2020-02-18 08:44

/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
336
const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {
42
336
  if (!FoundDecl)
43
0
    return nullptr;
44
336
  // If FoundDecl is a constructor or destructor, we want to instead take
45
336
  // the Decl of the corresponding class.
46
336
  if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
47
2
    FoundDecl = CtorDecl->getParent();
48
334
  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
49
0
    FoundDecl = DtorDecl->getParent();
50
336
  // FIXME: (Alex L): Canonicalize implicit template instantions, just like
51
336
  // the indexer does it.
52
336
53
336
  // Note: please update the declaration's doc comment every time the
54
336
  // canonicalization rules are changed.
55
336
  return FoundDecl;
56
336
}
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
337
      : FoundDecl(FoundDecl), Context(Context) {}
67
68
337
  std::vector<std::string> Find() {
69
337
    // Fill OverriddenMethods and PartialSpecs storages.
70
337
    TraverseAST(Context);
71
337
    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
297
    } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
79
120
      handleCXXRecordDecl(RecordDecl);
80
177
    } else if (const auto *TemplateDecl =
81
24
                   dyn_cast<ClassTemplateDecl>(FoundDecl)) {
82
24
      handleClassTemplateDecl(TemplateDecl);
83
153
    } else {
84
153
      USRSet.insert(getUSRForDecl(FoundDecl));
85
153
    }
86
337
    return std::vector<std::string>(USRSet.begin(), USRSet.end());
87
337
  }
88
89
557
  bool shouldVisitTemplateInstantiations() const { return true; }
90
91
1.35k
  bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
92
1.35k
    if (MethodDecl->isVirtual())
93
114
      OverriddenMethods.push_back(MethodDecl);
94
1.35k
    if (MethodDecl->getInstantiatedFromMemberFunction())
95
101
      InstantiatedMethods.push_back(MethodDecl);
96
1.35k
    return true;
97
1.35k
  }
98
99
  bool VisitClassTemplatePartialSpecializationDecl(
100
1
      const ClassTemplatePartialSpecializationDecl *PartialSpec) {
101
1
    PartialSpecs.push_back(PartialSpec);
102
1
    return true;
103
1
  }
104
105
private:
106
120
  void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
107
120
    if (!RecordDecl->getDefinition()) {
108
1
      USRSet.insert(getUSRForDecl(RecordDecl));
109
1
      return;
110
1
    }
111
119
    RecordDecl = RecordDecl->getDefinition();
112
119
    if (const auto *ClassTemplateSpecDecl =
113
0
            dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
114
0
      handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
115
119
    addUSRsOfCtorDtors(RecordDecl);
116
119
  }
117
118
24
  void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
119
24
    for (const auto *Specialization : TemplateDecl->specializations())
120
33
      addUSRsOfCtorDtors(Specialization);
121
24
122
24
    for (const auto *PartialSpec : PartialSpecs) {
123
0
      if (PartialSpec->getSpecializedTemplate() == TemplateDecl)
124
0
        addUSRsOfCtorDtors(PartialSpec);
125
0
    }
126
24
    addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
127
24
  }
128
129
176
  void addUSRsOfCtorDtors(const CXXRecordDecl *RecordDecl) {
130
176
    RecordDecl = RecordDecl->getDefinition();
131
176
132
176
    // Skip if the CXXRecordDecl doesn't have definition.
133
176
    if (!RecordDecl)
134
7
      return;
135
169
136
169
    for (const auto *CtorDecl : RecordDecl->ctors())
137
213
      USRSet.insert(getUSRForDecl(CtorDecl));
138
169
    // Add template constructor decls, they are not in ctors() unfortunately.
139
169
    if (RecordDecl->hasUserDeclaredConstructor())
140
16
      for (const auto *D : RecordDecl->decls())
141
147
        if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
142
2
          if (const auto *Ctor =
143
2
                  dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
144
2
            USRSet.insert(getUSRForDecl(Ctor));
145
169
146
169
    USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
147
169
    USRSet.insert(getUSRForDecl(RecordDecl));
148
169
  }
149
150
53
  void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
151
53
    USRSet.insert(getUSRForDecl(MethodDecl));
152
53
    // Recursively visit each OverridenMethod.
153
53
    for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
154
13
      addUSRsOfOverridenFunctions(OverriddenMethod);
155
53
  }
156
157
40
  void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
158
40
    // For renaming a class template method, all references of the instantiated
159
40
    // member methods should be renamed too, so add USRs of the instantiated
160
40
    // methods to the USR set.
161
40
    USRSet.insert(getUSRForDecl(MethodDecl));
162
40
    if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
163
3
      USRSet.insert(getUSRForDecl(FT));
164
44
    for (const auto *Method : InstantiatedMethods) {
165
44
      if (USRSet.find(getUSRForDecl(
166
44
              Method->getInstantiatedFromMemberFunction())) != USRSet.end())
167
20
        USRSet.insert(getUSRForDecl(Method));
168
44
    }
169
40
  }
170
171
105
  bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
172
105
    for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
173
51
      if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
174
30
        return true;
175
21
      return checkIfOverriddenFunctionAscends(OverriddenMethod);
176
21
    }
177
105
    
return false54
;
178
105
  }
179
180
  const Decl *FoundDecl;
181
  ASTContext &Context;
182
  std::set<std::string> USRSet;
183
  std::vector<const CXXMethodDecl *> OverriddenMethods;
184
  std::vector<const CXXMethodDecl *> InstantiatedMethods;
185
  std::vector<const ClassTemplatePartialSpecializationDecl *> PartialSpecs;
186
};
187
} // namespace
188
189
std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
190
13
                                               ASTContext &Context) {
191
13
  AdditionalUSRFinder Finder(ND, Context);
192
13
  return Finder.Find();
193
13
}
194
195
class NamedDeclFindingConsumer : public ASTConsumer {
196
public:
197
  NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
198
                           ArrayRef<std::string> QualifiedNames,
199
                           std::vector<std::string> &SpellingNames,
200
                           std::vector<std::vector<std::string>> &USRList,
201
                           bool Force, bool &ErrorOccurred)
202
      : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
203
        SpellingNames(SpellingNames), USRList(USRList), Force(Force),
204
322
        ErrorOccurred(ErrorOccurred) {}
205
206
private:
207
  bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
208
327
                  unsigned SymbolOffset, const std::string &QualifiedName) {
209
327
    DiagnosticsEngine &Engine = Context.getDiagnostics();
210
327
    const FileID MainFileID = SourceMgr.getMainFileID();
211
327
212
327
    if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
213
1
      ErrorOccurred = true;
214
1
      unsigned InvalidOffset = Engine.getCustomDiagID(
215
1
          DiagnosticsEngine::Error,
216
1
          "SourceLocation in file %0 at offset %1 is invalid");
217
1
      Engine.Report(SourceLocation(), InvalidOffset)
218
1
          << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
219
1
      return false;
220
1
    }
221
326
222
326
    const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
223
326
                                     .getLocWithOffset(SymbolOffset);
224
326
    const NamedDecl *FoundDecl = QualifiedName.empty()
225
326
                                     ? 
getNamedDeclAt(Context, Point)56
226
326
                                     : 
getNamedDeclFor(Context, QualifiedName)270
;
227
326
228
326
    if (FoundDecl == nullptr) {
229
2
      if (QualifiedName.empty()) {
230
0
        FullSourceLoc FullLoc(Point, SourceMgr);
231
0
        unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
232
0
            DiagnosticsEngine::Error,
233
0
            "clang-rename could not find symbol (offset %0)");
234
0
        Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
235
0
        ErrorOccurred = true;
236
0
        return false;
237
0
      }
238
2
239
2
      if (Force) {
240
2
        SpellingNames.push_back(std::string());
241
2
        USRList.push_back(std::vector<std::string>());
242
2
        return true;
243
2
      }
244
0
245
0
      unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
246
0
          DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
247
0
      Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
248
0
      ErrorOccurred = true;
249
0
      return false;
250
0
    }
251
324
252
324
    FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
253
324
    SpellingNames.push_back(FoundDecl->getNameAsString());
254
324
    AdditionalUSRFinder Finder(FoundDecl, Context);
255
324
    USRList.push_back(Finder.Find());
256
324
    return true;
257
324
  }
258
259
322
  void HandleTranslationUnit(ASTContext &Context) override {
260
322
    const SourceManager &SourceMgr = Context.getSourceManager();
261
322
    for (unsigned Offset : SymbolOffsets) {
262
57
      if (!FindSymbol(Context, SourceMgr, Offset, ""))
263
1
        return;
264
57
    }
265
322
    
for (const std::string &QualifiedName : QualifiedNames)321
{
266
270
      if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
267
0
        return;
268
270
    }
269
321
  }
270
271
  ArrayRef<unsigned> SymbolOffsets;
272
  ArrayRef<std::string> QualifiedNames;
273
  std::vector<std::string> &SpellingNames;
274
  std::vector<std::vector<std::string>> &USRList;
275
  bool Force;
276
  bool &ErrorOccurred;
277
};
278
279
322
std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
280
322
  return std::make_unique<NamedDeclFindingConsumer>(
281
322
      SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
282
322
      ErrorOccurred);
283
322
}
284
285
} // end namespace tooling
286
} // end namespace clang