brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.9 KiB · ae21a10 Raw
334 lines · cpp
1//===- Rewriter.cpp - Code rewriting interface ----------------------------===//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//  This file defines the Rewriter class, which is used for code10//  transformations.11//12//===----------------------------------------------------------------------===//13 14#include "clang/Rewrite/Core/Rewriter.h"15#include "clang/Basic/Diagnostic.h"16#include "clang/Basic/DiagnosticIDs.h"17#include "clang/Basic/SourceLocation.h"18#include "clang/Basic/SourceManager.h"19#include "clang/Lex/Lexer.h"20#include "llvm/ADT/RewriteBuffer.h"21#include "llvm/ADT/RewriteRope.h"22#include "llvm/ADT/SmallVector.h"23#include "llvm/ADT/StringRef.h"24#include "llvm/Support/Error.h"25#include "llvm/Support/raw_ostream.h"26#include <cassert>27#include <iterator>28#include <map>29#include <utility>30 31using namespace clang;32using llvm::RewriteBuffer;33 34//===----------------------------------------------------------------------===//35// Rewriter class36//===----------------------------------------------------------------------===//37 38/// Return true if this character is non-new-line whitespace:39/// ' ', '\\t', '\\f', '\\v', '\\r'.40static inline bool isWhitespaceExceptNL(unsigned char c) {41  return c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r';42}43 44/// getRangeSize - Return the size in bytes of the specified range if they45/// are in the same file.  If not, this returns -1.46int Rewriter::getRangeSize(const CharSourceRange &Range,47                           RewriteOptions opts) const {48  if (!isRewritable(Range.getBegin()) ||49      !isRewritable(Range.getEnd())) return -1;50 51  FileID StartFileID, EndFileID;52  unsigned StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);53  unsigned EndOff = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);54 55  if (StartFileID != EndFileID)56    return -1;57 58  // If edits have been made to this buffer, the delta between the range may59  // have changed.60  std::map<FileID, RewriteBuffer>::const_iterator I =61    RewriteBuffers.find(StartFileID);62  if (I != RewriteBuffers.end()) {63    const RewriteBuffer &RB = I->second;64    EndOff = RB.getMappedOffset(EndOff, opts.IncludeInsertsAtEndOfRange);65    StartOff = RB.getMappedOffset(StartOff, !opts.IncludeInsertsAtBeginOfRange);66  }67 68  // Adjust the end offset to the end of the last token, instead of being the69  // start of the last token if this is a token range.70  if (Range.isTokenRange())71    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);72 73  return EndOff-StartOff;74}75 76int Rewriter::getRangeSize(SourceRange Range, RewriteOptions opts) const {77  return getRangeSize(CharSourceRange::getTokenRange(Range), opts);78}79 80/// getRewrittenText - Return the rewritten form of the text in the specified81/// range.  If the start or end of the range was unrewritable or if they are82/// in different buffers, this returns an empty string.83///84/// Note that this method is not particularly efficient.85std::string Rewriter::getRewrittenText(CharSourceRange Range) const {86  if (!isRewritable(Range.getBegin()) ||87      !isRewritable(Range.getEnd()))88    return {};89 90  FileID StartFileID, EndFileID;91  unsigned StartOff, EndOff;92  StartOff = getLocationOffsetAndFileID(Range.getBegin(), StartFileID);93  EndOff   = getLocationOffsetAndFileID(Range.getEnd(), EndFileID);94 95  if (StartFileID != EndFileID)96    return {}; // Start and end in different buffers.97 98  // If edits have been made to this buffer, the delta between the range may99  // have changed.100  std::map<FileID, RewriteBuffer>::const_iterator I =101    RewriteBuffers.find(StartFileID);102  if (I == RewriteBuffers.end()) {103    // If the buffer hasn't been rewritten, just return the text from the input.104    const char *Ptr = SourceMgr->getCharacterData(Range.getBegin());105 106    // Adjust the end offset to the end of the last token, instead of being the107    // start of the last token.108    if (Range.isTokenRange())109      EndOff +=110          Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);111    return std::string(Ptr, Ptr+EndOff-StartOff);112  }113 114  const RewriteBuffer &RB = I->second;115  EndOff = RB.getMappedOffset(EndOff, true);116  StartOff = RB.getMappedOffset(StartOff);117 118  // Adjust the end offset to the end of the last token, instead of being the119  // start of the last token.120  if (Range.isTokenRange())121    EndOff += Lexer::MeasureTokenLength(Range.getEnd(), *SourceMgr, *LangOpts);122 123  // Advance the iterators to the right spot, yay for linear time algorithms.124  RewriteBuffer::iterator Start = RB.begin();125  std::advance(Start, StartOff);126  RewriteBuffer::iterator End = Start;127  assert(EndOff >= StartOff && "Invalid iteration distance");128  std::advance(End, EndOff-StartOff);129 130  return std::string(Start, End);131}132 133unsigned Rewriter::getLocationOffsetAndFileID(SourceLocation Loc,134                                              FileID &FID) const {135  assert(Loc.isValid() && "Invalid location");136  FileIDAndOffset V = SourceMgr->getDecomposedLoc(Loc);137  FID = V.first;138  return V.second;139}140 141/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID.142RewriteBuffer &Rewriter::getEditBuffer(FileID FID) {143  std::map<FileID, RewriteBuffer>::iterator I =144    RewriteBuffers.lower_bound(FID);145  if (I != RewriteBuffers.end() && I->first == FID)146    return I->second;147  I = RewriteBuffers.insert(I, std::make_pair(FID, RewriteBuffer()));148 149  StringRef MB = SourceMgr->getBufferData(FID);150  I->second.Initialize(MB.begin(), MB.end());151 152  return I->second;153}154 155/// InsertText - Insert the specified string at the specified location in the156/// original buffer.157bool Rewriter::InsertText(SourceLocation Loc, StringRef Str,158                          bool InsertAfter, bool indentNewLines) {159  if (!isRewritable(Loc)) return true;160  FileID FID;161  unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);162 163  SmallString<128> indentedStr;164  if (indentNewLines && Str.contains('\n')) {165    StringRef MB = SourceMgr->getBufferData(FID);166 167    unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1;168    const SrcMgr::ContentCache *Content =169        &SourceMgr->getSLocEntry(FID).getFile().getContentCache();170    unsigned lineOffs = Content->SourceLineCache[lineNo];171 172    // Find the whitespace at the start of the line.173    StringRef indentSpace;174    {175      unsigned i = lineOffs;176      while (isWhitespaceExceptNL(MB[i]))177        ++i;178      indentSpace = MB.substr(lineOffs, i-lineOffs);179    }180 181    SmallVector<StringRef, 4> lines;182    Str.split(lines, "\n");183 184    for (unsigned i = 0, e = lines.size(); i != e; ++i) {185      indentedStr += lines[i];186      if (i < e-1) {187        indentedStr += '\n';188        indentedStr += indentSpace;189      }190    }191    Str = indentedStr.str();192  }193 194  getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter);195  return false;196}197 198bool Rewriter::InsertTextAfterToken(SourceLocation Loc, StringRef Str) {199  if (!isRewritable(Loc)) return true;200  FileID FID;201  unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID);202  RewriteOptions rangeOpts;203  rangeOpts.IncludeInsertsAtBeginOfRange = false;204  StartOffs += getRangeSize(SourceRange(Loc, Loc), rangeOpts);205  getEditBuffer(FID).InsertText(StartOffs, Str, /*InsertAfter*/true);206  return false;207}208 209/// RemoveText - Remove the specified text region.210bool Rewriter::RemoveText(SourceLocation Start, unsigned Length,211                          RewriteOptions opts) {212  if (!isRewritable(Start)) return true;213  FileID FID;214  unsigned StartOffs = getLocationOffsetAndFileID(Start, FID);215  getEditBuffer(FID).RemoveText(StartOffs, Length, opts.RemoveLineIfEmpty);216  return false;217}218 219/// ReplaceText - This method replaces a range of characters in the input220/// buffer with a new string.  This is effectively a combined "remove/insert"221/// operation.222bool Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength,223                           StringRef NewStr) {224  if (!isRewritable(Start)) return true;225  FileID StartFileID;226  unsigned StartOffs = getLocationOffsetAndFileID(Start, StartFileID);227 228  getEditBuffer(StartFileID).ReplaceText(StartOffs, OrigLength, NewStr);229  return false;230}231 232bool Rewriter::ReplaceText(SourceRange range, SourceRange replacementRange) {233  if (!isRewritable(range.getBegin())) return true;234  if (!isRewritable(range.getEnd())) return true;235  if (replacementRange.isInvalid()) return true;236  SourceLocation start = range.getBegin();237  unsigned origLength = getRangeSize(range);238  unsigned newLength = getRangeSize(replacementRange);239  FileID FID;240  unsigned newOffs = getLocationOffsetAndFileID(replacementRange.getBegin(),241                                                FID);242  StringRef MB = SourceMgr->getBufferData(FID);243  return ReplaceText(start, origLength, MB.substr(newOffs, newLength));244}245 246bool Rewriter::IncreaseIndentation(CharSourceRange range,247                                   SourceLocation parentIndent) {248  if (range.isInvalid()) return true;249  if (!isRewritable(range.getBegin())) return true;250  if (!isRewritable(range.getEnd())) return true;251  if (!isRewritable(parentIndent)) return true;252 253  FileID StartFileID, EndFileID, parentFileID;254  unsigned StartOff, EndOff, parentOff;255 256  StartOff = getLocationOffsetAndFileID(range.getBegin(), StartFileID);257  EndOff   = getLocationOffsetAndFileID(range.getEnd(), EndFileID);258  parentOff = getLocationOffsetAndFileID(parentIndent, parentFileID);259 260  if (StartFileID != EndFileID || StartFileID != parentFileID)261    return true;262  if (StartOff > EndOff)263    return true;264 265  FileID FID = StartFileID;266  StringRef MB = SourceMgr->getBufferData(FID);267 268  unsigned parentLineNo = SourceMgr->getLineNumber(FID, parentOff) - 1;269  unsigned startLineNo = SourceMgr->getLineNumber(FID, StartOff) - 1;270  unsigned endLineNo = SourceMgr->getLineNumber(FID, EndOff) - 1;271 272  const SrcMgr::ContentCache *Content =273      &SourceMgr->getSLocEntry(FID).getFile().getContentCache();274 275  // Find where the lines start.276  unsigned parentLineOffs = Content->SourceLineCache[parentLineNo];277  unsigned startLineOffs = Content->SourceLineCache[startLineNo];278 279  // Find the whitespace at the start of each line.280  StringRef parentSpace, startSpace;281  {282    unsigned i = parentLineOffs;283    while (isWhitespaceExceptNL(MB[i]))284      ++i;285    parentSpace = MB.substr(parentLineOffs, i-parentLineOffs);286 287    i = startLineOffs;288    while (isWhitespaceExceptNL(MB[i]))289      ++i;290    startSpace = MB.substr(startLineOffs, i-startLineOffs);291  }292  if (parentSpace.size() >= startSpace.size())293    return true;294  if (!startSpace.starts_with(parentSpace))295    return true;296 297  StringRef indent = startSpace.substr(parentSpace.size());298 299  // Indent the lines between start/end offsets.300  RewriteBuffer &RB = getEditBuffer(FID);301  for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) {302    unsigned offs = Content->SourceLineCache[lineNo];303    unsigned i = offs;304    while (isWhitespaceExceptNL(MB[i]))305      ++i;306    StringRef origIndent = MB.substr(offs, i-offs);307    if (origIndent.starts_with(startSpace))308      RB.InsertText(offs, indent, /*InsertAfter=*/false);309  }310 311  return false;312}313 314bool Rewriter::overwriteChangedFiles() {315  bool AllWritten = true;316  auto& Diag = getSourceMgr().getDiagnostics();317  unsigned OverwriteFailure = Diag.getCustomDiagID(318      DiagnosticsEngine::Error, "unable to overwrite file %0: %1");319  for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {320    OptionalFileEntryRef Entry = getSourceMgr().getFileEntryRefForID(I->first);321    llvm::SmallString<128> Path(Entry->getName());322    getSourceMgr().getFileManager().makeAbsolutePath(Path);323    if (auto Error = llvm::writeToOutput(Path, [&](llvm::raw_ostream &OS) {324          I->second.write(OS);325          return llvm::Error::success();326        })) {327      Diag.Report(OverwriteFailure)328          << Entry->getName() << llvm::toString(std::move(Error));329      AllWritten = false;330    }331  }332  return !AllWritten;333}334