Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/Edit/EditedSource.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- EditedSource.cpp - Collection of source edits ----------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "clang/Edit/EditedSource.h"
10
#include "clang/Basic/CharInfo.h"
11
#include "clang/Basic/LLVM.h"
12
#include "clang/Basic/SourceLocation.h"
13
#include "clang/Basic/SourceManager.h"
14
#include "clang/Edit/Commit.h"
15
#include "clang/Edit/EditsReceiver.h"
16
#include "clang/Edit/FileOffset.h"
17
#include "clang/Lex/Lexer.h"
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 <algorithm>
23
#include <cassert>
24
#include <tuple>
25
#include <utility>
26
27
using namespace clang;
28
using namespace edit;
29
30
935
void EditsReceiver::remove(CharSourceRange range) {
31
935
  replace(range, StringRef());
32
935
}
33
34
void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
35
                                          SourceLocation &ExpansionLoc,
36
147
                                          MacroArgUse &ArgUse) {
37
147
  assert(SourceMgr.isMacroArgExpansion(Loc));
38
147
  SourceLocation DefArgLoc =
39
147
      SourceMgr.getImmediateExpansionRange(Loc).getBegin();
40
147
  SourceLocation ImmediateExpansionLoc =
41
147
      SourceMgr.getImmediateExpansionRange(DefArgLoc).getBegin();
42
147
  ExpansionLoc = ImmediateExpansionLoc;
43
193
  while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
44
46
    ExpansionLoc =
45
46
        SourceMgr.getImmediateExpansionRange(ExpansionLoc).getBegin();
46
147
  SmallString<20> Buf;
47
147
  StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
48
147
                                         Buf, SourceMgr, LangOpts);
49
147
  ArgUse = MacroArgUse{nullptr, SourceLocation(), SourceLocation()};
50
147
  if (!ArgName.empty())
51
147
    ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
52
147
              SourceMgr.getSpellingLoc(DefArgLoc)};
53
147
}
54
55
4.91k
void EditedSource::startingCommit() {}
56
57
4.91k
void EditedSource::finishedCommit() {
58
4.91k
  for (auto &ExpArg : CurrCommitMacroArgExps) {
59
66
    SourceLocation ExpLoc;
60
66
    MacroArgUse ArgUse;
61
66
    std::tie(ExpLoc, ArgUse) = ExpArg;
62
66
    auto &ArgUses = ExpansionToArgMap[ExpLoc.getRawEncoding()];
63
66
    if (llvm::find(ArgUses, ArgUse) == ArgUses.end())
64
36
      ArgUses.push_back(ArgUse);
65
66
  }
66
4.91k
  CurrCommitMacroArgExps.clear();
67
4.91k
}
68
69
126
StringRef EditedSource::copyString(const Twine &twine) {
70
126
  SmallString<128> Data;
71
126
  return copyString(twine.toStringRef(Data));
72
126
}
73
74
6.25k
bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
75
6.25k
  FileEditsTy::iterator FA = getActionForOffset(Offs);
76
6.25k
  if (FA != FileEdits.end()) {
77
2.31k
    if (FA->first != Offs)
78
0
      return false; // position has been removed.
79
6.25k
  }
80
6.25k
81
6.25k
  if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
82
81
    SourceLocation ExpLoc;
83
81
    MacroArgUse ArgUse;
84
81
    deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
85
81
    auto I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
86
81
    if (I != ExpansionToArgMap.end() &&
87
81
        
find_if(I->second, [&](const MacroArgUse &U) 24
{
88
25
          return ArgUse.Identifier == U.Identifier &&
89
25
                 std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
90
21
                     std::tie(U.ImmediateExpansionLoc, U.UseLoc);
91
25
        }) != I->second.end()) {
92
15
      // Trying to write in a macro argument input that has already been
93
15
      // written by a previous commit for another expansion of the same macro
94
15
      // argument name. For example:
95
15
      //
96
15
      // \code
97
15
      //   #define MAC(x) ((x)+(x))
98
15
      //   MAC(a)
99
15
      // \endcode
100
15
      //
101
15
      // A commit modified the macro argument 'a' due to the first '(x)'
102
15
      // expansion inside the macro definition, and a subsequent commit tried
103
15
      // to modify 'a' again for the second '(x)' expansion. The edits of the
104
15
      // second commit will be rejected.
105
15
      return false;
106
15
    }
107
6.23k
  }
108
6.23k
  return true;
109
6.23k
}
110
111
bool EditedSource::commitInsert(SourceLocation OrigLoc,
112
                                FileOffset Offs, StringRef text,
113
4.53k
                                bool beforePreviousInsertions) {
114
4.53k
  if (!canInsertInOffset(OrigLoc, Offs))
115
15
    return false;
116
4.51k
  if (text.empty())
117
0
    return true;
118
4.51k
119
4.51k
  if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
120
66
    MacroArgUse ArgUse;
121
66
    SourceLocation ExpLoc;
122
66
    deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
123
66
    if (ArgUse.Identifier)
124
66
      CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
125
66
  }
