diff options
author | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:06:33 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2012-03-06 20:06:33 +0000 |
commit | f7639e1b4a6c8cbf69c0005c6173ac863735b572 (patch) | |
tree | ec10ddb85eb9803b67b1053bcac26e0050810ff3 /clang/lib/Edit/Commit.cpp | |
parent | d151dde0e6673a15e9bf0b5f328df461dfd8cae9 (diff) | |
download | bcm5719-llvm-f7639e1b4a6c8cbf69c0005c6173ac863735b572.tar.gz bcm5719-llvm-f7639e1b4a6c8cbf69c0005c6173ac863735b572.zip |
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.
llvm-svn: 152141
Diffstat (limited to 'clang/lib/Edit/Commit.cpp')
-rw-r--r-- | clang/lib/Edit/Commit.cpp | 345 |
1 files changed, 345 insertions, 0 deletions
diff --git a/clang/lib/Edit/Commit.cpp b/clang/lib/Edit/Commit.cpp new file mode 100644 index 00000000000..e0250ccf056 --- /dev/null +++ b/clang/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); +} |