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;

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

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

97

109

116

117}

118}

119

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

121 SourceLocation Start, unsigned Length,

122 StringRef ReplacementText) {

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

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

129}

130

131

132

133

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

142 if (Range.isTokenRange())

144 return End.second - Start.second;

145}

146

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

148 const CharSourceRange &Range,

149 StringRef ReplacementText,

150 const LangOptions &LangOpts) {

153 ReplacementText);

154}

155

156Replacement

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

158 unsigned NewStart = getShiftedCodePosition(R.getOffset());

159 unsigned NewEnd = getShiftedCodePosition(R.getOffset() + R.getLength());

160 return Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,

162}

163

165 switch (Err) {

166 case replacement_error::fail_to_apply:

167 return "Failed to apply a replacement.";

168 case replacement_error::wrong_file_path:

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

170 "existing replacements";

171 case replacement_error::overlap_conflict:

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

173 case replacement_error::insert_conflict:

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

175 "replacement.";

176 }

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

178}

179

182 if (NewReplacement)

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

184 if (ExistingReplacement)

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

186 return Message;

187}

188

190

191Replacements Replacements::getCanonicalReplacements() const {

192 std::vector NewReplaces;

193

194 for (const auto &R : Replaces) {

195 if (NewReplaces.empty()) {

196 NewReplaces.push_back(R);

197 continue;

198 }

199 auto &Prev = NewReplaces.back();

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

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

202 NewReplaces.push_back(R);

203 } else {

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

205 "Existing replacements must not overlap.");

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

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

209 Prev = NewR;

210 }

211 }

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

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

214}

215

216

217

218

219llvm::Expected

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

221 Replacements Rs(R);

222

223

224 Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));

225

226

227 Replacements ReplacesShiftedByRs;

228 for (const auto &Replace : Replaces)

229 ReplacesShiftedByRs.Replaces.insert(

230 Rs.getReplacementInChangedCode(Replace));

231

232 auto MergeShiftedRs = merge(RsShiftedByReplaces);

233

234 auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);

235

236

237

238 if (MergeShiftedRs.getCanonicalReplacements() ==

239 MergeShiftedReplaces.getCanonicalReplacements())

240 return MergeShiftedRs;

241 return llvm::make_error(replacement_error::overlap_conflict,

242 R, *Replaces.begin());

243}

244

246

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

248 return llvm::make_error(

250

251

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

253 Replaces.insert(R);

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

255 }

256

257

258

259

260

261

263

264

265

266

267 auto I = Replaces.lower_bound(AtEnd);

268

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

271

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

273

274

277 return llvm::make_error(

279

283 Replaces.erase(I);

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

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

286 }

287

288

289

290

291

292

293

294 Replaces.insert(R);

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

296 }

297

298

299

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

301 Replaces.insert(R);

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

303 }

304 --I;

308 };

309

310

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

312

313

314

315

316

317

318

319 Replaces.insert(R);

320 } else {

321

322

323

324 auto MergeBegin = I;

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

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

327 --I;

328

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

330 break;

331 MergeBegin = I;

332 }

333 Replacements OverlapReplaces(MergeBegin, MergeEnd);

335 OverlapReplaces.mergeIfOrderIndependent(R);

336 if (!Merged)

337 return Merged.takeError();

338 Replaces.erase(MergeBegin, MergeEnd);

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

340 }

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

342}

343

344namespace {

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366class MergedReplacement {

367public:

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

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

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

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

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

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

374 }

375

376

377

378

380 if (MergeSecond) {

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

383 if (REnd > End) {

384 Length += REnd - End;

385 MergeSecond = false;

386 }

387 StringRef TextRef = Text;

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

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

392 } else {

393 unsigned End = Offset + Length;

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

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

399 MergeSecond = true;

400 } else {

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

402 }

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

404 }

405 }

406

407

408

409 bool endsBefore(const Replacement &R) const {

410 if (MergeSecond)

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

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

413 }

414

415

416 bool mergeSecond() const { return MergeSecond; }

417

418 int deltaFirst() const { return DeltaFirst; }

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

420

421private:

422 bool MergeSecond;

423

424

425

426 int Delta;

427

428

429

430

431 int DeltaFirst;

432

433

434

435 const StringRef FilePath;

436 const unsigned Offset;

437 unsigned Length;

438 std::string Text;

439};

440

441}

442

444 if (empty() || ReplacesToMerge.empty())

445 return empty() ? ReplacesToMerge : *this;

446

447 auto &First = Replaces;

448 auto &Second = ReplacesToMerge.Replaces;

449

450

451 int Delta = 0;

452 ReplacementsImpl Result;

453

454

455

456

457

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

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

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

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

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

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

464 Delta);

465 ++(NextIsFirst ? FirstI : SecondI);

466

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

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

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

470 if (Merged.endsBefore(*I))

471 break;

472 Merged.merge(*I);

473 ++I;

474 }

475 Delta -= Merged.deltaFirst();

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

477 }

479}

480

481

482

483

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

489 });

490 std::vector Result;

491 for (const auto &R : Ranges) {

492 if (Result.empty() ||

493 Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {

494 Result.push_back(R);

495 } else {

496 unsigned NewEnd =

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

499 Result[Result.size() - 1] =

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

501 }

502 }

503 return Result;

504}

505

506namespace clang {

507namespace tooling {

508

509std::vector

511 const std::vector &Ranges) {

512

513

514

515

516

518 if (Replaces.empty())

519 return MergedRanges;

521 for (const auto &R : MergedRanges) {

522 llvm::cantFail(

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

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

527 }

529}

530

531}

532}

533

535 std::vector ChangedRanges;

536 int Shift = 0;

537 for (const auto &R : Replaces) {

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

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

542 }

544}

545

547 unsigned Offset = 0;

548 for (const auto &R : Replaces) {

551 continue;

552 }

557 Position--;

558 }

559 break;

560 }

561 return Position + Offset;

562}

563

566

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

570 if (I->isApplicable()) {

572 } else {

574 }

575 }

577}

578

581 if (Replaces.empty())

582 return Code.str();

583

584 auto InMemoryFileSystem =

585 llvm::makeIntrusiveRefCntllvm::vfs::InMemoryFileSystem();

591 InMemoryFileSystem->addFile(

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

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

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

598 I->getReplacementText());

600 return llvm::make_error(

602 }

604 llvm::raw_string_ostream OS(Result);

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

607}

608

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

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

614 for (const auto &Entry : FileToReplaces) {

615 auto FE = FileMgr.getOptionalFileRef(Entry.first);

616 if (!FE)

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

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

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

620 }

622}

623

624}

625}

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)

Definition Replacement.cpp:134

static const char *const InvalidLocation

Definition Replacement.cpp:43

static std::string getReplacementErrString(replacement_error Err)

Definition Replacement.cpp:164

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

Definition Replacement.cpp:484

Defines the clang::SourceLocation class and associated facilities.

Defines the SourceManager interface.

Represents a character-granular source range.

static llvm::IntrusiveRefCntPtr< DiagnosticIDs > create()

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.

FileIDAndOffset getDecomposedLoc(SourceLocation Loc) const

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

OptionalFileEntryRef getFileEntryRefForID(FileID FID) const

Returns the FileEntryRef for the provided FileID.

SourceLocation getSpellingLoc(SourceLocation Loc) const

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

RangeSelector merge(RangeSelector First, RangeSelector Second)

Selects the merge of the two ranges, i.e.

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

CustomizableOptional< FileEntryRef > OptionalFileEntryRef

@ Rewrite

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

std::pair< FileID, unsigned > FileIDAndOffset

@ Result

The result type of a method or function.