Coverage Report

Created: 2024-01-17 10:31

/src/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
0
SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
25
0
  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
26
0
  Loc = Loc.getLocWithOffset(Offset.getOffset());
27
0
  assert(Loc.isFileID());
28
0
  return Loc;
29
0
}
30
31
0
CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
32
0
  SourceLocation Loc = getFileLocation(SM);
33
0
  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
34
0
}
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
0
      Editor(&Editor) {}
47
48
bool Commit::insert(SourceLocation loc, StringRef text,
49
0
                    bool afterToken, bool beforePreviousInsertions) {
50
0
  if (text.empty())
51
0
    return true;
52
53
0
  FileOffset Offs;
54
0
  if ((!afterToken && !canInsert(loc, Offs)) ||
55
0
      ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
56
0
    IsCommitable = false;
57
0
    return false;
58
0
  }
59
60
0
  addInsert(loc, Offs, text, beforePreviousInsertions);
61
0
  return true;
62
0
}
63
64
bool Commit::insertFromRange(SourceLocation loc,
65
                             CharSourceRange range,
66
0
                             bool afterToken, bool beforePreviousInsertions) {
67
0
  FileOffset RangeOffs;
68
0
  unsigned RangeLen;
69
0
  if (!canRemoveRange(range, RangeOffs, RangeLen)) {
70
0
    IsCommitable = false;
71
0
    return false;
72
0
  }
73
74
0
  FileOffset Offs;
75
0
  if ((!afterToken && !canInsert(loc, Offs)) ||
76
0
      ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
77
0
    IsCommitable = false;
78
0
    return false;
79
0
  }
80
81
0
  if (PPRec &&
82
0
      PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
83
0
    IsCommitable = false;
84
0
    return false;
85
0
  }
86
87
0
  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
88
0
  return true;
89
0
}
90
91
0
bool Commit::remove(CharSourceRange range) {
92
0
  FileOffset Offs;
93
0
  unsigned Len;
94
0
  if (!canRemoveRange(range, Offs, Len)) {
95
0
    IsCommitable = false;
96
0
    return false;
97
0
  }
98
99
0
  addRemove(range.getBegin(), Offs, Len);
100
0
  return true;
101
0
}
102
103
bool Commit::insertWrap(StringRef before, CharSourceRange range,
104
0
                        StringRef after) {
105
0
  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
106
0
                                 /*beforePreviousInsertions=*/true);
107
0
  bool commitableAfter;
108
0
  if (range.isTokenRange())
109
0
    commitableAfter = insertAfterToken(range.getEnd(), after);
110
0
  else
111
0
    commitableAfter = insert(range.getEnd(), after);
112
113
0
  return commitableBefore && commitableAfter;
114
0
}
115
116
0
bool Commit::replace(CharSourceRange range, StringRef text) {
117
0
  if (text.empty())
118
0
    return remove(range);
119
120
0
  FileOffset Offs;
121
0
  unsigned Len;
122
0
  if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
123
0
    IsCommitable = false;
124
0
    return false;
125
0
  }
126
127
0
  addRemove(range.getBegin(), Offs, Len);
128
0
  addInsert(range.getBegin(), Offs, text, false);
129
0
  return true;
130
0
}
131
132
bool Commit::replaceWithInner(CharSourceRange range,
133
0
                              CharSourceRange replacementRange) {
134
0
  FileOffset OuterBegin;
135
0
  unsigned OuterLen;
136
0
  if (!canRemoveRange(range, OuterBegin, OuterLen)) {
137
0
    IsCommitable = false;
138
0
    return false;
139
0
  }
140
141
0
  FileOffset InnerBegin;
142
0
  unsigned InnerLen;
143
0
  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
144
0
    IsCommitable = false;
145
0
    return false;
146
0
  }
147
148
0
  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
149
0
  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
150
0
  if (OuterBegin.getFID() != InnerBegin.getFID() ||
151
0
      InnerBegin < OuterBegin ||
152
0
      InnerBegin > OuterEnd ||
153
0
      InnerEnd > OuterEnd) {
154
0
    IsCommitable = false;
155
0
    return false;
156
0
  }
157
158
0
  addRemove(range.getBegin(),
159
0
            OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
160
0
  addRemove(replacementRange.getEnd(),
161
0
            InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
162
0
  return true;
163
0
}
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
0
                       bool beforePreviousInsertions) {
184
0
  if (text.empty())
185
0
    return;
186
187
0
  Edit data;
188
0
  data.Kind = Act_Insert;
189
0
  data.OrigLoc = OrigLoc;
190
0
  data.Offset = Offs;
191
0
  data.Text = text.copy(StrAlloc);
192
0
  data.BeforePrev = beforePreviousInsertions;
193
0
  CachedEdits.push_back(data);
194
0
}
195
196
void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
197
                                FileOffset RangeOffs, unsigned RangeLen,