126
4.51k
127
4.51k
  FileEdit &FA = FileEdits[Offs];
128
4.51k
  if (FA.Text.empty()) {
129
4.38k
    FA.Text = copyString(text);
130
4.38k
    return true;
131
4.38k
  }
132
126
133
126
  if (beforePreviousInsertions)
134
28
    FA.Text = copyString(Twine(text) + FA.Text);
135
98
  else
136
98
    FA.Text = copyString(Twine(FA.Text) + text);
137
126
138
126
  return true;
139
126
}
140
141
bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
142
                                   FileOffset Offs,
143
                                   FileOffset InsertFromRangeOffs, unsigned Len,
144
129
                                   bool beforePreviousInsertions) {
145
129
  if (Len == 0)
146
0
    return true;
147
129
148
129
  SmallString<128> StrVec;
149
129
  FileOffset BeginOffs = InsertFromRangeOffs;
150
129
  FileOffset EndOffs = BeginOffs.getWithOffset(Len);
151
129
  FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
152
129
  if (I != FileEdits.begin())
153
72
    --I;
154
129
155
172
  for (; I != FileEdits.end(); 
++I43
) {
156
103
    FileEdit &FA = I->second;
157
103
    FileOffset B = I->first;
158
103
    FileOffset E = B.getWithOffset(FA.RemoveLen);
159
103
160
103
    if (BeginOffs == B)
161
29
      break;
162
74
163
74
    if (BeginOffs < E) {
164
31
      if (BeginOffs > B) {
165
0
        BeginOffs = E;
166
0
        ++I;
167
0
      }
168
31
      break;
169
31
    }
170
74
  }
171
129
172
166
  for (; I != FileEdits.end() && 
EndOffs > I->first73
;
++I37
) {
173
37
    FileEdit &FA = I->second;
174
37
    FileOffset B = I->first;
175
37
    FileOffset E = B.getWithOffset(FA.RemoveLen);
176
37
177
37
    if (BeginOffs < B) {
178
4
      bool Invalid = false;
179
4
      StringRef text = getSourceText(BeginOffs, B, Invalid);
180
4
      if (Invalid)
181
0
        return false;
182
4
      StrVec += text;
183
4
    }
184
37
    StrVec += FA.Text;
185
37
    BeginOffs = E;
186
37
  }
187
129
188
129
  if (BeginOffs < EndOffs) {
189
124
    bool Invalid = false;
190
124
    StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
191
124
    if (Invalid)
192
0
      return false;
193
124
    StrVec += text;
194
124
  }
195
129
196
129
  return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
197
129
}
198
199
void EditedSource::commitRemove(SourceLocation OrigLoc,
200
3.51k
                                FileOffset BeginOffs, unsigned Len) {
201
3.51k
  if (Len == 0)
202
0
    return;
203
3.51k
204
3.51k
  FileOffset EndOffs = BeginOffs.getWithOffset(Len);
205
3.51k
  FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
206
3.51k
  if (I != FileEdits.begin())
207
1.78k
    --I;
208
3.51k
209
5.28k
  for (; I != FileEdits.end(); 
++I1.77k
) {
210
2.01k
    FileEdit &FA = I->second;
211
2.01k
    FileOffset B = I->first;
212
2.01k
    FileOffset E = B.getWithOffset(FA.RemoveLen);
213
2.01k
214
2.01k
    if (BeginOffs < E)
215
240
      break;
216
2.01k
  }
217
3.51k
218
3.51k
  FileOffset TopBegin, TopEnd;
219
3.51k
  FileEdit *TopFA = nullptr;
220
3.51k
221
3.51k
  if (I == FileEdits.end()) {
222
3.27k
    FileEditsTy::iterator
223
3.27k
      NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
224
3.27k
    NewI->second.RemoveLen = Len;
225
3.27k
    return;
226
3.27k
  }
227
240
228
240
  FileEdit &FA = I->second;
229
240
  FileOffset B = I->first;
230
240
  FileOffset E = B.getWithOffset(FA.RemoveLen);
231
240
  if (BeginOffs < B) {
232
227
    FileEditsTy::iterator
233
227
      NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
234
227
    TopBegin = BeginOffs;
235
227
    TopEnd = EndOffs;
236
227
    TopFA = &NewI->second;
237
227
    TopFA->RemoveLen = Len;
238
227
  } else {
239
13
    TopBegin = B;
240
13
    TopEnd = E;
241
13
    TopFA = &I->second;
242
13
    if (TopEnd >= EndOffs)
243
10
      return;
244
3
    unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
245
3
    TopEnd = EndOffs;
246
3
    TopFA->RemoveLen += diff;
247
3
    if (B == BeginOffs)
248
2
      TopFA->Text = StringRef();
249
3
    ++I;
250
3
  }
251
240
252
272
  
while (230
I != FileEdits.end()) {
253
265
    FileEdit &FA = I->second;
254
265
    FileOffset B = I->first;
255
265
    FileOffset E = B.getWithOffset(FA.RemoveLen);
256
265
257
265
    if (B >= TopEnd)
258
223
      break;
259
42
260
42
    if (E <= TopEnd) {
261
42
      FileEdits.erase(I++);
262
42
      continue;
263
42
    }
264
0
265
0
    if (B < TopEnd) {
266
0
      unsigned diff = E.getOffset() - TopEnd.getOffset();
267
0
      TopEnd = E;
268
0
      TopFA->RemoveLen += diff;
269
0
      FileEdits.erase(I);
270
0
    }
271
0
272
0
    break;
273
0
  }
274
230
}
275
276
5.01k
bool EditedSource::commit(const Commit &commit) {
277
5.01k
  if (!commit.isCommitable())
278
100
    return false;
279
4.91k
280
4.91k
  struct CommitRAII {
281
4.91k
    EditedSource &Editor;
282
4.91k
283
4.91k
    CommitRAII(EditedSource &Editor) : Editor(Editor) {
284
4.91k
      Editor.startingCommit();
285
4.91k
    }
286
4.91k
287
4.91k
    ~CommitRAII() {
288
4.91k
      Editor.finishedCommit();
289
4.91k
    }
290
4.91k
  } CommitRAII(*this);
291
4.91k
292
4.91k
  for (edit::Commit::edit_iterator
293
12.9k
         I = commit.edit_begin(), E = commit.edit_end(); I != E; 
++I8.04k
) {
294
8.04k
    const edit::Commit::Edit &edit = *I;
295
8.04k
    switch (edit.Kind) {
296
8.04k
    case edit::Commit::Act_Insert:
297
4.40k
      commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
298
4.40k
      break;
299
8.04k
    case edit::Commit::Act_InsertFromRange:
300
129
      commitInsertFromRange(edit.OrigLoc, edit.Offset,
301
129
                            edit.InsertFromRangeOffs, edit.Length,
302
129
                            edit.BeforePrev);
303
129
      break;
304
8.04k
    case edit::Commit::Act_Remove:
305
3.51k
      commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
306
3.51k
      break;
307
8.04k
    }
308
8.04k
  }
309
4.91k
310
4.91k
  return true;
311
4.91k
}
312
313
// Returns true if it is ok to make the two given characters adjacent.
314
915
static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
315
915
  // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
