clang: lib/Tooling/Core/Replacement.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

9

10

11

12

23#include "llvm/ADT/IntrusiveRefCntPtr.h"

24#include "llvm/ADT/RewriteBuffer.h"

25#include "llvm/ADT/SmallPtrSet.h"

26#include "llvm/ADT/StringRef.h"

27#include "llvm/Support/Error.h"

28#include "llvm/Support/ErrorHandling.h"

29#include "llvm/Support/MemoryBuffer.h"

30#include "llvm/Support/VirtualFileSystem.h"

31#include "llvm/Support/raw_ostream.h"

32#include

33#include

34#include

35#include

36#include

37#include

38#include

39

40using namespace clang;

41using namespace tooling;

42

44

46

48 StringRef ReplacementText)

49 : FilePath(std::string(FilePath)), ReplacementRange(Offset, Length),

50 ReplacementText(std::string(ReplacementText)) {}

51

53 unsigned Length, StringRef ReplacementText) {

54 setFromSourceLocation(Sources, Start, Length, ReplacementText);

55}

56

59 StringRef ReplacementText,

61 setFromSourceRange(Sources, Range, ReplacementText, LangOpts);

62}

63

66}

67

70 auto Entry = SM.getFileManager().getOptionalFileRef(FilePath);

71 if (!Entry)

72 return false;

73

76 SM.getLocForStartOfFile(ID).

77 getLocWithOffset(ReplacementRange.getOffset());

78

79

80

81 bool RewriteSucceeded = Rewrite.ReplaceText(

82 Start, ReplacementRange.getLength(), ReplacementText);

83 assert(RewriteSucceeded);

84 return RewriteSucceeded;

85}

86

89 llvm::raw_string_ostream Stream(Result);

90 Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"

91 << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";

92 return Stream.str();

93}

94

96namespace tooling {

97

101

104

108}

109

115}

116

117}

118}

119

120void Replacement::setFromSourceLocation(const SourceManager &Sources,

122 StringRef ReplacementText) {

123 const std::pair<FileID, unsigned> DecomposedLocation =

128 this->ReplacementRange = Range(DecomposedLocation.second, Length);

129 this->ReplacementText = std::string(ReplacementText);

130}

131

132

133

134

140 std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);

141 std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);

142 if (Start.first != End.first) return -1;

143 if (Range.isTokenRange())

145 return End.second - Start.second;

146}

147

148void Replacement::setFromSourceRange(const SourceManager &Sources,

150 StringRef ReplacementText,

154 ReplacementText);

155}

156

158Replacements::getReplacementInChangedCode(const Replacement &R) const {

163}

164

166 switch (Err) {

168 return "Failed to apply a replacement.";

170 return "The new replacement's file path is different from the file path of "

171 "existing replacements";

173 return "The new replacement overlaps with an existing replacement.";

175 return "The new insertion has the same insert location as an existing "

176 "replacement.";

177 }

178 llvm_unreachable("A value of replacement_error has no message.");

179}

180

183 if (NewReplacement)

184 Message += "\nNew replacement: " + NewReplacement->toString();

185 if (ExistingReplacement)

186 Message += "\nExisting replacement: " + ExistingReplacement->toString();

187 return Message;

188}

189

191

192Replacements Replacements::getCanonicalReplacements() const {

193 std::vector NewReplaces;

194

195 for (const auto &R : Replaces) {

196 if (NewReplaces.empty()) {

197 NewReplaces.push_back(R);

198 continue;

199 }

200 auto &Prev = NewReplaces.back();

201 unsigned PrevEnd = Prev.getOffset() + Prev.getLength();

202 if (PrevEnd < R.getOffset()) {

203 NewReplaces.push_back(R);

204 } else {

205 assert(PrevEnd == R.getOffset() &&

206 "Existing replacements must not overlap.");

208 R.getFilePath(), Prev.getOffset(), Prev.getLength() + R.getLength(),

209 (Prev.getReplacementText() + R.getReplacementText()).str());

210 Prev = NewR;

211 }

212 }

213 ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());

214 return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());

215}

216

217

218

219

221Replacements::mergeIfOrderIndependent(const Replacement &R) const {

223

224

225 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));

226

227

229 for (const auto &Replace : Replaces)

230 ReplacesShiftedByRs.Replaces.insert(

231 Rs.getReplacementInChangedCode(Replace));

232

233 auto MergeShiftedRs = merge(RsShiftedByReplaces);

234

235 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);

236

237

238

239 if (MergeShiftedRs.getCanonicalReplacements() ==

240 MergeShiftedReplaces.getCanonicalReplacements())

241 return MergeShiftedRs;

243 R, *Replaces.begin());

244}

245

247

248 if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath())

