[cfe-commits] r157260 - in /cfe/trunk: include/clang/Rewrite/Rewriter.h lib/Rewrite/Rewriter.cpp unittests/CMakeLists.txt unittests/Tooling/RewriterTest.cpp unittests/Tooling/RewriterTestContext.h
NAKAMURA Takumi
geek4civic at gmail.com
Tue May 22 15:57:29 PDT 2012
Manuel, I don't think it is compatible to Win32.
Manipulating opened files would be hard on Win32.
2012/05/23 2:05 "Manuel Klimek" <klimek at google.com>:
> Author: klimek
> Date: Tue May 22 12:01:35 2012
> New Revision: 157260
>
> URL: http://llvm.org/viewvc/llvm-project?rev=157260&view=rev
> Log:
> Adds a method overwriteChangedFiles to the Rewriter. This is implemented by
> first writing the changed files to a temporary location and then
> overwriting
> the original files atomically.
>
> Also adds a RewriterTestContext to aid unit testing rewrting logic in
> general.
>
>
> Added:
> cfe/trunk/unittests/Tooling/RewriterTest.cpp (with props)
> cfe/trunk/unittests/Tooling/RewriterTestContext.h (with props)
> Modified:
> cfe/trunk/include/clang/Rewrite/Rewriter.h
> cfe/trunk/lib/Rewrite/Rewriter.cpp
> cfe/trunk/unittests/CMakeLists.txt
>
> Modified: cfe/trunk/include/clang/Rewrite/Rewriter.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Rewrite/Rewriter.h?rev=157260&r1=157259&r2=157260&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Rewrite/Rewriter.h (original)
> +++ cfe/trunk/include/clang/Rewrite/Rewriter.h Tue May 22 12:01:35 2012
> @@ -279,6 +279,13 @@
> buffer_iterator buffer_begin() { return RewriteBuffers.begin(); }
> buffer_iterator buffer_end() { return RewriteBuffers.end(); }
>
> + /// SaveFiles - Save all changed files to disk.
> + ///
> + /// Returns whether not all changes were saved successfully.
> + /// Outputs diagnostics via the source manager's diagnostic engine
> + /// in case of an error.
> + bool overwriteChangedFiles();
> +
> private:
> unsigned getLocationOffsetAndFileID(SourceLocation Loc, FileID &FID)
> const;
> };
>
> Modified: cfe/trunk/lib/Rewrite/Rewriter.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Rewrite/Rewriter.cpp?rev=157260&r1=157259&r2=157260&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Rewrite/Rewriter.cpp (original)
> +++ cfe/trunk/lib/Rewrite/Rewriter.cpp Tue May 22 12:01:35 2012
> @@ -15,9 +15,12 @@
> #include "clang/Rewrite/Rewriter.h"
> #include "clang/AST/Stmt.h"
> #include "clang/AST/Decl.h"
> -#include "clang/Lex/Lexer.h"
> +#include "clang/Basic/DiagnosticIDs.h"
> +#include "clang/Basic/FileManager.h"
> #include "clang/Basic/SourceManager.h"
> +#include "clang/Lex/Lexer.h"
> #include "llvm/ADT/SmallString.h"
> +#include "llvm/Support/FileSystem.h"
> using namespace clang;
>
> raw_ostream &RewriteBuffer::write(raw_ostream &os) const {
> @@ -412,3 +415,68 @@
>
> return false;
> }
> +
> +// A wrapper for a file stream that atomically overwrites the target.
> +//
> +// Creates a file output stream for a temporary file in the constructor,
> +// which is later accessible via getStream() if ok() return true.
> +// Flushes the stream and moves the temporary file to the target location
> +// in the destructor.
> +class AtomicallyMovedFile {
> +public:
> + AtomicallyMovedFile(DiagnosticsEngine &Diagnostics, StringRef Filename,
> + bool &AllWritten)
> + : Diagnostics(Diagnostics), Filename(Filename),
> AllWritten(AllWritten) {
> + TempFilename = Filename;
> + TempFilename += "-%%%%%%%%";
> + int FD;
> + if (llvm::sys::fs::unique_file(TempFilename.str(), FD, TempFilename,
> + /*makeAbsolute=*/true, 0664)) {
> + AllWritten = false;
> + Diagnostics.Report(clang::diag::err_unable_to_make_temp)
> + << TempFilename;
> + } else {
> + FileStream.reset(new llvm::raw_fd_ostream(FD,
> /*shouldClose=*/true));
> + }
> + }
> +
> + ~AtomicallyMovedFile() {
> + if (!ok()) return;
> +
> + FileStream->flush();
> + if (llvm::error_code ec =
> + llvm::sys::fs::rename(TempFilename.str(), Filename)) {
> + AllWritten = false;
> + Diagnostics.Report(clang::diag::err_unable_to_rename_temp)
> + << TempFilename << Filename << ec.message();
> + bool existed;
> + // If the remove fails, there's not a lot we can do - this is
> already an
> + // error.
> + llvm::sys::fs::remove(TempFilename.str(), existed);
> + }
> + }
> +
> + bool ok() { return FileStream; }
> + llvm::raw_ostream &getStream() { return *FileStream; }
> +
> +private:
> + DiagnosticsEngine &Diagnostics;
> + StringRef Filename;
> + SmallString<128> TempFilename;
> + OwningPtr<llvm::raw_fd_ostream> FileStream;
> + bool &AllWritten;
> +};
> +
> +bool Rewriter::overwriteChangedFiles() {
> + bool AllWritten = true;
> + for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I)
> {
> + const FileEntry *Entry =
> + getSourceMgr().getFileEntryForID(I->first);
> + AtomicallyMovedFile File(getSourceMgr().getDiagnostics(),
> Entry->getName(),
> + AllWritten);
> + if (File.ok()) {
> + I->second.write(File.getStream());
> + }
> + }
> + return !AllWritten;
> +}
>
> Modified: cfe/trunk/unittests/CMakeLists.txt
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/CMakeLists.txt?rev=157260&r1=157259&r2=157260&view=diff
>
> ==============================================================================
> --- cfe/trunk/unittests/CMakeLists.txt (original)
> +++ cfe/trunk/unittests/CMakeLists.txt Tue May 22 12:01:35 2012
> @@ -70,5 +70,6 @@
> Tooling/CompilationDatabaseTest.cpp
> Tooling/ToolingTest.cpp
> Tooling/RecursiveASTVisitorTest.cpp
> - USED_LIBS gtest gtest_main clangAST clangTooling
> + Tooling/RewriterTest.cpp
> + USED_LIBS gtest gtest_main clangAST clangTooling clangRewrite
> )
>
> Added: cfe/trunk/unittests/Tooling/RewriterTest.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RewriterTest.cpp?rev=157260&view=auto
>
> ==============================================================================
> --- cfe/trunk/unittests/Tooling/RewriterTest.cpp (added)
> +++ cfe/trunk/unittests/Tooling/RewriterTest.cpp Tue May 22 12:01:35 2012
> @@ -0,0 +1,37 @@
> +//===- unittest/Tooling/RewriterTest.cpp
> ----------------------------------===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#include "RewriterTestContext.h"
> +#include "gtest/gtest.h"
> +
> +namespace clang {
> +
> +TEST(Rewriter, OverwritesChangedFiles) {
> + RewriterTestContext Context;
> + FileID ID = Context.createOnDiskFile("t.cpp",
> "line1\nline2\nline3\nline4");
> + Context.Rewrite.ReplaceText(Context.getLocation(ID, 2, 1), 5,
> "replaced");
> + EXPECT_FALSE(Context.Rewrite.overwriteChangedFiles());
> + EXPECT_EQ("line1\nreplaced\nline3\nline4",
> + Context.getFileContentFromDisk("t.cpp"));
> +}
> +
> +TEST(Rewriter, ContinuesOverwritingFilesOnError) {
> + RewriterTestContext Context;
> + FileID FailingID = Context.createInMemoryFile("invalid/failing.cpp",
> "test");
> + Context.Rewrite.ReplaceText(Context.getLocation(FailingID, 1, 2), 1,
> "other");
> + FileID WorkingID = Context.createOnDiskFile(
> + "working.cpp", "line1\nline2\nline3\nline4");
> + Context.Rewrite.ReplaceText(Context.getLocation(WorkingID, 2, 1), 5,
> + "replaced");
> + EXPECT_TRUE(Context.Rewrite.overwriteChangedFiles());
> + EXPECT_EQ("line1\nreplaced\nline3\nline4",
> + Context.getFileContentFromDisk("working.cpp"));
> +}
> +
> +} // end namespace clang
>
> Propchange: cfe/trunk/unittests/Tooling/RewriterTest.cpp
>
> ------------------------------------------------------------------------------
> svn:eol-style = LF
>
> Added: cfe/trunk/unittests/Tooling/RewriterTestContext.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/unittests/Tooling/RewriterTestContext.h?rev=157260&view=auto
>
> ==============================================================================
> --- cfe/trunk/unittests/Tooling/RewriterTestContext.h (added)
> +++ cfe/trunk/unittests/Tooling/RewriterTestContext.h Tue May 22 12:01:35
> 2012
> @@ -0,0 +1,120 @@
> +//===--- RewriterTestContext.h ----------------------------------*- C++
> -*-===//
> +//
> +// The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
>
> +//===----------------------------------------------------------------------===//
> +//
> +// This file defines a utility class for Rewriter related tests.
> +//
>
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_CLANG_REWRITER_TEST_CONTEXT_H
> +#define LLVM_CLANG_REWRITER_TEST_CONTEXT_H
> +
> +#include "clang/Basic/Diagnostic.h"
> +#include "clang/Basic/FileManager.h"
> +#include "clang/Basic/LangOptions.h"
> +#include "clang/Basic/SourceManager.h"
> +#include "clang/Frontend/DiagnosticOptions.h"
> +#include "clang/Frontend/TextDiagnosticPrinter.h"
> +#include "clang/Rewrite/Rewriter.h"
> +#include "llvm/Support/Path.h"
> +#include "llvm/Support/raw_ostream.h"
> +
> +namespace clang {
> +
> +/// \brief A class that sets up a ready to use Rewriter.
> +///
> +/// Useful in unit tests that need a Rewriter. Creates all dependencies
> +/// of a Rewriter with default values for testing and provides convenience
> +/// methods, which help with writing tests that change files.
> +class RewriterTestContext {
> + public:
> + RewriterTestContext()
> + : Diagnostics(llvm::IntrusiveRefCntPtr<DiagnosticIDs>()),
> + DiagnosticPrinter(llvm::outs(), DiagnosticOptions()),
> + Files((FileSystemOptions())),
> + Sources(Diagnostics, Files),
> + Rewrite(Sources, Options) {
> + Diagnostics.setClient(&DiagnosticPrinter, false);
> + }
> +
> + ~RewriterTestContext() {
> + if (TemporaryDirectory.isValid()) {
> + std::string ErrorInfo;
> + TemporaryDirectory.eraseFromDisk(true, &ErrorInfo);
> + assert(ErrorInfo.empty());
> + }
> + }
> +
> + FileID createInMemoryFile(StringRef Name, StringRef Content) {
> + const llvm::MemoryBuffer *Source =
> + llvm::MemoryBuffer::getMemBuffer(Content);
> + const FileEntry *Entry =
> + Files.getVirtualFile(Name, Source->getBufferSize(), 0);
> + Sources.overrideFileContents(Entry, Source, true);
> + assert(Entry != NULL);
> + return Sources.createFileID(Entry, SourceLocation(), SrcMgr::C_User);
> + }
> +
> + FileID createOnDiskFile(StringRef Name, StringRef Content) {
> + if (!TemporaryDirectory.isValid()) {
> + std::string ErrorInfo;
> + TemporaryDirectory =
> llvm::sys::Path::GetTemporaryDirectory(&ErrorInfo);
> + assert(ErrorInfo.empty());
> + }
> + llvm::SmallString<1024> Path(TemporaryDirectory.str());
> + llvm::sys::path::append(Path, Name);
> + std::string ErrorInfo;
> + llvm::raw_fd_ostream OutStream(Path.c_str(),
> + ErrorInfo,
> llvm::raw_fd_ostream::F_Binary);
> + assert(ErrorInfo.empty());
> + OutStream << Content;
> + OutStream.close();
> + const FileEntry *File = Files.getFile(Path);
> + assert(File != NULL);
> + return Sources.createFileID(File, SourceLocation(), SrcMgr::C_User);
> + }
> +
> + SourceLocation getLocation(FileID ID, unsigned Line, unsigned Column) {
> + SourceLocation Result = Sources.translateFileLineCol(
> + Sources.getFileEntryForID(ID), Line, Column);
> + assert(Result.isValid());
> + return Result;
> + }
> +
> + std::string getRewrittenText(FileID ID) {
> + std::string Result;
> + llvm::raw_string_ostream OS(Result);
> + Rewrite.getEditBuffer(ID).write(OS);
> + return Result;
> + }
> +
> + std::string getFileContentFromDisk(StringRef Name) {
> + llvm::SmallString<1024> Path(TemporaryDirectory.str());
> + llvm::sys::path::append(Path, Name);
> + // We need to read directly from the FileManager without relaying
> through
> + // a FileEntry, as otherwise we'd read through an already opened file
> + // descriptor, which might not see the changes made.
> + // FIXME: Figure out whether there is a way to get the SourceManger to
> + // reopen the file.
> + return Files.getBufferForFile(Path, NULL)->getBuffer();
> + }
> +
> + DiagnosticsEngine Diagnostics;
> + TextDiagnosticPrinter DiagnosticPrinter;
> + FileManager Files;
> + SourceManager Sources;
> + LangOptions Options;
> + Rewriter Rewrite;
> +
> + // Will be set once on disk files are generated.
> + llvm::sys::Path TemporaryDirectory;
> +};
> +
> +} // end namespace clang
> +
> +#endif
>
> Propchange: cfe/trunk/unittests/Tooling/RewriterTestContext.h
>
> ------------------------------------------------------------------------------
> svn:eol-style = LF
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20120523/6d8baccf/attachment.html>
More information about the cfe-commits
mailing list