aboutsummaryrefslogtreecommitdiff
path: root/lib/Edit
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2012-03-06 20:06:33 +0000
committerTed Kremenek <kremenek@apple.com>2012-03-06 20:06:33 +0000
commit30660a898545416f0fea2d717f16f75640001e38 (patch)
treea8408d8c12d992b4110fca1bfe78f337b48f5d11 /lib/Edit
parent2a253960f0312c9759965a1defb9326d6b2764a4 (diff)
Add new code migrator support for migrating existing Objective-C code to use
the new Objective-C NSArray/NSDictionary/NSNumber literal syntax. This introduces a new library, libEdit, which provides a new way to support migration of code that improves on the original ARC migrator. We now believe that most of its functionality can be refactored into the existing libraries, and thus this new library may shortly disappear. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152141 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Edit')
-rw-r--r--lib/Edit/CMakeLists.txt7
-rw-r--r--lib/Edit/Commit.cpp345
-rw-r--r--lib/Edit/EditedSource.cpp329
-rw-r--r--lib/Edit/Makefile14
-rw-r--r--lib/Edit/RewriteObjCFoundationAPI.cpp589
5 files changed, 1284 insertions, 0 deletions
diff --git a/lib/Edit/CMakeLists.txt b/lib/Edit/CMakeLists.txt
new file mode 100644
index 0000000000..c87478cf7d
--- /dev/null
+++ b/lib/Edit/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(LLVM_USED_LIBS clangBasic clangAST clangLex)
+
+add_clang_library(clangEdit
+ Commit.cpp
+ EditedSource.cpp
+ RewriteObjCFoundationAPI.cpp
+ )
diff --git a/lib/Edit/Commit.cpp b/lib/Edit/Commit.cpp
new file mode 100644
index 0000000000..e0250ccf05
--- /dev/null
+++ b/lib/Edit/Commit.cpp
@@ -0,0 +1,345 @@
+//===----- Commit.cpp - A unit of edits -----------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Edit/Commit.h"
+#include "clang/Edit/EditedSource.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/PreprocessingRecord.h"
+#include "clang/Basic/SourceManager.h"
+
+using namespace clang;
+using namespace edit;
+
+SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
+ SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
+ Loc = Loc.getLocWithOffset(Offset.getOffset());
+ assert(Loc.isFileID());
+ return Loc;
+}
+
+CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
+ SourceLocation Loc = getFileLocation(SM);
+ return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
+}
+
+CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
+ SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
+ Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
+ assert(Loc.isFileID());
+ return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
+}
+
+Commit::Commit(EditedSource &Editor)
+ : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOptions()),
+ PPRec(Editor.getPreprocessingRecord()),
+ Editor(&Editor), IsCommitable(true) { }
+
+bool Commit::insert(SourceLocation loc, StringRef text,
+ bool afterToken, bool beforePreviousInsertions) {
+ if (text.empty())
+ return true;
+
+ FileOffset Offs;
+ if ((!afterToken && !canInsert(loc, Offs)) ||
+ ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
+ IsCommitable = false;
+ return false;
+ }
+
+ addInsert(loc, Offs, text, beforePreviousInsertions);
+ return true;
+}
+
+bool Commit::insertFromRange(SourceLocation loc,
+ CharSourceRange range,
+ bool afterToken, bool beforePreviousInsertions) {
+ FileOffset RangeOffs;
+ unsigned RangeLen;
+ if (!canRemoveRange(range, RangeOffs, RangeLen)) {
+ IsCommitable = false;
+ return false;
+ }
+
+ FileOffset Offs;
+ if ((!afterToken && !canInsert(loc, Offs)) ||
+ ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
+ IsCommitable = false;
+ return false;
+ }
+
+ if (PPRec &&
+ PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
+ IsCommitable = false;
+ return false;
+ }
+
+ addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
+ return true;
+}
+
+bool Commit::remove(CharSourceRange range) {
+ FileOffset Offs;
+ unsigned Len;
+ if (!canRemoveRange(range, Offs, Len)) {
+ IsCommitable = false;
+ return false;
+ }
+
+ addRemove(range.getBegin(), Offs, Len);
+ return true;
+}
+
+bool Commit::insertWrap(StringRef before, CharSourceRange range,
+ StringRef after) {
+ bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
+ /*beforePreviousInsertions=*/true);
+ bool commitableAfter;
+ if (range.isTokenRange())
+ commitableAfter = insertAfterToken(range.getEnd(), after);
+ else
+ commitableAfter = insert(range.getEnd(), after);
+
+ return commitableBefore && commitableAfter;
+}
+
+bool Commit::replace(CharSourceRange range, StringRef text) {
+ if (text.empty())
+ return remove(range);
+
+ FileOffset Offs;
+ unsigned Len;
+ if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
+ IsCommitable = false;
+ return false;
+ }
+
+ addRemove(range.getBegin(), Offs, Len);
+ addInsert(range.getBegin(), Offs, text, false);
+ return true;
+}
+
+bool Commit::replaceWithInner(CharSourceRange range,
+ CharSourceRange replacementRange) {
+ FileOffset OuterBegin;
+ unsigned OuterLen;
+ if (!canRemoveRange(range, OuterBegin, OuterLen)) {
+ IsCommitable = false;
+ return false;
+ }
+
+ FileOffset InnerBegin;
+ unsigned InnerLen;
+ if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
+ IsCommitable = false;
+ return false;
+ }
+
+ FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
+ FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);
+ if (OuterBegin.getFID() != InnerBegin.getFID() ||
+ InnerBegin < OuterBegin ||
+ InnerBegin > OuterEnd ||
+ InnerEnd > OuterEnd) {
+ IsCommitable = false;
+ return false;
+ }
+
+ addRemove(range.getBegin(),
+ OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
+ addRemove(replacementRange.getEnd(),
+ InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
+ return true;
+}
+
+bool Commit::replaceText(SourceLocation loc, StringRef text,
+ StringRef replacementText) {
+ if (text.empty() || replacementText.empty())
+ return true;
+
+ FileOffset Offs;
+ unsigned Len;
+ if (!canReplaceText(loc, replacementText, Offs, Len)) {
+ IsCommitable = false;
+ return false;
+ }
+
+ addRemove(loc, Offs, Len);
+ addInsert(loc, Offs, text, false);
+ return true;
+}
+
+void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
+ bool beforePreviousInsertions) {
+ if (text.empty())
+ return;
+
+ Edit data;
+ data.Kind = Act_Insert;
+ data.OrigLoc = OrigLoc;
+ data.Offset = Offs;
+ data.Text = text;
+ data.BeforePrev = beforePreviousInsertions;
+ CachedEdits.push_back(data);
+}
+
+void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
+ FileOffset RangeOffs, unsigned RangeLen,
+ bool beforePreviousInsertions) {
+ if (RangeLen == 0)
+ return;
+
+ Edit data;
+ data.Kind = Act_InsertFromRange;
+ data.OrigLoc = OrigLoc;
+ data.Offset = Offs;
+ data.InsertFromRangeOffs = RangeOffs;
+ data.Length = RangeLen;
+ data.BeforePrev = beforePreviousInsertions;
+ CachedEdits.push_back(data);
+}
+
+void Commit::addRemove(SourceLocation OrigLoc,
+ FileOffset Offs, unsigned Len) {
+ if (Len == 0)
+ return;
+
+ Edit data;
+ data.Kind = Act_Remove;
+ data.OrigLoc = OrigLoc;
+ data.Offset = Offs;
+ data.Length = Len;
+ CachedEdits.push_back(data);
+}
+
+bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
+ if (loc.isInvalid())
+ return false;
+
+ if (loc.isMacroID())
+ isAtStartOfMacroExpansion(loc, &loc);
+
+ const SourceManager &SM = SourceMgr;
+ while (SM.isMacroArgExpansion(loc))
+ loc = SM.getImmediateSpellingLoc(loc);
+
+ if (loc.isMacroID())
+ if (!isAtStartOfMacroExpansion(loc, &loc))
+ return false;
+
+ if (SM.isInSystemHeader(loc))
+ return false;
+
+ std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
+ if (locInfo.first.isInvalid())
+ return false;
+ offs = FileOffset(locInfo.first, locInfo.second);
+ return canInsertInOffset(loc, offs);
+}
+
+bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
+ SourceLocation &AfterLoc) {
+ if (loc.isInvalid())
+
+ return false;
+
+ SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
+ unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
+ AfterLoc = loc.getLocWithOffset(tokLen);
+
+ if (loc.isMacroID())
+ isAtEndOfMacroExpansion(loc, &loc);
+
+ const SourceManager &SM = SourceMgr;
+ while (SM.isMacroArgExpansion(loc))
+ loc = SM.getImmediateSpellingLoc(loc);
+
+ if (loc.isMacroID())
+ if (!isAtEndOfMacroExpansion(loc, &loc))
+ return false;
+
+ if (SM.isInSystemHeader(loc))
+ return false;
+
+ loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
+ if (loc.isInvalid())
+ return false;
+
+ std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
+ if (locInfo.first.isInvalid())
+ return false;
+ offs = FileOffset(locInfo.first, locInfo.second);
+ return canInsertInOffset(loc, offs);
+}
+
+bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
+ for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
+ Edit &act = CachedEdits[i];
+ if (act.Kind == Act_Remove) {
+ if (act.Offset.getFID() == Offs.getFID() &&
+ Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
+ return false; // position has been removed.
+ }
+ }
+
+ if (!Editor)
+ return true;
+ return Editor->canInsertInOffset(OrigLoc, Offs);
+}
+
+bool Commit::canRemoveRange(CharSourceRange range,
+ FileOffset &Offs, unsigned &Len) {
+ const SourceManager &SM = SourceMgr;
+ range = Lexer::makeFileCharRange(range, SM, LangOpts);
+ if (range.isInvalid())
+ return false;
+
+ if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
+ return false;
+ if (SM.isInSystemHeader(range.getBegin()) ||
+ SM.isInSystemHeader(range.getEnd()))
+ return false;
+
+ if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
+ return false;
+
+ std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
+ std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
+ if (beginInfo.first != endInfo.first ||
+ beginInfo.second > endInfo.second)
+ return false;
+
+ Offs = FileOffset(beginInfo.first, beginInfo.second);
+ Len = endInfo.second - beginInfo.second;
+ return true;
+}
+
+bool Commit::canReplaceText(SourceLocation loc, StringRef text,
+ FileOffset &Offs, unsigned &Len) {
+ assert(!text.empty());
+
+ if (!canInsert(loc, Offs))
+ return false;
+
+ // Try to load the file buffer.
+ bool invalidTemp = false;
+ StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
+ if (invalidTemp)
+ return false;
+
+ return file.substr(Offs.getOffset()).startswith(text);
+}
+
+bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
+ SourceLocation *MacroBegin) const {
+ return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
+}
+bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
+ SourceLocation *MacroEnd) const {
+ return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
+}
diff --git a/lib/Edit/EditedSource.cpp b/lib/Edit/EditedSource.cpp
new file mode 100644
index 0000000000..5b7fa4ad1b
--- /dev/null
+++ b/lib/Edit/EditedSource.cpp
@@ -0,0 +1,329 @@
+//===----- EditedSource.cpp - Collection of source edits ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Edit/EditedSource.h"
+#include "clang/Edit/Commit.h"
+#include "clang/Edit/EditsReceiver.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Basic/SourceManager.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Twine.h"
+
+using namespace clang;
+using namespace edit;
+
+void EditsReceiver::remove(CharSourceRange range) {
+ replace(range, StringRef());
+}
+
+StringRef EditedSource::copyString(const Twine &twine) {
+ llvm::SmallString<128> Data;
+ return copyString(twine.toStringRef(Data));
+}
+
+bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
+ FileEditsTy::iterator FA = getActionForOffset(Offs);
+ if (FA != FileEdits.end()) {
+ if (FA->first != Offs)
+ return false; // position has been removed.
+ }
+
+ if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
+ SourceLocation
+ DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
+ SourceLocation
+ ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
+ llvm::DenseMap<unsigned, SourceLocation>::iterator
+ I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
+ if (I != ExpansionToArgMap.end() && I->second != DefArgLoc)
+ return false; // Trying to write in a macro argument input that has
+ // already been written for another argument of the same macro.
+ }
+
+ return true;
+}
+
+bool EditedSource::commitInsert(SourceLocation OrigLoc,
+ FileOffset Offs, StringRef text,
+ bool beforePreviousInsertions) {
+ if (!canInsertInOffset(OrigLoc, Offs))
+ return false;
+ if (text.empty())
+ return true;
+
+ if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
+ SourceLocation
+ DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
+ SourceLocation
+ ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
+ ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc;
+ }
+
+ FileEdit &FA = FileEdits[Offs];
+ if (FA.Text.empty()) {
+ FA.Text = copyString(text);
+ return true;
+ }
+
+ Twine concat;
+ if (beforePreviousInsertions)
+ concat = Twine(text) + FA.Text;
+ else
+ concat = Twine(FA.Text) + text;
+
+ FA.Text = copyString(concat);
+ return true;
+}
+
+bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
+ FileOffset Offs,
+ FileOffset InsertFromRangeOffs, unsigned Len,
+ bool beforePreviousInsertions) {
+ if (Len == 0)
+ return true;
+
+ llvm::SmallString<128> StrVec;
+ FileOffset BeginOffs = InsertFromRangeOffs;
+ FileOffset EndOffs = BeginOffs.getWithOffset(Len);
+ FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
+ if (I != FileEdits.begin())
+ --I;
+
+ for (; I != FileEdits.end(); ++I) {
+ FileEdit &FA = I->second;
+ FileOffset B = I->first;
+ FileOffset E = B.getWithOffset(FA.RemoveLen);
+
+ if (BeginOffs < E) {
+ if (BeginOffs >= B) {
+ BeginOffs = E;
+ ++I;
+ }
+ break;
+ }
+ }
+
+ for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
+ FileEdit &FA = I->second;
+ FileOffset B = I->first;
+ FileOffset E = B.getWithOffset(FA.RemoveLen);
+
+ if (BeginOffs < B) {
+ bool Invalid = false;
+ StringRef text = getSourceText(BeginOffs, B, Invalid);
+ if (Invalid)
+ return false;
+ StrVec += text;
+ }
+ StrVec += FA.Text;
+ BeginOffs = E;
+ }
+
+ if (BeginOffs < EndOffs) {
+ bool Invalid = false;
+ StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
+ if (Invalid)
+ return false;
+ StrVec += text;
+ }
+
+ return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions);
+}
+
+void EditedSource::commitRemove(SourceLocation OrigLoc,
+ FileOffset BeginOffs, unsigned Len) {
+ if (Len == 0)
+ return;
+
+ FileOffset EndOffs = BeginOffs.getWithOffset(Len);
+ FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
+ if (I != FileEdits.begin())
+ --I;
+
+ for (; I != FileEdits.end(); ++I) {
+ FileEdit &FA = I->second;
+ FileOffset B = I->first;
+ FileOffset E = B.getWithOffset(FA.RemoveLen);
+
+ if (BeginOffs < E)
+ break;
+ }
+
+ FileOffset TopBegin, TopEnd;
+ FileEdit *TopFA = 0;
+
+ if (I == FileEdits.end()) {
+ FileEditsTy::iterator
+ NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
+ NewI->second.RemoveLen = Len;
+ return;
+ }
+
+ FileEdit &FA = I->second;
+ FileOffset B = I->first;
+ FileOffset E = B.getWithOffset(FA.RemoveLen);
+ if (BeginOffs < B) {
+ FileEditsTy::iterator
+ NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
+ TopBegin = BeginOffs;
+ TopEnd = EndOffs;
+ TopFA = &NewI->second;
+ TopFA->RemoveLen = Len;
+ } else {
+ TopBegin = B;
+ TopEnd = E;
+ TopFA = &I->second;
+ if (TopEnd >= EndOffs)
+ return;
+ unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
+ TopEnd = EndOffs;
+ TopFA->RemoveLen += diff;
+ ++I;
+ }
+
+ while (I != FileEdits.end()) {
+ FileEdit &FA = I->second;
+ FileOffset B = I->first;
+ FileOffset E = B.getWithOffset(FA.RemoveLen);
+
+ if (B >= TopEnd)
+ break;
+
+ if (E <= TopEnd) {
+ FileEdits.erase(I++);
+ continue;
+ }
+
+ if (B < TopEnd) {
+ unsigned diff = E.getOffset() - TopEnd.getOffset();
+ TopEnd = E;
+ TopFA->RemoveLen += diff;
+ FileEdits.erase(I);
+ }
+
+ break;
+ }
+}
+
+bool EditedSource::commit(const Commit &commit) {
+ if (!commit.isCommitable())
+ return false;
+
+ for (edit::Commit::edit_iterator
+ I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
+ const edit::Commit::Edit &edit = *I;
+ switch (edit.Kind) {
+ case edit::Commit::Act_Insert:
+ commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
+ break;
+ case edit::Commit::Act_InsertFromRange:
+ commitInsertFromRange(edit.OrigLoc, edit.Offset,
+ edit.InsertFromRangeOffs, edit.Length,
+ edit.BeforePrev);
+ break;
+ case edit::Commit::Act_Remove:
+ commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
+ break;
+ }
+ }
+
+ return true;
+}
+
+static void applyRewrite(EditsReceiver &receiver,
+ StringRef text, FileOffset offs, unsigned len,
+ const SourceManager &SM) {
+ assert(!offs.getFID().isInvalid());
+ SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
+ Loc = Loc.getLocWithOffset(offs.getOffset());
+ assert(Loc.isFileID());
+ CharSourceRange range = CharSourceRange::getCharRange(Loc,
+ Loc.getLocWithOffset(len));
+
+ if (text.empty()) {
+ assert(len);
+ receiver.remove(range);
+ return;
+ }
+
+ if (len)
+ receiver.replace(range, text);
+ else
+ receiver.insert(Loc, text);
+}
+
+void EditedSource::applyRewrites(EditsReceiver &receiver) {
+ llvm::SmallString<128> StrVec;
+ FileOffset CurOffs, CurEnd;
+ unsigned CurLen;
+
+ if (FileEdits.empty())
+ return;
+
+ FileEditsTy::iterator I = FileEdits.begin();
+ CurOffs = I->first;
+ StrVec = I->second.Text;
+ CurLen = I->second.RemoveLen;
+ CurEnd = CurOffs.getWithOffset(CurLen);
+ ++I;
+
+ for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
+ FileOffset offs = I->first;
+ FileEdit act = I->second;
+ assert(offs >= CurEnd);
+
+ if (offs == CurEnd) {
+ StrVec += act.Text;
+ CurLen += act.RemoveLen;
+ CurEnd.getWithOffset(act.RemoveLen);
+ continue;
+ }
+
+ applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
+ CurOffs = offs;
+ StrVec = act.Text;
+ CurLen = act.RemoveLen;
+ CurEnd = CurOffs.getWithOffset(CurLen);
+ }
+
+ applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
+}
+
+void EditedSource::clearRewrites() {
+ FileEdits.clear();
+ StrAlloc.Reset();
+}
+
+StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
+ bool &Invalid) {
+ assert(BeginOffs.getFID() == EndOffs.getFID());
+ assert(BeginOffs <= EndOffs);
+ SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
+ BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
+ assert(BLoc.isFileID());
+ SourceLocation
+ ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
+ return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
+ SourceMgr, LangOpts, &Invalid);
+}
+
+EditedSource::FileEditsTy::iterator
+EditedSource::getActionForOffset(FileOffset Offs) {
+ FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
+ if (I == FileEdits.begin())
+ return FileEdits.end();
+ --I;
+ FileEdit &FA = I->second;
+ FileOffset B = I->first;
+ FileOffset E = B.getWithOffset(FA.RemoveLen);
+ if (Offs >= B && Offs < E)
+ return I;
+
+ return FileEdits.end();
+}
diff --git a/lib/Edit/Makefile b/lib/Edit/Makefile
new file mode 100644
index 0000000000..92a67ebc82
--- /dev/null
+++ b/lib/Edit/Makefile
@@ -0,0 +1,14 @@
+##===- clang/lib/Edit/Makefile -----------------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+LIBRARYNAME := clangEdit
+
+include $(CLANG_LEVEL)/Makefile
+
diff --git a/lib/Edit/RewriteObjCFoundationAPI.cpp b/lib/Edit/RewriteObjCFoundationAPI.cpp
new file mode 100644
index 0000000000..a092768034
--- /dev/null
+++ b/lib/Edit/RewriteObjCFoundationAPI.cpp
@@ -0,0 +1,589 @@
+//===--- RewriteObjCFoundationAPI.cpp - Foundation API Rewriter -----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Rewrites legacy method calls to modern syntax.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Edit/Rewriters.h"
+#include "clang/Edit/Commit.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/NSAPI.h"
+
+using namespace clang;
+using namespace edit;
+
+static bool checkForLiteralCreation(const ObjCMessageExpr *Msg,
+ IdentifierInfo *&ClassId) {
+ if (!Msg || Msg->isImplicit() || !Msg->getMethodDecl())
+ return false;
+
+ const ObjCInterfaceDecl *Receiver = Msg->getReceiverInterface();
+ if (!Receiver)
+ return false;
+ ClassId = Receiver->getIdentifier();
+
+ if (Msg->getReceiverKind() == ObjCMessageExpr::Class)
+ return true;
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteObjCRedundantCallWithLiteral.
+//===----------------------------------------------------------------------===//
+
+bool edit::rewriteObjCRedundantCallWithLiteral(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ IdentifierInfo *II = 0;
+ if (!checkForLiteralCreation(Msg, II))
+ return false;
+ if (Msg->getNumArgs() != 1)
+ return false;
+
+ const Expr *Arg = Msg->getArg(0)->IgnoreParenImpCasts();
+ Selector Sel = Msg->getSelector();
+
+ if ((isa<ObjCStringLiteral>(Arg) &&
+ NS.getNSClassId(NSAPI::ClassId_NSString) == II &&
+ NS.getNSStringSelector(NSAPI::NSStr_stringWithString) == Sel) ||
+
+ (isa<ObjCArrayLiteral>(Arg) &&
+ NS.getNSClassId(NSAPI::ClassId_NSArray) == II &&
+ NS.getNSArraySelector(NSAPI::NSArr_arrayWithArray) == Sel) ||
+
+ (isa<ObjCDictionaryLiteral>(Arg) &&
+ NS.getNSClassId(NSAPI::ClassId_NSDictionary) == II &&
+ NS.getNSDictionarySelector(
+ NSAPI::NSDict_dictionaryWithDictionary) == Sel)) {
+
+ commit.replaceWithInner(Msg->getSourceRange(),
+ Msg->getArg(0)->getSourceRange());
+ return true;
+ }
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteToObjCSubscriptSyntax.
+//===----------------------------------------------------------------------===//
+
+static void maybePutParensOnReceiver(const Expr *Receiver, Commit &commit) {
+ Receiver = Receiver->IgnoreImpCasts();
+ if (isa<BinaryOperator>(Receiver) || isa<UnaryOperator>(Receiver)) {
+ SourceRange RecRange = Receiver->getSourceRange();
+ commit.insertWrap("(", RecRange, ")");
+ }
+}
+
+static bool rewriteToSubscriptGet(const ObjCMessageExpr *Msg, Commit &commit) {
+ if (Msg->getNumArgs() != 1)
+ return false;
+ const Expr *Rec = Msg->getInstanceReceiver();
+ if (!Rec)
+ return false;
+
+ SourceRange MsgRange = Msg->getSourceRange();
+ SourceRange RecRange = Rec->getSourceRange();
+ SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
+
+ commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
+ ArgRange.getBegin()),
+ CharSourceRange::getTokenRange(RecRange));
+ commit.replaceWithInner(SourceRange(ArgRange.getBegin(), MsgRange.getEnd()),
+ ArgRange);
+ commit.insertWrap("[", ArgRange, "]");
+ maybePutParensOnReceiver(Rec, commit);
+ return true;
+}
+
+static bool rewriteToArraySubscriptSet(const ObjCMessageExpr *Msg,
+ Commit &commit) {
+ if (Msg->getNumArgs() != 2)
+ return false;
+ const Expr *Rec = Msg->getInstanceReceiver();
+ if (!Rec)
+ return false;
+
+ SourceRange MsgRange = Msg->getSourceRange();
+ SourceRange RecRange = Rec->getSourceRange();
+ SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
+ SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
+
+ commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
+ Arg0Range.getBegin()),
+ CharSourceRange::getTokenRange(RecRange));
+ commit.replaceWithInner(CharSourceRange::getCharRange(Arg0Range.getBegin(),
+ Arg1Range.getBegin()),
+ CharSourceRange::getTokenRange(Arg0Range));
+ commit.replaceWithInner(SourceRange(Arg1Range.getBegin(), MsgRange.getEnd()),
+ Arg1Range);
+ commit.insertWrap("[", CharSourceRange::getCharRange(Arg0Range.getBegin(),
+ Arg1Range.getBegin()),
+ "] = ");
+ maybePutParensOnReceiver(Rec, commit);
+ return true;
+}
+
+static bool rewriteToDictionarySubscriptSet(const ObjCMessageExpr *Msg,
+ Commit &commit) {
+ if (Msg->getNumArgs() != 2)
+ return false;
+ const Expr *Rec = Msg->getInstanceReceiver();
+ if (!Rec)
+ return false;
+
+ SourceRange MsgRange = Msg->getSourceRange();
+ SourceRange RecRange = Rec->getSourceRange();
+ SourceRange Arg0Range = Msg->getArg(0)->getSourceRange();
+ SourceRange Arg1Range = Msg->getArg(1)->getSourceRange();
+
+ SourceLocation LocBeforeVal = Arg0Range.getBegin();
+ commit.insertBefore(LocBeforeVal, "] = ");
+ commit.insertFromRange(LocBeforeVal, Arg1Range, /*afterToken=*/false,
+ /*beforePreviousInsertions=*/true);
+ commit.insertBefore(LocBeforeVal, "[");
+ commit.replaceWithInner(CharSourceRange::getCharRange(MsgRange.getBegin(),
+ Arg0Range.getBegin()),
+ CharSourceRange::getTokenRange(RecRange));
+ commit.replaceWithInner(SourceRange(Arg0Range.getBegin(), MsgRange.getEnd()),
+ Arg0Range);
+ maybePutParensOnReceiver(Rec, commit);
+ return true;
+}
+
+bool edit::rewriteToObjCSubscriptSyntax(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ if (!Msg || Msg->isImplicit() ||
+ Msg->getReceiverKind() != ObjCMessageExpr::Instance)
+ return false;
+ const ObjCMethodDecl *Method = Msg->getMethodDecl();
+ if (!Method)
+ return false;
+
+ const ObjCInterfaceDecl *
+ IFace = NS.getASTContext().getObjContainingInterface(
+ const_cast<ObjCMethodDecl *>(Method));
+ if (!IFace)
+ return false;
+ IdentifierInfo *II = IFace->getIdentifier();
+ Selector Sel = Msg->getSelector();
+
+ if ((II == NS.getNSClassId(NSAPI::ClassId_NSArray) &&
+ Sel == NS.getNSArraySelector(NSAPI::NSArr_objectAtIndex)) ||
+ (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary) &&
+ Sel == NS.getNSDictionarySelector(NSAPI::NSDict_objectForKey)))
+ return rewriteToSubscriptGet(Msg, commit);
+
+ if (Msg->getNumArgs() != 2)
+ return false;
+
+ if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableArray) &&
+ Sel == NS.getNSArraySelector(NSAPI::NSMutableArr_replaceObjectAtIndex))
+ return rewriteToArraySubscriptSet(Msg, commit);
+
+ if (II == NS.getNSClassId(NSAPI::ClassId_NSMutableDictionary) &&
+ Sel == NS.getNSDictionarySelector(NSAPI::NSMutableDict_setObjectForKey))
+ return rewriteToDictionarySubscriptSet(Msg, commit);
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteToObjCLiteralSyntax.
+//===----------------------------------------------------------------------===//
+
+static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit);
+static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit);
+static bool rewriteToNumberLiteral(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit);
+
+bool edit::rewriteToObjCLiteralSyntax(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ IdentifierInfo *II = 0;
+ if (!checkForLiteralCreation(Msg, II))
+ return false;
+
+ if (II == NS.getNSClassId(NSAPI::ClassId_NSArray))
+ return rewriteToArrayLiteral(Msg, NS, commit);
+ if (II == NS.getNSClassId(NSAPI::ClassId_NSDictionary))
+ return rewriteToDictionaryLiteral(Msg, NS, commit);
+ if (II == NS.getNSClassId(NSAPI::ClassId_NSNumber))
+ return rewriteToNumberLiteral(Msg, NS, commit);
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteToArrayLiteral.
+//===----------------------------------------------------------------------===//
+
+static bool rewriteToArrayLiteral(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ Selector Sel = Msg->getSelector();
+ SourceRange MsgRange = Msg->getSourceRange();
+
+ if (Sel == NS.getNSArraySelector(NSAPI::NSArr_array)) {
+ if (Msg->getNumArgs() != 0)
+ return false;
+ commit.replace(MsgRange, "@[]");
+ return true;
+ }
+
+ if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObject)) {
+ if (Msg->getNumArgs() != 1)
+ return false;
+ SourceRange ArgRange = Msg->getArg(0)->getSourceRange();
+ commit.replaceWithInner(MsgRange, ArgRange);
+ commit.insertWrap("@[", ArgRange, "]");
+ return true;
+ }
+
+ if (Sel == NS.getNSArraySelector(NSAPI::NSArr_arrayWithObjects)) {
+ if (Msg->getNumArgs() == 0)
+ return false;
+ const Expr *SentinelExpr = Msg->getArg(Msg->getNumArgs() - 1);
+ if (!NS.getASTContext().isSentinelNullExpr(SentinelExpr))
+ return false;
+
+ if (Msg->getNumArgs() == 1) {
+ commit.replace(MsgRange, "@[]");
+ return true;
+ }
+ SourceRange ArgRange(Msg->getArg(0)->getLocStart(),
+ Msg->getArg(Msg->getNumArgs()-2)->getLocEnd());
+ commit.replaceWithInner(MsgRange, ArgRange);
+ commit.insertWrap("@[", ArgRange, "]");
+ return true;
+ }
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// rewriteToDictionaryLiteral.
+//===----------------------------------------------------------------------===//
+
+static bool rewriteToDictionaryLiteral(const ObjCMessageExpr *Msg,
+ const NSAPI &NS, Commit &commit) {
+ Selector Sel = Msg->getSelector();
+ SourceRange MsgRange = Msg->getSourceRange();
+
+ if (Sel == NS.getNSDictionarySelector(NSAPI::NSDict_dictionary)) {
+ if (Msg->getNumArgs() != 0)
+ return false;
+ commit.replace(MsgRange, "@{}");
+ return true;
+ }
+
+ if (Sel == NS.getNSDictionarySelector(
+ NSAPI::NSDict_dictionaryWithObjectForKey)) {
+ if (Msg->getNumArgs() != 2)
+ return false;
+ SourceRange ValRange = Msg->getArg(0)->getSourceRange();
+ SourceRange KeyRange = Msg->getArg(1)->getSourceRange();
+ // Insert key before the value.
+ commit.insertBefore(ValRange.getBegin(), ": ");
+ commit.insertFromRange(ValRange.getBegin(),