Path: blob/main/contrib/llvm-project/clang/lib/Edit/Commit.cpp
35262 views
//===- Commit.cpp - A unit of edits ---------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "clang/Edit/Commit.h"9#include "clang/Basic/LLVM.h"10#include "clang/Basic/SourceLocation.h"11#include "clang/Basic/SourceManager.h"12#include "clang/Edit/EditedSource.h"13#include "clang/Edit/FileOffset.h"14#include "clang/Lex/Lexer.h"15#include "clang/Lex/PPConditionalDirectiveRecord.h"16#include "llvm/ADT/StringRef.h"17#include <cassert>18#include <utility>1920using namespace clang;21using namespace edit;2223SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const {24SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID());25Loc = Loc.getLocWithOffset(Offset.getOffset());26assert(Loc.isFileID());27return Loc;28}2930CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const {31SourceLocation Loc = getFileLocation(SM);32return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));33}3435CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const {36SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID());37Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset());38assert(Loc.isFileID());39return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length));40}4142Commit::Commit(EditedSource &Editor)43: SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()),44PPRec(Editor.getPPCondDirectiveRecord()),45Editor(&Editor) {}4647bool Commit::insert(SourceLocation loc, StringRef text,48bool afterToken, bool beforePreviousInsertions) {49if (text.empty())50return true;5152FileOffset Offs;53if ((!afterToken && !canInsert(loc, Offs)) ||54( afterToken && !canInsertAfterToken(loc, Offs, loc))) {55IsCommitable = false;56return false;57}5859addInsert(loc, Offs, text, beforePreviousInsertions);60return true;61}6263bool Commit::insertFromRange(SourceLocation loc,64CharSourceRange range,65bool afterToken, bool beforePreviousInsertions) {66FileOffset RangeOffs;67unsigned RangeLen;68if (!canRemoveRange(range, RangeOffs, RangeLen)) {69IsCommitable = false;70return false;71}7273FileOffset Offs;74if ((!afterToken && !canInsert(loc, Offs)) ||75( afterToken && !canInsertAfterToken(loc, Offs, loc))) {76IsCommitable = false;77return false;78}7980if (PPRec &&81PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) {82IsCommitable = false;83return false;84}8586addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions);87return true;88}8990bool Commit::remove(CharSourceRange range) {91FileOffset Offs;92unsigned Len;93if (!canRemoveRange(range, Offs, Len)) {94IsCommitable = false;95return false;96}9798addRemove(range.getBegin(), Offs, Len);99return true;100}101102bool Commit::insertWrap(StringRef before, CharSourceRange range,103StringRef after) {104bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false,105/*beforePreviousInsertions=*/true);106bool commitableAfter;107if (range.isTokenRange())108commitableAfter = insertAfterToken(range.getEnd(), after);109else110commitableAfter = insert(range.getEnd(), after);111112return commitableBefore && commitableAfter;113}114115bool Commit::replace(CharSourceRange range, StringRef text) {116if (text.empty())117return remove(range);118119FileOffset Offs;120unsigned Len;121if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) {122IsCommitable = false;123return false;124}125126addRemove(range.getBegin(), Offs, Len);127addInsert(range.getBegin(), Offs, text, false);128return true;129}130131bool Commit::replaceWithInner(CharSourceRange range,132CharSourceRange replacementRange) {133FileOffset OuterBegin;134unsigned OuterLen;135if (!canRemoveRange(range, OuterBegin, OuterLen)) {136IsCommitable = false;137return false;138}139140FileOffset InnerBegin;141unsigned InnerLen;142if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) {143IsCommitable = false;144return false;145}146147FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen);148FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen);149if (OuterBegin.getFID() != InnerBegin.getFID() ||150InnerBegin < OuterBegin ||151InnerBegin > OuterEnd ||152InnerEnd > OuterEnd) {153IsCommitable = false;154return false;155}156157addRemove(range.getBegin(),158OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset());159addRemove(replacementRange.getEnd(),160InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset());161return true;162}163164bool Commit::replaceText(SourceLocation loc, StringRef text,165StringRef replacementText) {166if (text.empty() || replacementText.empty())167return true;168169FileOffset Offs;170unsigned Len;171if (!canReplaceText(loc, replacementText, Offs, Len)) {172IsCommitable = false;173return false;174}175176addRemove(loc, Offs, Len);177addInsert(loc, Offs, text, false);178return true;179}180181void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text,182bool beforePreviousInsertions) {183if (text.empty())184return;185186Edit data;187data.Kind = Act_Insert;188data.OrigLoc = OrigLoc;189data.Offset = Offs;190data.Text = text.copy(StrAlloc);191data.BeforePrev = beforePreviousInsertions;192CachedEdits.push_back(data);193}194195void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs,196FileOffset RangeOffs, unsigned RangeLen,197bool beforePreviousInsertions) {198if (RangeLen == 0)199return;200201Edit data;202data.Kind = Act_InsertFromRange;203data.OrigLoc = OrigLoc;204data.Offset = Offs;205data.InsertFromRangeOffs = RangeOffs;206data.Length = RangeLen;207data.BeforePrev = beforePreviousInsertions;208CachedEdits.push_back(data);209}210211void Commit::addRemove(SourceLocation OrigLoc,212FileOffset Offs, unsigned Len) {213if (Len == 0)214return;215216Edit data;217data.Kind = Act_Remove;218data.OrigLoc = OrigLoc;219data.Offset = Offs;220data.Length = Len;221CachedEdits.push_back(data);222}223224bool Commit::canInsert(SourceLocation loc, FileOffset &offs) {225if (loc.isInvalid())226return false;227228if (loc.isMacroID())229isAtStartOfMacroExpansion(loc, &loc);230231const SourceManager &SM = SourceMgr;232loc = SM.getTopMacroCallerLoc(loc);233234if (loc.isMacroID())235if (!isAtStartOfMacroExpansion(loc, &loc))236return false;237238if (SM.isInSystemHeader(loc))239return false;240241std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);242if (locInfo.first.isInvalid())243return false;244offs = FileOffset(locInfo.first, locInfo.second);245return canInsertInOffset(loc, offs);246}247248bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs,249SourceLocation &AfterLoc) {250if (loc.isInvalid())251252return false;253254SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc);255unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts);256AfterLoc = loc.getLocWithOffset(tokLen);257258if (loc.isMacroID())259isAtEndOfMacroExpansion(loc, &loc);260261const SourceManager &SM = SourceMgr;262loc = SM.getTopMacroCallerLoc(loc);263264if (loc.isMacroID())265if (!isAtEndOfMacroExpansion(loc, &loc))266return false;267268if (SM.isInSystemHeader(loc))269return false;270271loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts);272if (loc.isInvalid())273return false;274275std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc);276if (locInfo.first.isInvalid())277return false;278offs = FileOffset(locInfo.first, locInfo.second);279return canInsertInOffset(loc, offs);280}281282bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {283for (const auto &act : CachedEdits)284if (act.Kind == Act_Remove) {285if (act.Offset.getFID() == Offs.getFID() &&286Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length))287return false; // position has been removed.288}289290if (!Editor)291return true;292return Editor->canInsertInOffset(OrigLoc, Offs);293}294295bool Commit::canRemoveRange(CharSourceRange range,296FileOffset &Offs, unsigned &Len) {297const SourceManager &SM = SourceMgr;298range = Lexer::makeFileCharRange(range, SM, LangOpts);299if (range.isInvalid())300return false;301302if (range.getBegin().isMacroID() || range.getEnd().isMacroID())303return false;304if (SM.isInSystemHeader(range.getBegin()) ||305SM.isInSystemHeader(range.getEnd()))306return false;307308if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange()))309return false;310311std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin());312std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd());313if (beginInfo.first != endInfo.first ||314beginInfo.second > endInfo.second)315return false;316317Offs = FileOffset(beginInfo.first, beginInfo.second);318Len = endInfo.second - beginInfo.second;319return true;320}321322bool Commit::canReplaceText(SourceLocation loc, StringRef text,323FileOffset &Offs, unsigned &Len) {324assert(!text.empty());325326if (!canInsert(loc, Offs))327return false;328329// Try to load the file buffer.330bool invalidTemp = false;331StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp);332if (invalidTemp)333return false;334335Len = text.size();336return file.substr(Offs.getOffset()).starts_with(text);337}338339bool Commit::isAtStartOfMacroExpansion(SourceLocation loc,340SourceLocation *MacroBegin) const {341return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin);342}343344bool Commit::isAtEndOfMacroExpansion(SourceLocation loc,345SourceLocation *MacroEnd) const {346return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd);347}348349350