clang: lib/Edit/EditedSource.cpp Source File (original) (raw)

1

2

3

4

5

6

7

8

18#include "llvm/ADT/STLExtras.h"

19#include "llvm/ADT/SmallString.h"

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

21#include "llvm/ADT/Twine.h"

22#include

23#include

24#include

25#include

26

27using namespace clang;

28using namespace edit;

29

31 replace(range, StringRef());

32}

33

36 MacroArgUse &ArgUse) {

42 ExpansionLoc = ImmediateExpansionLoc;

44 ExpansionLoc =

48 Buf, SourceMgr, LangOpts);

50 if (!ArgName.empty())

51 ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,

53}

54

55void EditedSource::startingCommit() {}

56

57void EditedSource::finishedCommit() {

58 for (auto &ExpArg : CurrCommitMacroArgExps) {

60 MacroArgUse ArgUse;

61 std::tie(ExpLoc, ArgUse) = ExpArg;

62 auto &ArgUses = ExpansionToArgMap[ExpLoc];

63 if (!llvm::is_contained(ArgUses, ArgUse))

64 ArgUses.push_back(ArgUse);

65 }

66 CurrCommitMacroArgExps.clear();

67}

68

72}

73

75 FileEditsTy::iterator FA = getActionForOffset(Offs);

76 if (FA != FileEdits.end()) {

77 if (FA->first != Offs)

78 return false;

79 }

80

83 MacroArgUse ArgUse;

84 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);

85 auto I = ExpansionToArgMap.find(ExpLoc);

86 if (I != ExpansionToArgMap.end() &&

87 llvm::any_of(I->second, [&](const MacroArgUse &U) {

88 return ArgUse.Identifier == U.Identifier &&

89 std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=

90 std::tie(U.ImmediateExpansionLoc, U.UseLoc);

91 })) {

92

93

94

95

96

97

98

99

100

101

102

103

104

105 return false;

106 }

107 }

108 return true;

109}

110

111bool EditedSource::commitInsert(SourceLocation OrigLoc,

113 bool beforePreviousInsertions) {

115 return false;

116 if (text.empty())

117 return true;

118

120 MacroArgUse ArgUse;

122 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);

123 if (ArgUse.Identifier)

124 CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);

125 }

126

127 FileEdit &FA = FileEdits[Offs];

128 if (FA.Text.empty()) {

130 return true;

131 }

132

133 if (beforePreviousInsertions)

134 FA.Text = copyString(Twine(text) + FA.Text);

135 else

136 FA.Text = copyString(Twine(FA.Text) + text);

137

138 return true;

139}

140

141bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,

143 FileOffset InsertFromRangeOffs, unsigned Len,

144 bool beforePreviousInsertions) {

145 if (Len == 0)

146 return true;

147

149 FileOffset BeginOffs = InsertFromRangeOffs;

151 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);

152 if (I != FileEdits.begin())

153 --I;

154

155 for (; I != FileEdits.end(); ++I) {

156 FileEdit &FA = I->second;

159

160 if (BeginOffs == B)

161 break;

162

163 if (BeginOffs < E) {

164 if (BeginOffs > B) {

165 BeginOffs = E;

166 ++I;

167 }

168 break;

169 }

170 }

171

172 for (; I != FileEdits.end() && EndOffs > I->first; ++I) {

173 FileEdit &FA = I->second;

176

177 if (BeginOffs < B) {

179 StringRef text = getSourceText(BeginOffs, B, Invalid);

181 return false;

182 StrVec += text;

183 }

184 StrVec += FA.Text;

185 BeginOffs = E;

186 }

187

188 if (BeginOffs < EndOffs) {

190 StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);

192 return false;

193 StrVec += text;

194 }

195

196 return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);

197}

198

199void EditedSource::commitRemove(SourceLocation OrigLoc,

200 FileOffset BeginOffs, unsigned Len) {

201 if (Len == 0)

202 return;

203

205 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);

206 if (I != FileEdits.begin())

