From: Chris Lattner Date: Thu, 11 Oct 2007 18:38:32 +0000 (+0000) Subject: Push the rewriting APIs along. Build a trivial client that replaces tabs X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8a12c2777cccdf629b89745b6ecc89a8c1641e4e;p=clang Push the rewriting APIs along. Build a trivial client that replaces tabs with x's for now. The APIs are all unimplemented, so it doesn't do anything yet! :) git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@42868 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Basic/SourceManager.cpp b/Basic/SourceManager.cpp index 380edc0f93..cc1312f049 100644 --- a/Basic/SourceManager.cpp +++ b/Basic/SourceManager.cpp @@ -209,6 +209,13 @@ SourceLocation SourceManager::getInstantiationLoc(SourceLocation PhysLoc, return SourceLocation::getMacroLoc(MacroIDs.size()-1, 0, 0); } +/// getBufferData - Return a pointer to the start and end of the character +/// data for the specified FileID. +std::pair +SourceManager::getBufferData(unsigned FileID) const { + const llvm::MemoryBuffer *Buf = getBuffer(FileID); + return std::make_pair(Buf->getBufferStart(), Buf->getBufferEnd()); +} /// getCharacterData - Return a pointer to the start of the specified location diff --git a/Driver/RewriteTest.cpp b/Driver/RewriteTest.cpp index ef1a9b0822..9e32c8c211 100644 --- a/Driver/RewriteTest.cpp +++ b/Driver/RewriteTest.cpp @@ -12,32 +12,82 @@ //===----------------------------------------------------------------------===// #include "ASTConsumers.h" +#include "clang/Rewrite/Rewriter.h" #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" - +#include "clang/Basic/SourceManager.h" using namespace clang; namespace { - class ASTViewer : public ASTConsumer { + class RewriteTest : public ASTConsumer { SourceManager *SM; + unsigned MainFileID; public: - void Initialize(ASTContext &Context, unsigned MainFileID) { + void Initialize(ASTContext &Context, unsigned mainFileID) { SM = &Context.SourceMgr; + MainFileID = mainFileID; } virtual void HandleTopLevelDecl(Decl *D); + + + ~RewriteTest(); }; } -ASTConsumer *clang::CreateCodeRewriterTest() { return new ASTViewer(); } - +ASTConsumer *clang::CreateCodeRewriterTest() { return new RewriteTest(); } - - -void ASTViewer::HandleTopLevelDecl(Decl *D) { +void RewriteTest::HandleTopLevelDecl(Decl *D) { + // Nothing to do here yet. +#if 0 if (NamedDecl *ND = dyn_cast(D)) if (ND->getName()) printf("%s\n", ND->getName()); +#endif +} + + + +RewriteTest::~RewriteTest() { + Rewriter Rewrite(*SM); + + // Get the top-level buffer that this corresponds to. + std::pair MainBuf = SM->getBufferData(MainFileID); + const char *MainBufStart = MainBuf.first; + const char *MainBufEnd = MainBuf.second; + + // Loop over the whole file, looking for tabs. + for (const char *BufPtr = MainBufStart; BufPtr != MainBufEnd; ++BufPtr) { + if (*BufPtr != '\t') + continue; + + // Okay, we found a tab. This tab will turn into at least one character, + // but it depends on which 'virtual column' it is in. Compute that now. + unsigned VCol = 0; + while (BufPtr-VCol != MainBufStart && BufPtr[-VCol-1] != '\t' && + BufPtr[-VCol-1] != '\n' && BufPtr[-VCol-1] != '\r') + ++VCol; + + // Okay, now that we know the virtual column, we know how many spaces to + // insert. We assume 8-character tab-stops. + unsigned Spaces = 8-(VCol & 7); + + // Get the location of the tab. + SourceLocation TabLoc = + SourceLocation::getFileLoc(MainFileID, BufPtr-MainBufStart); + + // Rewrite the single tab character into a sequence of spaces. + Rewrite.ReplaceText(TabLoc, 1, "xxxxxxxxxxx", Spaces); + } + // Get the buffer corresponding to MainFileID. If we haven't changed it, then + // we are done. + if (const RewriteBuffer *RewriteBuf = + Rewrite.getRewriteBufferFor(MainFileID)) { + RewriteBuf = 0; + printf("Changed\n"); + } else { + printf("No changes\n"); + } } diff --git a/Rewrite/Rewriter.cpp b/Rewrite/Rewriter.cpp index c4856adeea..19e901242d 100644 --- a/Rewrite/Rewriter.cpp +++ b/Rewrite/Rewriter.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/Rewrite/Rewriter.h" +#include "clang/Basic/SourceManager.h" using namespace clang; @@ -24,3 +25,31 @@ void RewriteBuffer::InsertText(unsigned OrigOffset, const char *StrData, unsigned StrLen) { // FIXME: } + + + +//===----------------------------------------------------------------------===// +// Rewriter class +//===----------------------------------------------------------------------===// + +/// getEditBuffer - Get or create a RewriteBuffer for the specified FileID. +/// +RewriteBuffer &Rewriter::getEditBuffer(unsigned FileID) { + std::map::iterator I = + RewriteBuffers.lower_bound(FileID); + if (I != RewriteBuffers.end() && I->first == FileID) + return I->second; + I = RewriteBuffers.insert(I, std::make_pair(FileID, RewriteBuffer())); + + std::pair MB = SourceMgr.getBufferData(FileID); + I->second.Initialize(MB.first, MB.second); + + return I->second; +} + + +void Rewriter::ReplaceText(SourceLocation Start, unsigned OrigLength, + const char *NewStr, unsigned NewLength) { + assert(isRewritable(Start) && "Not a rewritable location!"); + +} diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index f479b94e5d..901010b947 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -198,6 +198,10 @@ public: return getFileInfo(FileID)->Buffer; } + /// getBufferData - Return a pointer to the start and end of the character + /// data for the specified FileID. + std::pair getBufferData(unsigned FileID) const; + /// getIncludeLoc - Return the location of the #include for the specified /// SourceLocation. If this is a macro expansion, this transparently figures /// out which file includes the file being expanded into. diff --git a/include/clang/Rewrite/Rewriter.h b/include/clang/Rewrite/Rewriter.h index 09a99808ed..2c45a7cd5a 100644 --- a/include/clang/Rewrite/Rewriter.h +++ b/include/clang/Rewrite/Rewriter.h @@ -15,11 +15,13 @@ #ifndef LLVM_CLANG_REWRITER_H #define LLVM_CLANG_REWRITER_H +#include "clang/Basic/SourceLocation.h" +#include #include namespace clang { class SourceManager; - class SourceLocation; + class Rewriter; /// SourceDelta - As code in the original input buffer is added and deleted, /// SourceDelta records are used to keep track of how the input SourceLocation @@ -37,6 +39,7 @@ struct SourceDelta { /// RewriteBuffer. For example, if text is inserted into the buffer, any /// locations after the insertion point have to be mapped. class RewriteBuffer { + friend class Rewriter; /// Deltas - Keep track of all the deltas in the source code due to insertions /// and deletions. These are kept in sorted order based on the FileLoc. std::vector Deltas; @@ -46,11 +49,20 @@ class RewriteBuffer { /// instead. std::vector Buffer; public: + + + +private: // Methods only usable by Rewriter. + + /// Initialize - Start this rewrite buffer out with a copy of the unmodified + /// input buffer. + void Initialize(const char *BufStart, const char *BufEnd) { + Buffer.assign(BufStart, BufEnd); + } /// RemoveText - Remove the specified text. void RemoveText(unsigned OrigOffset, unsigned Size); - /// InsertText - Insert some text at the specified point, where the offset in /// the buffer is specified relative to the original SourceBuffer. /// @@ -58,29 +70,50 @@ public: /// after the atomic point: i.e. whether the atomic point is moved to after /// the inserted text or not. void InsertText(unsigned OrigOffset, const char *StrData, unsigned StrLen); - }; + +/// Rewriter - This is the main interface to the rewrite buffers. Its primary +/// job is to dispatch high-level requests to the low-level RewriteBuffers that +/// are involved. class Rewriter { SourceManager &SourceMgr; - // FIXME: list of buffers. + std::map RewriteBuffers; public: explicit Rewriter(SourceManager &SM) : SourceMgr(SM) {} - /// InsertText - Insert the specified string at the specified location in the - /// original buffer. - bool InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen); - - /// RemoveText - Remove the specified text region. - bool RemoveText(SourceLocation Start, SourceLocation End); - - - // TODO: Replace Stmt/Expr with another. - - - // Write out output buffer. + /// isRewritable - Return true if this location is a raw file location, which + /// is rewritable. Locations from macros, etc are not rewritable. + static bool isRewritable(SourceLocation Loc) { + return Loc.isFileID(); + } + /// InsertText - Insert the specified string at the specified location in the + /// original buffer. This method is only valid on rewritable source + /// locations. + void InsertText(SourceLocation Loc, const char *StrData, unsigned StrLen); + + /// RemoveText - Remove the specified text region. This method is only valid + /// on rewritable source locations. + void RemoveText(SourceLocation Start, SourceLocation End); + + + void ReplaceText(SourceLocation Start, unsigned OrigLength, + const char *NewStr, unsigned NewLength); + + // TODO: Replace Stmt/Expr with another. Return bool to indicate whether the + // locations were rewritable. + + /// getRewriteBufferFor - Return the rewrite buffer for the specified FileID. + /// If no modification has been made to it, return null. + const RewriteBuffer *getRewriteBufferFor(unsigned FileID) const { + std::map::const_iterator I = + RewriteBuffers.find(FileID); + return I == RewriteBuffers.end() ? 0 : &I->second; + } +private: + RewriteBuffer &getEditBuffer(unsigned FileID); }; } // end namespace clang