clang API Documentation

Commit.cpp
Go to the documentation of this file.
00001 //===----- Commit.cpp - A unit of edits -----------------------------------===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 
00010 #include "clang/Edit/Commit.h"
00011 #include "clang/Basic/SourceManager.h"
00012 #include "clang/Edit/EditedSource.h"
00013 #include "clang/Lex/Lexer.h"
00014 #include "clang/Lex/PPConditionalDirectiveRecord.h"
00015 
00016 using namespace clang;
00017 using namespace edit;
00018 
00019 SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {
00020   SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());
00021   Loc = Loc.getLocWithOffset(Offset.getOffset());
00022   assert(Loc.isFileID());
00023   return Loc;
00024 }
00025 
00026 CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {
00027   SourceLocation Loc = getFileLocation(SM);
00028   return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
00029 }
00030 
00031 CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {
00032   SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());
00033   Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());
00034   assert(Loc.isFileID());
00035   return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));
00036 }
00037 
00038 Commit::Commit(EditedSource &Editor)
00039   : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),
00040     PPRec(Editor.getPPCondDirectiveRecord()),
00041     Editor(&Editor), IsCommitable(true) { }
00042 
00043 bool Commit::insert(SourceLocation loc, StringRef text,
00044                     bool afterToken, bool beforePreviousInsertions) {
00045   if (text.empty())
00046     return true;
00047 
00048   FileOffset Offs;
00049   if ((!afterToken && !canInsert(loc, Offs)) ||
00050       ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
00051     IsCommitable = false;
00052     return false;
00053   }
00054 
00055   addInsert(loc, Offs, text, beforePreviousInsertions);
00056   return true;
00057 }
00058 
00059 bool Commit::insertFromRange(SourceLocation loc,
00060                              CharSourceRange range,
00061                              bool afterToken, bool beforePreviousInsertions) {
00062   FileOffset RangeOffs;
00063   unsigned RangeLen;
00064   if (!canRemoveRange(range, RangeOffs, RangeLen)) {
00065     IsCommitable = false;
00066     return false;
00067   }
00068 
00069   FileOffset Offs;
00070   if ((!afterToken && !canInsert(loc, Offs)) ||
00071       ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {
00072     IsCommitable = false;
00073     return false;
00074   }
00075 
00076   if (PPRec &&
00077       PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {
00078     IsCommitable = false;
00079     return false;
00080   }
00081 
00082   addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);
00083   return true;
00084 }
00085 
00086 bool Commit::remove(CharSourceRange range) {
00087   FileOffset Offs;
00088   unsigned Len;
00089   if (!canRemoveRange(range, Offs, Len)) {
00090     IsCommitable = false;
00091     return false;
00092   }
00093 
00094   addRemove(range.getBegin(), Offs, Len);
00095   return true;
00096 }
00097 
00098 bool Commit::insertWrap(StringRef before, CharSourceRange range,
00099                         StringRef after) {
00100   bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,
00101                                  /*beforePreviousInsertions=*/true);
00102   bool commitableAfter;
00103   if (range.isTokenRange())
00104     commitableAfter = insertAfterToken(range.getEnd(), after);
00105   else
00106     commitableAfter = insert(range.getEnd(), after);
00107 
00108   return commitableBefore && commitableAfter;
00109 }
00110 
00111 bool Commit::replace(CharSourceRange range, StringRef text) {
00112   if (text.empty())
00113     return remove(range);
00114 
00115   FileOffset Offs;
00116   unsigned Len;
00117   if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {
00118     IsCommitable = false;
00119     return false;
00120   }
00121 
00122   addRemove(range.getBegin(), Offs, Len);
00123   addInsert(range.getBegin(), Offs, text, false);
00124   return true;
00125 }
00126 
00127 bool Commit::replaceWithInner(CharSourceRange range,
00128                               CharSourceRange replacementRange) {
00129   FileOffset OuterBegin;
00130   unsigned OuterLen;
00131   if (!canRemoveRange(range, OuterBegin, OuterLen)) {
00132     IsCommitable = false;
00133     return false;
00134   }
00135 
00136   FileOffset InnerBegin;
00137   unsigned InnerLen;
00138   if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {
00139     IsCommitable = false;
00140     return false;
00141   }
00142 
00143   FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);
00144   FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen); 
00145   if (OuterBegin.getFID() != InnerBegin.getFID() ||
00146       InnerBegin < OuterBegin ||
00147       InnerBegin > OuterEnd ||
00148       InnerEnd > OuterEnd) {
00149     IsCommitable = false;
00150     return false;
00151   }
00152 
00153   addRemove(range.getBegin(),
00154             OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());
00155   addRemove(replacementRange.getEnd(),
00156             InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());
00157   return true;
00158 }
00159 
00160 bool Commit::replaceText(SourceLocation loc, StringRef text,
00161                          StringRef replacementText) {
00162   if (text.empty() || replacementText.empty())
00163     return true;
00164 
00165   FileOffset Offs;
00166   unsigned Len;
00167   if (!canReplaceText(loc, replacementText, Offs, Len)) {
00168     IsCommitable = false;
00169     return false;
00170   }
00171 
00172   addRemove(loc, Offs, Len);
00173   addInsert(loc, Offs, text, false);
00174   return true;
00175 }
00176 
00177 void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,
00178                        bool beforePreviousInsertions) {
00179   if (text.empty())
00180     return;
00181 
00182   Edit data;
00183   data.Kind = Act_Insert;
00184   data.OrigLoc = OrigLoc;
00185   data.Offset = Offs;
00186   data.Text = copyString(text);
00187   data.BeforePrev = beforePreviousInsertions;
00188   CachedEdits.push_back(data);
00189 }
00190 
00191 void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,
00192                                 FileOffset RangeOffs, unsigned RangeLen,
00193                                 bool beforePreviousInsertions) {
00194   if (RangeLen == 0)
00195     return;
00196 
00197   Edit data;
00198   data.Kind = Act_InsertFromRange;
00199   data.OrigLoc = OrigLoc;
00200   data.Offset = Offs;
00201   data.InsertFromRangeOffs = RangeOffs;
00202   data.Length = RangeLen;
00203   data.BeforePrev = beforePreviousInsertions;
00204   CachedEdits.push_back(data);
00205 }
00206 
00207 void Commit::addRemove(SourceLocation OrigLoc,
00208                        FileOffset Offs, unsigned Len) {
00209   if (Len == 0)
00210     return;
00211 
00212   Edit data;
00213   data.Kind = Act_Remove;
00214   data.OrigLoc = OrigLoc;
00215   data.Offset = Offs;
00216   data.Length = Len;
00217   CachedEdits.push_back(data);
00218 }
00219 
00220 bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {
00221   if (loc.isInvalid())
00222     return false;
00223 
00224   if (loc.isMacroID())
00225     isAtStartOfMacroExpansion(loc, &loc);
00226 
00227   const SourceManager &SM = SourceMgr;
00228   while (SM.isMacroArgExpansion(loc))
00229     loc = SM.getImmediateSpellingLoc(loc);
00230 
00231   if (loc.isMacroID())
00232     if (!isAtStartOfMacroExpansion(loc, &loc))
00233       return false;
00234 
00235   if (SM.isInSystemHeader(loc))
00236     return false;
00237 
00238   std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
00239   if (locInfo.first.isInvalid())
00240     return false;
00241   offs = FileOffset(locInfo.first, locInfo.second);
00242   return canInsertInOffset(loc, offs);
00243 }
00244 
00245 bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,
00246                                  SourceLocation &AfterLoc) {
00247   if (loc.isInvalid())
00248 
00249     return false;
00250 
00251   SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);
00252   unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);
00253   AfterLoc = loc.getLocWithOffset(tokLen);
00254 
00255   if (loc.isMacroID())
00256     isAtEndOfMacroExpansion(loc, &loc);
00257 
00258   const SourceManager &SM = SourceMgr;
00259   while (SM.isMacroArgExpansion(loc))
00260     loc = SM.getImmediateSpellingLoc(loc);
00261 
00262   if (loc.isMacroID())
00263     if (!isAtEndOfMacroExpansion(loc, &loc))
00264       return false;
00265 
00266   if (SM.isInSystemHeader(loc))
00267     return false;
00268 
00269   loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);
00270   if (loc.isInvalid())
00271     return false;
00272 
00273   std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);
00274   if (locInfo.first.isInvalid())
00275     return false;
00276   offs = FileOffset(locInfo.first, locInfo.second);
00277   return canInsertInOffset(loc, offs);
00278 }
00279 
00280 bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
00281   for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) {
00282     Edit &act = CachedEdits[i];
00283     if (act.Kind == Act_Remove) {
00284       if (act.Offset.getFID() == Offs.getFID() &&
00285           Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))
00286         return false; // position has been removed.
00287     }
00288   }
00289 
00290   if (!Editor)
00291     return true;
00292   return Editor->canInsertInOffset(OrigLoc, Offs);
00293 }
00294 
00295 bool Commit::canRemoveRange(CharSourceRange range,
00296                             FileOffset &Offs, unsigned &Len) {
00297   const SourceManager &SM = SourceMgr;
00298   range = Lexer::makeFileCharRange(range, SM, LangOpts);
00299   if (range.isInvalid())
00300     return false;
00301   
00302   if (range.getBegin().isMacroID() || range.getEnd().isMacroID())
00303     return false;
00304   if (SM.isInSystemHeader(range.getBegin()) ||
00305       SM.isInSystemHeader(range.getEnd()))
00306     return false;
00307 
00308   if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))
00309     return false;
00310 
00311   std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());
00312   std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());
00313   if (beginInfo.first != endInfo.first ||
00314       beginInfo.second > endInfo.second)
00315     return false;
00316 
00317   Offs = FileOffset(beginInfo.first, beginInfo.second);
00318   Len = endInfo.second - beginInfo.second;
00319   return true;
00320 }
00321 
00322 bool Commit::canReplaceText(SourceLocation loc, StringRef text,
00323                             FileOffset &Offs, unsigned &Len) {
00324   assert(!text.empty());
00325 
00326   if (!canInsert(loc, Offs))
00327     return false;
00328 
00329   // Try to load the file buffer.
00330   bool invalidTemp = false;
00331   StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);
00332   if (invalidTemp)
00333     return false;
00334 
00335   Len = text.size();
00336   return file.substr(Offs.getOffset()).startswith(text);
00337 }
00338 
00339 bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,
00340                                        SourceLocation *MacroBegin) const {
00341   return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);
00342 }
00343 bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,
00344                                      SourceLocation *MacroEnd) const {
00345   return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);
00346 }