1 //===--- RewriterTestContext.h ----------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file defines a utility class for Rewriter related tests. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_CLANG_UNITTESTS_TOOLING_REWRITERTESTCONTEXT_H 14 #define LLVM_CLANG_UNITTESTS_TOOLING_REWRITERTESTCONTEXT_H 15 16 #include "clang/Basic/Diagnostic.h" 17 #include "clang/Basic/DiagnosticOptions.h" 18 #include "clang/Basic/FileManager.h" 19 #include "clang/Basic/LangOptions.h" 20 #include "clang/Basic/SourceManager.h" 21 #include "clang/Rewrite/Core/Rewriter.h" 22 #include "llvm/Support/FileSystem.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/raw_ostream.h" 25 26 namespace clang { 27 28 /// \brief A very simple diagnostic consumer that prints to stderr and keeps 29 /// track of the number of diagnostics. 30 /// 31 /// This avoids a dependency on clangFrontend for FormatTests. 32 struct RewriterDiagnosticConsumer : public DiagnosticConsumer { RewriterDiagnosticConsumerRewriterDiagnosticConsumer33 RewriterDiagnosticConsumer() : NumDiagnosticsSeen(0) {} HandleDiagnosticRewriterDiagnosticConsumer34 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 35 const Diagnostic &Info) override { 36 ++NumDiagnosticsSeen; 37 SmallString<100> OutStr; 38 Info.FormatDiagnostic(OutStr); 39 llvm::errs() << OutStr; 40 } 41 unsigned NumDiagnosticsSeen; 42 }; 43 44 /// \brief A class that sets up a ready to use Rewriter. 45 /// 46 /// Useful in unit tests that need a Rewriter. Creates all dependencies 47 /// of a Rewriter with default values for testing and provides convenience 48 /// methods, which help with writing tests that change files. 49 class RewriterTestContext { 50 public: RewriterTestContext()51 RewriterTestContext() 52 : DiagOpts(new DiagnosticOptions()), 53 Diagnostics(IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), 54 &*DiagOpts), 55 InMemoryFileSystem(new llvm::vfs::InMemoryFileSystem), 56 OverlayFileSystem( 57 new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem())), 58 Files(FileSystemOptions(), OverlayFileSystem), 59 Sources(Diagnostics, Files), Rewrite(Sources, Options) { 60 Diagnostics.setClient(&DiagnosticPrinter, false); 61 // FIXME: To make these tests truly in-memory, we need to overlay the 62 // builtin headers. 63 OverlayFileSystem->pushOverlay(InMemoryFileSystem); 64 } 65 ~RewriterTestContext()66 ~RewriterTestContext() {} 67 createInMemoryFile(StringRef Name,StringRef Content)68 FileID createInMemoryFile(StringRef Name, StringRef Content) { 69 std::unique_ptr<llvm::MemoryBuffer> Source = 70 llvm::MemoryBuffer::getMemBuffer(Content); 71 InMemoryFileSystem->addFile(Name, 0, std::move(Source)); 72 73 auto Entry = Files.getOptionalFileRef(Name); 74 assert(Entry); 75 return Sources.createFileID(*Entry, SourceLocation(), SrcMgr::C_User); 76 } 77 78 // FIXME: this code is mostly a duplicate of 79 // unittests/Tooling/RefactoringTest.cpp. Figure out a way to share it. createOnDiskFile(StringRef Name,StringRef Content)80 FileID createOnDiskFile(StringRef Name, StringRef Content) { 81 SmallString<1024> Path; 82 int FD; 83 std::error_code EC = llvm::sys::fs::createTemporaryFile(Name, "", FD, Path); 84 assert(!EC); 85 (void)EC; 86 87 llvm::raw_fd_ostream OutStream(FD, true); 88 OutStream << Content; 89 OutStream.close(); 90 auto File = Files.getOptionalFileRef(Path); 91 assert(File); 92 93 StringRef Found = 94 TemporaryFiles.insert(std::make_pair(Name, std::string(Path.str()))) 95 .first->second; 96 assert(Found == Path); 97 (void)Found; 98 return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User); 99 } 100 getLocation(FileID ID,unsigned Line,unsigned Column)101 SourceLocation getLocation(FileID ID, unsigned Line, unsigned Column) { 102 SourceLocation Result = Sources.translateFileLineCol( 103 Sources.getFileEntryForID(ID), Line, Column); 104 assert(Result.isValid()); 105 return Result; 106 } 107 getRewrittenText(FileID ID)108 std::string getRewrittenText(FileID ID) { 109 std::string Result; 110 llvm::raw_string_ostream OS(Result); 111 Rewrite.getEditBuffer(ID).write(OS); 112 OS.flush(); 113 return Result; 114 } 115 getFileContentFromDisk(StringRef Name)116 std::string getFileContentFromDisk(StringRef Name) { 117 std::string Path = TemporaryFiles.lookup(Name); 118 assert(!Path.empty()); 119 // We need to read directly from the FileManager without relaying through 120 // a FileEntry, as otherwise we'd read through an already opened file 121 // descriptor, which might not see the changes made. 122 // FIXME: Figure out whether there is a way to get the SourceManger to 123 // reopen the file. 124 auto FileBuffer = Files.getBufferForFile(Path); 125 return std::string((*FileBuffer)->getBuffer()); 126 } 127 128 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 129 DiagnosticsEngine Diagnostics; 130 RewriterDiagnosticConsumer DiagnosticPrinter; 131 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; 132 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem; 133 FileManager Files; 134 SourceManager Sources; 135 LangOptions Options; 136 Rewriter Rewrite; 137 138 // Will be set once on disk files are generated. 139 llvm::StringMap<std::string> TemporaryFiles; 140 }; 141 142 } // end namespace clang 143 144 #endif 145