/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- USRLocFinder.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 | | /// Methods for finding all instances of a USR. Our strategy is very |
11 | | /// simple; we just compare the USR at every relevant AST node with the one |
12 | | /// provided. |
13 | | /// |
14 | | //===----------------------------------------------------------------------===// |
15 | | |
16 | | #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" |
17 | | #include "clang/AST/ASTContext.h" |
18 | | #include "clang/AST/ParentMapContext.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/Refactoring/Lookup.h" |
25 | | #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h" |
26 | | #include "clang/Tooling/Refactoring/Rename/SymbolName.h" |
27 | | #include "clang/Tooling/Refactoring/Rename/USRFinder.h" |
28 | | #include "llvm/ADT/StringRef.h" |
29 | | #include "llvm/Support/Casting.h" |
30 | | #include <cstddef> |
31 | | #include <set> |
32 | | #include <string> |
33 | | #include <vector> |
34 | | |
35 | | using namespace llvm; |
36 | | |
37 | | namespace clang { |
38 | | namespace tooling { |
39 | | |
40 | | namespace { |
41 | | |
42 | | // Returns true if the given Loc is valid for edit. We don't edit the |
43 | | // SourceLocations that are valid or in temporary buffer. |
44 | 550 | bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) { |
45 | 550 | if (Loc.isInvalid()) |
46 | 0 | return false; |
47 | 550 | const clang::FullSourceLoc FullLoc(Loc, SM); |
48 | 550 | std::pair<clang::FileID, unsigned> FileIdAndOffset = |
49 | 550 | FullLoc.getSpellingLoc().getDecomposedLoc(); |
50 | 550 | return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr; |
51 | 550 | } |
52 | | |
53 | | // This visitor recursively searches for all instances of a USR in a |
54 | | // translation unit and stores them for later usage. |
55 | | class USRLocFindingASTVisitor |
56 | | : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> { |
57 | | public: |
58 | | explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs, |
59 | | StringRef PrevName, |
60 | | const ASTContext &Context) |
61 | | : RecursiveSymbolVisitor(Context.getSourceManager(), |
62 | | Context.getLangOpts()), |
63 | 84 | USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { |
64 | 84 | } |
65 | | |
66 | | bool visitSymbolOccurrence(const NamedDecl *ND, |
67 | 1.52k | ArrayRef<SourceRange> NameRanges) { |
68 | 1.52k | if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) { |
69 | 460 | assert(NameRanges.size() == 1 && |
70 | 460 | "Multiple name pieces are not supported yet!"); |
71 | 460 | SourceLocation Loc = NameRanges[0].getBegin(); |
72 | 460 | const SourceManager &SM = Context.getSourceManager(); |
73 | | // TODO: Deal with macro occurrences correctly. |
74 | 460 | if (Loc.isMacroID()) |
75 | 16 | Loc = SM.getSpellingLoc(Loc); |
76 | 460 | checkAndAddLocation(Loc); |
77 | 460 | } |
78 | 1.52k | return true; |
79 | 1.52k | } |
80 | | |
81 | | // Non-visitors: |
82 | | |
83 | | /// Returns a set of unique symbol occurrences. Duplicate or |
84 | | /// overlapping occurrences are erroneous and should be reported! |
85 | 84 | SymbolOccurrences takeOccurrences() { return std::move(Occurrences); } |
86 | | |
87 | | private: |
88 | 460 | void checkAndAddLocation(SourceLocation Loc) { |
89 | 460 | const SourceLocation BeginLoc = Loc; |
90 | 460 | const SourceLocation EndLoc = Lexer::getLocForEndOfToken( |
91 | 460 | BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); |
92 | 460 | StringRef TokenName = |
93 | 460 | Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), |
94 | 460 | Context.getSourceManager(), Context.getLangOpts()); |
95 | 460 | size_t Offset = TokenName.find(PrevName.getNamePieces()[0]); |
96 | | |
97 | | // The token of the source location we find actually has the old |
98 | | // name. |
99 | 460 | if (Offset != StringRef::npos) |
100 | 459 | Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol, |
101 | 459 | BeginLoc.getLocWithOffset(Offset)); |
102 | 460 | } |
103 | | |
104 | | const std::set<std::string> USRSet; |
105 | | const SymbolName PrevName; |
106 | | SymbolOccurrences Occurrences; |
107 | | const ASTContext &Context; |
108 | | }; |
109 | | |
110 | 215 | SourceLocation StartLocationForType(TypeLoc TL) { |
111 | | // For elaborated types (e.g. `struct a::A`) we want the portion after the |
112 | | // `struct` but including the namespace qualifier, `a::`. |
113 | 215 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) { |
114 | 151 | NestedNameSpecifierLoc NestedNameSpecifier = |
115 | 151 | ElaboratedTypeLoc.getQualifierLoc(); |
116 | 151 | if (NestedNameSpecifier.getNestedNameSpecifier()) |
117 | 149 | return NestedNameSpecifier.getBeginLoc(); |
118 | 2 | TL = TL.getNextTypeLoc(); |
119 | 2 | } |
120 | 66 | return TL.getBeginLoc(); |
121 | 215 | } |
122 | | |
123 | 302 | SourceLocation EndLocationForType(TypeLoc TL) { |
124 | | // Dig past any namespace or keyword qualifications. |
125 | 453 | while (TL.getTypeLocClass() == TypeLoc::Elaborated || |
126 | 302 | TL.getTypeLocClass() == TypeLoc::Qualified) |
127 | 151 | TL = TL.getNextTypeLoc(); |
128 | | |
129 | | // The location for template specializations (e.g. Foo<int>) includes the |
130 | | // templated types in its location range. We want to restrict this to just |
131 | | // before the `<` character. |
132 | 302 | if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) { |
133 | 31 | return TL.castAs<TemplateSpecializationTypeLoc>() |
134 | 31 | .getLAngleLoc() |
135 | 31 | .getLocWithOffset(-1); |
136 | 31 | } |
137 | 271 | return TL.getEndLoc(); |
138 | 271 | } |
139 | | |
140 | 214 | NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) { |
141 | | // Dig past any keyword qualifications. |
142 | 214 | while (TL.getTypeLocClass() == TypeLoc::Qualified) |
143 | 0 | TL = TL.getNextTypeLoc(); |
144 | | |
145 | | // For elaborated types (e.g. `struct a::A`) we want the portion after the |
146 | | // `struct` but including the namespace qualifier, `a::`. |
147 | 214 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) |
148 | 151 | return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier(); |
149 | 63 | return nullptr; |
150 | 63 | } |
151 | | |
152 | | // Find all locations identified by the given USRs for rename. |
153 | | // |
154 | | // This class will traverse the AST and find every AST node whose USR is in the |
155 | | // given USRs' set. |
156 | | class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> { |
157 | | public: |
158 | | RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context) |
159 | 260 | : USRSet(USRs.begin(), USRs.end()), Context(Context) {} |
160 | | |
161 | | // A structure records all information of a symbol reference being renamed. |
162 | | // We try to add as few prefix qualifiers as possible. |
163 | | struct RenameInfo { |
164 | | // The begin location of a symbol being renamed. |
165 | | SourceLocation Begin; |
166 | | // The end location of a symbol being renamed. |
167 | | SourceLocation End; |
168 | | // The declaration of a symbol being renamed (can be nullptr). |
169 | | const NamedDecl *FromDecl; |
170 | | // The declaration in which the nested name is contained (can be nullptr). |
171 | | const Decl *Context; |
172 | | // The nested name being replaced (can be nullptr). |
173 | | const NestedNameSpecifier *Specifier; |
174 | | // Determine whether the prefix qualifiers of the NewName should be ignored. |
175 | | // Normally, we set it to true for the symbol declaration and definition to |
176 | | // avoid adding prefix qualifiers. |
177 | | // For example, if it is true and NewName is "a::b::foo", then the symbol |
178 | | // occurrence which the RenameInfo points to will be renamed to "foo". |
179 | | bool IgnorePrefixQualifers; |
180 | | }; |
181 | | |
182 | 6.27k | bool VisitNamedDecl(const NamedDecl *Decl) { |
183 | | // UsingDecl has been handled in other place. |
184 | 6.27k | if (llvm::isa<UsingDecl>(Decl)) |
185 | 19 | return true; |
186 | | |
187 | | // DestructorDecl has been handled in Typeloc. |
188 | 6.25k | if (llvm::isa<CXXDestructorDecl>(Decl)) |
189 | 6 | return true; |
190 | | |
191 | 6.25k | if (Decl->isImplicit()) |
192 | 0 | return true; |
193 | | |
194 | 6.25k | if (isInUSRSet(Decl)) { |
195 | | // For the case of renaming an alias template, we actually rename the |
196 | | // underlying alias declaration of the template. |
197 | 304 | if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl)) |
198 | 3 | Decl = TAT->getTemplatedDecl(); |
199 | | |
200 | 304 | auto StartLoc = Decl->getLocation(); |
201 | 304 | auto EndLoc = StartLoc; |
202 | 304 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
203 | 303 | RenameInfo Info = {StartLoc, |
204 | 303 | EndLoc, |
205 | 303 | /*FromDecl=*/nullptr, |
206 | 303 | /*Context=*/nullptr, |
207 | 303 | /*Specifier=*/nullptr, |
208 | 303 | /*IgnorePrefixQualifers=*/true}; |
209 | 303 | RenameInfos.push_back(Info); |
210 | 303 | } |
211 | 304 | } |
212 | 6.25k | return true; |
213 | 6.25k | } |
214 | | |
215 | 24 | bool VisitMemberExpr(const MemberExpr *Expr) { |
216 | 24 | const NamedDecl *Decl = Expr->getFoundDecl(); |
217 | 24 | auto StartLoc = Expr->getMemberLoc(); |
218 | 24 | auto EndLoc = Expr->getMemberLoc(); |
219 | 24 | if (isInUSRSet(Decl)) { |
220 | 14 | RenameInfos.push_back({StartLoc, EndLoc, |
221 | 14 | /*FromDecl=*/nullptr, |
222 | 14 | /*Context=*/nullptr, |
223 | 14 | /*Specifier=*/nullptr, |
224 | 14 | /*IgnorePrefixQualifiers=*/true}); |
225 | 14 | } |
226 | 24 | return true; |
227 | 24 | } |
228 | | |
229 | 11 | bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) { |
230 | | // Fix the constructor initializer when renaming class members. |
231 | 4 | for (const auto *Initializer : CD->inits()) { |
232 | | // Ignore implicit initializers. |
233 | 4 | if (!Initializer->isWritten()) |
234 | 1 | continue; |
235 | | |
236 | 3 | if (const FieldDecl *FD = Initializer->getMember()) { |
237 | 3 | if (isInUSRSet(FD)) { |
238 | 1 | auto Loc = Initializer->getSourceLocation(); |
239 | 1 | RenameInfos.push_back({Loc, Loc, |
240 | 1 | /*FromDecl=*/nullptr, |
241 | 1 | /*Context=*/nullptr, |
242 | 1 | /*Specifier=*/nullptr, |
243 | 1 | /*IgnorePrefixQualifiers=*/true}); |
244 | 1 | } |
245 | 3 | } |
246 | 3 | } |
247 | 11 | return true; |
248 | 11 | } |
249 | | |
250 | 192 | bool VisitDeclRefExpr(const DeclRefExpr *Expr) { |
251 | 192 | const NamedDecl *Decl = Expr->getFoundDecl(); |
252 | | // Get the underlying declaration of the shadow declaration introduced by a |
253 | | // using declaration. |
254 | 192 | if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) { |
255 | 1 | Decl = UsingShadow->getTargetDecl(); |
256 | 1 | } |
257 | | |
258 | 192 | auto StartLoc = Expr->getBeginLoc(); |
259 | | // For template function call expressions like `foo<int>()`, we want to |
260 | | // restrict the end of location to just before the `<` character. |
261 | 192 | SourceLocation EndLoc = Expr->hasExplicitTemplateArgs() |
262 | 10 | ? Expr->getLAngleLoc().getLocWithOffset(-1) |
263 | 182 | : Expr->getEndLoc(); |
264 | | |
265 | 192 | if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) { |
266 | 44 | if (isInUSRSet(MD)) { |
267 | | // Handle renaming static template class methods, we only rename the |
268 | | // name without prefix qualifiers and restrict the source range to the |
269 | | // name. |
270 | 21 | RenameInfos.push_back({EndLoc, EndLoc, |
271 | 21 | /*FromDecl=*/nullptr, |
272 | 21 | /*Context=*/nullptr, |
273 | 21 | /*Specifier=*/nullptr, |
274 | 21 | /*IgnorePrefixQualifiers=*/true}); |
275 | 21 | return true; |
276 | 21 | } |
277 | 171 | } |
278 | | |
279 | | // In case of renaming an enum declaration, we have to explicitly handle |
280 | | // unscoped enum constants referenced in expressions (e.g. |
281 | | // "auto r = ns1::ns2::Green" where Green is an enum constant of an unscoped |
282 | | // enum decl "ns1::ns2::Color") as these enum constants cannot be caught by |
283 | | // TypeLoc. |
284 | 171 | if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) { |
285 | | // FIXME: Handle the enum constant without prefix qualifiers (`a = Green`) |
286 | | // when renaming an unscoped enum declaration with a new namespace. |
287 | 17 | if (!Expr->hasQualifier()) |
288 | 2 | return true; |
289 | | |
290 | 15 | if (const auto *ED = |
291 | 15 | llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) { |
292 | 15 | if (ED->isScoped()) |
293 | 4 | return true; |
294 | 11 | Decl = ED; |
295 | 11 | } |
296 | | // The current fix would qualify "ns1::ns2::Green" as |
297 | | // "ns1::ns2::Color::Green". |
298 | | // |
299 | | // Get the EndLoc of the replacement by moving 1 character backward ( |
300 | | // to exclude the last '::'). |
301 | | // |
302 | | // ns1::ns2::Green; |
303 | | // ^ ^^ |
304 | | // BeginLoc |EndLoc of the qualifier |
305 | | // new EndLoc |
306 | 11 | EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1); |
307 | 11 | assert(EndLoc.isValid() && |
308 | 11 | "The enum constant should have prefix qualifers."); |
309 | 11 | } |
310 | 165 | if (isInUSRSet(Decl) && |
311 | 31 | IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
312 | 31 | RenameInfo Info = {StartLoc, |
313 | 31 | EndLoc, |
314 | 31 | Decl, |
315 | 31 | getClosestAncestorDecl(*Expr), |
316 | 31 | Expr->getQualifier(), |
317 | 31 | /*IgnorePrefixQualifers=*/false}; |
318 | 31 | RenameInfos.push_back(Info); |
319 | 31 | } |
320 | | |
321 | 165 | return true; |
322 | 171 | } |
323 | | |
324 | 19 | bool VisitUsingDecl(const UsingDecl *Using) { |
325 | 21 | for (const auto *UsingShadow : Using->shadows()) { |
326 | 21 | if (isInUSRSet(UsingShadow->getTargetDecl())) { |
327 | 18 | UsingDecls.push_back(Using); |
328 | 18 | break; |
329 | 18 | } |
330 | 21 | } |
331 | 19 | return true; |
332 | 19 | } |
333 | | |
334 | 134 | bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) { |
335 | 134 | if (!NestedLoc.getNestedNameSpecifier()->getAsType()) |
336 | 0 | return true; |
337 | | |
338 | 134 | if (const auto *TargetDecl = |
339 | 132 | getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) { |
340 | 132 | if (isInUSRSet(TargetDecl)) { |
341 | 87 | RenameInfo Info = {NestedLoc.getBeginLoc(), |
342 | 87 | EndLocationForType(NestedLoc.getTypeLoc()), |
343 | 87 | TargetDecl, |
344 | 87 | getClosestAncestorDecl(NestedLoc), |
345 | 87 | NestedLoc.getNestedNameSpecifier()->getPrefix(), |
346 | 87 | /*IgnorePrefixQualifers=*/false}; |
347 | 87 | RenameInfos.push_back(Info); |
348 | 87 | } |
349 | 132 | } |
350 | 134 | return true; |
351 | 134 | } |
352 | | |
353 | 5.21k | bool VisitTypeLoc(TypeLoc Loc) { |
354 | 5.21k | auto Parents = Context.getParents(Loc); |
355 | 5.21k | TypeLoc ParentTypeLoc; |
356 | 5.21k | if (!Parents.empty()) { |
357 | | // Handle cases of nested name specificier locations. |
358 | | // |
359 | | // The VisitNestedNameSpecifierLoc interface is not impelmented in |
360 | | // RecursiveASTVisitor, we have to handle it explicitly. |
361 | 5.17k | if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) { |
362 | 134 | VisitNestedNameSpecifierLocations(*NSL); |
363 | 134 | return true; |
364 | 134 | } |
365 | | |
366 | 5.03k | if (const auto *TL = Parents[0].get<TypeLoc>()) |
367 | 2.31k | ParentTypeLoc = *TL; |
368 | 5.03k | } |
369 | | |
370 | | // Handle the outermost TypeLoc which is directly linked to the interesting |
371 | | // declaration and don't handle nested name specifier locations. |
372 | 5.07k | if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) { |
373 | 1.04k | if (isInUSRSet(TargetDecl)) { |
374 | | // Only handle the outermost typeLoc. |
375 | | // |
376 | | // For a type like "a::Foo", there will be two typeLocs for it. |
377 | | // One ElaboratedType, the other is RecordType: |
378 | | // |
379 | | // ElaboratedType 0x33b9390 'a::Foo' sugar |
380 | | // `-RecordType 0x338fef0 'class a::Foo' |
381 | | // `-CXXRecord 0x338fe58 'Foo' |
382 | | // |
383 | | // Skip if this is an inner typeLoc. |
384 | 360 | if (!ParentTypeLoc.isNull() && |
385 | 193 | isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc))) |
386 | 149 | return true; |
387 | | |
388 | 211 | auto StartLoc = StartLocationForType(Loc); |
389 | 211 | auto EndLoc = EndLocationForType(Loc); |
390 | 211 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
391 | 210 | RenameInfo Info = {StartLoc, |
392 | 210 | EndLoc, |
393 | 210 | TargetDecl, |
394 | 210 | getClosestAncestorDecl(Loc), |
395 | 210 | GetNestedNameForType(Loc), |
396 | 210 | /*IgnorePrefixQualifers=*/false}; |
397 | 210 | RenameInfos.push_back(Info); |
398 | 210 | } |
399 | 211 | return true; |
400 | 211 | } |
401 | 1.04k | } |
402 | | |
403 | | // Handle specific template class specialiation cases. |
404 | 4.71k | if (const auto *TemplateSpecType = |
405 | 152 | dyn_cast<TemplateSpecializationType>(Loc.getType())) { |
406 | 152 | TypeLoc TargetLoc = Loc; |
407 | 152 | if (!ParentTypeLoc.isNull()) { |
408 | 4 | if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) |
409 | 4 | TargetLoc = ParentTypeLoc; |
410 | 4 | } |
411 | | |
412 | 152 | if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) { |
413 | 4 | TypeLoc TargetLoc = Loc; |
414 | | // FIXME: Find a better way to handle this case. |
415 | | // For the qualified template class specification type like |
416 | | // "ns::Foo<int>" in "ns::Foo<int>& f();", we want the parent typeLoc |
417 | | // (ElaboratedType) of the TemplateSpecializationType in order to |
418 | | // catch the prefix qualifiers "ns::". |
419 | 4 | if (!ParentTypeLoc.isNull() && |
420 | 2 | llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) |
421 | 2 | TargetLoc = ParentTypeLoc; |
422 | | |
423 | 4 | auto StartLoc = StartLocationForType(TargetLoc); |
424 | 4 | auto EndLoc = EndLocationForType(TargetLoc); |
425 | 4 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
426 | 4 | RenameInfo Info = { |
427 | 4 | StartLoc, |
428 | 4 | EndLoc, |
429 | 4 | TemplateSpecType->getTemplateName().getAsTemplateDecl(), |
430 | 4 | getClosestAncestorDecl(DynTypedNode::create(TargetLoc)), |
431 | 4 | GetNestedNameForType(TargetLoc), |
432 | 4 | /*IgnorePrefixQualifers=*/false}; |
433 | 4 | RenameInfos.push_back(Info); |
434 | 4 | } |
435 | 4 | } |
436 | 152 | } |
437 | 4.71k | return true; |
438 | 4.71k | } |
439 | | |
440 | | // Returns a list of RenameInfo. |
441 | 260 | const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; } |
442 | | |
443 | | // Returns a list of using declarations which are needed to update. |
444 | 260 | const std::vector<const UsingDecl *> &getUsingDecls() const { |
445 | 260 | return UsingDecls; |
446 | 260 | } |
447 | | |
448 | | private: |
449 | | // Get the supported declaration from a given typeLoc. If the declaration type |
450 | | // is not supported, returns nullptr. |
451 | 5.40k | const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) { |
452 | 5.40k | if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>()) |
453 | 177 | return TT->getDecl(); |
454 | 5.22k | if (const auto *RD = Loc.getType()->getAsCXXRecordDecl()) |
455 | 1.03k | return RD; |
456 | 4.19k | if (const auto *ED = |
457 | 127 | llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl())) |
458 | 127 | return ED; |
459 | 4.06k | return nullptr; |
460 | 4.06k | } |
461 | | |
462 | | // Get the closest ancester which is a declaration of a given AST node. |
463 | | template <typename ASTNodeType> |
464 | 646 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { |
465 | 646 | auto Parents = Context.getParents(Node); |
466 | | // FIXME: figure out how to handle it when there are multiple parents. |
467 | 646 | if (Parents.size() != 1) |
468 | 9 | return nullptr; |
469 | 637 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) |
470 | 338 | return Parents[0].template get<Decl>(); |
471 | 299 | return getClosestAncestorDecl(Parents[0]); |
472 | 299 | } USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::NestedNameSpecifierLoc>(clang::NestedNameSpecifierLoc const&) Line | Count | Source | 464 | 87 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 465 | 87 | auto Parents = Context.getParents(Node); | 466 | | // FIXME: figure out how to handle it when there are multiple parents. | 467 | 87 | if (Parents.size() != 1) | 468 | 0 | return nullptr; | 469 | 87 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 470 | 54 | return Parents[0].template get<Decl>(); | 471 | 33 | return getClosestAncestorDecl(Parents[0]); | 472 | 33 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::TypeLoc>(clang::TypeLoc const&) Line | Count | Source | 464 | 210 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 465 | 210 | auto Parents = Context.getParents(Node); | 466 | | // FIXME: figure out how to handle it when there are multiple parents. | 467 | 210 | if (Parents.size() != 1) | 468 | 9 | return nullptr; | 469 | 201 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 470 | 134 | return Parents[0].template get<Decl>(); | 471 | 67 | return getClosestAncestorDecl(Parents[0]); | 472 | 67 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::DynTypedNode>(clang::DynTypedNode const&) Line | Count | Source | 464 | 303 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 465 | 303 | auto Parents = Context.getParents(Node); | 466 | | // FIXME: figure out how to handle it when there are multiple parents. | 467 | 303 | if (Parents.size() != 1) | 468 | 0 | return nullptr; | 469 | 303 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 470 | 128 | return Parents[0].template get<Decl>(); | 471 | 175 | return getClosestAncestorDecl(Parents[0]); | 472 | 175 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::EnumConstantDecl>(clang::EnumConstantDecl const&) Line | Count | Source | 464 | 15 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 465 | 15 | auto Parents = Context.getParents(Node); | 466 | | // FIXME: figure out how to handle it when there are multiple parents. | 467 | 15 | if (Parents.size() != 1) | 468 | 0 | return nullptr; | 469 | 15 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 470 | 15 | return Parents[0].template get<Decl>(); | 471 | 0 | return getClosestAncestorDecl(Parents[0]); | 472 | 0 | } |
USRLocFinder.cpp:clang::Decl const* clang::tooling::(anonymous namespace)::RenameLocFinder::getClosestAncestorDecl<clang::DeclRefExpr>(clang::DeclRefExpr const&) Line | Count | Source | 464 | 31 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { | 465 | 31 | auto Parents = Context.getParents(Node); | 466 | | // FIXME: figure out how to handle it when there are multiple parents. | 467 | 31 | if (Parents.size() != 1) | 468 | 0 | return nullptr; | 469 | 31 | if (ASTNodeKind::getFromNodeKind<Decl>().isBaseOf(Parents[0].getNodeKind())) | 470 | 7 | return Parents[0].template get<Decl>(); | 471 | 24 | return getClosestAncestorDecl(Parents[0]); | 472 | 24 | } |
|
473 | | |
474 | | // Get the parent typeLoc of a given typeLoc. If there is no such parent, |
475 | | // return nullptr. |
476 | 0 | const TypeLoc *getParentTypeLoc(TypeLoc Loc) const { |
477 | 0 | auto Parents = Context.getParents(Loc); |
478 | 0 | // FIXME: figure out how to handle it when there are multiple parents. |
479 | 0 | if (Parents.size() != 1) |
480 | 0 | return nullptr; |
481 | 0 | return Parents[0].get<TypeLoc>(); |
482 | 0 | } |
483 | | |
484 | | // Check whether the USR of a given Decl is in the USRSet. |
485 | 8.03k | bool isInUSRSet(const Decl *Decl) const { |
486 | 8.03k | auto USR = getUSRForDecl(Decl); |
487 | 8.03k | if (USR.empty()) |
488 | 36 | return false; |
489 | 7.99k | return llvm::is_contained(USRSet, USR); |
490 | 7.99k | } |
491 | | |
492 | | const std::set<std::string> USRSet; |
493 | | ASTContext &Context; |
494 | | std::vector<RenameInfo> RenameInfos; |
495 | | // Record all interested using declarations which contains the using-shadow |
496 | | // declarations of the symbol declarations being renamed. |
497 | | std::vector<const UsingDecl *> UsingDecls; |
498 | | }; |
499 | | |
500 | | } // namespace |
501 | | |
502 | | SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs, |
503 | 84 | StringRef PrevName, Decl *Decl) { |
504 | 84 | USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); |
505 | 84 | Visitor.TraverseDecl(Decl); |
506 | 84 | return Visitor.takeOccurrences(); |
507 | 84 | } |
508 | | |
509 | | std::vector<tooling::AtomicChange> |
510 | | createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs, |
511 | 260 | llvm::StringRef NewName, Decl *TranslationUnitDecl) { |
512 | 260 | RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext()); |
513 | 260 | Finder.TraverseDecl(TranslationUnitDecl); |
514 | | |
515 | 260 | const SourceManager &SM = |
516 | 260 | TranslationUnitDecl->getASTContext().getSourceManager(); |
517 | | |
518 | 260 | std::vector<tooling::AtomicChange> AtomicChanges; |
519 | 260 | auto Replace = [&](SourceLocation Start, SourceLocation End, |
520 | 689 | llvm::StringRef Text) { |
521 | 689 | tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start); |
522 | 689 | llvm::Error Err = ReplaceChange.replace( |
523 | 689 | SM, CharSourceRange::getTokenRange(Start, End), Text); |
524 | 689 | if (Err) { |
525 | 0 | llvm::errs() << "Failed to add replacement to AtomicChange: " |
526 | 0 | << llvm::toString(std::move(Err)) << "\n"; |
527 | 0 | return; |
528 | 0 | } |
529 | 689 | AtomicChanges.push_back(std::move(ReplaceChange)); |
530 | 689 | }; |
531 | | |
532 | 671 | for (const auto &RenameInfo : Finder.getRenameInfos()) { |
533 | 671 | std::string ReplacedName = NewName.str(); |
534 | 671 | if (RenameInfo.IgnorePrefixQualifers) { |
535 | | // Get the name without prefix qualifiers from NewName. |
536 | 339 | size_t LastColonPos = NewName.find_last_of(':'); |
537 | 339 | if (LastColonPos != std::string::npos) |
538 | 301 | ReplacedName = std::string(NewName.substr(LastColonPos + 1)); |
539 | 332 | } else { |
540 | 332 | if (RenameInfo.FromDecl && RenameInfo.Context) { |
541 | 323 | if (!llvm::isa<clang::TranslationUnitDecl>( |
542 | 226 | RenameInfo.Context->getDeclContext())) { |
543 | 226 | ReplacedName = tooling::replaceNestedName( |
544 | 226 | RenameInfo.Specifier, RenameInfo.Begin, |
545 | 226 | RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl, |
546 | 18 | NewName.startswith("::") ? NewName.str() |
547 | 208 | : ("::" + NewName).str()); |
548 | 97 | } else { |
549 | | // This fixes the case where type `T` is a parameter inside a function |
550 | | // type (e.g. `std::function<void(T)>`) and the DeclContext of `T` |
551 | | // becomes the translation unit. As a workaround, we simply use |
552 | | // fully-qualified name here for all references whose `DeclContext` is |
553 | | // the translation unit and ignore the possible existence of |
554 | | // using-decls (in the global scope) that can shorten the replaced |
555 | | // name. |
556 | 97 | llvm::StringRef ActualName = Lexer::getSourceText( |
557 | 97 | CharSourceRange::getTokenRange( |
558 | 97 | SourceRange(RenameInfo.Begin, RenameInfo.End)), |
559 | 97 | SM, TranslationUnitDecl->getASTContext().getLangOpts()); |
560 | | // Add the leading "::" back if the name written in the code contains |
561 | | // it. |
562 | 97 | if (ActualName.startswith("::") && !NewName.startswith("::")4 ) { |
563 | 4 | ReplacedName = "::" + NewName.str(); |
564 | 4 | } |
565 | 97 | } |
566 | 323 | } |
567 | | // If the NewName contains leading "::", add it back. |
568 | 332 | if (NewName.startswith("::") && NewName.substr(2) == ReplacedName22 ) |
569 | 9 | ReplacedName = NewName.str(); |
570 | 332 | } |
571 | 671 | Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName); |
572 | 671 | } |
573 | | |
574 | | // Hanlde using declarations explicitly as "using a::Foo" don't trigger |
575 | | // typeLoc for "a::Foo". |
576 | 260 | for (const auto *Using : Finder.getUsingDecls()) |
577 | 18 | Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str()); |
578 | | |
579 | 260 | return AtomicChanges; |
580 | 260 | } |
581 | | |
582 | | } // end namespace tooling |
583 | | } // end namespace clang |