249 return llvm::make_error(

251

252

253 if (R.getOffset() == std::numeric_limits::max()) {

254 Replaces.insert(R);

255 return llvm::Error::success();

256 }

257

258

259

260

261

262

264

265

266

267

268 auto I = Replaces.lower_bound(AtEnd);

269

270 if (I != Replaces.end() && R.getOffset() == I->getOffset()) {

272

273 if (I->getLength() == 0) {

274

275

278 return llvm::make_error(

280

284 Replaces.erase(I);

285 Replaces.insert(std::move(NewR));

286 return llvm::Error::success();

287 }

288

289

290

291

292

293

294

295 Replaces.insert(R);

296 return llvm::Error::success();

297 }

298

299

300

301 if (I == Replaces.begin()) {

302 Replaces.insert(R);

303 return llvm::Error::success();

304 }

305 --I;

309 };

310

311

312 if (!Overlap(R, *I)) {

313

314

315

316

317

318

319

320 Replaces.insert(R);

321 } else {

322

323

324

325 auto MergeBegin = I;

326 auto MergeEnd = std::next(I);

327 while (I != Replaces.begin()) {

328 --I;

329

330 if (!Overlap(R, *I))

331 break;

332 MergeBegin = I;

333 }

334 Replacements OverlapReplaces(MergeBegin, MergeEnd);

336 OverlapReplaces.mergeIfOrderIndependent(R);

337 if (!Merged)

338 return Merged.takeError();

339 Replaces.erase(MergeBegin, MergeEnd);

340 Replaces.insert(Merged->begin(), Merged->end());

341 }

342 return llvm::Error::success();

343}

344

345namespace {

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367class MergedReplacement {

368public:

369 MergedReplacement(const Replacement &R, bool MergeSecond, int D)

370 : MergeSecond(MergeSecond), Delta(D), FilePath(R.getFilePath()),

371 Offset(R.getOffset() + (MergeSecond ? 0 : Delta)),

372 Length(R.getLength()), Text(std::string(R.getReplacementText())) {

373 Delta += MergeSecond ? 0 : Text.size() - Length;

374 DeltaFirst = MergeSecond ? Text.size() - Length : 0;

375 }

376

377

378

379

381 if (MergeSecond) {

383 unsigned End = Offset + Text.size();

384 if (REnd > End) {

385 Length += REnd - End;

386 MergeSecond = false;

387 }

388 StringRef TextRef = Text;

389 StringRef Head = TextRef.substr(0, R.getOffset() + Delta - Offset);

390 StringRef Tail = TextRef.substr(REnd - Offset);

393 } else {

394 unsigned End = Offset + Length;

396 StringRef Tail = RText.substr(End - R.getOffset());

398 if (R.getOffset() + RText.size() > End) {

400 MergeSecond = true;

401 } else {

402 Length += R.getLength() - RText.size();

403 }

404 DeltaFirst += RText.size() - R.getLength();

405 }

406 }

407

408

409

410 bool endsBefore(const Replacement &R) const {

411 if (MergeSecond)

412 return Offset + Text.size() < R.getOffset() + Delta;

413 return Offset + Length < R.getOffset();

414 }

415

416

417 bool mergeSecond() const { return MergeSecond; }

418

419 int deltaFirst() const { return DeltaFirst; }

420 Replacement asReplacement() const { return {FilePath, Offset, Length, Text}; }

421

422private:

423 bool MergeSecond;

424

425

426

427 int Delta;

428

429

430

431

432 int DeltaFirst;

433

434

435

436 const StringRef FilePath;

437 const unsigned Offset;

438 unsigned Length;

439 std::string Text;

440};

441

442}

443

445 if (empty() || ReplacesToMerge.empty())

446 return empty() ? ReplacesToMerge : *this;

447

448 auto &First = Replaces;

449 auto &Second = ReplacesToMerge.Replaces;

450

451

452 int Delta = 0;

453 ReplacementsImpl Result;

454

455

456

457

458

459 for (auto FirstI = First.begin(), SecondI = Second.begin();

460 FirstI != First.end() || SecondI != Second.end();) {

461 bool NextIsFirst = SecondI == Second.end() ||

462 (FirstI != First.end() &&

463 FirstI->getOffset() < SecondI->getOffset() + Delta);

464 MergedReplacement Merged(NextIsFirst ? *FirstI : *SecondI, NextIsFirst,

465 Delta);

466 ++(NextIsFirst ? FirstI : SecondI);

467

468 while ((Merged.mergeSecond() && SecondI != Second.end()) ||

469 (!Merged.mergeSecond() && FirstI != First.end())) {

470 auto &I = Merged.mergeSecond() ? SecondI : FirstI;

471 if (Merged.endsBefore(*I))

472 break;

473 Merged.merge(*I);

474 ++I;

475 }

476 Delta -= Merged.deltaFirst();

477 Result.insert(Merged.asReplacement());

478 }

