1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | #include "clang/Tooling/Refactoring/Rename/USRLocFinder.h" |
17 | #include "clang/AST/ASTContext.h" |
18 | #include "clang/AST/RecursiveASTVisitor.h" |
19 | #include "clang/Basic/LLVM.h" |
20 | #include "clang/Basic/SourceLocation.h" |
21 | #include "clang/Basic/SourceManager.h" |
22 | #include "clang/Lex/Lexer.h" |
23 | #include "clang/Tooling/Core/Lookup.h" |
24 | #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h" |
25 | #include "clang/Tooling/Refactoring/Rename/SymbolName.h" |
26 | #include "clang/Tooling/Refactoring/Rename/USRFinder.h" |
27 | #include "llvm/ADT/StringRef.h" |
28 | #include "llvm/Support/Casting.h" |
29 | #include <cstddef> |
30 | #include <set> |
31 | #include <string> |
32 | #include <vector> |
33 | |
34 | using namespace llvm; |
35 | |
36 | namespace clang { |
37 | namespace tooling { |
38 | |
39 | namespace { |
40 | |
41 | |
42 | |
43 | bool IsValidEditLoc(const clang::SourceManager& SM, clang::SourceLocation Loc) { |
44 | if (Loc.isInvalid()) |
45 | return false; |
46 | const clang::FullSourceLoc FullLoc(Loc, SM); |
47 | std::pair<clang::FileID, unsigned> FileIdAndOffset = |
48 | FullLoc.getSpellingLoc().getDecomposedLoc(); |
49 | return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr; |
50 | } |
51 | |
52 | |
53 | |
54 | class USRLocFindingASTVisitor |
55 | : public RecursiveSymbolVisitor<USRLocFindingASTVisitor> { |
56 | public: |
57 | explicit USRLocFindingASTVisitor(const std::vector<std::string> &USRs, |
58 | StringRef PrevName, |
59 | const ASTContext &Context) |
60 | : RecursiveSymbolVisitor(Context.getSourceManager(), |
61 | Context.getLangOpts()), |
62 | USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) { |
63 | } |
64 | |
65 | bool visitSymbolOccurrence(const NamedDecl *ND, |
66 | ArrayRef<SourceRange> NameRanges) { |
67 | if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) { |
68 | (0) . __assert_fail ("NameRanges.size() == 1 && \"Multiple name pieces are not supported yet!\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp", 69, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(NameRanges.size() == 1 && |
69 | (0) . __assert_fail ("NameRanges.size() == 1 && \"Multiple name pieces are not supported yet!\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp", 69, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true"> "Multiple name pieces are not supported yet!"); |
70 | SourceLocation Loc = NameRanges[0].getBegin(); |
71 | const SourceManager &SM = Context.getSourceManager(); |
72 | |
73 | if (Loc.isMacroID()) |
74 | Loc = SM.getSpellingLoc(Loc); |
75 | checkAndAddLocation(Loc); |
76 | } |
77 | return true; |
78 | } |
79 | |
80 | |
81 | |
82 | |
83 | |
84 | SymbolOccurrences takeOccurrences() { return std::move(Occurrences); } |
85 | |
86 | private: |
87 | void checkAndAddLocation(SourceLocation Loc) { |
88 | const SourceLocation BeginLoc = Loc; |
89 | const SourceLocation EndLoc = Lexer::getLocForEndOfToken( |
90 | BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts()); |
91 | StringRef TokenName = |
92 | Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc), |
93 | Context.getSourceManager(), Context.getLangOpts()); |
94 | size_t Offset = TokenName.find(PrevName.getNamePieces()[0]); |
95 | |
96 | |
97 | |
98 | if (Offset != StringRef::npos) |
99 | Occurrences.emplace_back(PrevName, SymbolOccurrence::MatchingSymbol, |
100 | BeginLoc.getLocWithOffset(Offset)); |
101 | } |
102 | |
103 | const std::set<std::string> USRSet; |
104 | const SymbolName PrevName; |
105 | SymbolOccurrences Occurrences; |
106 | const ASTContext &Context; |
107 | }; |
108 | |
109 | SourceLocation StartLocationForType(TypeLoc TL) { |
110 | |
111 | |
112 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) { |
113 | NestedNameSpecifierLoc NestedNameSpecifier = |
114 | ElaboratedTypeLoc.getQualifierLoc(); |
115 | if (NestedNameSpecifier.getNestedNameSpecifier()) |
116 | return NestedNameSpecifier.getBeginLoc(); |
117 | TL = TL.getNextTypeLoc(); |
118 | } |
119 | return TL.getBeginLoc(); |
120 | } |
121 | |
122 | SourceLocation EndLocationForType(TypeLoc TL) { |
123 | |
124 | while (TL.getTypeLocClass() == TypeLoc::Elaborated || |
125 | TL.getTypeLocClass() == TypeLoc::Qualified) |
126 | TL = TL.getNextTypeLoc(); |
127 | |
128 | |
129 | |
130 | |
131 | if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) { |
132 | return TL.castAs<TemplateSpecializationTypeLoc>() |
133 | .getLAngleLoc() |
134 | .getLocWithOffset(-1); |
135 | } |
136 | return TL.getEndLoc(); |
137 | } |
138 | |
139 | NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) { |
140 | |
141 | while (TL.getTypeLocClass() == TypeLoc::Qualified) |
142 | TL = TL.getNextTypeLoc(); |
143 | |
144 | |
145 | |
146 | if (auto ElaboratedTypeLoc = TL.getAs<clang::ElaboratedTypeLoc>()) |
147 | return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier(); |
148 | return nullptr; |
149 | } |
150 | |
151 | |
152 | |
153 | |
154 | |
155 | class RenameLocFinder : public RecursiveASTVisitor<RenameLocFinder> { |
156 | public: |
157 | RenameLocFinder(llvm::ArrayRef<std::string> USRs, ASTContext &Context) |
158 | : USRSet(USRs.begin(), USRs.end()), Context(Context) {} |
159 | |
160 | |
161 | |
162 | struct RenameInfo { |
163 | |
164 | SourceLocation Begin; |
165 | |
166 | SourceLocation End; |
167 | |
168 | const NamedDecl *FromDecl; |
169 | |
170 | const Decl *Context; |
171 | |
172 | const NestedNameSpecifier *Specifier; |
173 | |
174 | |
175 | |
176 | |
177 | |
178 | bool IgnorePrefixQualifers; |
179 | }; |
180 | |
181 | bool VisitNamedDecl(const NamedDecl *Decl) { |
182 | |
183 | if (llvm::isa<UsingDecl>(Decl)) |
184 | return true; |
185 | |
186 | |
187 | if (llvm::isa<CXXDestructorDecl>(Decl)) |
188 | return true; |
189 | |
190 | if (Decl->isImplicit()) |
191 | return true; |
192 | |
193 | if (isInUSRSet(Decl)) { |
194 | |
195 | |
196 | if (const auto* TAT = dyn_cast<TypeAliasTemplateDecl>(Decl)) |
197 | Decl = TAT->getTemplatedDecl(); |
198 | |
199 | auto StartLoc = Decl->getLocation(); |
200 | auto EndLoc = StartLoc; |
201 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
202 | RenameInfo Info = {StartLoc, |
203 | EndLoc, |
204 | , |
205 | , |
206 | , |
207 | }; |
208 | RenameInfos.push_back(Info); |
209 | } |
210 | } |
211 | return true; |
212 | } |
213 | |
214 | bool VisitMemberExpr(const MemberExpr *Expr) { |
215 | const NamedDecl *Decl = Expr->getFoundDecl(); |
216 | auto StartLoc = Expr->getMemberLoc(); |
217 | auto EndLoc = Expr->getMemberLoc(); |
218 | if (isInUSRSet(Decl)) { |
219 | RenameInfos.push_back({StartLoc, EndLoc, |
220 | , |
221 | , |
222 | , |
223 | }); |
224 | } |
225 | return true; |
226 | } |
227 | |
228 | bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) { |
229 | |
230 | for (const auto *Initializer : CD->inits()) { |
231 | |
232 | if (!Initializer->isWritten()) |
233 | continue; |
234 | |
235 | if (const FieldDecl *FD = Initializer->getMember()) { |
236 | if (isInUSRSet(FD)) { |
237 | auto Loc = Initializer->getSourceLocation(); |
238 | RenameInfos.push_back({Loc, Loc, |
239 | , |
240 | , |
241 | , |
242 | }); |
243 | } |
244 | } |
245 | } |
246 | return true; |
247 | } |
248 | |
249 | bool VisitDeclRefExpr(const DeclRefExpr *Expr) { |
250 | const NamedDecl *Decl = Expr->getFoundDecl(); |
251 | |
252 | |
253 | if (auto *UsingShadow = llvm::dyn_cast<UsingShadowDecl>(Decl)) { |
254 | Decl = UsingShadow->getTargetDecl(); |
255 | } |
256 | |
257 | auto StartLoc = Expr->getBeginLoc(); |
258 | |
259 | |
260 | SourceLocation EndLoc = Expr->hasExplicitTemplateArgs() |
261 | ? Expr->getLAngleLoc().getLocWithOffset(-1) |
262 | : Expr->getEndLoc(); |
263 | |
264 | if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(Decl)) { |
265 | if (isInUSRSet(MD)) { |
266 | |
267 | |
268 | |
269 | RenameInfos.push_back({EndLoc, EndLoc, |
270 | , |
271 | , |
272 | , |
273 | }); |
274 | return true; |
275 | } |
276 | } |
277 | |
278 | |
279 | |
280 | |
281 | |
282 | |
283 | if (const auto *T = llvm::dyn_cast<EnumConstantDecl>(Decl)) { |
284 | |
285 | |
286 | if (!Expr->hasQualifier()) |
287 | return true; |
288 | |
289 | if (const auto *ED = |
290 | llvm::dyn_cast_or_null<EnumDecl>(getClosestAncestorDecl(*T))) { |
291 | if (ED->isScoped()) |
292 | return true; |
293 | Decl = ED; |
294 | } |
295 | |
296 | |
297 | |
298 | |
299 | |
300 | |
301 | |
302 | |
303 | |
304 | |
305 | EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1); |
306 | (0) . __assert_fail ("EndLoc.isValid() && \"The enum constant should have prefix qualifers.\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp", 307, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true">assert(EndLoc.isValid() && |
307 | (0) . __assert_fail ("EndLoc.isValid() && \"The enum constant should have prefix qualifers.\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Refactoring/Rename/USRLocFinder.cpp", 307, __PRETTY_FUNCTION__))" file_link="../../../../../include/assert.h.html#88" macro="true"> "The enum constant should have prefix qualifers."); |
308 | } |
309 | if (isInUSRSet(Decl) && |
310 | IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
311 | RenameInfo Info = {StartLoc, |
312 | EndLoc, |
313 | Decl, |
314 | getClosestAncestorDecl(*Expr), |
315 | Expr->getQualifier(), |
316 | }; |
317 | RenameInfos.push_back(Info); |
318 | } |
319 | |
320 | return true; |
321 | } |
322 | |
323 | bool VisitUsingDecl(const UsingDecl *Using) { |
324 | for (const auto *UsingShadow : Using->shadows()) { |
325 | if (isInUSRSet(UsingShadow->getTargetDecl())) { |
326 | UsingDecls.push_back(Using); |
327 | break; |
328 | } |
329 | } |
330 | return true; |
331 | } |
332 | |
333 | bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) { |
334 | if (!NestedLoc.getNestedNameSpecifier()->getAsType()) |
335 | return true; |
336 | |
337 | if (const auto *TargetDecl = |
338 | getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) { |
339 | if (isInUSRSet(TargetDecl)) { |
340 | RenameInfo Info = {NestedLoc.getBeginLoc(), |
341 | EndLocationForType(NestedLoc.getTypeLoc()), |
342 | TargetDecl, |
343 | getClosestAncestorDecl(NestedLoc), |
344 | NestedLoc.getNestedNameSpecifier()->getPrefix(), |
345 | }; |
346 | RenameInfos.push_back(Info); |
347 | } |
348 | } |
349 | return true; |
350 | } |
351 | |
352 | bool VisitTypeLoc(TypeLoc Loc) { |
353 | auto Parents = Context.getParents(Loc); |
354 | TypeLoc ParentTypeLoc; |
355 | if (!Parents.empty()) { |
356 | |
357 | |
358 | |
359 | |
360 | if (const auto *NSL = Parents[0].get<NestedNameSpecifierLoc>()) { |
361 | VisitNestedNameSpecifierLocations(*NSL); |
362 | return true; |
363 | } |
364 | |
365 | if (const auto *TL = Parents[0].get<TypeLoc>()) |
366 | ParentTypeLoc = *TL; |
367 | } |
368 | |
369 | |
370 | |
371 | if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) { |
372 | if (isInUSRSet(TargetDecl)) { |
373 | |
374 | |
375 | |
376 | |
377 | |
378 | |
379 | |
380 | |
381 | |
382 | |
383 | if (!ParentTypeLoc.isNull() && |
384 | isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc))) |
385 | return true; |
386 | |
387 | auto StartLoc = StartLocationForType(Loc); |
388 | auto EndLoc = EndLocationForType(Loc); |
389 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
390 | RenameInfo Info = {StartLoc, |
391 | EndLoc, |
392 | TargetDecl, |
393 | getClosestAncestorDecl(Loc), |
394 | GetNestedNameForType(Loc), |
395 | }; |
396 | RenameInfos.push_back(Info); |
397 | } |
398 | return true; |
399 | } |
400 | } |
401 | |
402 | |
403 | if (const auto *TemplateSpecType = |
404 | dyn_cast<TemplateSpecializationType>(Loc.getType())) { |
405 | TypeLoc TargetLoc = Loc; |
406 | if (!ParentTypeLoc.isNull()) { |
407 | if (llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) |
408 | TargetLoc = ParentTypeLoc; |
409 | } |
410 | |
411 | if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) { |
412 | TypeLoc TargetLoc = Loc; |
413 | |
414 | |
415 | |
416 | |
417 | |
418 | if (!ParentTypeLoc.isNull() && |
419 | llvm::isa<ElaboratedType>(ParentTypeLoc.getType())) |
420 | TargetLoc = ParentTypeLoc; |
421 | |
422 | auto StartLoc = StartLocationForType(TargetLoc); |
423 | auto EndLoc = EndLocationForType(TargetLoc); |
424 | if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) { |
425 | RenameInfo Info = { |
426 | StartLoc, |
427 | EndLoc, |
428 | TemplateSpecType->getTemplateName().getAsTemplateDecl(), |
429 | getClosestAncestorDecl( |
430 | ast_type_traits::DynTypedNode::create(TargetLoc)), |
431 | GetNestedNameForType(TargetLoc), |
432 | }; |
433 | RenameInfos.push_back(Info); |
434 | } |
435 | } |
436 | } |
437 | return true; |
438 | } |
439 | |
440 | |
441 | const std::vector<RenameInfo> &getRenameInfos() const { return RenameInfos; } |
442 | |
443 | |
444 | const std::vector<const UsingDecl *> &getUsingDecls() const { |
445 | return UsingDecls; |
446 | } |
447 | |
448 | private: |
449 | |
450 | |
451 | const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) { |
452 | if (const auto* TT = Loc.getType()->getAs<clang::TypedefType>()) |
453 | return TT->getDecl(); |
454 | if (const auto *RD = Loc.getType()->getAsCXXRecordDecl()) |
455 | return RD; |
456 | if (const auto *ED = |
457 | llvm::dyn_cast_or_null<EnumDecl>(Loc.getType()->getAsTagDecl())) |
458 | return ED; |
459 | return nullptr; |
460 | } |
461 | |
462 | |
463 | template <typename ASTNodeType> |
464 | const Decl *getClosestAncestorDecl(const ASTNodeType &Node) { |
465 | auto Parents = Context.getParents(Node); |
466 | |
467 | if (Parents.size() != 1) |
468 | return nullptr; |
469 | if (ast_type_traits::ASTNodeKind::getFromNodeKind<Decl>().isBaseOf( |
470 | Parents[0].getNodeKind())) |
471 | return Parents[0].template get<Decl>(); |
472 | return getClosestAncestorDecl(Parents[0]); |
473 | } |
474 | |
475 | |
476 | |
477 | const TypeLoc *getParentTypeLoc(TypeLoc Loc) const { |
478 | auto Parents = Context.getParents(Loc); |
479 | |
480 | if (Parents.size() != 1) |
481 | return nullptr; |
482 | return Parents[0].get<TypeLoc>(); |
483 | } |
484 | |
485 | |
486 | bool isInUSRSet(const Decl *Decl) const { |
487 | auto USR = getUSRForDecl(Decl); |
488 | if (USR.empty()) |
489 | return false; |
490 | return llvm::is_contained(USRSet, USR); |
491 | } |
492 | |
493 | const std::set<std::string> USRSet; |
494 | ASTContext &Context; |
495 | std::vector<RenameInfo> RenameInfos; |
496 | |
497 | |
498 | std::vector<const UsingDecl *> UsingDecls; |
499 | }; |
500 | |
501 | } |
502 | |
503 | SymbolOccurrences getOccurrencesOfUSRs(ArrayRef<std::string> USRs, |
504 | StringRef PrevName, Decl *Decl) { |
505 | USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext()); |
506 | Visitor.TraverseDecl(Decl); |
507 | return Visitor.takeOccurrences(); |
508 | } |
509 | |
510 | std::vector<tooling::AtomicChange> |
511 | createRenameAtomicChanges(llvm::ArrayRef<std::string> USRs, |
512 | llvm::StringRef NewName, Decl *TranslationUnitDecl) { |
513 | RenameLocFinder Finder(USRs, TranslationUnitDecl->getASTContext()); |
514 | Finder.TraverseDecl(TranslationUnitDecl); |
515 | |
516 | const SourceManager &SM = |
517 | TranslationUnitDecl->getASTContext().getSourceManager(); |
518 | |
519 | std::vector<tooling::AtomicChange> AtomicChanges; |
520 | auto Replace = [&](SourceLocation Start, SourceLocation End, |
521 | llvm::StringRef Text) { |
522 | tooling::AtomicChange ReplaceChange = tooling::AtomicChange(SM, Start); |
523 | llvm::Error Err = ReplaceChange.replace( |
524 | SM, CharSourceRange::getTokenRange(Start, End), Text); |
525 | if (Err) { |
526 | llvm::errs() << "Failed to add replacement to AtomicChange: " |
527 | << llvm::toString(std::move(Err)) << "\n"; |
528 | return; |
529 | } |
530 | AtomicChanges.push_back(std::move(ReplaceChange)); |
531 | }; |
532 | |
533 | for (const auto &RenameInfo : Finder.getRenameInfos()) { |
534 | std::string ReplacedName = NewName.str(); |
535 | if (RenameInfo.IgnorePrefixQualifers) { |
536 | |
537 | size_t LastColonPos = NewName.find_last_of(':'); |
538 | if (LastColonPos != std::string::npos) |
539 | ReplacedName = NewName.substr(LastColonPos + 1); |
540 | } else { |
541 | if (RenameInfo.FromDecl && RenameInfo.Context) { |
542 | if (!llvm::isa<clang::TranslationUnitDecl>( |
543 | RenameInfo.Context->getDeclContext())) { |
544 | ReplacedName = tooling::replaceNestedName( |
545 | RenameInfo.Specifier, RenameInfo.Context->getDeclContext(), |
546 | RenameInfo.FromDecl, |
547 | NewName.startswith("::") ? NewName.str() |
548 | : ("::" + NewName).str()); |
549 | } else { |
550 | |
551 | |
552 | |
553 | |
554 | |
555 | |
556 | |
557 | llvm::StringRef ActualName = Lexer::getSourceText( |
558 | CharSourceRange::getTokenRange( |
559 | SourceRange(RenameInfo.Begin, RenameInfo.End)), |
560 | SM, TranslationUnitDecl->getASTContext().getLangOpts()); |
561 | |
562 | |
563 | if (ActualName.startswith("::") && !NewName.startswith("::")) { |
564 | ReplacedName = "::" + NewName.str(); |
565 | } |
566 | } |
567 | } |
568 | |
569 | if (NewName.startswith("::") && NewName.substr(2) == ReplacedName) |
570 | ReplacedName = NewName.str(); |
571 | } |
572 | Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName); |
573 | } |
574 | |
575 | |
576 | |
577 | for (const auto *Using : Finder.getUsingDecls()) |
578 | Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str()); |
579 | |
580 | return AtomicChanges; |
581 | } |
582 | |
583 | } |
584 | } |
585 | |