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 { 33 RewriterDiagnosticConsumer() : NumDiagnosticsSeen(0) {} 34 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: 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 66 ~RewriterTestContext() {} 67 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. 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 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 108 std::string getRewrittenText(FileID ID) { 109 std::string Result; 110 llvm::raw_string_ostream OS(Result); 111 Rewrite.getEditBuffer(ID).write(OS); 112 return Result; 113 } 114 115 std::string getFileContentFromDisk(StringRef Name) { 116 std::string Path = TemporaryFiles.lookup(Name); 117 assert(!Path.empty()); 118 // We need to read directly from the FileManager without relaying through 119 // a FileEntry, as otherwise we'd read through an already opened file 120 // descriptor, which might not see the changes made. 121 // FIXME: Figure out whether there is a way to get the SourceManger to 122 // reopen the file. 123 auto FileBuffer = Files.getBufferForFile(Path); 124 return std::string((*FileBuffer)->getBuffer()); 125 } 126 127 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts; 128 DiagnosticsEngine Diagnostics; 129 RewriterDiagnosticConsumer DiagnosticPrinter; 130 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem; 131 IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayFileSystem; 132 FileManager Files; 133 SourceManager Sources; 134 LangOptions Options; 135 Rewriter Rewrite; 136 137 // Will be set once on disk files are generated. 138 llvm::StringMap<std::string> TemporaryFiles; 139 }; 140 141 } // end namespace clang 142 143 #endif 144