[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
Sun May 27 06:57:21 PDT 2012
Manuel, I have tweaked in r157528.
I wonder it kept semantics...
...Takumi
2012/5/23 Manuel Klimek <klimek at google.com>:
> 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
>
>
More information about the cfe-commits
mailing list