Home | History | Annotate | Download | only in Edit
      1 //===----- EditedSource.cpp - Collection of source edits ------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/Edit/EditedSource.h"
     11 #include "clang/Edit/Commit.h"
     12 #include "clang/Edit/EditsReceiver.h"
     13 #include "clang/Lex/Lexer.h"
     14 #include "clang/Basic/SourceManager.h"
     15 #include "llvm/ADT/SmallString.h"
     16 #include "llvm/ADT/Twine.h"
     17 
     18 using namespace clang;
     19 using namespace edit;
     20 
     21 void EditsReceiver::remove(CharSourceRange range) {
     22   replace(range, StringRef());
     23 }
     24 
     25 StringRef EditedSource::copyString(const Twine &twine) {
     26   llvm::SmallString<128> Data;
     27   return copyString(twine.toStringRef(Data));
     28 }
     29 
     30 bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
     31   FileEditsTy::iterator FA = getActionForOffset(Offs);
     32   if (FA != FileEdits.end()) {
     33     if (FA->first != Offs)
     34       return false; // position has been removed.
     35   }
     36 
     37   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
     38     SourceLocation
     39       DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
     40     SourceLocation
     41       ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
     42     llvm::DenseMap<unsigned, SourceLocation>::iterator
     43       I = ExpansionToArgMap.find(ExpLoc.getRawEncoding());
     44     if (I != ExpansionToArgMap.end() && I->second != DefArgLoc)
     45       return false; // Trying to write in a macro argument input that has
     46                  // already been written for another argument of the same macro.
     47   }
     48 
     49   return true;
     50 }
     51 
     52 bool EditedSource::commitInsert(SourceLocation OrigLoc,
     53                                 FileOffset Offs, StringRef text,
     54                                 bool beforePreviousInsertions) {
     55   if (!canInsertInOffset(OrigLoc, Offs))
     56     return false;
     57   if (text.empty())
     58     return true;
     59 
     60   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
     61     SourceLocation
     62       DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first;
     63     SourceLocation
     64       ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first;
     65     ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc;
     66   }
     67 
     68   FileEdit &FA = FileEdits[Offs];
     69   if (FA.Text.empty()) {
     70     FA.Text = copyString(text);
     71     return true;
     72   }
     73 
     74   Twine concat;
     75   if (beforePreviousInsertions)
     76     concat = Twine(text) + FA.Text;
     77   else
     78     concat = Twine(FA.Text) +  text;
     79 
     80   FA.Text = copyString(concat);
     81   return true;
     82 }
     83 
     84 bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
     85                                    FileOffset Offs,
     86                                    FileOffset InsertFromRangeOffs, unsigned Len,
     87                                    bool beforePreviousInsertions) {
     88   if (Len == 0)
     89     return true;
     90 
     91   llvm::SmallString<128> StrVec;
     92   FileOffset BeginOffs = InsertFromRangeOffs;
     93   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
     94   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
     95   if (I != FileEdits.begin())
     96     --I;
     97 
     98   for (; I != FileEdits.end(); ++I) {
     99     FileEdit &FA = I->second;
    100     FileOffset B = I->first;
    101     FileOffset E = B.getWithOffset(FA.RemoveLen);
    102 
    103     if (BeginOffs < E) {
    104       if (BeginOffs >= B) {
    105         BeginOffs = E;
    106         ++I;
    107       }
    108       break;
    109     }
    110   }
    111 
    112   for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
    113     FileEdit &FA = I->second;
    114     FileOffset B = I->first;
    115     FileOffset E = B.getWithOffset(FA.RemoveLen);
    116 
    117     if (BeginOffs < B) {
    118       bool Invalid = false;
    119       StringRef text = getSourceText(BeginOffs, B, Invalid);
    120       if (Invalid)
    121         return false;
    122       StrVec += text;
    123     }
    124     StrVec += FA.Text;
    125     BeginOffs = E;
    126   }
    127 
    128   if (BeginOffs < EndOffs) {
    129     bool Invalid = false;
    130     StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
    131     if (Invalid)
    132       return false;
    133     StrVec += text;
    134   }
    135 
    136   return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions);
    137 }
    138 
    139 void EditedSource::commitRemove(SourceLocation OrigLoc,
    140                                 FileOffset BeginOffs, unsigned Len) {
    141   if (Len == 0)
    142     return;
    143 
    144   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
    145   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
    146   if (I != FileEdits.begin())
    147     --I;
    148 
    149   for (; I != FileEdits.end(); ++I) {
    150     FileEdit &FA = I->second;
    151     FileOffset B = I->first;
    152     FileOffset E = B.getWithOffset(FA.RemoveLen);
    153 
    154     if (BeginOffs < E)
    155       break;
    156   }
    157 
    158   FileOffset TopBegin, TopEnd;
    159   FileEdit *TopFA = 0;
    160 
    161   if (I == FileEdits.end()) {
    162     FileEditsTy::iterator
    163       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
    164     NewI->second.RemoveLen = Len;
    165     return;
    166   }
    167 
    168   FileEdit &FA = I->second;
    169   FileOffset B = I->first;
    170   FileOffset E = B.getWithOffset(FA.RemoveLen);
    171   if (BeginOffs < B) {
    172     FileEditsTy::iterator
    173       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
    174     TopBegin = BeginOffs;
    175     TopEnd = EndOffs;
    176     TopFA = &NewI->second;
    177     TopFA->RemoveLen = Len;
    178   } else {
    179     TopBegin = B;
    180     TopEnd = E;
    181     TopFA = &I->second;
    182     if (TopEnd >= EndOffs)
    183       return;
    184     unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
    185     TopEnd = EndOffs;
    186     TopFA->RemoveLen += diff;
    187     ++I;
    188   }
    189 
    190   while (I != FileEdits.end()) {
    191     FileEdit &FA = I->second;
    192     FileOffset B = I->first;
    193     FileOffset E = B.getWithOffset(FA.RemoveLen);
    194 
    195     if (B >= TopEnd)
    196       break;
    197 
    198     if (E <= TopEnd) {
    199       FileEdits.erase(I++);
    200       continue;
    201     }
    202 
    203     if (B < TopEnd) {
    204       unsigned diff = E.getOffset() - TopEnd.getOffset();
    205       TopEnd = E;
    206       TopFA->RemoveLen += diff;
    207       FileEdits.erase(I);
    208     }
    209 
    210     break;
    211   }
    212 }
    213 
    214 bool EditedSource::commit(const Commit &commit) {
    215   if (!commit.isCommitable())
    216     return false;
    217 
    218   for (edit::Commit::edit_iterator
    219          I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
    220     const edit::Commit::Edit &edit = *I;
    221     switch (edit.Kind) {
    222     case edit::Commit::Act_Insert:
    223       commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
    224       break;
    225     case edit::Commit::Act_InsertFromRange:
    226       commitInsertFromRange(edit.OrigLoc, edit.Offset,
    227                             edit.InsertFromRangeOffs, edit.Length,
    228                             edit.BeforePrev);
    229       break;
    230     case edit::Commit::Act_Remove:
    231       commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
    232       break;
    233     }
    234   }
    235 
    236   return true;
    237 }
    238 
    239 static void applyRewrite(EditsReceiver &receiver,
    240                          StringRef text, FileOffset offs, unsigned len,
    241                          const SourceManager &SM) {
    242   assert(!offs.getFID().isInvalid());
    243   SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
    244   Loc = Loc.getLocWithOffset(offs.getOffset());
    245   assert(Loc.isFileID());
    246   CharSourceRange range = CharSourceRange::getCharRange(Loc,
    247                                                      Loc.getLocWithOffset(len));
    248 
    249   if (text.empty()) {
    250     assert(len);
    251     receiver.remove(range);
    252     return;
    253   }
    254 
    255   if (len)
    256     receiver.replace(range, text);
    257   else
    258     receiver.insert(Loc, text);
    259 }
    260 
    261 void EditedSource::applyRewrites(EditsReceiver &receiver) {
    262   llvm::SmallString<128> StrVec;
    263   FileOffset CurOffs, CurEnd;
    264   unsigned CurLen;
    265 
    266   if (FileEdits.empty())
    267     return;
    268 
    269   FileEditsTy::iterator I = FileEdits.begin();
    270   CurOffs = I->first;
    271   StrVec = I->second.Text;
    272   CurLen = I->second.RemoveLen;
    273   CurEnd = CurOffs.getWithOffset(CurLen);
    274   ++I;
    275 
    276   for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
    277     FileOffset offs = I->first;
    278     FileEdit act = I->second;
    279     assert(offs >= CurEnd);
    280 
    281     if (offs == CurEnd) {
    282       StrVec += act.Text;
    283       CurLen += act.RemoveLen;
    284       CurEnd.getWithOffset(act.RemoveLen);
    285       continue;
    286     }
    287 
    288     applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
    289     CurOffs = offs;
    290     StrVec = act.Text;
    291     CurLen = act.RemoveLen;
    292     CurEnd = CurOffs.getWithOffset(CurLen);
    293   }
    294 
    295   applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr);
    296 }
    297 
    298 void EditedSource::clearRewrites() {
    299   FileEdits.clear();
    300   StrAlloc.Reset();
    301 }
    302 
    303 StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
    304                                       bool &Invalid) {
    305   assert(BeginOffs.getFID() == EndOffs.getFID());
    306   assert(BeginOffs <= EndOffs);
    307   SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
    308   BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
    309   assert(BLoc.isFileID());
    310   SourceLocation
    311     ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
    312   return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
    313                               SourceMgr, LangOpts, &Invalid);
    314 }
    315 
    316 EditedSource::FileEditsTy::iterator
    317 EditedSource::getActionForOffset(FileOffset Offs) {
    318   FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
    319   if (I == FileEdits.begin())
    320     return FileEdits.end();
    321   --I;
    322   FileEdit &FA = I->second;
    323   FileOffset B = I->first;
    324   FileOffset E = B.getWithOffset(FA.RemoveLen);
    325   if (Offs >= B && Offs < E)
    326     return I;
    327 
    328   return FileEdits.end();
    329 }
    330