316
915
  // making two '<' adjacent.
317
915
  return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
318
915
           
Lexer::isIdentifierBodyChar(right, LangOpts)166
);
319
915
}
320
321
/// Returns true if it is ok to eliminate the trailing whitespace between
322
/// the given characters.
323
static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
324
191
                                const LangOptions &LangOpts) {
325
191
  if (!canBeJoined(left, right, LangOpts))
326
10
    return false;
327
181
  if (isWhitespace(left) || 
isWhitespace(right)32
)
328
149
    return true;
329
32
  if (canBeJoined(beforeWSpace, right, LangOpts))
330
31
    return false; // the whitespace was intentional, keep it.
331
1
  return true;
332
1
}
333
334
/// Check the range that we are going to remove and:
335
/// -Remove any trailing whitespace if possible.
336
/// -Insert a space if removing the range is going to mess up the source tokens.
337
static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
338
                          SourceLocation Loc, FileOffset offs,
339
936
                          unsigned &len, StringRef &text) {
340
936
  assert(len && text.empty());
341
936
  SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
342
936
  if (BeginTokLoc != Loc)
343
53
    return; // the range is not at the beginning of a token, keep the range.
344
883
345
883
  bool Invalid = false;
346
883
  StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
347
883
  if (Invalid)
348
0
    return;
349
883
350
883
  unsigned begin = offs.getOffset();
351
883
  unsigned end = begin + len;
352
883
353
883
  // Do not try to extend the removal if we're at the end of the buffer already.
354
883
  if (end == buffer.size())
355
0
    return;
356
883
357
883
  assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
358
883
359
883
  // FIXME: Remove newline.
360
883
361
883
  if (begin == 0) {
362
0
    if (buffer[end] == ' ')
363
0
      ++len;
364
0
    return;
365
0
  }
366
883
367
883
  if (buffer[end] == ' ') {
368
191
    assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
369
191
           "buffer not zero-terminated!");
370
191
    if (canRemoveWhitespace(/*left=*/buffer[begin-1],
371
191
                            /*beforeWSpace=*/buffer[end-1],
372
191
                            /*right=*/buffer.data()[end + 1], // zero-terminated
373
191
                            LangOpts))
374
150
      ++len;
375
191
    return;
376
191
  }
