clang API Documentation
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 }