198
0
                                bool beforePreviousInsertions) {
199
0
  if (RangeLen == 0)
200
0
    return;
201
202
0
  Edit data;
203
0
  data.Kind = Act_InsertFromRange;
204
0
  data.OrigLoc = OrigLoc;
205
0
  data.Offset = Offs;
206
0
  data.InsertFromRangeOffs = RangeOffs;
207
0
  data.Length = RangeLen;
208
0
  data.BeforePrev = beforePreviousInsertions;
209
0
  CachedEdits.push_back(data);
210
0
}
211
212
void Commit::addRemove(SourceLocation OrigLoc,
213
0
                       FileOffset Offs, unsigned Len) {
214
0
  if (Len == 0)
215
0
    return;
216
217
0
  Edit data;
218
0
  data.Kind = Act_Remove;
219
0
  data.OrigLoc = OrigLoc;
220
0
  data.Offset = Offs;
221
0
  data.Length = Len;
222
0
  CachedEdits.push_back(data);
223
0
}
224
225
0
bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
226
0
  if (loc.isInvalid())
227
0
    return false;
228
229
0
  if (loc.isMacroID())
230
0
    isAtStartOfMacroExpansion(loc, &loc);
231
232
0
  const SourceManager &SM = SourceMgr;
233
0
  loc = SM.getTopMacroCallerLoc(loc);
234
235
0
  if (loc.isMacroID())
236
0
    if (!isAtStartOfMacroExpansion(loc, &loc))
237
0
      return false;
238
239
0
  if (SM.isInSystemHeader(loc))
240
0
    return false;
241
242
0
  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
243
0
  if (locInfo.first.isInvalid())
244
0
    return false;
245
0
  offs = FileOffset(locInfo.first, locInfo.second);
246
0
  return canInsertInOffset(loc, offs);
247
0
}
248
249
bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
250
0
                                 SourceLocation &AfterLoc) {
251
0
  if (loc.isInvalid())
252
253
0
    return false;
254
255
0
  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
256
0
  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
257
0
  AfterLoc = loc.getLocWithOffset(tokLen);
258
259
0
  if (loc.isMacroID())
260
0
    isAtEndOfMacroExpansion(loc, &loc);
261
262
0
  const SourceManager &SM = SourceMgr;
263
0
  loc = SM.getTopMacroCallerLoc(loc);
264
265
0
  if (loc.isMacroID())
266
0
    if (!isAtEndOfMacroExpansion(loc, &loc))
267
0
      return false;
268
269
0
  if (SM.isInSystemHeader(loc))
270
0
    return false;
271
272
0
  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
273
0
  if (loc.isInvalid())
274
0
    return false;
275
276
0
  std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
277
0
  if (locInfo.first.isInvalid())
278
0
    return false;
279
0
  offs = FileOffset(locInfo.first, locInfo.second);
280
0
  return canInsertInOffset(loc, offs);
281
0
}
282
283
0
bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
284
0
  for (const auto &act : CachedEdits)
285
0
    if (act.Kind == Act_Remove) {
286
0
      if (act.Offset.getFID() == Offs.getFID() &&
287
0
          Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
288
0
        return false; // position has been removed.
289
0
    }
290
291
0
  if (!Editor)
292
0
    return true;
293
0
  return Editor->canInsertInOffset(OrigLoc, Offs);
294
0
}
295
296
bool Commit::canRemoveRange(CharSourceRange range,
297
0
                            FileOffset &Offs, unsigned &Len) {
298
0
  const SourceManager &SM = SourceMgr;
299
0
  range = Lexer::makeFileCharRange(range, SM, LangOpts);
300
0
  if (range.isInvalid())
301
0
    return false;
302
303
0
  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
304
0
    return false;
305
0
  if (SM.isInSystemHeader(range.getBegin()) ||
306
0
      SM.isInSystemHeader(range.getEnd()))
307
0
    return false;
308
309
0
  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
310
0
    return false;
311
312
0
  std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
313
0
  std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
314
0
  if (beginInfo.first != endInfo.first ||
315
0
      beginInfo.second > endInfo.second)
316
0
    return false;
317
318
0
  Offs = FileOffset(beginInfo.first, beginInfo.second);
319
0
  Len = endInfo.second - beginInfo.second;
320
0
  return true;
321
0
}
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()).starts_with(text);
338
0
}
339
340
bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
341
0
                                       SourceLocation *MacroBegin) const {
342
0
  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
343
0
}
344
345
bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
346
0
                                     SourceLocation *MacroEnd) const {
347
0
  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
348
0
}