Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/Edit/Commit.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- Commit.cpp - A unit of 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/Commit.h"
10
#include "clang/Basic/LLVM.h"
11
#include "clang/Basic/SourceLocation.h"
12
#include "clang/Basic/SourceManager.h"
13
#include "clang/Edit/EditedSource.h"
14
#include "clang/Edit/FileOffset.h"
15
#include "clang/Lex/Lexer.h"
16
#include "clang/Lex/PPConditionalDirectiveRecord.h"
17
#include "llvm/ADT/StringRef.h"
18
#include <cassert>
19
#include <utility>
20
21
using namespace clang;
22
using namespace edit;
23
24
24
SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
25
24
  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
26
24
  Loc = Loc.getLocWithOffset(Offset.getOffset());
27
24
  assert(Loc.isFileID());
28
24
  return Loc;
29
24
}
30
31
24
CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
32
24
  SourceLocation Loc = getFileLocation(SM);
33
24
  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
34
24
}
35
36
0
CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
37
0
  SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
38
0
  Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
39
0
  assert(Loc.isFileID());
40
0
  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
41
0
}
42
43
Commit::Commit(EditedSource &Editor)
44
    : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
45
      PPRec(Editor.getPPCondDirectiveRecord()),
46
2.34k
      Editor(&Editor) {}
47
48
bool Commit::insert(SourceLocation loc, StringRef text,
49
2.31k
                    bool afterToken, bool beforePreviousInsertions) {
50
2.31k
  if (text.empty())
51
0
    return true;
52
2.31k
53
2.31k
  FileOffset Offs;
54
2.31k
  if ((!afterToken && 
!canInsert(loc, Offs)2.20k
) ||
55
2.31k
      
( 2.31k
afterToken2.31k
&&
!canInsertAfterToken(loc, Offs, loc)114
)) {
56
1
    IsCommitable = false;
57
1
    return false;
58
1
  }
59
2.31k
60
2.31k
  addInsert(loc, Offs, text, beforePreviousInsertions);
61
2.31k
  return true;
62
2.31k
}
63
64
bool Commit::insertFromRange(SourceLocation loc,
65
                             CharSourceRange range,
66
134
                             bool afterToken, bool beforePreviousInsertions) {
67
134
  FileOffset RangeOffs;
68
134
  unsigned RangeLen;
69
134
  if (!canRemoveRange(range, RangeOffs, RangeLen)) {
70
2
    IsCommitable = false;
71
2
    return false;
72
2
  }
73
132
74
132
  FileOffset Offs;
75
132
  if ((!afterToken && 
!canInsert(loc, Offs)114
) ||
76
132
      ( afterToken && 
!canInsertAfterToken(loc, Offs, loc)18
)) {
77
0
    IsCommitable = false;
78
0
    return false;
79
0
  }
80
132
81
132
  if (PPRec &&
82
132
      
PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())52
) {
83
1
    IsCommitable = false;
84
1
    return false;
85
1
  }
86
131
87
131
  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
88
131
  return true;
89
131
}
90
91
928
bool Commit::remove(CharSourceRange range) {
92
928
  FileOffset Offs;
93
928
  unsigned Len;
94
928
  if (!canRemoveRange(range, Offs, Len)) {
95
2
    IsCommitable = false;
96
2
    return false;
97
2
  }
98
926
99
926
  addRemove(range.getBegin(), Offs, Len);
100
926
  return true;
101
926
}
102
103
bool Commit::insertWrap(StringRef before, CharSourceRange range,
104
59
                        StringRef after) {
105
59
  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
106
59
                                 /*beforePreviousInsertions=*/true);
107
59
  bool commitableAfter;
108
59
  if (range.isTokenRange())
109
56
    commitableAfter = insertAfterToken(range.getEnd(), after);
110
3
  else
111
3
    commitableAfter = insert(range.getEnd(), after);
112
59
113
59
  return commitableBefore && 
