19#include "llvm/ADT/STLExtras.h"
20#include "llvm/ADT/StringRef.h"
21#include "llvm/ADT/Twine.h"
35 MacroArgUse &ArgUse) {
36 assert(SourceMgr.isMacroArgExpansion(Loc));
38 SourceMgr.getImmediateExpansionRange(Loc).getBegin();
40 SourceMgr.getImmediateExpansionRange(DefArgLoc).getBegin();
41 ExpansionLoc = ImmediateExpansionLoc;
42 while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
44 SourceMgr.getImmediateExpansionRange(ExpansionLoc).getBegin();
47 Buf, SourceMgr, LangOpts);
50 ArgUse = {&IdentTable.
get(ArgName), ImmediateExpansionLoc,
51 SourceMgr.getSpellingLoc(DefArgLoc)};
54void EditedSource::startingCommit() {}
56void EditedSource::finishedCommit() {
57 for (
auto &ExpArg : CurrCommitMacroArgExps) {
58 SourceLocation ExpLoc;
60 std::tie(ExpLoc, ArgUse) = ExpArg;
61 auto &ArgUses = ExpansionToArgMap[ExpLoc];
62 if (!llvm::is_contained(ArgUses, ArgUse))
63 ArgUses.push_back(ArgUse);
65 CurrCommitMacroArgExps.clear();
74 FileEditsTy::iterator FA = getActionForOffset(Offs);
75 if (FA != FileEdits.end()) {
76 if (FA->first != Offs)
80 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
83 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
84 auto I = ExpansionToArgMap.find(ExpLoc);
85 if (I != ExpansionToArgMap.end() &&
86 llvm::any_of(I->second, [&](
const MacroArgUse &
U) {
87 return ArgUse.Identifier == U.Identifier &&
88 std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
89 std::tie(U.ImmediateExpansionLoc, U.UseLoc);
112 bool beforePreviousInsertions) {
118 if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
121 deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
122 if (ArgUse.Identifier)
123 CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
126 FileEdit &FA = FileEdits[Offs];
127 if (FA.Text.empty()) {
132 if (beforePreviousInsertions)
140bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
143 bool beforePreviousInsertions) {
147 SmallString<128> StrVec;
148 FileOffset BeginOffs = InsertFromRangeOffs;
150 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
151 if (I != FileEdits.begin())
154 for (; I != FileEdits.end(); ++I) {
155 FileEdit &FA = I->second;
156 FileOffset B = I->first;
171 for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
172 FileEdit &FA = I->second;
173 FileOffset B = I->first;
178 StringRef
text = getSourceText(BeginOffs, B,
Invalid);
187 if (BeginOffs < EndOffs) {
189 StringRef
text = getSourceText(BeginOffs, EndOffs,
Invalid);
195 return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
198void EditedSource::commitRemove(SourceLocation OrigLoc,
204 FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
205 if (I != FileEdits.begin())
208 for (; I != FileEdits.end(); ++I) {
209 FileEdit &FA = I->second;
210 FileOffset B = I->first;
217 FileOffset TopBegin, TopEnd;
218 FileEdit *TopFA =
nullptr;
220 if (I == FileEdits.end()) {
221 FileEditsTy::iterator
222 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
223 NewI->second.RemoveLen = Len;
227 FileEdit &FA = I->second;
228 FileOffset B = I->first;
231 FileEditsTy::iterator
232 NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
233 TopBegin = BeginOffs;
235 TopFA = &NewI->second;
236 TopFA->RemoveLen = Len;
241 if (TopEnd >= EndOffs)
245 TopFA->RemoveLen += diff;
247 TopFA->Text = StringRef();
251 while (I != FileEdits.end()) {
252 FileEdit &FA = I->second;
253 FileOffset B = I->first;
260 FileEdits.erase(I++);
267 TopFA->RemoveLen += diff;
276 if (!
commit.isCommitable())
283 Editor.startingCommit();
287 Editor.finishedCommit();
292 I =
commit.edit_begin(), E =
commit.edit_end(); I != E; ++I) {
299 commitInsertFromRange(
edit.OrigLoc,
edit.Offset,
300 edit.InsertFromRangeOffs,
edit.Length,
338 unsigned &len, StringRef &text) {
339 assert(len && text.empty());
341 if (BeginTokLoc != Loc)
350 unsigned end = begin + len;
353 if (end == buffer.size())
356 assert(begin < buffer.size() && end < buffer.size() &&
"Invalid range!");
361 if (buffer[end] ==
' ')
366 if (buffer[end] ==
' ') {
367 assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
368 "buffer not zero-terminated!");
371 buffer.data()[end + 1],
377 if (!
canBeJoined(buffer[begin-1], buffer[end], LangOpts))
382 StringRef text,
FileOffset offs,
unsigned len,
384 bool shouldAdjustRemovals) {
390 if (text.empty() && shouldAdjustRemovals)
405 receiver.
insert(Loc, text);
409 bool shouldAdjustRemovals) {
414 if (FileEdits.empty())
417 FileEditsTy::iterator I = FileEdits.begin();
419 StrVec = I->second.Text;
420 CurLen = I->second.RemoveLen;
424 for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
426 FileEdit act = I->second;
427 assert(offs >= CurEnd);
429 if (offs == CurEnd) {
431 CurLen += act.RemoveLen;
436 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
437 shouldAdjustRemovals);
440 CurLen = act.RemoveLen;
444 applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
445 shouldAdjustRemovals);
456 assert(BeginOffs <= EndOffs);
463 SourceMgr, LangOpts, &
Invalid);
466EditedSource::FileEditsTy::iterator
467EditedSource::getActionForOffset(
FileOffset Offs) {
468 FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
469 if (I == FileEdits.begin())
470 return FileEdits.end();
472 FileEdit &FA = I->second;
475 if (Offs >= B && Offs < E)
478 return FileEdits.end();
490 : MergedFixits(MergedFixits) {}
492 void insert(SourceLocation loc, StringRef text)
override {
496 void replace(CharSourceRange range, StringRef text)
override {
506 MergedFixits.clear();
508 for (
const auto &Hint : FixItHints)
509 if (Hint.CodeToInsert.empty()) {
510 if (Hint.InsertFromRange.isValid())
512 Hint.InsertFromRange,
false,
513 Hint.BeforePreviousInsertions);
515 commit.
remove(Hint.RemoveRange);
517 if (Hint.RemoveRange.isTokenRange() ||
518 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
519 commit.
replace(Hint.RemoveRange, Hint.CodeToInsert);
521 commit.
insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
522 false, Hint.BeforePreviousInsertions);
526 if (Editor.
commit(commit)) {
527 FixitReceiver Rec(MergedFixits);
Defines the Diagnostic-related interfaces.
static bool canBeJoined(char left, char right, const LangOptions &LangOpts)
static void applyRewrite(EditsReceiver &receiver, StringRef text, FileOffset offs, unsigned len, const SourceManager &SM, const LangOptions &LangOpts, bool shouldAdjustRemovals)
static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation Loc, FileOffset offs, unsigned &len, StringRef &text)
Check the range that we are going to remove and: -Remove any trailing whitespace if possible.
static bool canRemoveWhitespace(char left, char beforeWSpace, char right, const LangOptions &LangOpts)
Returns true if it is ok to eliminate the trailing whitespace between the given characters.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Represents a byte-granular source range.
static CharSourceRange getCharRange(SourceRange R)
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
static unsigned getSpelling(const Token &Tok, const char *&Buffer, const SourceManager &SourceMgr, const LangOptions &LangOpts, bool *Invalid=nullptr)
getSpelling - This method is used to get the spelling of a token into a preallocated buffer,...
static bool isAsciiIdentifierContinueChar(char c, const LangOptions &LangOpts)
Returns true if the given character could appear in an identifier.
static SourceLocation GetBeginningOfToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Given a location any where in a source buffer, find the location that corresponds to the beginning of...
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
StringRef getBufferData(FileID FID, bool *Invalid=nullptr) const
Return a StringRef to the source buffer data for the specified FileID.
SourceLocation getLocForStartOfFile(FileID FID) const
Return the source location corresponding to the first byte of the specified file.
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
SmallVectorImpl< Edit >::const_iterator edit_iterator
bool remove(CharSourceRange range)
bool replace(CharSourceRange range, StringRef text)
StringRef copyString(StringRef str)
bool canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs)
void applyRewrites(EditsReceiver &receiver, bool adjustRemovals=true)
bool commit(const Commit &commit)
EditedSource(const SourceManager &SM, const LangOptions &LangOpts, const PPConditionalDirectiveRecord *PPRec=nullptr)
virtual void insert(SourceLocation loc, StringRef text)=0
virtual void remove(CharSourceRange range)
By default it calls replace with an empty string.
virtual void replace(CharSourceRange range, StringRef text)=0
FileOffset getWithOffset(unsigned offset) const
unsigned getOffset() const
void mergeFixits(ArrayRef< FixItHint > FixItHints, const SourceManager &SM, const LangOptions &LangOpts, SmallVectorImpl< FixItHint > &MergedFixits)
Merges FixItHints into a normalized set of file edits.
Top level wrappers for InstallAPI frontend operations.
LLVM_READONLY bool isWhitespace(unsigned char c)
Return true if this character is horizontal or vertical ASCII whitespace: ' ', '\t',...