207 --I;

208

209 for (; I != FileEdits.end(); ++I) {

210 FileEdit &FA = I->second;

213

214 if (BeginOffs < E)

215 break;

216 }

217

219 FileEdit *TopFA = nullptr;

220

221 if (I == FileEdits.end()) {

222 FileEditsTy::iterator

223 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));

224 NewI->second.RemoveLen = Len;

225 return;

226 }

227

228 FileEdit &FA = I->second;

231 if (BeginOffs < B) {

232 FileEditsTy::iterator

233 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));

234 TopBegin = BeginOffs;

235 TopEnd = EndOffs;

236 TopFA = &NewI->second;

237 TopFA->RemoveLen = Len;

238 } else {

239 TopBegin = B;

240 TopEnd = E;

241 TopFA = &I->second;

242 if (TopEnd >= EndOffs)

243 return;

245 TopEnd = EndOffs;

246 TopFA->RemoveLen += diff;

247 if (B == BeginOffs)

248 TopFA->Text = StringRef();

249 ++I;

250 }

251

252 while (I != FileEdits.end()) {

253 FileEdit &FA = I->second;

256

257 if (B >= TopEnd)

258 break;

259

260 if (E <= TopEnd) {

261 FileEdits.erase(I++);

262 continue;

263 }

264

265 if (B < TopEnd) {

266 unsigned diff = E.getOffset() - TopEnd.getOffset();

267 TopEnd = E;

268 TopFA->RemoveLen += diff;

269 FileEdits.erase(I);

270 }

271

272 break;

273 }

274}

275

277 if (commit.isCommitable())

278 return false;

279

280 struct CommitRAII {

282

283 CommitRAII(EditedSource &Editor) : Editor(Editor) {

284 Editor.startingCommit();

285 }

286

287 ~CommitRAII() {

288 Editor.finishedCommit();

289 }

290 } CommitRAII(*this);

291

293 I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {

295 switch (edit.Kind) {

297 commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);

298 break;

300 commitInsertFromRange(edit.OrigLoc, edit.Offset,

301 edit.InsertFromRangeOffs, edit.Length,

302 edit.BeforePrev);

303 break;

305 commitRemove(edit.OrigLoc, edit.Offset, edit.Length);

306 break;

307 }

308 }

309

310 return true;

311}

312

313

315

316

319}

320

321

322

326 return false;

328 return true;

329 if (canBeJoined(beforeWSpace, right, LangOpts))

330 return false;

331 return true;

332}

333

334

335

336

339 unsigned &len, StringRef &text) {

340 assert(len && text.empty());

342 if (BeginTokLoc != Loc)

343 return;

344

346 StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);

348 return;

349

350 unsigned begin = offs.getOffset();

351 unsigned end = begin + len;

352

353

354 if (end == buffer.size())

355 return;

356

357 assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");

358

359

360

361 if (begin == 0) {

362 if (buffer[end] == ' ')

363 ++len;

364 return;

365 }

366

367 if (buffer[end] == ' ') {

368 assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&

369 "buffer not zero-terminated!");

371 buffer[end-1],

372 buffer.data()[end + 1],

373 LangOpts))

374 ++len;

375 return;

376 }

377

378 if (canBeJoined(buffer[begin-1], buffer[end], LangOpts))

379 text = " ";

380}

381

383 StringRef text, FileOffset offs, unsigned len,

385 bool shouldAdjustRemovals) {

390

391 if (text.empty() && shouldAdjustRemovals)

393

396

397 if (text.empty()) {

398 assert(len);

399 receiver.remove(range);

400 return;

401 }

402

403 if (len)

404 receiver.replace(range, text);

405 else

407}

408

