brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.0 KiB · 415b157 Raw
348 lines · cpp
1//===- Commit.cpp - A unit of edits ---------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Edit/Commit.h"10#include "clang/Basic/LLVM.h"11#include "clang/Basic/SourceLocation.h"12#include "clang/Basic/SourceManager.h"13#include "clang/Edit/EditedSource.h"14#include "clang/Edit/FileOffset.h"15#include "clang/Lex/Lexer.h"16#include "clang/Lex/PPConditionalDirectiveRecord.h"17#include "llvm/ADT/StringRef.h"18#include <cassert>19 20using namespace clang;21using namespace edit;22 23SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {24  SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());25  Loc = Loc.getLocWithOffset(Offset.getOffset());26  assert(Loc.isFileID());27  return Loc;28}29 30CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {31  SourceLocation Loc = getFileLocation(SM);32  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));33}34 35CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {36  SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());37  Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());38  assert(Loc.isFileID());39  return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));40}41 42Commit::Commit(EditedSource &Editor)43    : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),44      PPRec(Editor.getPPCondDirectiveRecord()),45      Editor(&Editor) {}46 47bool Commit::insert(SourceLocation loc, StringRef text,48                    bool afterToken, bool beforePreviousInsertions) {49  if (text.empty())50    return true;51 52  FileOffset Offs;53  if ((!afterToken && !canInsert(loc, Offs)) ||54      ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {55    IsCommitable = false;56    return false;57  }58 59  addInsert(loc, Offs, text, beforePreviousInsertions);60  return true;61}62 63bool Commit::insertFromRange(SourceLocation loc,64                             CharSourceRange range,65                             bool afterToken, bool beforePreviousInsertions) {66  FileOffset RangeOffs;67  unsigned RangeLen;68  if (!canRemoveRange(range, RangeOffs, RangeLen)) {69    IsCommitable = false;70    return false;71  }72 73  FileOffset Offs;74  if ((!afterToken && !canInsert(loc, Offs)) ||75      ( afterToken && !canInsertAfterToken(loc, Offs, loc))) {76    IsCommitable = false;77    return false;78  }79 80  if (PPRec &&81      PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {82    IsCommitable = false;83    return false;84  }85 86  addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);87  return true;88}89 90bool Commit::remove(CharSourceRange range) {91  FileOffset Offs;92  unsigned Len;93  if (!canRemoveRange(range, Offs, Len)) {94    IsCommitable = false;95    return false;96  }97 98  addRemove(range.getBegin(), Offs, Len);99  return true;100}101 102bool Commit::insertWrap(StringRef before, CharSourceRange range,103                        StringRef after) {104  bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,105                                 /*beforePreviousInsertions=*/true);106  bool commitableAfter;107  if (range.isTokenRange())108    commitableAfter = insertAfterToken(range.getEnd(), after);109  else110    commitableAfter = insert(range.getEnd(), after);111 112  return commitableBefore && commitableAfter;113}114 115bool Commit::replace(CharSourceRange range, StringRef text) {116  if (text.empty())117    return remove(range);118 119  FileOffset Offs;120  unsigned Len;121  if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {122    IsCommitable = false;123    return false;124  }125 126  addRemove(range.getBegin(), Offs, Len);127  addInsert(range.getBegin(), Offs, text, false);128  return true;129}130 131bool Commit::replaceWithInner(CharSourceRange range,132                              CharSourceRange replacementRange) {133  FileOffset OuterBegin;134  unsigned OuterLen;135  if (!canRemoveRange(range, OuterBegin, OuterLen)) {136    IsCommitable = false;137    return false;138  }139 140  FileOffset InnerBegin;141  unsigned InnerLen;142  if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {143    IsCommitable = false;144    return false;145  }146 147  FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);148  FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);149  if (OuterBegin.getFID() != InnerBegin.getFID() ||150      InnerBegin < OuterBegin ||151      InnerBegin > OuterEnd ||152      InnerEnd > OuterEnd) {153    IsCommitable = false;154    return false;155  }156 157  addRemove(range.getBegin(),158            OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());159  addRemove(replacementRange.getEnd(),160            InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());161  return true;162}163 164bool Commit::replaceText(SourceLocation loc, StringRef text,165                         StringRef replacementText) {166  if (text.empty() || replacementText.empty())167    return true;168 169  FileOffset Offs;170  unsigned Len;171  if (!canReplaceText(loc, replacementText, Offs, Len)) {172    IsCommitable = false;173    return false;174  }175 176  addRemove(loc, Offs, Len);177  addInsert(loc, Offs, text, false);178  return true;179}180 181void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,182                       bool beforePreviousInsertions) {183  if (text.empty())184    return;185 186  Edit data;187  data.Kind = Act_Insert;188  data.OrigLoc = OrigLoc;189  data.Offset = Offs;190  data.Text = text.copy(StrAlloc);191  data.BeforePrev = beforePreviousInsertions;192  CachedEdits.push_back(data);193}194 195void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,196                                FileOffset RangeOffs, unsigned RangeLen,197                                bool beforePreviousInsertions) {198  if (RangeLen == 0)199    return;200 201  Edit data;202  data.Kind = Act_InsertFromRange;203  data.OrigLoc = OrigLoc;204  data.Offset = Offs;205  data.InsertFromRangeOffs = RangeOffs;206  data.Length = RangeLen;207  data.BeforePrev = beforePreviousInsertions;208  CachedEdits.push_back(data);209}210 211void Commit::addRemove(SourceLocation OrigLoc,212                       FileOffset Offs, unsigned Len) {213  if (Len == 0)214    return;215 216  Edit data;217  data.Kind = Act_Remove;218  data.OrigLoc = OrigLoc;219  data.Offset = Offs;220  data.Length = Len;221  CachedEdits.push_back(data);222}223 224bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {225  if (loc.isInvalid())226    return false;227 228  if (loc.isMacroID())229    isAtStartOfMacroExpansion(loc, &loc);230 231  const SourceManager &SM = SourceMgr;232  loc = SM.getTopMacroCallerLoc(loc);233 234  if (loc.isMacroID())235    if (!isAtStartOfMacroExpansion(loc, &loc))236      return false;237 238  if (SM.isInSystemHeader(loc))239    return false;240 241  FileIDAndOffset locInfo = SM.getDecomposedLoc(loc);242  if (locInfo.first.isInvalid())243    return false;244  offs = FileOffset(locInfo.first, locInfo.second);245  return canInsertInOffset(loc, offs);246}247 248bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,249                                 SourceLocation &AfterLoc) {250  if (loc.isInvalid())251 252    return false;253 254  SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);255  unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);256  AfterLoc = loc.getLocWithOffset(tokLen);257 258  if (loc.isMacroID())259    isAtEndOfMacroExpansion(loc, &loc);260 261  const SourceManager &SM = SourceMgr;262  loc = SM.getTopMacroCallerLoc(loc);263 264  if (loc.isMacroID())265    if (!isAtEndOfMacroExpansion(loc, &loc))266      return false;267 268  if (SM.isInSystemHeader(loc))269    return false;270 271  loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);272  if (loc.isInvalid())273    return false;274 275  FileIDAndOffset locInfo = SM.getDecomposedLoc(loc);276  if (locInfo.first.isInvalid())277    return false;278  offs = FileOffset(locInfo.first, locInfo.second);279  return canInsertInOffset(loc, offs);280}281 282bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {283  for (const auto &act : CachedEdits)284    if (act.Kind == Act_Remove) {285      if (act.Offset.getFID() == Offs.getFID() &&286          Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))287        return false; // position has been removed.288    }289 290  if (!Editor)291    return true;292  return Editor->canInsertInOffset(OrigLoc, Offs);293}294 295bool Commit::canRemoveRange(CharSourceRange range,296                            FileOffset &Offs, unsigned &Len) {297  const SourceManager &SM = SourceMgr;298  range = Lexer::makeFileCharRange(range, SM, LangOpts);299  if (range.isInvalid())300    return false;301 302  if (range.getBegin().isMacroID() || range.getEnd().isMacroID())303    return false;304  if (SM.isInSystemHeader(range.getBegin()) ||305      SM.isInSystemHeader(range.getEnd()))306    return false;307 308  if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))309    return false;310 311  FileIDAndOffset beginInfo = SM.getDecomposedLoc(range.getBegin());312  FileIDAndOffset endInfo = SM.getDecomposedLoc(range.getEnd());313  if (beginInfo.first != endInfo.first ||314      beginInfo.second > endInfo.second)315    return false;316 317  Offs = FileOffset(beginInfo.first, beginInfo.second);318  Len = endInfo.second - beginInfo.second;319  return true;320}321 322bool Commit::canReplaceText(SourceLocation loc, StringRef text,323                            FileOffset &Offs, unsigned &Len) {324  assert(!text.empty());325 326  if (!canInsert(loc, Offs))327    return false;328 329  // Try to load the file buffer.330  bool invalidTemp = false;331  StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);332  if (invalidTemp)333    return false;334 335  Len = text.size();336  return file.substr(Offs.getOffset()).starts_with(text);337}338 339bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,340                                       SourceLocation *MacroBegin) const {341  return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);342}343 344bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,345                                     SourceLocation *MacroEnd) const {346  return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);347}348