commitableAfter58
;
114
59
}
115
116
2.27k
bool Commit::replace(CharSourceRange range, StringRef text) {
117
2.27k
  if (text.empty())
118
80
    return remove(range);
119
2.19k
120
2.19k
  FileOffset Offs;
121
2.19k
  unsigned Len;
122
2.19k
  if (!canInsert(range.getBegin(), Offs) || 
!canRemoveRange(range, Offs, Len)2.10k
) {
123
93
    IsCommitable = false;
124
93
    return false;
125
93
  }
126
2.10k
127
2.10k
  addRemove(range.getBegin(), Offs, Len);
128
2.10k
  addInsert(range.getBegin(), Offs, text, false);
129
2.10k
  return true;
130
2.10k
}
131
132
bool Commit::replaceWithInner(CharSourceRange range,
133
279
                              CharSourceRange replacementRange) {
134
279
  FileOffset OuterBegin;
135
279
  unsigned OuterLen;
136
279
  if (!canRemoveRange(range, OuterBegin, OuterLen)) {
137
6
    IsCommitable = false;
138
6
    return false;
139
6
  }
140
273
141
273
  FileOffset InnerBegin;
142
273
  unsigned InnerLen;
143
273
  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
144
2
    IsCommitable = false;
145
2
    return false;
146
2
  }
147
271
148
271
  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
149
271
  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
150
271
  if (OuterBegin.getFID() != InnerBegin.getFID() ||
151
271
      InnerBegin < OuterBegin ||
152
271
      InnerBegin > OuterEnd ||
153
271
      InnerEnd > OuterEnd) {
154
0
    IsCommitable = false;
155
0
    return false;
156
0
  }
157
271
158
271
  addRemove(range.getBegin(),
159
271
            OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
160
271
  addRemove(replacementRange.getEnd(),
161
271
            InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
162
271
  return true;
163
271
}
164
165
bool Commit::replaceText(SourceLocation loc, StringRef text,
166
0
                         StringRef replacementText) {
167
0
  if (text.empty() || replacementText.empty())
168
0
    return true;
169
0
170
0
  FileOffset Offs;
171
0
  unsigned Len;
172
0
  if (!canReplaceText(loc, replacementText, Offs, Len)) {
173
0
    IsCommitable = false;
174
0
    return false;
175
0
  }
176
0
177
0
  addRemove(loc, Offs, Len);
178
0
  addInsert(loc, Offs, text, false);
179
0
  return true;
180
0
}
181
182
void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
183
4.41k
                       bool beforePreviousInsertions) {
184
4.41k
  if (text.empty())
185
0
    return;
186
4.41k
187
4.41k
  Edit data;
188
4.41k
  data.Kind = Act_Insert;
189
4.41k
  data.OrigLoc = OrigLoc;
190
4.41k
  data.Offset = Offs;
191
4.41k
  data.Text = text.copy(StrAlloc);
192
4.41k
  data.BeforePrev = beforePreviousInsertions;
193
4.41k
  CachedEdits.push_back(data);
194
4.41k
}
195
196
void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
197
                                FileOffset RangeOffs, unsigned RangeLen,
198
131
                                bool beforePreviousInsertions) {
199
131
  if (RangeLen == 0)
200
0
    return;
201
131
202
131
  Edit data;
203
131
  data.Kind = Act_InsertFromRange;
204
131
  data.OrigLoc = OrigLoc;
205
131
  data.Offset = Offs;
206
131
  data.InsertFromRangeOffs = RangeOffs;
207
131
  data.Length = RangeLen;
208
131
  data.BeforePrev = beforePreviousInsertions;
209
131
  CachedEdits.push_back(data);
210
131
}
211
212
void Commit::addRemove(SourceLocation OrigLoc,
213
3.56k
                       FileOffset Offs, unsigned Len) {
214
3.56k
  if (Len == 0)
215
30
    return;
216
3.53k
217
3.53k
  Edit data;
218
3.53k
  data.Kind = Act_Remove;
219
3.53k
  data.OrigLoc = OrigLoc;
220
3.53k
  data.Offset = Offs;
221
3.53k
  data.Length = Len;
222
3.53k
  CachedEdits.push_back(data);
223
3.53k
}
224
225
4.51k
bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
226
4.51k
  if (loc.isInvalid())
227
0
    return false;
228
4.51k
229
4.51k
  if (loc.isMacroID())
230
97
    isAtStartOfMacroExpansion(loc, &loc);
231
4.51k
232
4.51k
  const SourceManager &SM = SourceMgr;
233
4.51k
  loc = SM.getTopMacroCallerLoc(loc);
234
4.51k
235
4.51k
  if (loc.isMacroID())
