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.