[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