Coverage Report

Created: 2022-01-25 06:29

/Users/buildslave/jenkins/workspace/coverage/llvm-project/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
0
  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.41k
      Editor(&Editor) {}
47
48
bool Commit::insert(SourceLocation loc, StringRef text,
49
2.56k
                    bool afterToken, bool beforePreviousInsertions) {
50
2.56k
  if (text.empty())
51
0
    return true;
52
53
2.56k
  FileOffset Offs;
54
2.56k
  if ((!afterToken && 
!canInsert(loc, Offs)2.45k
) ||
55
2.56k
      
( 2.56k
afterToken2.56k
&&
!canInsertAfterToken(loc, Offs, loc)114
)) {
56
1
    IsCommitable = false;
57
1
    return false;
58
1
  }
59
60
2.56k
  addInsert(loc, Offs, text, beforePreviousInsertions);
61
2.56k
  return true;
62
2.56k
}
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
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
81
132
  if (PPRec &&
82
132
      
PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())52
) {
83
1
    IsCommitable = false;
84
1
    return false;
85
1
  }
86
87
131
  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
88
131
  return true;
89
132
}
90
91
1.02k
bool Commit::remove(CharSourceRange range) {
92
1.02k
  FileOffset Offs;
93
1.02k
  unsigned Len;
94
1.02k
  if (!canRemoveRange(range, Offs, Len)) {
95
2
    IsCommitable = false;
96
2
    return false;
97
2
  }
98
99
1.02k
  addRemove(range.getBegin(), Offs, Len);
100
1.02k
  return true;
101
1.02k
}
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
113
59
  return commitableBefore && 
commitableAfter58
;
114
59
}
115
116
2.38k
bool Commit::replace(CharSourceRange range, StringRef text) {
117
2.38k
  if (text.empty())
118
80
    return remove(range);
119
120
2.30k
  FileOffset Offs;
121
2.30k
  unsigned Len;
122
2.30k
  if (!canInsert(range.getBegin(), Offs) || 
!canRemoveRange(range, Offs, Len)2.29k
) {
123
7
    IsCommitable = false;
124
7
    return false;
125
7
  }
126
127
2.29k
  addRemove(range.getBegin(), Offs, Len);
128
2.29k
  addInsert(range.getBegin(), Offs, text, false);
129
2.29k
  return true;
130
2.30k
}
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
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
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
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
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
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.86k
                       bool beforePreviousInsertions) {
184
4.86k
  if (text.empty())
185
0
    return;
186
187
4.86k
  Edit data;
188
4.86k
  data.Kind = Act_Insert;
189
4.86k
  data.OrigLoc = OrigLoc;
190
4.86k
  data.Offset = Offs;
191
4.86k
  data.Text = text.copy(StrAlloc);
192
4.86k
  data.BeforePrev = beforePreviousInsertions;
193
4.86k
  CachedEdits.push_back(data);
194
4.86k
}
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
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.86k
                       FileOffset Offs, unsigned Len) {
214
3.86k
  if (Len == 0)
215
30
    return;
216
217
3.83k
  Edit data;
218
3.83k
  data.Kind = Act_Remove;
219
3.83k
  data.OrigLoc = OrigLoc;
220
3.83k
  data.Offset = Offs;
221
3.83k
  data.Length = Len;
222
3.83k
  CachedEdits.push_back(data);
223
3.83k
}
224
225
4.86k
bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
226
4.86k
  if (loc.isInvalid())
227
0
    return false;
228
229
4.86k
  if (loc.isMacroID())
230
110
    isAtStartOfMacroExpansion(loc, &loc);
231
232
4.86k
  const SourceManager &SM = SourceMgr;
233
4.86k
  loc = SM.getTopMacroCallerLoc(loc);
234
235
4.86k
  if (loc.isMacroID())
236
8
    if (!isAtStartOfMacroExpansion(loc, &loc))
237
8
      return false;
238
239
4.86k
  if (SM.isInSystemHeader(loc))
240
0
    return false;
241
242
4.86k
  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
243
4.86k
  if (locInfo.first.isInvalid())
244
0
    return false;
245
4.86k
  offs = FileOffset(locInfo.first, locInfo.second);
246
4.86k
  return canInsertInOffset(loc, offs);
247
4.86k
}
248
249
bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
250
132
                                 SourceLocation &AfterLoc) {
251
132
  if (loc.isInvalid())
252
253
0
    return false;
254
255
132
  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
256
132
  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
257
132
  AfterLoc = loc.getLocWithOffset(tokLen);
258
259
132
  if (loc.isMacroID())
260
7
    isAtEndOfMacroExpansion(loc, &loc);
261
262
132
  const SourceManager &SM = SourceMgr;
263
132
  loc = SM.getTopMacroCallerLoc(loc);
264
265
132
  if (loc.isMacroID())
266
0
    if (!isAtEndOfMacroExpansion(loc, &loc))
267
0
      return false;
268
269
132
  if (SM.isInSystemHeader(loc))
270
0
    return false;
271
272
132
  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
273
132
  if (loc.isInvalid())
274
0
    return false;
275
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.99k
bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
284
4.99k
  for (const auto &act : CachedEdits)
285
2.67k
    if (act.Kind == Act_Remove) {
286
1.17k
      if (act.Offset.getFID() == Offs.getFID() &&
287
1.17k
          Offs > act.Offset && 
Offs < act.Offset.getWithOffset(act.Length)747
)
288
0
        return false; // position has been removed.
289
1.17k
    }
290
291
4.99k
  if (!Editor)
292
3.18k
    return true;
293
1.80k
  return Editor->canInsertInOffset(OrigLoc, Offs);
294
4.99k
}
295
296
bool Commit::canRemoveRange(CharSourceRange range,
297
4.01k
                            FileOffset &Offs, unsigned &Len) {
298
4.01k
  const SourceManager &SM = SourceMgr;
299
4.01k
  range = Lexer::makeFileCharRange(range, SM, LangOpts);
300
4.01k
  if (range.isInvalid())
301
7
    return false;
302
303
4.00k
  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
304
0
    return false;
305
4.00k
  if (SM.isInSystemHeader(range.getBegin()) ||
306
4.00k
      SM.isInSystemHeader(range.getEnd()))
307
0
    return false;
308
309
4.00k
  if (PPRec && 
PPRec->rangeIntersectsConditionalDirective(range.getAsRange())1.21k
)
310
5
    return false;
311
312
4.00k
  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
313
4.00k
  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
314
4.00k
  if (beginInfo.first != endInfo.first ||
315
4.00k
      beginInfo.second > endInfo.second)
316
0
    return false;
317
318
4.00k
  Offs = FileOffset(beginInfo.first, beginInfo.second);
319
4.00k
  Len = endInfo.second - beginInfo.second;
320
4.00k
  return true;
321
4.00k
}
322
323
bool Commit::canReplaceText(SourceLocation loc, StringRef text,
324
0
                            FileOffset &Offs, unsigned &Len) {
325
0
  assert(!text.empty());
326
327
0
  if (!canInsert(loc, Offs))
328
0
    return false;
329
330
  // 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
336
0
  Len = text.size();
337
0
  return file.substr(Offs.getOffset()).startswith(text);
338
0
}
339
340
bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
341
118
                                       SourceLocation *MacroBegin) const {
342
118
  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
343
118
}
344
345
bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
346
7
                                     SourceLocation *MacroEnd) const {
347
7
  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
348
7
}