diff options
author | Daniel Jasper <djasper@google.com> | 2013-05-16 10:40:07 +0000 |
---|---|---|
committer | Daniel Jasper <djasper@google.com> | 2013-05-16 10:40:07 +0000 |
commit | ec04c0dba0b2ca2ad6906009e8eca54928b99c46 (patch) | |
tree | 90b56b68a2c79a436457b6cdf858c147c17b732f | |
parent | f9eb9b18830349057d4608ecbac2077e84906f5c (diff) | |
download | bcm5719-llvm-ec04c0dba0b2ca2ad6906009e8eca54928b99c46.tar.gz bcm5719-llvm-ec04c0dba0b2ca2ad6906009e8eca54928b99c46.zip |
Add a more convenient interface to use clang-format.
It turns out that several implementations go through the trouble of
setting up a SourceManager and Lexer and abstracting this into a
function makes usage easier.
Also abstracts SourceManager-independent ranges out of
tooling::Refactoring and provides a convenience function to create them
from line ranges.
llvm-svn: 181997
-rw-r--r-- | clang/include/clang/Format/Format.h | 7 | ||||
-rw-r--r-- | clang/include/clang/Tooling/Refactoring.h | 27 | ||||
-rw-r--r-- | clang/lib/Format/Format.cpp | 26 | ||||
-rw-r--r-- | clang/lib/Tooling/Refactoring.cpp | 57 | ||||
-rw-r--r-- | clang/unittests/Format/FormatTest.cpp | 23 |
5 files changed, 107 insertions, 33 deletions
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index ba44c04024c..4150ad73acf 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -183,6 +183,13 @@ tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, SourceManager &SourceMgr, std::vector<CharSourceRange> Ranges); +/// \brief Reformats the given \p Ranges in \p Code. +/// +/// Otherwise identical to the reformat() function consuming a \c Lexer. +tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, + std::vector<tooling::Range> Ranges, + StringRef FileName = "<stdin>"); + /// \brief Returns the \c LangOpts that the formatter expects you to set. LangOptions getFormattingLangOpts(); diff --git a/clang/include/clang/Tooling/Refactoring.h b/clang/include/clang/Tooling/Refactoring.h index 079ce7420db..10bae096144 100644 --- a/clang/include/clang/Tooling/Refactoring.h +++ b/clang/include/clang/Tooling/Refactoring.h @@ -32,6 +32,20 @@ class SourceLocation; namespace tooling { +/// \brief A source range independent of the \c SourceManager. +class Range { +public: + Range() : Offset(0), Length(0) {} + Range(unsigned Offset, unsigned Length) : Offset(Offset), Length(Length) {} + + unsigned getOffset() const { return Offset; } + unsigned getLength() const { return Length; } + +private: + unsigned Offset; + unsigned Length; +}; + /// \brief A text replacement. /// /// Represents a SourceManager independent replacement of a range of text in a @@ -72,8 +86,8 @@ public: /// \brief Accessors. /// @{ StringRef getFilePath() const { return FilePath; } - unsigned getOffset() const { return Offset; } - unsigned getLength() const { return Length; } + unsigned getOffset() const { return ReplacementRange.getOffset(); } + unsigned getLength() const { return ReplacementRange.getLength(); } StringRef getReplacementText() const { return ReplacementText; } /// @} @@ -96,8 +110,7 @@ public: StringRef ReplacementText); std::string FilePath; - unsigned Offset; - unsigned Length; + Range ReplacementRange; std::string ReplacementText; }; @@ -113,6 +126,12 @@ typedef std::set<Replacement, Replacement::Less> Replacements; /// \returns true if all replacements apply. false otherwise. bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite); +/// \brief Applies all replacements in \p Replaces to \p Code. +/// +/// This completely ignores the path stored in each replacement. If one or more +/// replacements cannot be applied, this returns an empty \c string. +std::string applyAllReplacements(StringRef Code, Replacements &Replaces); + /// \brief A tool to run refactorings. /// /// This is a refactoring specific version of \see ClangTool. FrontendActions diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 93d2a93be97..b64437046f6 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -19,6 +19,7 @@ #include "TokenAnnotator.h" #include "UnwrappedLineParser.h" #include "WhitespaceManager.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/OperatorPrecedence.h" #include "clang/Basic/SourceManager.h" #include "clang/Format/Format.h" @@ -1553,6 +1554,31 @@ tooling::Replacements reformat(const FormatStyle &Style, Lexer &Lex, return formatter.format(); } +tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, + std::vector<tooling::Range> Ranges, + StringRef FileName) { + FileManager Files((FileSystemOptions())); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + new DiagnosticOptions); + SourceManager SourceMgr(Diagnostics, Files); + llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(Code, FileName); + const clang::FileEntry *Entry = + Files.getVirtualFile(FileName, Buf->getBufferSize(), 0); + SourceMgr.overrideFileContents(Entry, Buf); + FileID ID = + SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User); + Lexer Lex(ID, SourceMgr.getBuffer(ID), SourceMgr, getFormattingLangOpts()); + SourceLocation StartOfFile = SourceMgr.getLocForStartOfFile(ID); + std::vector<CharSourceRange> CharRanges; + for (unsigned i = 0, e = Ranges.size(); i != e; ++i) { + SourceLocation Start = StartOfFile.getLocWithOffset(Ranges[i].getOffset()); + SourceLocation End = Start.getLocWithOffset(Ranges[i].getLength()); + CharRanges.push_back(CharSourceRange::getCharRange(Start, End)); + } + return reformat(Style, Lex, SourceMgr, CharRanges); +} + LangOptions getFormattingLangOpts() { LangOptions LangOpts; LangOpts.CPlusPlus = 1; diff --git a/clang/lib/Tooling/Refactoring.cpp b/clang/lib/Tooling/Refactoring.cpp index d8440d639d0..f795def74ff 100644 --- a/clang/lib/Tooling/Refactoring.cpp +++ b/clang/lib/Tooling/Refactoring.cpp @@ -26,12 +26,12 @@ namespace tooling { static const char * const InvalidLocation = ""; Replacement::Replacement() - : FilePath(InvalidLocation), Offset(0), Length(0) {} + : FilePath(InvalidLocation) {} -Replacement::Replacement(StringRef FilePath, unsigned Offset, - unsigned Length, StringRef ReplacementText) - : FilePath(FilePath), Offset(Offset), - Length(Length), ReplacementText(ReplacementText) {} +Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length, + StringRef ReplacementText) + : FilePath(FilePath), ReplacementRange(Offset, Length), + ReplacementText(ReplacementText) {} Replacement::Replacement(SourceManager &Sources, SourceLocation Start, unsigned Length, StringRef ReplacementText) { @@ -62,11 +62,12 @@ bool Replacement::apply(Rewriter &Rewrite) const { // the remapping API is not public in the RewriteBuffer. const SourceLocation Start = SM.getLocForStartOfFile(ID). - getLocWithOffset(Offset); + getLocWithOffset(ReplacementRange.getOffset()); // ReplaceText returns false on success. // ReplaceText only fails if the source location is not a file location, in // which case we already returned false earlier. - bool RewriteSucceeded = !Rewrite.ReplaceText(Start, Length, ReplacementText); + bool RewriteSucceeded = !Rewrite.ReplaceText( + Start, ReplacementRange.getLength(), ReplacementText); assert(RewriteSucceeded); return RewriteSucceeded; } @@ -74,16 +75,18 @@ bool Replacement::apply(Rewriter &Rewrite) const { std::string Replacement::toString() const { std::string result; llvm::raw_string_ostream stream(result); - stream << FilePath << ": " << Offset << ":+" << Length - << ":\"" << ReplacementText << "\""; + stream << FilePath << ": " << ReplacementRange.getOffset() << ":+" + << ReplacementRange.getLength() << ":\"" << ReplacementText << "\""; return result; } bool Replacement::Less::operator()(const Replacement &R1, const Replacement &R2) const { if (R1.FilePath != R2.FilePath) return R1.FilePath < R2.FilePath; - if (R1.Offset != R2.Offset) return R1.Offset < R2.Offset; - if (R1.Length != R2.Length) return R1.Length < R2.Length; + if (R1.ReplacementRange.getOffset() != R2.ReplacementRange.getOffset()) + return R1.ReplacementRange.getOffset() < R2.ReplacementRange.getOffset(); + if (R1.ReplacementRange.getLength() != R2.ReplacementRange.getLength()) + return R1.ReplacementRange.getLength() < R2.ReplacementRange.getLength(); return R1.ReplacementText < R2.ReplacementText; } @@ -94,8 +97,7 @@ void Replacement::setFromSourceLocation(SourceManager &Sources, Sources.getDecomposedLoc(Start); const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first); this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation; - this->Offset = DecomposedLocation.second; - this->Length = Length; + this->ReplacementRange = Range(DecomposedLocation.second, Length); this->ReplacementText = ReplacementText; } @@ -135,6 +137,35 @@ bool applyAllReplacements(Replacements &Replaces, Rewriter &Rewrite) { return Result; } +std::string applyAllReplacements(StringRef Code, Replacements &Replaces) { + FileManager Files((FileSystemOptions())); + DiagnosticsEngine Diagnostics( + IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), + new DiagnosticOptions); + Diagnostics.setClient(new TextDiagnosticPrinter( + llvm::outs(), &Diagnostics.getDiagnosticOptions())); + SourceManager SourceMgr(Diagnostics, Files); + Rewriter Rewrite(SourceMgr, LangOptions()); + llvm::MemoryBuffer *Buf = llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"); + const clang::FileEntry *Entry = + Files.getVirtualFile("<stdin>", Buf->getBufferSize(), 0); + SourceMgr.overrideFileContents(Entry, Buf); + FileID ID = + SourceMgr.createFileID(Entry, SourceLocation(), clang::SrcMgr::C_User); + for (Replacements::iterator I = Replaces.begin(), E = Replaces.end(); I != E; + ++I) { + Replacement Replace("<stdin>", I->getOffset(), I->getLength(), + I->getReplacementText()); + if (!Replace.apply(Rewrite)) + return ""; + } + std::string Result; + llvm::raw_string_ostream OS(Result); + Rewrite.getEditBuffer(ID).write(OS); + OS.flush(); + return Result; +} + RefactoringTool::RefactoringTool(const CompilationDatabase &Compilations, ArrayRef<std::string> SourcePaths) : ClangTool(Compilations, SourcePaths) {} diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 8ee0245d1db..76ec61385dd 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -10,7 +10,6 @@ #define DEBUG_TYPE "format-test" #include "clang/Format/Format.h" -#include "../Tooling/RewriterTestContext.h" #include "clang/Lex/Lexer.h" #include "llvm/Support/Debug.h" #include "gtest/gtest.h" @@ -23,21 +22,13 @@ protected: std::string format(llvm::StringRef Code, unsigned Offset, unsigned Length, const FormatStyle &Style) { DEBUG(llvm::errs() << "---\n"); - RewriterTestContext Context; - FileID ID = Context.createInMemoryFile("input.cc", Code); - SourceLocation Start = - Context.Sources.getLocForStartOfFile(ID).getLocWithOffset(Offset); - std::vector<CharSourceRange> Ranges( - 1, - CharSourceRange::getCharRange(Start, Start.getLocWithOffset(Length))); - Lexer Lex(ID, Context.Sources.getBuffer(ID), Context.Sources, - getFormattingLangOpts()); - tooling::Replacements Replace = - reformat(Style, Lex, Context.Sources, Ranges); - ReplacementCount = Replace.size(); - EXPECT_TRUE(applyAllReplacements(Replace, Context.Rewrite)); - DEBUG(llvm::errs() << "\n" << Context.getRewrittenText(ID) << "\n\n"); - return Context.getRewrittenText(ID); + std::vector<tooling::Range> Ranges(1, tooling::Range(Offset, Length)); + tooling::Replacements Replaces = reformat(Style, Code, Ranges); + ReplacementCount = Replaces.size(); + std::string Result = applyAllReplacements(Code, Replaces); + EXPECT_NE("", Result); + DEBUG(llvm::errs() << "\n" << Result << "\n\n"); + return Result; } std::string |