410 bool shouldAdjustRemovals) {

413 unsigned CurLen;

414

415 if (FileEdits.empty())

416 return;

417

418 FileEditsTy::iterator I = FileEdits.begin();

419 CurOffs = I->first;

420 StrVec = I->second.Text;

421 CurLen = I->second.RemoveLen;

423 ++I;

424

425 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {

427 FileEdit act = I->second;

428 assert(offs >= CurEnd);

429

430 if (offs == CurEnd) {

431 StrVec += act.Text;

432 CurLen += act.RemoveLen;

434 continue;

435 }

436

437 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,

438 shouldAdjustRemovals);

439 CurOffs = offs;

440 StrVec = act.Text;

441 CurLen = act.RemoveLen;

443 }

444

445 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,

446 shouldAdjustRemovals);

447}

448

450 FileEdits.clear();

451 StrAlloc.Reset();

452}

453

454StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,

456 assert(BeginOffs.getFID() == EndOffs.getFID());

457 assert(BeginOffs <= EndOffs);

464 SourceMgr, LangOpts, &Invalid);

465}

466

467EditedSource::FileEditsTy::iterator

468EditedSource::getActionForOffset(FileOffset Offs) {

469 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);

470 if (I == FileEdits.begin())

471 return FileEdits.end();

472 --I;

473 FileEdit &FA = I->second;

476 if (Offs >= B && Offs < E)

477 return I;

478

479 return FileEdits.end();

480}

static bool canBeJoined(char left, char right, const LangOptions &LangOpts)

static void applyRewrite(EditsReceiver &receiver, StringRef text, FileOffset offs, unsigned len, const SourceManager &SM, const LangOptions &LangOpts, bool shouldAdjustRemovals)

static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation Loc, FileOffset offs, unsigned &len, StringRef &text)

Check the range that we are going to remove and: -Remove any trailing whitespace if possible.

static bool canRemoveWhitespace(char left, char beforeWSpace, char right, const LangOptions &LangOpts)

Returns true if it is ok to eliminate the trailing whitespace between the given characters.

Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.

Defines the clang::SourceLocation class and associated facilities.

Defines the SourceManager interface.

Represents a character-granular source range.

static CharSourceRange getCharRange(SourceRange R)

SourceLocation getBegin() const

IdentifierInfo & get(StringRef Name)

Return the identifier token info for the specified named identifier.

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

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 unsigned getSpelling(const Token &Tok, const char *&Buffer, const SourceManager &SourceMgr, const LangOptions &LangOpts, bool *Invalid=nullptr)

getSpelling - This method is used to get the spelling of a token into a preallocated buffer,...

static bool isAsciiIdentifierContinueChar(char c, const LangOptions &LangOpts)

Returns true if the given character could appear in an identifier.

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

Given a location any where in a source buffer, find the location that corresponds to the beginning of...

Encodes a location in the source.

SourceLocation getLocWithOffset(IntTy Offset) const

Return a source location with the specified offset from this SourceLocation.

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

bool isMacroBodyExpansion(SourceLocation Loc) const

Tests whether the given source location represents the expansion of a macro body.

bool isMacroArgExpansion(SourceLocation Loc, SourceLocation *StartLoc=nullptr) const

Tests whether the given source location represents a macro argument's expansion into the function-lik...

SourceLocation getSpellingLoc(SourceLocation Loc) const

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

CharSourceRange getImmediateExpansionRange(SourceLocation Loc) const

Return the start/end of the expansion information for an expansion location.

SourceLocation getLocForStartOfFile(FileID FID) const

Return the source location corresponding to the first byte of the specified file.

SmallVectorImpl< Edit >::const_iterator edit_iterator

StringRef copyString(StringRef str)

bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)

void applyRewrites(EditsReceiver &receiver, bool adjustRemovals=true)

bool commit(const Commit &commit)

virtual void insert(SourceLocation loc, StringRef text)=0

virtual void remove(CharSourceRange range)

By default it calls replace with an empty string.

virtual void replace(CharSourceRange range, StringRef text)=0

FileOffset getWithOffset(unsigned offset) const

unsigned getOffset() const

EditGenerator edit(ASTEdit E)

Generates a single (specified) edit.

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

LLVM_READONLY bool isWhitespace(unsigned char c)

Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...