blob: dc21a94610cbb1ad1b6e09676b6f0fc3cc87253c [file] [log] [blame]
Alex Lorenz4abbd922017-06-30 16:36:09 +00001//===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
Manuel Klimekde237262014-08-20 01:39:05 +00002//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
Alex Lorenz4abbd922017-06-30 16:36:09 +000011/// \brief Methods for finding all instances of a USR. Our strategy is very
Manuel Klimekde237262014-08-20 01:39:05 +000012/// simple; we just compare the USR at every relevant AST node with the one
13/// provided.
14///
15//===----------------------------------------------------------------------===//
16
Alex Lorenz4abbd922017-06-30 16:36:09 +000017#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
Manuel Klimekde237262014-08-20 01:39:05 +000018#include "clang/AST/ASTContext.h"
19#include "clang/AST/RecursiveASTVisitor.h"
Eugene Zelenko86150472016-11-29 18:24:01 +000020#include "clang/Basic/LLVM.h"
Manuel Klimekde237262014-08-20 01:39:05 +000021#include "clang/Basic/SourceLocation.h"
Eugene Zelenko86150472016-11-29 18:24:01 +000022#include "clang/Basic/SourceManager.h"
Miklos Vajna1d48e502016-05-13 09:17:32 +000023#include "clang/Lex/Lexer.h"
Haojian Wu74f823a2017-04-04 09:30:06 +000024#include "clang/Tooling/Core/Lookup.h"
Alex Lorenz98394f82017-07-13 10:36:33 +000025#include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
Alex Lorenz4abbd922017-06-30 16:36:09 +000026#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
Eugene Zelenko86150472016-11-29 18:24:01 +000027#include "llvm/ADT/StringRef.h"
28#include "llvm/Support/Casting.h"
29#include <cstddef>
30#include <set>
31#include <string>
32#include <vector>
Manuel Klimekde237262014-08-20 01:39:05 +000033
34using namespace llvm;
35
36namespace clang {
Alex Lorenz4abbd922017-06-30 16:36:09 +000037namespace tooling {
Manuel Klimekde237262014-08-20 01:39:05 +000038
39namespace {
Eugene Zelenko86150472016-11-29 18:24:01 +000040
Manuel Klimekde237262014-08-20 01:39:05 +000041// \brief This visitor recursively searches for all instances of a USR in a
42// translation unit and stores them for later usage.
43class USRLocFindingASTVisitor
Alex Lorenz98394f82017-07-13 10:36:33 +000044 : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> {
Manuel Klimekde237262014-08-20 01:39:05 +000045public:
Kirill Bobyrev83d5d562016-07-29 10:16:45 +000046 explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
47 StringRef PrevName,
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000048 const ASTContext &Context)
Alex Lorenz98394f82017-07-13 10:36:33 +000049 : RecursiveSymbolVisitor(Context.getSourceManager(),
50 Context.getLangOpts()),
51 USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
Kirill Bobyrev83d5d562016-07-29 10:16:45 +000052 }
Manuel Klimekde237262014-08-20 01:39:05 +000053
Alex Lorenz98394f82017-07-13 10:36:33 +000054 bool visitSymbolOccurrence(const NamedDecl *ND,
55 ArrayRef<SourceRange> NameRanges) {
56 if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
57 assert(NameRanges.size() == 1 &&
58 "Multiple name pieces are not supported yet!");
59 SourceLocation Loc = NameRanges[0].getBegin();
60 const SourceManager &SM = Context.getSourceManager();
61 // TODO: Deal with macro occurrences correctly.
62 if (Loc.isMacroID())
63 Loc = SM.getSpellingLoc(Loc);
64 checkAndAddLocation(Loc);
Kirill Bobyrev9e0dab92016-08-02 09:38:38 +000065 }
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000066 return true;
67 }
68
Manuel Klimekde237262014-08-20 01:39:05 +000069 // Non-visitors:
70
71 // \brief Returns a list of unique locations. Duplicate or overlapping
72 // locations are erroneous and should be reported!
73 const std::vector<clang::SourceLocation> &getLocationsFound() const {
74 return LocationsFound;
75 }
76
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000077private:
78 void checkAndAddLocation(SourceLocation Loc) {
Kirill Bobyrev6b7d8c22016-08-15 23:20:05 +000079 const SourceLocation BeginLoc = Loc;
80 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
Kirill Bobyrev83d5d562016-07-29 10:16:45 +000081 BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000082 StringRef TokenName =
83 Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
84 Context.getSourceManager(), Context.getLangOpts());
85 size_t Offset = TokenName.find(PrevName);
Kirill Bobyrev87697782016-09-04 22:50:41 +000086
87 // The token of the source location we find actually has the old
88 // name.
89 if (Offset != StringRef::npos)
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000090 LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000091 }
92
Kirill Bobyrev83d5d562016-07-29 10:16:45 +000093 const std::set<std::string> USRSet;
Miklos Vajnaa7445f12016-05-17 18:17:16 +000094 const std::string PrevName;
Manuel Klimekde237262014-08-20 01:39:05 +000095 std::vector<clang::SourceLocation> LocationsFound;
Kirill Bobyreva3432fa2016-07-22 13:41:09 +000096 const ASTContext &Context;
Manuel Klimekde237262014-08-20 01:39:05 +000097};
Eugene Zelenko86150472016-11-29 18:24:01 +000098
Haojian Wu74f823a2017-04-04 09:30:06 +000099SourceLocation StartLocationForType(TypeLoc TL) {
100 // For elaborated types (e.g. `struct a::A`) we want the portion after the
101 // `struct` but including the namespace qualifier, `a::`.
102 if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
103 NestedNameSpecifierLoc NestedNameSpecifier =
104 ElaboratedTypeLoc.getQualifierLoc();
105 if (NestedNameSpecifier.getNestedNameSpecifier())
106 return NestedNameSpecifier.getBeginLoc();
107 TL = TL.getNextTypeLoc();
108 }
109 return TL.getLocStart();
110}
111
112SourceLocation EndLocationForType(TypeLoc TL) {
113 // Dig past any namespace or keyword qualifications.
114 while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
115 TL.getTypeLocClass() == TypeLoc::Qualified)
116 TL = TL.getNextTypeLoc();
117
118 // The location for template specializations (e.g. Foo<int>) includes the
119 // templated types in its location range. We want to restrict this to just
120 // before the `<` character.
121 if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
122 return TL.castAs<TemplateSpecializationTypeLoc>()
123 .getLAngleLoc()
124 .getLocWithOffset(-1);
125 }
126 return TL.getEndLoc();
127}
128
129NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
130 // Dig past any keyword qualifications.
131 while (TL.getTypeLocClass() == TypeLoc::Qualified)
132 TL = TL.getNextTypeLoc();
133
134 // For elaborated types (e.g. `struct a::A`) we want the portion after the
135 // `struct` but including the namespace qualifier, `a::`.
136 if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
137 return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
138 return nullptr;
139}
140
141// Find all locations identified by the given USRs for rename.
142//
143// This class will traverse the AST and find every AST node whose USR is in the
144// given USRs' set.
Miklos Vajnaa60cae22017-04-23 16:07:06 +0000145class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
Haojian Wu74f823a2017-04-04 09:30:06 +0000146public:
147 RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
148 : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
149
150 // A structure records all information of a symbol reference being renamed.
151 // We try to add as few prefix qualifiers as possible.
152 struct RenameInfo {
153 // The begin location of a symbol being renamed.
154 SourceLocation Begin;
155 // The end location of a symbol being renamed.
156 SourceLocation End;
157 // The declaration of a symbol being renamed (can be nullptr).
158 const NamedDecl *FromDecl;
159 // The declaration in which the nested name is contained (can be nullptr).
160 const Decl *Context;
161 // The nested name being replaced (can be nullptr).
162 const NestedNameSpecifier *Specifier;
163 };
164
165 // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
166 // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
167 // "a::Foo" to "b::Bar").
168 // For renaming declarations/definitions, prefix qualifiers should be filtered
169 // out.
170 bool VisitNamedDecl(const NamedDecl *Decl) {
171 // UsingDecl has been handled in other place.
172 if (llvm::isa<UsingDecl>(Decl))
173 return true;
174
175 // DestructorDecl has been handled in Typeloc.
176 if (llvm::isa<CXXDestructorDecl>(Decl))
177 return true;
178
179 if (Decl->isImplicit())
180 return true;
181
182 if (isInUSRSet(Decl)) {
183 RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
184 nullptr, nullptr};
185 RenameInfos.push_back(Info);
186 }
187 return true;
188 }
189
190 bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
191 const NamedDecl *Decl = Expr->getFoundDecl();
192 if (isInUSRSet(Decl)) {
193 RenameInfo Info = {Expr->getSourceRange().getBegin(),
194 Expr->getSourceRange().getEnd(), Decl,
195 getClosestAncestorDecl(*Expr), Expr->getQualifier()};
196 RenameInfos.push_back(Info);
197 }
198
199 return true;
200 }
201
202 bool VisitUsingDecl(const UsingDecl *Using) {
203 for (const auto *UsingShadow : Using->shadows()) {
204 if (isInUSRSet(UsingShadow->getTargetDecl())) {
205 UsingDecls.push_back(Using);
206 break;
207 }
208 }
209 return true;
210 }
211
212 bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
213 if (!NestedLoc.getNestedNameSpecifier()->getAsType())
214 return true;
215 if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
216 return true;
217
218 if (const auto *TargetDecl =
219 getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
220 if (isInUSRSet(TargetDecl)) {
221 RenameInfo Info = {NestedLoc.getBeginLoc(),
222 EndLocationForType(NestedLoc.getTypeLoc()),
223 TargetDecl, getClosestAncestorDecl(NestedLoc),
224 NestedLoc.getNestedNameSpecifier()->getPrefix()};
225 RenameInfos.push_back(Info);
226 }
227 }
228 return true;
229 }
230
231 bool VisitTypeLoc(TypeLoc Loc) {
232 if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
233 return true;
234
235 auto Parents = Context.getParents(Loc);
236 TypeLoc ParentTypeLoc;
237 if (!Parents.empty()) {
238 // Handle cases of nested name specificier locations.
239 //
240 // The VisitNestedNameSpecifierLoc interface is not impelmented in
241 // RecursiveASTVisitor, we have to handle it explicitly.
242 if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
243 VisitNestedNameSpecifierLocations(*NSL);
244 return true;
245 }
246
247 if (const auto *TL = Parents[0].get<TypeLoc>())
248 ParentTypeLoc = *TL;
249 }
250
251 // Handle the outermost TypeLoc which is directly linked to the interesting
252 // declaration and don't handle nested name specifier locations.
253 if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
254 if (isInUSRSet(TargetDecl)) {
255 // Only handle the outermost typeLoc.
256 //
257 // For a type like "a::Foo", there will be two typeLocs for it.
258 // One ElaboratedType, the other is RecordType:
259 //
260 // ElaboratedType 0x33b9390 'a::Foo' sugar
261 // `-RecordType 0x338fef0 'class a::Foo'
262 // `-CXXRecord 0x338fe58 'Foo'
263 //
264 // Skip if this is an inner typeLoc.
265 if (!ParentTypeLoc.isNull() &&
266 isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
267 return true;
268 RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
269 TargetDecl, getClosestAncestorDecl(Loc),
270 GetNestedNameForType(Loc)};
271 RenameInfos.push_back(Info);
272 return true;
273 }
274 }
275
276 // Handle specific template class specialiation cases.
277 if (const auto *TemplateSpecType =
278 dyn_cast<TemplateSpecializationType>(Loc.getType())) {
279 TypeLoc TargetLoc = Loc;
280 if (!ParentTypeLoc.isNull()) {
281 if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
282 TargetLoc = ParentTypeLoc;
283 }
284
285 if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
286 TypeLoc TargetLoc = Loc;
287 // FIXME: Find a better way to handle this case.
288 // For the qualified template class specification type like
289 // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
290 // (ElaboratedType) of the TemplateSpecializationType in order to
291 // catch the prefix qualifiers "ns::".
292 if (!ParentTypeLoc.isNull() &&
293 llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
294 TargetLoc = ParentTypeLoc;
295 RenameInfo Info = {
296 StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
297 TemplateSpecType->getTemplateName().getAsTemplateDecl(),
298 getClosestAncestorDecl(
299 ast_type_traits::DynTypedNode::create(TargetLoc)),
300 GetNestedNameForType(TargetLoc)};
301 RenameInfos.push_back(Info);
302 }
303 }
304 return true;
305 }
306
307 // Returns a list of RenameInfo.
308 const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
309
310 // Returns a list of using declarations which are needed to update.
311 const std::vector<const UsingDecl *> &getUsingDecls() const {
312 return UsingDecls;
313 }
314
315private:
316 // FIXME: This method may not be suitable for renaming other types like alias
317 // types. Need to figure out a way to handle it.
318 bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
319 while (!TL.isNull()) {
320 // SubstTemplateTypeParm is the TypeLocation class for a substituted type
321 // inside a template expansion so we ignore these. For example:
322 //
323 // template<typename T> struct S {
324 // T t; // <-- this T becomes a TypeLoc(int) with class
325 // // SubstTemplateTypeParm when S<int> is instantiated
326 // }
327 if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
328 return true;
329
330 // Typedef is the TypeLocation class for a type which is a typedef to the
331 // type we want to replace. We ignore the use of the typedef as we will
332 // replace the definition of it. For example:
333 //
334 // typedef int T;
335 // T a; // <--- This T is a TypeLoc(int) with class Typedef.
336 if (TL.getTypeLocClass() == TypeLoc::Typedef)
337 return true;
338 TL = TL.getNextTypeLoc();
339 }
340 return false;
341 }
342
343 // Get the supported declaration from a given typeLoc. If the declaration type
344 // is not supported, returns nullptr.
345 //
346 // FIXME: support more types, e.g. enum, type alias.
347 const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
348 if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
349 return RD;
350 return nullptr;
351 }
352
353 // Get the closest ancester which is a declaration of a given AST node.
354 template <typename ASTNodeType>
Haojian Wu43ba5252017-04-04 09:53:55 +0000355 const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
Haojian Wu74f823a2017-04-04 09:30:06 +0000356 auto Parents = Context.getParents(Node);
357 // FIXME: figure out how to handle it when there are multiple parents.
358 if (Parents.size() != 1)
359 return nullptr;
360 if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
361 Parents[0].getNodeKind()))
362 return Parents[0].template get<Decl>();
363 return getClosestAncestorDecl(Parents[0]);
364 }
365
366 // Get the parent typeLoc of a given typeLoc. If there is no such parent,
367 // return nullptr.
368 const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
369 auto Parents = Context.getParents(Loc);
370 // FIXME: figure out how to handle it when there are multiple parents.
371 if (Parents.size() != 1)
372 return nullptr;
373 return Parents[0].get<TypeLoc>();
374 }
375
376 // Check whether the USR of a given Decl is in the USRSet.
377 bool isInUSRSet(const Decl *Decl) const {
378 auto USR = getUSRForDecl(Decl);
379 if (USR.empty())
380 return false;
381 return llvm::is_contained(USRSet, USR);
382 }
383
384 const std::set<std::string> USRSet;
385 ASTContext &Context;
386 std::vector<RenameInfo> RenameInfos;
387 // Record all interested using declarations which contains the using-shadow
388 // declarations of the symbol declarations being renamed.
389 std::vector<const UsingDecl *> UsingDecls;
390};
391
Manuel Klimekde237262014-08-20 01:39:05 +0000392} // namespace
393
Kirill Bobyrev83d5d562016-07-29 10:16:45 +0000394std::vector<SourceLocation>
395getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
396 Decl *Decl) {
397 USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
Kirill Bobyrev32db7692016-07-15 11:29:16 +0000398 Visitor.TraverseDecl(Decl);
399 return Visitor.getLocationsFound();
Manuel Klimekde237262014-08-20 01:39:05 +0000400}
401
Haojian Wu74f823a2017-04-04 09:30:06 +0000402std::vector<tooling::AtomicChange>
403createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
404 llvm::StringRef NewName, Decl *TranslationUnitDecl) {
405 RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
406 Finder.TraverseDecl(TranslationUnitDecl);
407
408 const SourceManager &SM =
409 TranslationUnitDecl->getASTContext().getSourceManager();
410
411 std::vector<tooling::AtomicChange> AtomicChanges;
412 auto Replace = [&](SourceLocation Start, SourceLocation End,
413 llvm::StringRef Text) {
414 tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
415 llvm::Error Err = ReplaceChange.replace(
416 SM, CharSourceRange::getTokenRange(Start, End), Text);
417 if (Err) {
418 llvm::errs() << "Faile to add replacement to AtomicChange: "
419 << llvm::toString(std::move(Err)) << "\n";
420 return;
421 }
422 AtomicChanges.push_back(std::move(ReplaceChange));
423 };
424
425 for (const auto &RenameInfo : Finder.getRenameInfos()) {
426 std::string ReplacedName = NewName.str();
427 if (RenameInfo.FromDecl && RenameInfo.Context) {
428 if (!llvm::isa<clang::TranslationUnitDecl>(
429 RenameInfo.Context->getDeclContext())) {
430 ReplacedName = tooling::replaceNestedName(
431 RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
432 RenameInfo.FromDecl,
433 NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
434 }
435 }
436 // If the NewName contains leading "::", add it back.
437 if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
438 ReplacedName = NewName.str();
439 Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
440 }
441
442 // Hanlde using declarations explicitly as "using a::Foo" don't trigger
443 // typeLoc for "a::Foo".
444 for (const auto *Using : Finder.getUsingDecls())
445 Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
446
447 return AtomicChanges;
448}
449
Alex Lorenz4abbd922017-06-30 16:36:09 +0000450} // end namespace tooling
451} // end namespace clang