[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
Manuel Klimek
klimek at google.com
Wed May 23 01:23:12 PDT 2012
On Wed, May 23, 2012 at 12:57 AM, NAKAMURA Takumi <geek4civic at gmail.com>wrote:
> Manuel, I don't think it is compatible to Win32.
> Manipulating opened files would be hard on Win32.
>
I cargo culted the general concept here from other parts of clang. Which
parts exactly don't work with Win32?
Thanks,
/Manuel
> 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/3aa553c9/attachment.html>
More information about the cfe-commits
mailing list