1 //===----- Commit.cpp - A unit of edits -----------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "clang/Edit/Commit.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Edit/EditedSource.h"
13 #include "clang/Lex/Lexer.h"
14 #include "clang/Lex/PPConditionalDirectiveRecord.h"
16 using namespace clang
;
19 SourceLocation
Commit::Edit::getFileLocation(SourceManager
&SM
) const {
20 SourceLocation Loc
= SM
.getLocForStartOfFile(Offset
.getFID());
21 Loc
= Loc
.getLocWithOffset(Offset
.getOffset());
22 assert(Loc
.isFileID());
26 CharSourceRange
Commit::Edit::getFileRange(SourceManager
&SM
) const {
27 SourceLocation Loc
= getFileLocation(SM
);
28 return CharSourceRange::getCharRange(Loc
, Loc
.getLocWithOffset(Length
));
31 CharSourceRange
Commit::Edit::getInsertFromRange(SourceManager
&SM
) const {
32 SourceLocation Loc
= SM
.getLocForStartOfFile(InsertFromRangeOffs
.getFID());
33 Loc
= Loc
.getLocWithOffset(InsertFromRangeOffs
.getOffset());
34 assert(Loc
.isFileID());
35 return CharSourceRange::getCharRange(Loc
, Loc
.getLocWithOffset(Length
));
38 Commit::Commit(EditedSource
&Editor
)
39 : SourceMgr(Editor
.getSourceManager()), LangOpts(Editor
.getLangOpts()),
40 PPRec(Editor
.getPPCondDirectiveRecord()),
41 Editor(&Editor
), IsCommitable(true) { }
43 bool Commit::insert(SourceLocation loc
, StringRef text
,
44 bool afterToken
, bool beforePreviousInsertions
) {
49 if ((!afterToken
&& !canInsert(loc
, Offs
)) ||
50 ( afterToken
&& !canInsertAfterToken(loc
, Offs
, loc
))) {
55 addInsert(loc
, Offs
, text
, beforePreviousInsertions
);
59 bool Commit::insertFromRange(SourceLocation loc
,
60 CharSourceRange range
,
61 bool afterToken
, bool beforePreviousInsertions
) {
64 if (!canRemoveRange(range
, RangeOffs
, RangeLen
)) {
70 if ((!afterToken
&& !canInsert(loc
, Offs
)) ||
71 ( afterToken
&& !canInsertAfterToken(loc
, Offs
, loc
))) {
77 PPRec
->areInDifferentConditionalDirectiveRegion(loc
, range
.getBegin())) {
82 addInsertFromRange(loc
, Offs
, RangeOffs
, RangeLen
, beforePreviousInsertions
);
86 bool Commit::remove(CharSourceRange range
) {
89 if (!canRemoveRange(range
, Offs
, Len
)) {
94 addRemove(range
.getBegin(), Offs
, Len
);
98 bool Commit::insertWrap(StringRef before
, CharSourceRange range
,
100 bool commitableBefore
= insert(range
.getBegin(), before
, /*afterToken=*/false,
101 /*beforePreviousInsertions=*/true);
102 bool commitableAfter
;
103 if (range
.isTokenRange())
104 commitableAfter
= insertAfterToken(range
.getEnd(), after
);
106 commitableAfter
= insert(range
.getEnd(), after
);
108 return commitableBefore
&& commitableAfter
;
111 bool Commit::replace(CharSourceRange range
, StringRef text
) {
113 return remove(range
);
117 if (!canInsert(range
.getBegin(), Offs
) || !canRemoveRange(range
, Offs
, Len
)) {
118 IsCommitable
= false;
122 addRemove(range
.getBegin(), Offs
, Len
);
123 addInsert(range
.getBegin(), Offs
, text
, false);
127 bool Commit::replaceWithInner(CharSourceRange range
,
128 CharSourceRange replacementRange
) {
129 FileOffset OuterBegin
;
131 if (!canRemoveRange(range
, OuterBegin
, OuterLen
)) {
132 IsCommitable
= false;
136 FileOffset InnerBegin
;
138 if (!canRemoveRange(replacementRange
, InnerBegin
, InnerLen
)) {
139 IsCommitable
= false;
143 FileOffset OuterEnd
= OuterBegin
.getWithOffset(OuterLen
);
144 FileOffset InnerEnd
= InnerBegin
.getWithOffset(InnerLen
);
145 if (OuterBegin
.getFID() != InnerBegin
.getFID() ||
146 InnerBegin
< OuterBegin
||
147 InnerBegin
> OuterEnd
||
148 InnerEnd
> OuterEnd
) {
149 IsCommitable
= false;
153 addRemove(range
.getBegin(),
154 OuterBegin
, InnerBegin
.getOffset() - OuterBegin
.getOffset());
155 addRemove(replacementRange
.getEnd(),
156 InnerEnd
, OuterEnd
.getOffset() - InnerEnd
.getOffset());
160 bool Commit::replaceText(SourceLocation loc
, StringRef text
,
161 StringRef replacementText
) {
162 if (text
.empty() || replacementText
.empty())
167 if (!canReplaceText(loc
, replacementText
, Offs
, Len
)) {
168 IsCommitable
= false;
172 addRemove(loc
, Offs
, Len
);
173 addInsert(loc
, Offs
, text
, false);
177 void Commit::addInsert(SourceLocation OrigLoc
, FileOffset Offs
, StringRef text
,
178 bool beforePreviousInsertions
) {
183 data
.Kind
= Act_Insert
;
184 data
.OrigLoc
= OrigLoc
;
186 data
.Text
= copyString(text
);
187 data
.BeforePrev
= beforePreviousInsertions
;
188 CachedEdits
.push_back(data
);
191 void Commit::addInsertFromRange(SourceLocation OrigLoc
, FileOffset Offs
,
192 FileOffset RangeOffs
, unsigned RangeLen
,
193 bool beforePreviousInsertions
) {
198 data
.Kind
= Act_InsertFromRange
;
199 data
.OrigLoc
= OrigLoc
;
201 data
.InsertFromRangeOffs
= RangeOffs
;
202 data
.Length
= RangeLen
;
203 data
.BeforePrev
= beforePreviousInsertions
;
204 CachedEdits
.push_back(data
);
207 void Commit::addRemove(SourceLocation OrigLoc
,
208 FileOffset Offs
, unsigned Len
) {
213 data
.Kind
= Act_Remove
;
214 data
.OrigLoc
= OrigLoc
;
217 CachedEdits
.push_back(data
);
220 bool Commit::canInsert(SourceLocation loc
, FileOffset
&offs
) {
225 isAtStartOfMacroExpansion(loc
, &loc
);
227 const SourceManager
&SM
= SourceMgr
;
228 while (SM
.isMacroArgExpansion(loc
))
229 loc
= SM
.getImmediateSpellingLoc(loc
);
232 if (!isAtStartOfMacroExpansion(loc
, &loc
))
235 if (SM
.isInSystemHeader(loc
))
238 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(loc
);
239 if (locInfo
.first
.isInvalid())
241 offs
= FileOffset(locInfo
.first
, locInfo
.second
);
242 return canInsertInOffset(loc
, offs
);
245 bool Commit::canInsertAfterToken(SourceLocation loc
, FileOffset
&offs
,
246 SourceLocation
&AfterLoc
) {
251 SourceLocation spellLoc
= SourceMgr
.getSpellingLoc(loc
);
252 unsigned tokLen
= Lexer::MeasureTokenLength(spellLoc
, SourceMgr
, LangOpts
);
253 AfterLoc
= loc
.getLocWithOffset(tokLen
);
256 isAtEndOfMacroExpansion(loc
, &loc
);
258 const SourceManager
&SM
= SourceMgr
;
259 while (SM
.isMacroArgExpansion(loc
))
260 loc
= SM
.getImmediateSpellingLoc(loc
);
263 if (!isAtEndOfMacroExpansion(loc
, &loc
))
266 if (SM
.isInSystemHeader(loc
))
269 loc
= Lexer::getLocForEndOfToken(loc
, 0, SourceMgr
, LangOpts
);
273 std::pair
<FileID
, unsigned> locInfo
= SM
.getDecomposedLoc(loc
);
274 if (locInfo
.first
.isInvalid())
276 offs
= FileOffset(locInfo
.first
, locInfo
.second
);
277 return canInsertInOffset(loc
, offs
);
280 bool Commit::canInsertInOffset(SourceLocation OrigLoc
, FileOffset Offs
) {
281 for (unsigned i
= 0, e
= CachedEdits
.size(); i
!= e
; ++i
) {
282 Edit
&act
= CachedEdits
[i
];
283 if (act
.Kind
== Act_Remove
) {
284 if (act
.Offset
.getFID() == Offs
.getFID() &&
285 Offs
> act
.Offset
&& Offs
< act
.Offset
.getWithOffset(act
.Length
))
286 return false; // position has been removed.
292 return Editor
->canInsertInOffset(OrigLoc
, Offs
);
295 bool Commit::canRemoveRange(CharSourceRange range
,
296 FileOffset
&Offs
, unsigned &Len
) {
297 const SourceManager
&SM
= SourceMgr
;
298 range
= Lexer::makeFileCharRange(range
, SM
, LangOpts
);
299 if (range
.isInvalid())
302 if (range
.getBegin().isMacroID() || range
.getEnd().isMacroID())
304 if (SM
.isInSystemHeader(range
.getBegin()) ||
305 SM
.isInSystemHeader(range
.getEnd()))
308 if (PPRec
&& PPRec
->rangeIntersectsConditionalDirective(range
.getAsRange()))
311 std::pair
<FileID
, unsigned> beginInfo
= SM
.getDecomposedLoc(range
.getBegin());
312 std::pair
<FileID
, unsigned> endInfo
= SM
.getDecomposedLoc(range
.getEnd());
313 if (beginInfo
.first
!= endInfo
.first
||
314 beginInfo
.second
> endInfo
.second
)
317 Offs
= FileOffset(beginInfo
.first
, beginInfo
.second
);
318 Len
= endInfo
.second
- beginInfo
.second
;
322 bool Commit::canReplaceText(SourceLocation loc
, StringRef text
,
323 FileOffset
&Offs
, unsigned &Len
) {
324 assert(!text
.empty());
326 if (!canInsert(loc
, Offs
))
329 // Try to load the file buffer.
330 bool invalidTemp
= false;
331 StringRef file
= SourceMgr
.getBufferData(Offs
.getFID(), &invalidTemp
);
336 return file
.substr(Offs
.getOffset()).startswith(text
);
339 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc
,
340 SourceLocation
*MacroBegin
) const {
341 return Lexer::isAtStartOfMacroExpansion(loc
, SourceMgr
, LangOpts
, MacroBegin
);
343 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc
,
344 SourceLocation
*MacroEnd
) const {
345 return Lexer::isAtEndOfMacroExpansion(loc
, SourceMgr
, LangOpts
, MacroEnd
);