1 //===--- USRLocFinder.cpp - Clang refactoring library ---------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
11 /// \brief Methods for finding all instances of a USR. Our strategy is very
12 /// simple; we just compare the USR at every relevant AST node with the one
15 //===----------------------------------------------------------------------===//
17 #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
18 #include "clang/AST/ASTContext.h"
19 #include "clang/AST/RecursiveASTVisitor.h"
20 #include "clang/Basic/LLVM.h"
21 #include "clang/Basic/SourceLocation.h"
22 #include "clang/Basic/SourceManager.h"
23 #include "clang/Lex/Lexer.h"
24 #include "clang/Tooling/Core/Lookup.h"
25 #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/Support/Casting.h"
40 // \brief This visitor recursively searches for all instances of a USR in a
41 // translation unit and stores them for later usage.
42 class USRLocFindingASTVisitor
43 : public clang::RecursiveASTVisitor<USRLocFindingASTVisitor> {
45 explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs,
47 const ASTContext &Context)
48 : USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
51 // Declaration visitors:
53 bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) {
54 for (const auto *Initializer : ConstructorDecl->inits()) {
55 // Ignore implicit initializers.
56 if (!Initializer->isWritten())
58 if (const clang::FieldDecl *FieldDecl = Initializer->getMember()) {
59 if (USRSet.find(getUSRForDecl(FieldDecl)) != USRSet.end())
60 LocationsFound.push_back(Initializer->getSourceLocation());
66 bool VisitNamedDecl(const NamedDecl *Decl) {
67 if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
68 checkAndAddLocation(Decl->getLocation());
72 // Expression visitors:
74 bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
75 const NamedDecl *Decl = Expr->getFoundDecl();
77 if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
78 const SourceManager &Manager = Decl->getASTContext().getSourceManager();
79 SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation());
80 checkAndAddLocation(Location);
86 bool VisitMemberExpr(const MemberExpr *Expr) {
87 const NamedDecl *Decl = Expr->getFoundDecl().getDecl();
88 if (USRSet.find(getUSRForDecl(Decl)) != USRSet.end()) {
89 const SourceManager &Manager = Decl->getASTContext().getSourceManager();
90 SourceLocation Location = Manager.getSpellingLoc(Expr->getMemberLoc());
91 checkAndAddLocation(Location);
98 bool VisitTypeLoc(const TypeLoc Loc) {
99 if (USRSet.find(getUSRForDecl(Loc.getType()->getAsCXXRecordDecl())) !=
101 checkAndAddLocation(Loc.getBeginLoc());
102 if (const auto *TemplateTypeParm =
103 dyn_cast<TemplateTypeParmType>(Loc.getType())) {
104 if (USRSet.find(getUSRForDecl(TemplateTypeParm->getDecl())) !=
106 checkAndAddLocation(Loc.getBeginLoc());
113 // \brief Returns a list of unique locations. Duplicate or overlapping
114 // locations are erroneous and should be reported!
115 const std::vector<clang::SourceLocation> &getLocationsFound() const {
116 return LocationsFound;
119 // Namespace traversal:
120 void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) {
122 const NamespaceDecl *Decl =
123 NameLoc.getNestedNameSpecifier()->getAsNamespace();
124 if (Decl && USRSet.find(getUSRForDecl(Decl)) != USRSet.end())
125 checkAndAddLocation(NameLoc.getLocalBeginLoc());
126 NameLoc = NameLoc.getPrefix();
131 void checkAndAddLocation(SourceLocation Loc) {
132 const SourceLocation BeginLoc = Loc;
133 const SourceLocation EndLoc = Lexer::getLocForEndOfToken(
134 BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
135 StringRef TokenName =
136 Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
137 Context.getSourceManager(), Context.getLangOpts());
138 size_t Offset = TokenName.find(PrevName);
140 // The token of the source location we find actually has the old
142 if (Offset != StringRef::npos)
143 LocationsFound.push_back(BeginLoc.getLocWithOffset(Offset));
146 const std::set<std::string> USRSet;
147 const std::string PrevName;
148 std::vector<clang::SourceLocation> LocationsFound;
149 const ASTContext &Context;
152 SourceLocation StartLocationForType(TypeLoc TL) {
153 // For elaborated types (e.g. `struct a::A`) we want the portion after the
154 // `struct` but including the namespace qualifier, `a::`.
155 if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) {
156 NestedNameSpecifierLoc NestedNameSpecifier =
157 ElaboratedTypeLoc.getQualifierLoc();
158 if (NestedNameSpecifier.getNestedNameSpecifier())
159 return NestedNameSpecifier.getBeginLoc();
160 TL = TL.getNextTypeLoc();
162 return TL.getLocStart();
165 SourceLocation EndLocationForType(TypeLoc TL) {
166 // Dig past any namespace or keyword qualifications.
167 while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
168 TL.getTypeLocClass() == TypeLoc::Qualified)
169 TL = TL.getNextTypeLoc();
171 // The location for template specializations (e.g. Foo<int>) includes the
172 // templated types in its location range. We want to restrict this to just
173 // before the `<` character.
174 if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
175 return TL.castAs<TemplateSpecializationTypeLoc>()
177 .getLocWithOffset(-1);
179 return TL.getEndLoc();
182 NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
183 // Dig past any keyword qualifications.
184 while (TL.getTypeLocClass() == TypeLoc::Qualified)
185 TL = TL.getNextTypeLoc();
187 // For elaborated types (e.g. `struct a::A`) we want the portion after the
188 // `struct` but including the namespace qualifier, `a::`.
189 if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>())
190 return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
194 // Find all locations identified by the given USRs for rename.
196 // This class will traverse the AST and find every AST node whose USR is in the
198 class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> {
200 RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context)
201 : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
203 // A structure records all information of a symbol reference being renamed.
204 // We try to add as few prefix qualifiers as possible.
206 // The begin location of a symbol being renamed.
207 SourceLocation Begin;
208 // The end location of a symbol being renamed.
210 // The declaration of a symbol being renamed (can be nullptr).
211 const NamedDecl *FromDecl;
212 // The declaration in which the nested name is contained (can be nullptr).
214 // The nested name being replaced (can be nullptr).
215 const NestedNameSpecifier *Specifier;
218 // FIXME: Currently, prefix qualifiers will be added to the renamed symbol
219 // definition (e.g. "class Foo {};" => "class b::Bar {};" when renaming
220 // "a::Foo" to "b::Bar").
221 // For renaming declarations/definitions, prefix qualifiers should be filtered
223 bool VisitNamedDecl(const NamedDecl *Decl) {
224 // UsingDecl has been handled in other place.
225 if (llvm::isa<UsingDecl>(Decl))
228 // DestructorDecl has been handled in Typeloc.
229 if (llvm::isa<CXXDestructorDecl>(Decl))
232 if (Decl->isImplicit())
235 if (isInUSRSet(Decl)) {
236 RenameInfo Info = {Decl->getLocation(), Decl->getLocation(), nullptr,
238 RenameInfos.push_back(Info);
243 bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
244 const NamedDecl *Decl = Expr->getFoundDecl();
245 if (isInUSRSet(Decl)) {
246 RenameInfo Info = {Expr->getSourceRange().getBegin(),
247 Expr->getSourceRange().getEnd(), Decl,
248 getClosestAncestorDecl(*Expr), Expr->getQualifier()};
249 RenameInfos.push_back(Info);
255 bool VisitUsingDecl(const UsingDecl *Using) {
256 for (const auto *UsingShadow : Using->shadows()) {
257 if (isInUSRSet(UsingShadow->getTargetDecl())) {
258 UsingDecls.push_back(Using);
265 bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
266 if (!NestedLoc.getNestedNameSpecifier()->getAsType())
268 if (IsTypeAliasWhichWillBeRenamedElsewhere(NestedLoc.getTypeLoc()))
271 if (const auto *TargetDecl =
272 getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
273 if (isInUSRSet(TargetDecl)) {
274 RenameInfo Info = {NestedLoc.getBeginLoc(),
275 EndLocationForType(NestedLoc.getTypeLoc()),
276 TargetDecl, getClosestAncestorDecl(NestedLoc),
277 NestedLoc.getNestedNameSpecifier()->getPrefix()};
278 RenameInfos.push_back(Info);
284 bool VisitTypeLoc(TypeLoc Loc) {
285 if (IsTypeAliasWhichWillBeRenamedElsewhere(Loc))
288 auto Parents = Context.getParents(Loc);
289 TypeLoc ParentTypeLoc;
290 if (!Parents.empty()) {
291 // Handle cases of nested name specificier locations.
293 // The VisitNestedNameSpecifierLoc interface is not impelmented in
294 // RecursiveASTVisitor, we have to handle it explicitly.
295 if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) {
296 VisitNestedNameSpecifierLocations(*NSL);
300 if (const auto *TL = Parents[0].get<TypeLoc>())
304 // Handle the outermost TypeLoc which is directly linked to the interesting
305 // declaration and don't handle nested name specifier locations.
306 if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
307 if (isInUSRSet(TargetDecl)) {
308 // Only handle the outermost typeLoc.
310 // For a type like "a::Foo", there will be two typeLocs for it.
311 // One ElaboratedType, the other is RecordType:
313 // ElaboratedType 0x33b9390 'a::Foo' sugar
314 // `-RecordType 0x338fef0 'class a::Foo'
315 // `-CXXRecord 0x338fe58 'Foo'
317 // Skip if this is an inner typeLoc.
318 if (!ParentTypeLoc.isNull() &&
319 isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
321 RenameInfo Info = {StartLocationForType(Loc), EndLocationForType(Loc),
322 TargetDecl, getClosestAncestorDecl(Loc),
323 GetNestedNameForType(Loc)};
324 RenameInfos.push_back(Info);
329 // Handle specific template class specialiation cases.
330 if (const auto *TemplateSpecType =
331 dyn_cast<TemplateSpecializationType>(Loc.getType())) {
332 TypeLoc TargetLoc = Loc;
333 if (!ParentTypeLoc.isNull()) {
334 if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
335 TargetLoc = ParentTypeLoc;
338 if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
339 TypeLoc TargetLoc = Loc;
340 // FIXME: Find a better way to handle this case.
341 // For the qualified template class specification type like
342 // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc
343 // (ElaboratedType) of the TemplateSpecializationType in order to
344 // catch the prefix qualifiers "ns::".
345 if (!ParentTypeLoc.isNull() &&
346 llvm::isa<ElaboratedType>(ParentTypeLoc.getType()))
347 TargetLoc = ParentTypeLoc;
349 StartLocationForType(TargetLoc), EndLocationForType(TargetLoc),
350 TemplateSpecType->getTemplateName().getAsTemplateDecl(),
351 getClosestAncestorDecl(
352 ast_type_traits::DynTypedNode::create(TargetLoc)),
353 GetNestedNameForType(TargetLoc)};
354 RenameInfos.push_back(Info);
360 // Returns a list of RenameInfo.
361 const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; }
363 // Returns a list of using declarations which are needed to update.
364 const std::vector<const UsingDecl *> &getUsingDecls() const {
369 // FIXME: This method may not be suitable for renaming other types like alias
370 // types. Need to figure out a way to handle it.
371 bool IsTypeAliasWhichWillBeRenamedElsewhere(TypeLoc TL) const {
372 while (!TL.isNull()) {
373 // SubstTemplateTypeParm is the TypeLocation class for a substituted type
374 // inside a template expansion so we ignore these. For example:
376 // template<typename T> struct S {
377 // T t; // <-- this T becomes a TypeLoc(int) with class
378 // // SubstTemplateTypeParm when S<int> is instantiated
380 if (TL.getTypeLocClass() == TypeLoc::SubstTemplateTypeParm)
383 // Typedef is the TypeLocation class for a type which is a typedef to the
384 // type we want to replace. We ignore the use of the typedef as we will
385 // replace the definition of it. For example:
388 // T a; // <--- This T is a TypeLoc(int) with class Typedef.
389 if (TL.getTypeLocClass() == TypeLoc::Typedef)
391 TL = TL.getNextTypeLoc();
396 // Get the supported declaration from a given typeLoc. If the declaration type
397 // is not supported, returns nullptr.
399 // FIXME: support more types, e.g. enum, type alias.
400 const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
401 if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
406 // Get the closest ancester which is a declaration of a given AST node.
407 template <typename ASTNodeType>
408 const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
409 auto Parents = Context.getParents(Node);
410 // FIXME: figure out how to handle it when there are multiple parents.
411 if (Parents.size() != 1)
413 if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(
414 Parents[0].getNodeKind()))
415 return Parents[0].template get<Decl>();
416 return getClosestAncestorDecl(Parents[0]);
419 // Get the parent typeLoc of a given typeLoc. If there is no such parent,
421 const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
422 auto Parents = Context.getParents(Loc);
423 // FIXME: figure out how to handle it when there are multiple parents.
424 if (Parents.size() != 1)
426 return Parents[0].get<TypeLoc>();
429 // Check whether the USR of a given Decl is in the USRSet.
430 bool isInUSRSet(const Decl *Decl) const {
431 auto USR = getUSRForDecl(Decl);
434 return llvm::is_contained(USRSet, USR);
437 const std::set<std::string> USRSet;
439 std::vector<RenameInfo> RenameInfos;
440 // Record all interested using declarations which contains the using-shadow
441 // declarations of the symbol declarations being renamed.
442 std::vector<const UsingDecl *> UsingDecls;
447 std::vector<SourceLocation>
448 getLocationsOfUSRs(const std::vector<std::string> &USRs, StringRef PrevName,
450 USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
451 Visitor.TraverseDecl(Decl);
452 NestedNameSpecifierLocFinder Finder(Decl->getASTContext());
454 for (const auto &Location : Finder.getNestedNameSpecifierLocations())
455 Visitor.handleNestedNameSpecifierLoc(Location);
457 return Visitor.getLocationsFound();
460 std::vector<tooling::AtomicChange>
461 createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs,
462 llvm::StringRef NewName, Decl *TranslationUnitDecl) {
463 RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext());
464 Finder.TraverseDecl(TranslationUnitDecl);
466 const SourceManager &SM =
467 TranslationUnitDecl->getASTContext().getSourceManager();
469 std::vector<tooling::AtomicChange> AtomicChanges;
470 auto Replace = [&](SourceLocation Start, SourceLocation End,
471 llvm::StringRef Text) {
472 tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start);
473 llvm::Error Err = ReplaceChange.replace(
474 SM, CharSourceRange::getTokenRange(Start, End), Text);
476 llvm::errs() << "Faile to add replacement to AtomicChange: "
477 << llvm::toString(std::move(Err)) << "\n";
480 AtomicChanges.push_back(std::move(ReplaceChange));
483 for (const auto &RenameInfo : Finder.getRenameInfos()) {
484 std::string ReplacedName = NewName.str();
485 if (RenameInfo.FromDecl && RenameInfo.Context) {
486 if (!llvm::isa<clang::TranslationUnitDecl>(
487 RenameInfo.Context->getDeclContext())) {
488 ReplacedName = tooling::replaceNestedName(
489 RenameInfo.Specifier, RenameInfo.Context->getDeclContext(),
491 NewName.startswith("::") ? NewName.str() : ("::" + NewName).str());
494 // If the NewName contains leading "::", add it back.
495 if (NewName.startswith("::") && NewName.substr(2) == ReplacedName)
496 ReplacedName = NewName.str();
497 Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
500 // Hanlde using declarations explicitly as "using a::Foo" don't trigger
501 // typeLoc for "a::Foo".
502 for (const auto *Using : Finder.getUsingDecls())
503 Replace(Using->getLocStart(), Using->getLocEnd(), "using " + NewName.str());
505 return AtomicChanges;
508 } // end namespace tooling
509 } // end namespace clang