377
692
378
692
  if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
379
1
    text = " ";
380
692
}
381
382
static void applyRewrite(EditsReceiver &receiver,
383
                         StringRef text, FileOffset offs, unsigned len,
384
                         const SourceManager &SM, const LangOptions &LangOpts,
385
5.28k
                         bool shouldAdjustRemovals) {
386
5.28k
  assert(offs.getFID().isValid());
387
5.28k
  SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
388
5.28k
  Loc = Loc.getLocWithOffset(offs.getOffset());
389
5.28k
  assert(Loc.isFileID());
390
5.28k
391
5.28k
  if (text.empty() && 
shouldAdjustRemovals943
)
392
936
    adjustRemoval(SM, LangOpts, Loc, offs, len, text);
393
5.28k
394
5.28k
  CharSourceRange range = CharSourceRange::getCharRange(Loc,
395
5.28k
                                                     Loc.getLocWithOffset(len));
396
5.28k
397
5.28k
  if (text.empty()) {
398
942
    assert(len);
399
942
    receiver.remove(range);
400
942
    return;
401
942
  }
402
4.34k
403
4.34k
  if (len)
404
2.47k
    receiver.replace(range, text);
405
1.87k
  else
406
1.87k
    receiver.insert(Loc, text);
407
4.34k
}
408
409
void EditedSource::applyRewrites(EditsReceiver &receiver,
410
2.73k
                                 bool shouldAdjustRemovals) {
411
2.73k
  SmallString<128> StrVec;
412
2.73k
  FileOffset CurOffs, CurEnd;
413
2.73k
  unsigned CurLen;
414
2.73k
415
2.73k
  if (FileEdits.empty())
416
10
    return;
417
2.72k
418
2.72k
  FileEditsTy::iterator I = FileEdits.begin();
419
2.72k
  CurOffs = I->first;
420
2.72k
  StrVec = I->second.Text;
421
2.72k
  CurLen = I->second.RemoveLen;
422
2.72k
  CurEnd = CurOffs.getWithOffset(CurLen);
423
2.72k
  ++I;
424
2.72k
425
5.56k
  for (FileEditsTy::iterator E = FileEdits.end(); I != E; 
++I2.84k
) {
426
2.84k
    FileOffset offs = I->first;
427
2.84k
    FileEdit act = I->second;
428
2.84k
    assert(offs >= CurEnd);
429
2.84k
430
2.84k
    if (offs == CurEnd) {
431
279
      StrVec += act.Text;
432
279
      CurLen += act.RemoveLen;
433
279
      CurEnd.getWithOffset(act.RemoveLen);
434
279
      continue;
435
279
    }
436
2.56k
437
2.56k
    applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
438
2.56k
                 shouldAdjustRemovals);
439
2.56k
    CurOffs = offs;
440
2.56k
    StrVec = act.Text;
441
2.56k
    CurLen = act.RemoveLen;
442
2.56k
    CurEnd = CurOffs.getWithOffset(CurLen);
443
2.56k
  }
444
2.72k
445
2.72k
  applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
446
2.72k
               shouldAdjustRemovals);
447
2.72k
}
448
449
0
void EditedSource::clearRewrites() {
450
0
  FileEdits.clear();
451
0
  StrAlloc.Reset();
452
0
}
453
454
StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
455
128
                                      bool &Invalid) {
456
128
  assert(BeginOffs.getFID() == EndOffs.getFID());
457
128
  assert(BeginOffs <= EndOffs);
458
128
  SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
459
128
  BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
460
128
  assert(BLoc.isFileID());
461
128
  SourceLocation
462
128
    ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
463
128
  return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
464
128
                              SourceMgr, LangOpts, &Invalid);
465
128
}
466
467
EditedSource::FileEditsTy::iterator
468
6.25k
EditedSource::getActionForOffset(FileOffset Offs) {
469
6.25k
  FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
470
6.25k
  if (I == FileEdits.begin())
471
1.16k
    return FileEdits.end();
472
5.09k
  --I;
473
5.09k
  FileEdit &FA = I->second;
474
5.09k
  FileOffset B = I->first;
475
5.09k
  FileOffset E = B.getWithOffset(FA.RemoveLen);
476
5.09k
  if (Offs >= B && Offs < E)
477
2.31k
    return I;
478
2.78k
479
2.78k
  return FileEdits.end();
480
2.78k
}