clang: lib/Tooling/Refactoring/Rename/USRLocFinder.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
28#include "llvm/ADT/StringRef.h"
29#include "llvm/Support/Casting.h"
30#include
31#include
32#include
33#include
34
35using namespace llvm;
36
38namespace tooling {
39
40namespace {
41
42
43
45 if (Loc.isInvalid())
46 return false;
48 std::pair<clang::FileID, unsigned> FileIdAndOffset =
49 FullLoc.getSpellingLoc().getDecomposedLoc();
50 return SM.getFileEntryForID(FileIdAndOffset.first) != nullptr;
51}
52
53
54
55class USRLocFindingASTVisitor
56 : public RecursiveSymbolVisitor {
57public:
58 explicit USRLocFindingASTVisitor(const std::vectorstd::string &USRs,
59 StringRef PrevName,
60 const ASTContext &Context)
61 : RecursiveSymbolVisitor(Context.getSourceManager(),
62 Context.getLangOpts()),
63 USRSet(USRs.begin(), USRs.end()), PrevName(PrevName), Context(Context) {
64 }
65
66 bool visitSymbolOccurrence(const NamedDecl *ND,
68 if (USRSet.find(getUSRForDecl(ND)) != USRSet.end()) {
69 assert(NameRanges.size() == 1 &&
70 "Multiple name pieces are not supported yet!");
71 SourceLocation Loc = NameRanges[0].getBegin();
72 const SourceManager &SM = Context.getSourceManager();
73
74 if (Loc.isMacroID())
76 checkAndAddLocation(Loc);
77 }
78 return true;
79 }
80
81
82
83
84
85 SymbolOccurrences takeOccurrences() { return std::move(Occurrences); }
86
87private:
88 void checkAndAddLocation(SourceLocation Loc) {
89 const SourceLocation BeginLoc = Loc;
91 BeginLoc, 0, Context.getSourceManager(), Context.getLangOpts());
92 StringRef TokenName =
94 Context.getSourceManager(), Context.getLangOpts());
95 size_t Offset = TokenName.find(PrevName.getNamePieces()[0]);
96
97
98
99 if (Offset != StringRef::npos)
101 BeginLoc.getLocWithOffset(Offset));
102 }
103
104 const std::setstd::string USRSet;
105 const SymbolName PrevName;
107 const ASTContext &Context;
108};
109
110SourceLocation StartLocationForType(TypeLoc TL) {
111
112
114 NestedNameSpecifierLoc NestedNameSpecifier =
115 ElaboratedTypeLoc.getQualifierLoc();
116 if (NestedNameSpecifier.getNestedNameSpecifier())
117 return NestedNameSpecifier.getBeginLoc();
118 TL = TL.getNextTypeLoc();
119 }
120 return TL.getBeginLoc();
121}
122
123SourceLocation EndLocationForType(TypeLoc TL) {
124
125 while (TL.getTypeLocClass() == TypeLoc::Elaborated ||
127 TL = TL.getNextTypeLoc();
128
129
130
131
132 if (TL.getTypeLocClass() == TypeLoc::TemplateSpecialization) {
133 return TL.castAs()
134 .getLAngleLoc()
135 .getLocWithOffset(-1);
136 }
137 return TL.getEndLoc();
138}
139
140NestedNameSpecifier *GetNestedNameForType(TypeLoc TL) {
141
143 TL = TL.getNextTypeLoc();
144
145
146
148 return ElaboratedTypeLoc.getQualifierLoc().getNestedNameSpecifier();
149 return nullptr;
150}
151
152
153
154
155
156class RenameLocFinder : public RecursiveASTVisitor {
157public:
159 : USRSet(USRs.begin(), USRs.end()), Context(Context) {}
160
161
162
163 struct RenameInfo {
164
166
167 SourceLocation End;
168
170
171 const Decl *Context;
172
174
175
176
177
178
180 };
181
182 bool VisitNamedDecl(const NamedDecl *Decl) {
183
184 if (llvm::isa(Decl))
185 return true;
186
187
188 if (llvm::isa(Decl))
189 return true;
190
191 if (Decl->isImplicit())
192 return true;
193
194 if (isInUSRSet(Decl)) {
195
196
197 if (const auto* TAT = dyn_cast(Decl))
198 Decl = TAT->getTemplatedDecl();
199
200 auto StartLoc = Decl->getLocation();
201 auto EndLoc = StartLoc;
202 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
203 RenameInfo Info = {StartLoc,
204 EndLoc,
205 nullptr,
206 nullptr,
207 nullptr,
208 true};
209 RenameInfos.push_back(Info);
210 }
211 }
212 return true;
213 }
214
215 bool VisitMemberExpr(const MemberExpr *Expr) {
216 const NamedDecl *Decl = Expr->getFoundDecl();
217 auto StartLoc = Expr->getMemberLoc();
218 auto EndLoc = Expr->getMemberLoc();
219 if (isInUSRSet(Decl)) {
220 RenameInfos.push_back({StartLoc, EndLoc,
221 nullptr,
222 nullptr,
223 nullptr,
224 true});
225 }
226 return true;
227 }
228
229 bool VisitDesignatedInitExpr(const DesignatedInitExpr *E) {
230 for (const DesignatedInitExpr::Designator &D : E->designators()) {
231 if (D.isFieldDesignator()) {
232 if (const FieldDecl *Decl = D.getFieldDecl()) {
233 if (isInUSRSet(Decl)) {
234 auto StartLoc = D.getFieldLoc();
235 auto EndLoc = D.getFieldLoc();
236 RenameInfos.push_back({StartLoc, EndLoc,
237 nullptr,
238 nullptr,
239 nullptr,
240 true});
241 }
242 }
243 }
244 }
245 return true;
246 }
247
248 bool VisitCXXConstructorDecl(const CXXConstructorDecl *CD) {
249
250 for (const auto *Initializer : CD->inits()) {
251
253 continue;
254
255 if (const FieldDecl *FD = Initializer->getMember()) {
256 if (isInUSRSet(FD)) {
258 RenameInfos.push_back({Loc, Loc,
259 nullptr,
260 nullptr,
261 nullptr,
262 true});
263 }
264 }
265 }
266 return true;
267 }
268
269 bool VisitDeclRefExpr(const DeclRefExpr *Expr) {
270 const NamedDecl *Decl = Expr->getFoundDecl();
271
272
273 if (auto *UsingShadow = llvm::dyn_cast(Decl)) {
274 Decl = UsingShadow->getTargetDecl();
275 }
276
277 auto StartLoc = Expr->getBeginLoc();
278
279
280 SourceLocation EndLoc = Expr->hasExplicitTemplateArgs()
281 ? Expr->getLAngleLoc().getLocWithOffset(-1)
282 : Expr->getEndLoc();
283
284 if (const auto *MD = llvm::dyn_cast(Decl)) {
285 if (isInUSRSet(MD)) {
286
287
288
289 RenameInfos.push_back({EndLoc, EndLoc,
290 nullptr,
291 nullptr,
292 nullptr,
293 true});
294 return true;
295 }
296 }
297
298
299
300
301
302
303 if (const auto *T = llvm::dyn_cast(Decl)) {
304
305
306 if (!Expr->hasQualifier())
307 return true;
308
309 if (const auto *ED =
310 llvm::dyn_cast_or_null(getClosestAncestorDecl(*T))) {
311 if (ED->isScoped())
312 return true;
314 }
315
316
317
318
319
320
321
322
323
324
325 EndLoc = Expr->getQualifierLoc().getEndLoc().getLocWithOffset(-1);
326 assert(EndLoc.isValid() &&
327 "The enum constant should have prefix qualifers.");
328 }
329 if (isInUSRSet(Decl) &&
330 IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
331 RenameInfo Info = {StartLoc,
332 EndLoc,
334 getClosestAncestorDecl(*Expr),
335 Expr->getQualifier(),
336 false};
337 RenameInfos.push_back(Info);
338 }
339
340 return true;
341 }
342
343 bool VisitUsingDecl(const UsingDecl *Using) {
344 for (const auto *UsingShadow : Using->shadows()) {
345 if (isInUSRSet(UsingShadow->getTargetDecl())) {
346 UsingDecls.push_back(Using);
347 break;
348 }
349 }
350 return true;
351 }
352
353 bool VisitNestedNameSpecifierLocations(NestedNameSpecifierLoc NestedLoc) {
354 if (!NestedLoc.getNestedNameSpecifier()->getAsType())
355 return true;
356
357 if (const auto *TargetDecl =
358 getSupportedDeclFromTypeLoc(NestedLoc.getTypeLoc())) {
359 if (isInUSRSet(TargetDecl)) {
360 RenameInfo Info = {NestedLoc.getBeginLoc(),
361 EndLocationForType(NestedLoc.getTypeLoc()),
362 TargetDecl,
363 getClosestAncestorDecl(NestedLoc),
364 NestedLoc.getNestedNameSpecifier()->getPrefix(),
365 false};
366 RenameInfos.push_back(Info);
367 }
368 }
369 return true;
370 }
371
372 bool VisitTypeLoc(TypeLoc Loc) {
373 auto Parents = Context.getParents(Loc);
374 TypeLoc ParentTypeLoc;
375 if (!Parents.empty()) {
376
377
378
379
380 if (const auto *NSL = Parents[0].get()) {
381 VisitNestedNameSpecifierLocations(*NSL);
382 return true;
383 }
384
385 if (const auto *TL = Parents[0].get())
386 ParentTypeLoc = *TL;
387 }
388
389
390
391 if (const auto *TargetDecl = getSupportedDeclFromTypeLoc(Loc)) {
392 if (isInUSRSet(TargetDecl)) {
393
394
395
396
397
398
399
400
401
402
403 if (!ParentTypeLoc.isNull() &&
404 isInUSRSet(getSupportedDeclFromTypeLoc(ParentTypeLoc)))
405 return true;
406
407 auto StartLoc = StartLocationForType(Loc);
408 auto EndLoc = EndLocationForType(Loc);
409 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
410 RenameInfo Info = {StartLoc,
411 EndLoc,
412 TargetDecl,
413 getClosestAncestorDecl(Loc),
414 GetNestedNameForType(Loc),
415 false};
416 RenameInfos.push_back(Info);
417 }
418 return true;
419 }
420 }
421
422
423 if (const auto *TemplateSpecType =
424 dyn_cast(Loc.getType())) {
425 TypeLoc TargetLoc = Loc;
426 if (!ParentTypeLoc.isNull()) {
427 if (llvm::isa(ParentTypeLoc.getType()))
428 TargetLoc = ParentTypeLoc;
429 }
430
431 if (isInUSRSet(TemplateSpecType->getTemplateName().getAsTemplateDecl())) {
432 TypeLoc TargetLoc = Loc;
433
434
435
436
437
438 if (!ParentTypeLoc.isNull() &&
439 llvm::isa(ParentTypeLoc.getType()))
440 TargetLoc = ParentTypeLoc;
441
442 auto StartLoc = StartLocationForType(TargetLoc);
443 auto EndLoc = EndLocationForType(TargetLoc);
444 if (IsValidEditLoc(Context.getSourceManager(), StartLoc)) {
445 RenameInfo Info = {
446 StartLoc,
447 EndLoc,
448 TemplateSpecType->getTemplateName().getAsTemplateDecl(),
450 GetNestedNameForType(TargetLoc),
451 false};
452 RenameInfos.push_back(Info);
453 }
454 }
455 }
456 return true;
457 }
458
459
460 const std::vector &getRenameInfos() const { return RenameInfos; }
461
462
463 const std::vector<const UsingDecl *> &getUsingDecls() const {
464 return UsingDecls;
465 }
466
467private:
468
469
470 const NamedDecl *getSupportedDeclFromTypeLoc(TypeLoc Loc) {
472 return TT->getDecl();
473 if (const auto *RD = Loc.getType()->getAsCXXRecordDecl())
474 return RD;
475 if (const auto *ED =
476 llvm::dyn_cast_or_null(Loc.getType()->getAsTagDecl()))
477 return ED;
478 return nullptr;
479 }
480
481
482 template
483 const Decl *getClosestAncestorDecl(const ASTNodeType &Node) {
484 auto Parents = Context.getParents(Node);
485
486 if (Parents.size() != 1)
487 return nullptr;
488 if (ASTNodeKind::getFromNodeKind().isBaseOf(Parents[0].getNodeKind()))
489 return Parents[0].template get();
490 return getClosestAncestorDecl(Parents[0]);
491 }
492
493
494
495 const TypeLoc *getParentTypeLoc(TypeLoc Loc) const {
496 auto Parents = Context.getParents(Loc);
497
498 if (Parents.size() != 1)
499 return nullptr;
500 return Parents[0].get();
501 }
502
503
504 bool isInUSRSet(const Decl *Decl) const {
506 if (USR.empty())
507 return false;
508 return llvm::is_contained(USRSet, USR);
509 }
510
511 const std::setstd::string USRSet;
512 ASTContext &Context;
513 std::vector RenameInfos;
514
515
516 std::vector<const UsingDecl *> UsingDecls;
517};
518
519}
520
522 StringRef PrevName, Decl *Decl) {
523 USRLocFindingASTVisitor Visitor(USRs, PrevName, Decl->getASTContext());
524 Visitor.TraverseDecl(Decl);
525 return Visitor.takeOccurrences();
526}
527
528std::vectortooling::AtomicChange
533
536
537 std::vectortooling::AtomicChange AtomicChanges;
539 llvm::StringRef Text) {
541 llvm::Error Err = ReplaceChange.replace(
543 if (Err) {
544 llvm::errs() << "Failed to add replacement to AtomicChange: "
545 << llvm::toString(std::move(Err)) << "\n";
546 return;
547 }
548 AtomicChanges.push_back(std::move(ReplaceChange));
549 };
550
551 for (const auto &RenameInfo : Finder.getRenameInfos()) {
552 std::string ReplacedName = NewName.str();
553 if (RenameInfo.IgnorePrefixQualifers) {
554
555 size_t LastColonPos = NewName.find_last_of(':');
556 if (LastColonPos != std:🧵:npos)
557 ReplacedName = std::string(NewName.substr(LastColonPos + 1));
558 } else {
559 if (RenameInfo.FromDecl && RenameInfo.Context) {
560 if (!llvm::isaclang::TranslationUnitDecl(
561 RenameInfo.Context->getDeclContext())) {
563 RenameInfo.Specifier, RenameInfo.Begin,
564 RenameInfo.Context->getDeclContext(), RenameInfo.FromDecl,
565 NewName.starts_with("::") ? NewName.str()
566 : ("::" + NewName).str());
567 } else {
568
569
570
571
572
573
574
577 SourceRange(RenameInfo.Begin, RenameInfo.End)),
579
580
581 if (ActualName.starts_with("::") && !NewName.starts_with("::")) {
582 ReplacedName = "::" + NewName.str();
583 }
584 }
585 }
586
587 if (NewName.starts_with("::") && NewName.substr(2) == ReplacedName)
588 ReplacedName = NewName.str();
589 }
590 Replace(RenameInfo.Begin, RenameInfo.End, ReplacedName);
591 }
592
593
594
595 for (const auto *Using : Finder.getUsingDecls())
596 Replace(Using->getBeginLoc(), Using->getEndLoc(), "using " + NewName.str());
597
599}
600
601}
602}
Defines the clang::ASTContext interface.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
A wrapper class around RecursiveASTVisitor that visits each occurrences of a named symbol.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Methods for determining the USR of a symbol at a location in source code.
const NamedDecl * FromDecl
bool IgnorePrefixQualifers
const NestedNameSpecifier * Specifier
Provides functionality for finding all instances of a USR in a given AST.
SourceManager & getSourceManager()
const LangOptions & getLangOpts() const
static CharSourceRange getTokenRange(SourceRange R)
Decl - This represents one declaration (or definition), e.g.
ASTContext & getASTContext() const LLVM_READONLY
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
A SourceLocation and its associated SourceManager.
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Encodes a location in the source.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
The top declaration context.
ASTContext & getASTContext() const
@ Decl
The l-value was an access to a declared entity or something equivalently strong, like the address of ...
The JSON file list parser is used to communicate input to InstallAPI.
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.