480}

481

482

483

484

486 llvm::sort(Ranges, [](const Range &LHS, const Range &RHS) {

490 });

491 std::vector Result;

492 for (const auto &R : Ranges) {

493 if (Result.empty() ||

496 } else {

497 unsigned NewEnd =

498 std::max(Result.back().getOffset() + Result.back().getLength(),

501 Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());

502 }

503 }

505}

506

508namespace tooling {

509

510std::vector

512 const std::vector &Ranges) {

513

514

515

516

517

519 if (Replaces.empty())

520 return MergedRanges;

522 for (const auto &R : MergedRanges) {

523 llvm::cantFail(

526 std::string(R.getLength(), ' '))),

527 "Replacements must not conflict since ranges have been merged.");

528 }

530}

531

532}

533}

534

536 std::vector ChangedRanges;

537 int Shift = 0;

538 for (const auto &R : Replaces) {

539 unsigned Offset = R.getOffset() + Shift;

542 ChangedRanges.push_back(Range(Offset, Length));

543 }

545}

546

548 unsigned Offset = 0;

549 for (const auto &R : Replaces) {

552 continue;

553 }

558 Position--;

559 }

560 break;

561 }

562 return Position + Offset;

563}

564

566namespace tooling {

567

569 bool Result = true;

570 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {

571 if (I->isApplicable()) {

572 Result = I->apply(Rewrite) && Result;

573 } else {

574 Result = false;

575 }

576 }

577 return Result;

578}

579

582 if (Replaces.empty())

583 return Code.str();

584

586 new llvm::vfs::InMemoryFileSystem);

593 InMemoryFileSystem->addFile(

594 "", 0, llvm::MemoryBuffer::getMemBuffer(Code, ""));

598 for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {

599 Replacement Replace("", I->getOffset(), I->getLength(),

600 I->getReplacementText());

602 return llvm::make_error(

603 replacement_error::fail_to_apply, Replace);

604 }

605 std::string Result;

606 llvm::raw_string_ostream OS(Result);

607 Rewrite.getEditBuffer(ID).write(OS);

608 return Result;

609}

610

613 const std::map<std::string, Replacements> &FileToReplaces) {

614 std::map<std::string, Replacements> Result;

616 for (const auto &Entry : FileToReplaces) {

618 if (!FE)

619 llvm::errs() << "File path " << Entry.first << " is invalid.\n";

620 else if (ProcessedFileEntries.insert(*FE).second)

621 Result[Entry.first] = std::move(Entry.second);

622 }

623 return Result;

624}

625

626}

627}

Defines the Diagnostic-related interfaces.

Defines the Diagnostic IDs-related interfaces.

Defines the clang::FileManager interface and associated types.

Defines the clang::FileSystemOptions interface.

static int getRangeSize(const SourceManager &Sources, const CharSourceRange &Range, const LangOptions &LangOpts)

static const char *const InvalidLocation

static std::string getReplacementErrString(replacement_error Err)

static std::vector< Range > combineAndSortRanges(std::vector< Range > Ranges)

Defines the clang::SourceLocation class and associated facilities.

Defines the SourceManager interface.

Represents a character-granular source range.

Used for handling and querying diagnostic IDs.

Options for controlling the compiler diagnostics engine.

Concrete class used by the front-end to report problems and issues.

StringRef getName() const

The name of this FileEntry.

An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...

Implements support for file system lookup, file system caching, and directory search management.

OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=true)

Get a FileEntryRef if it exists, without doing anything on error.

Keeps track of options that affect how file operations are performed.

Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...

static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)

MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...

Rewriter - This is the main interface to the rewrite buffers.

Encodes a location in the source.

This class handles loading and caching of source files into memory.

OptionalFileEntryRef getFileEntryRefForID(FileID FID) const

Returns the FileEntryRef for the provided FileID.

FileID createFileID(FileEntryRef SourceFile, SourceLocation IncludePos, SrcMgr::CharacteristicKind FileCharacter, int LoadedID=0, SourceLocation::UIntTy LoadedOffset=0)

Create a new FileID that represents the specified file being #included from the specified IncludePosi...

SourceLocation getSpellingLoc(SourceLocation Loc) const

Given a SourceLocation object, return the spelling location referenced by the ID.

std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const

Decompose the specified location into a raw FileID + Offset pair.

The JSON file list parser is used to communicate input to InstallAPI.

@ Rewrite

We are substituting template parameters for (typically) other template parameters in order to rewrite...

bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)

bool operator<(DeclarationName LHS, DeclarationName RHS)

Ordering on two declaration names.

@ Result

The result type of a method or function.