clang 8.0.0

Commit.cpp
Go to the documentation of this file.
1 //===- Commit.cpp - A unit of edits ---------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "clang/Edit/Commit.h"
11 #include "clang/Basic/LLVM.h"
12 #include "clang/Basic/SourceLocation.h"
13 #include "clang/Basic/SourceManager.h"
14 #include "clang/Edit/EditedSource.h"
15 #include "clang/Edit/FileOffset.h"
16 #include "clang/Lex/Lexer.h"
17 #include "clang/Lex/PPConditionalDirectiveRecord.h"
18 #include "llvm/ADT/StringRef.h"
19 #include <cassert>
20 #include <utility>
21
22 using namespace clang;
23 using namespace edit;
24
25 SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
26 SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
27 Loc = Loc.getLocWithOffset(Offset.getOffset());
28 assert(Loc.isFileID());
29 return Loc;
30 }
31
32 CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
33 SourceLocation Loc = getFileLocation(SM);
34 return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
35 }
36
37 CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
38 SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
39 Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
40 assert(Loc.isFileID());
41 return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
42 }
43
44 Commit::Commit(EditedSource &Editor)
45 : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
46 PPRec(Editor.getPPCondDirectiveRecord()),
47 Editor(&Editor) {}
48
49 bool Commit::insert(SourceLocation loc, StringRef text,
50 bool afterToken, bool beforePreviousInsertions) {
51 if (text.empty())
52 return true;
53
54 FileOffset Offs;
55 if ((!afterToken && !canInsert(loc, Offs)) ||
56 ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
57 IsCommitable = false;
58 return false;
59 }
60
61 addInsert(loc, Offs, text, beforePreviousInsertions);
62 return true;
63 }
64
65 bool Commit::insertFromRange(SourceLocation loc,
66 CharSourceRange range,
67 bool afterToken, bool beforePreviousInsertions) {
68 FileOffset RangeOffs;
69 unsigned RangeLen;
70 if (!canRemoveRange(range, RangeOffs, RangeLen)) {
71 IsCommitable = false;
72 return false;
73 }
74
75 FileOffset Offs;
76 if ((!afterToken && !canInsert(loc, Offs)) ||
77 ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
78 IsCommitable = false;
79 return false;
80 }
81
82 if (PPRec &&
83 PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
84 IsCommitable = false;
85 return false;
86 }
87
88 addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
89 return true;
90 }
91
92 bool Commit::remove(CharSourceRange range) {
93 FileOffset Offs;
94 unsigned Len;
95 if (!canRemoveRange(range, Offs, Len)) {
96 IsCommitable = false;
97 return false;
98 }
99
100 addRemove(range.getBegin(), Offs, Len);
101 return true;
102 }
103
104 bool Commit::insertWrap(StringRef before, CharSourceRange range,
105 StringRef after) {
106 bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
107 /*beforePreviousInsertions=*/true);
108 bool commitableAfter;
109 if (range.isTokenRange())
110 commitableAfter = insertAfterToken(range.getEnd(), after);
111 else
112 commitableAfter = insert(range.getEnd(), after);
113
114 return commitableBefore && commitableAfter;
115 }
116
117 bool Commit::replace(CharSourceRange range, StringRef text) {
118 if (text.empty())
119 return remove(range);
120
121 FileOffset Offs;
122 unsigned Len;
123 if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
124 IsCommitable = false;
125 return false;
126 }
127
128 addRemove(range.getBegin(), Offs, Len);
129 addInsert(range.getBegin(), Offs, text, false);
130 return true;
131 }
132
133 bool Commit::replaceWithInner(CharSourceRange range,
134 CharSourceRange replacementRange) {
135 FileOffset OuterBegin;
136 unsigned OuterLen;
137 if (!canRemoveRange(range, OuterBegin, OuterLen)) {
138 IsCommitable = false;
139 return false;
140 }
141
142 FileOffset InnerBegin;
143 unsigned InnerLen;
144 if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
145 IsCommitable = false;
146 return false;
147 }
148
149 FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
150 FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
151 if (OuterBegin.getFID() != InnerBegin.getFID() ||
152 InnerBegin < OuterBegin ||
153 InnerBegin > OuterEnd ||
154 InnerEnd > OuterEnd) {
155 IsCommitable = false;
156 return false;
157 }
158
159 addRemove(range.getBegin(),
160 OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
161 addRemove(replacementRange.getEnd(),
162 InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
163 return true;
164 }
165
166 bool Commit::replaceText(SourceLocation loc, StringRef text,
167 StringRef replacementText) {
168 if (text.empty() || replacementText.empty())
169 return true;
170
171 FileOffset Offs;
172 unsigned Len;
173 if (!canReplaceText(loc, replacementText, Offs, Len)) {
174 IsCommitable = false;
175 return false;
176 }
177
178 addRemove(loc, Offs, Len);
179 addInsert(loc, Offs, text, false);
180 return true;
181 }
182
183 void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
184 bool beforePreviousInsertions) {
185 if (text.empty())
186 return;
187
188 Edit data;
189 data.Kind = Act_Insert;
190 data.OrigLoc = OrigLoc;
191 data.Offset = Offs;
192 data.Text = text.copy(StrAlloc);
193 data.BeforePrev = beforePreviousInsertions;
194 CachedEdits.push_back(data);
195 }
196
197 void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
198 FileOffset RangeOffs, unsigned RangeLen,
199 bool beforePreviousInsertions) {
200 if (RangeLen == 0)
201 return;
202
203 Edit data;
204 data.Kind = Act_InsertFromRange;
205 data.OrigLoc = OrigLoc;
206 data.Offset = Offs;
207 data.InsertFromRangeOffs = RangeOffs;
208 data.Length = RangeLen;
209 data.BeforePrev = beforePreviousInsertions;
210 CachedEdits.push_back(data);
211 }
212
213 void Commit::addRemove(SourceLocation OrigLoc,
214 FileOffset Offs, unsigned Len) {
215 if (Len == 0)
216 return;
217
218 Edit data;
219 data.Kind = Act_Remove;
220 data.OrigLoc = OrigLoc;
221 data.Offset = Offs;
222 data.Length = Len;
223 CachedEdits.push_back(data);
224 }
225
226 bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
227 if (loc.isInvalid())
228 return false;
229
230 if (loc.isMacroID())
231 isAtStartOfMacroExpansion(loc, &loc);
232
233 const SourceManager &SM = SourceMgr;
234 loc = SM.getTopMacroCallerLoc(loc);
235
236 if (loc.isMacroID())
237 if (!isAtStartOfMacroExpansion(loc, &loc))
238 return false;
239
240 if (SM.isInSystemHeader(loc))
241 return false;
242
243 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
244 if (locInfo.first.isInvalid())
245 return false;
246 offs = FileOffset(locInfo.first, locInfo.second);
247 return canInsertInOffset(loc, offs);
248 }
249
250 bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
251 SourceLocation &AfterLoc) {
252 if (loc.isInvalid())
253
254 return false;
255
256 SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
257 unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
258 AfterLoc = loc.getLocWithOffset(tokLen);
259
260 if (loc.isMacroID())
261 isAtEndOfMacroExpansion(loc, &loc);
262
263 const SourceManager &SM = SourceMgr;
264 loc = SM.getTopMacroCallerLoc(loc);
265
266 if (loc.isMacroID())
267 if (!isAtEndOfMacroExpansion(loc, &loc))
268 return false;
269
270 if (SM.isInSystemHeader(loc))
271 return false;
272
273 loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
274 if (loc.isInvalid())
275 return false;
276
277 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
278 if (locInfo.first.isInvalid())
279 return false;
280 offs = FileOffset(locInfo.first, locInfo.second);
281 return canInsertInOffset(loc, offs);
282 }
283
284 bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
285 for (const auto &act : CachedEdits)
286 if (act.Kind == Act_Remove) {
287 if (act.Offset.getFID() == Offs.getFID() &&
288 Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
289 return false; // position has been removed.
290 }
291
292 if (!Editor)
293 return true;
294 return Editor->canInsertInOffset(OrigLoc, Offs);
295 }
296
297 bool Commit::canRemoveRange(CharSourceRange range,
298 FileOffset &Offs, unsigned &Len) {
299 const SourceManager &SM = SourceMgr;
300 range = Lexer::makeFileCharRange(range, SM, LangOpts);
301 if (range.isInvalid())
302 return false;
303
304 if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
305 return false;
306 if (SM.isInSystemHeader(range.getBegin()) ||
307 SM.isInSystemHeader(range.getEnd()))
308 return false;
309
310 if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
311 return false;
312
313 std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
314 std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
315 if (beginInfo.first != endInfo.first ||
316 beginInfo.second > endInfo.second)
317 return false;
318
319 Offs = FileOffset(beginInfo.first, beginInfo.second);
320 Len = endInfo.second - beginInfo.second;
321 return true;
322 }
323
324 bool Commit::canReplaceText(SourceLocation loc, StringRef text,
325 FileOffset &Offs, unsigned &Len) {
326 assert(!text.empty());
327
328 if (!canInsert(loc, Offs))
329 return false;
330
331 // Try to load the file buffer.
332 bool invalidTemp = false;
333 StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
334 if (invalidTemp)
335 return false;
336
337 Len = text.size();
338 return file.substr(Offs.getOffset()).startswith(text);
339 }
340
341 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
342 SourceLocation *MacroBegin) const {
343 return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
344 }
345
346 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
347 SourceLocation *MacroEnd) const {
348 return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
349 }
clang::SourceManager::getLocForStartOfFile
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file. ...
Definition: SourceManager.h:1067
clang::edit::Commit::remove
bool remove(CharSourceRange range)
Definition: Commit.cpp:92
clang::SourceLocation::getLocWithOffset
SourceLocation getLocWithOffset(int Offset) const
Return a source location with the specified offset from this SourceLocation.
Definition: SourceLocation.h:134
PPConditionalDirectiveRecord.h
Commit.h
clang::edit::Commit::insertWrap
bool insertWrap(StringRef before, CharSourceRange range, StringRef after)
Definition: Commit.cpp:104
SourceManager.h
Defines the SourceManager interface.
clang::edit::Commit::insertAfterToken
bool insertAfterToken(SourceLocation loc, StringRef text, bool beforePreviousInsertions=false)
Definition: Commit.h:74
clang::edit::EditedSource
Definition: EditedSource.h:36
clang::edit::Commit::insertFromRange
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:65
clang::SourceManager::getBufferData
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
Definition: SourceManager.cpp:670
clang::Lexer::isAtStartOfMacroExpansion
static bool isAtStartOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroBegin=nullptr)
Returns true if the given MacroID location points at the first token of the macro expansion...
Definition: Lexer.cpp:793
clang::edit::Commit::insert
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition: Commit.cpp:49
clang::CharSourceRange::getBegin
SourceLocation getBegin() const
Definition: SourceLocation.h:266
clang::edit::Commit::Edit::BeforePrev
bool BeforePrev
Definition: Commit.h:45
clang::edit::Commit::replace
bool replace(CharSourceRange range, StringRef text)
Definition: Commit.cpp:117
clang::edit::FileOffset::getFID
FileID getFID() const
Definition: FileOffset.h:29
LLVM.h
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified...
clang::PPConditionalDirectiveRecord::areInDifferentConditionalDirectiveRegion
bool areInDifferentConditionalDirectiveRegion(SourceLocation LHS, SourceLocation RHS) const
Returns true if the given locations are in different regions, separated by conditional directive bloc...
Definition: PPConditionalDirectiveRecord.h:80
clang::SourceManager::getSpellingLoc
SourceLocation getSpellingLoc(SourceLocation Loc) const
Given a SourceLocation object, return the spelling location referenced by the ID. ...
Definition: SourceManager.h:1165
clang::SourceLocation::isFileID
bool isFileID() const
Definition: SourceLocation.h:100
clang::edit::Commit::Edit::OrigLoc
SourceLocation OrigLoc
Definition: Commit.h:41
clang::edit::Commit::Act_Remove
Definition: Commit.h:35
clang::edit::Commit::Edit::getFileRange
CharSourceRange getFileRange(SourceManager &SM) const
Definition: Commit.cpp:32
clang::edit::Commit::Edit::Length
unsigned Length
Definition: Commit.h:44
clang::Lexer::isAtEndOfMacroExpansion
static bool isAtEndOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroEnd=nullptr)
Returns true if the given MacroID location points at the last token of the macro expansion.
Definition: Lexer.cpp:815
clang::Lexer::getLocForEndOfToken
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition: Lexer.cpp:771
clang::CharSourceRange
Represents a character-granular source range.
Definition: SourceLocation.h:236
EditedSource.h
clang::SourceManager::isInSystemHeader
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
Definition: SourceManager.h:1445
clang::Lexer::MeasureTokenLength
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Definition: Lexer.cpp:436
clang::CharSourceRange::isInvalid
bool isInvalid() const
Definition: SourceLocation.h:275
Lexer.h
clang::edit::Commit::Edit::InsertFromRangeOffs
FileOffset InsertFromRangeOffs
Definition: Commit.h:43
clang::edit::Commit::Edit
Definition: Commit.h:38
SM
const SourceManager & SM
Definition: Format.cpp:1490
clang::edit::FileOffset::getWithOffset
FileOffset getWithOffset(unsigned offset) const
Definition: FileOffset.h:32
clang::CharSourceRange::getCharRange
static CharSourceRange getCharRange(SourceRange R)
Definition: SourceLocation.h:248
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
clang::PPConditionalDirectiveRecord::rangeIntersectsConditionalDirective
bool rangeIntersectsConditionalDirective(SourceRange Range) const
Returns true if the given range intersects with a conditional directive.
Definition: PPConditionalDirectiveRecord.cpp:24
clang::edit::Commit::Act_Insert
Definition: Commit.h:33
FileOffset.h
clang::CharSourceRange::isTokenRange
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
Definition: SourceLocation.h:263
clang::CharSourceRange::getAsRange
SourceRange getAsRange() const
Definition: SourceLocation.h:268
clang
Dataflow Directional Tag Classes.
Definition: CFGReachabilityAnalysis.h:22
clang::Lexer::makeFileCharRange
static CharSourceRange makeFileCharRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Accepts a range and returns a character range with file locations.
Definition: Lexer.cpp:868
clang::edit::Commit::Edit::getInsertFromRange
CharSourceRange getInsertFromRange(SourceManager &SM) const
Definition: Commit.cpp:37
clang::edit::FileOffset::getOffset
unsigned getOffset() const
Definition: FileOffset.h:30
clang::comments::tok::text
Definition: CommentLexer.h:36
clang::edit::Commit::replaceText
bool replaceText(SourceLocation loc, StringRef text, StringRef replacementText)
Definition: Commit.cpp:166
clang::SourceLocation::isMacroID
bool isMacroID() const
Definition: SourceLocation.h:101
clang::edit::Commit::Edit::Kind
EditKind Kind
Definition: Commit.h:39
clang::edit::Commit::Edit::Offset
FileOffset Offset
Definition: Commit.h:42
clang::edit::Commit::Commit
Commit(EditedSource &Editor)
Definition: Commit.cpp:44
clang::CharSourceRange::getEnd
SourceLocation getEnd() const
Definition: SourceLocation.h:267
clang::edit::FileOffset
Definition: FileOffset.h:19
clang::edit::EditedSource::canInsertInOffset
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
Definition: EditedSource.cpp:75
SourceLocation.h
Defines the clang::SourceLocation class and associated facilities.
clang::SourceLocation::isInvalid
bool isInvalid() const
Definition: SourceLocation.h:109
clang::edit::Commit::Edit::getFileLocation
SourceLocation getFileLocation(SourceManager &SM) const
Definition: Commit.cpp:25
clang::edit::Commit::replaceWithInner
bool replaceWithInner(CharSourceRange range, CharSourceRange innerRange)
Definition: Commit.cpp:133
clang::edit::Commit::Edit::Text
StringRef Text
Definition: Commit.h:40
clang::edit::Commit::Act_InsertFromRange
Definition: Commit.h:34
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:582
clang::SourceManager::getDecomposedLoc
std::pair< FileID, unsigned > getDecomposedLoc(SourceLocation Loc) const
Decompose the specified location into a raw FileID + Offset pair.
Definition: SourceManager.h:1196
clang::SourceManager::getTopMacroCallerLoc
SourceLocation getTopMacroCallerLoc(SourceLocation Loc) const
Definition: SourceManager.cpp:970