236
8
    if (!isAtStartOfMacroExpansion(loc, &loc))
237
8
      return false;
238
4.50k
239
4.50k
  if (SM.isInSystemHeader(loc))
240
86
    return false;
241
4.41k
242
4.41k
  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
243
4.41k
  if (locInfo.first.isInvalid())
244
0
    return false;
245
4.41k
  offs = FileOffset(locInfo.first, locInfo.second);
246
4.41k
  return canInsertInOffset(loc, offs);
247
4.41k
}
248
249
bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
250
132
                                 SourceLocation &AfterLoc) {
251
132
  if (loc.isInvalid())
252
0
253
0
    return false;
254
132
255
132
  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
256
132
  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
257
132
  AfterLoc = loc.getLocWithOffset(tokLen);
258
132
259
132
  if (loc.isMacroID())
260
7
    isAtEndOfMacroExpansion(loc, &loc);
261
132
262
132
  const SourceManager &SM = SourceMgr;
263
132
  loc = SM.getTopMacroCallerLoc(loc);
264
132
265
132
  if (loc.isMacroID())
266
0
    if (!isAtEndOfMacroExpansion(loc, &loc))
267
0
      return false;
268
132
269
132
  if (SM.isInSystemHeader(loc))
270
0
    return false;
271
132
272
132
  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
273
132
  if (loc.isInvalid())
274
0
    return false;
275
132
276
132
  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
277
132
  if (locInfo.first.isInvalid())
278
0
    return false;
279
132
  offs = FileOffset(locInfo.first, locInfo.second);
280
132
  return canInsertInOffset(loc, offs);
281
132
}
282
283
4.54k
bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
284
4.54k
  for (const auto &act : CachedEdits)
285
2.57k
    if (act.Kind == Act_Remove) {
286
1.14k
      if (act.Offset.getFID() == Offs.getFID() &&
287
1.14k
          Offs > act.Offset && 
Offs < act.Offset.getWithOffset(act.Length)713
)
288
0
        return false; // position has been removed.
289
1.14k
    }
290
4.54k
291
4.54k
  if (!Editor)
292
2.82k
    return true;
293
1.72k
  return Editor->canInsertInOffset(OrigLoc, Offs);
294
1.72k
}
295
296
bool Commit::canRemoveRange(CharSourceRange range,
297
3.71k
                            FileOffset &Offs, unsigned &Len) {
298
3.71k
  const SourceManager &SM = SourceMgr;
299
3.71k
  range = Lexer::makeFileCharRange(range, SM, LangOpts);
300
3.71k
  if (range.isInvalid())
301
7
    return false;
302
3.70k
303
3.70k
  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
304
0
    return false;
305
3.70k
  if (SM.isInSystemHeader(range.getBegin()) ||
306
3.70k
      SM.isInSystemHeader(range.getEnd()))
307
0
    return false;
308
3.70k
309
3.70k
  if (PPRec && 
PPRec->rangeIntersectsConditionalDirective(range.getAsRange())1.21k
)
310
5
    return false;
311
3.70k
312
3.70k
  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
313
3.70k
  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
314
3.70k
  if (beginInfo.first != endInfo.first ||
315
3.70k
      beginInfo.second > endInfo.second)
316
0
    return false;
317
3.70k
318
3.70k
  Offs = FileOffset(beginInfo.first, beginInfo.second);
319
3.70k
  Len = endInfo.second - beginInfo.second;
320
3.70k
  return true;
321
3.70k
}
322
323
bool Commit::canReplaceText(SourceLocation loc, StringRef text,
324
0
                            FileOffset &Offs, unsigned &Len) {
325
0
  assert(!text.empty());
326
0
327
0
  if (!canInsert(loc, Offs))
328
0
    return false;
329
0
330
0
  // Try to load the file buffer.
331
0
  bool invalidTemp = false;
332
0
  StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
333
0
  if (invalidTemp)
334
0
    return false;
335
0
336
0
  Len = text.size();
337
0
  return file.substr(Offs.getOffset()).startswith(text);
338
0
}
339
340
bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
341
105
                                       SourceLocation *MacroBegin) const {
342
105
  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
343
105
}
344
345
bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
346
7
                                     SourceLocation *MacroEnd) const {
347
7
  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
348
7
}