1 //===--- TestAST.cpp ------------------------------------------------------===// 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 #include "clang/Testing/TestAST.h" 10 #include "clang/Basic/Diagnostic.h" 11 #include "clang/Basic/LangOptions.h" 12 #include "clang/Frontend/FrontendActions.h" 13 #include "clang/Frontend/TextDiagnostic.h" 14 #include "clang/Testing/CommandLineArgs.h" 15 #include "llvm/ADT/ScopeExit.h" 16 #include "llvm/Support/Error.h" 17 #include "llvm/Support/VirtualFileSystem.h" 18 19 #include "gtest/gtest.h" 20 #include <string> 21 22 namespace clang { 23 namespace { 24 25 // Captures diagnostics into a vector, optionally reporting errors to gtest. 26 class StoreDiagnostics : public DiagnosticConsumer { 27 std::vector<StoredDiagnostic> &Out; 28 bool ReportErrors; 29 LangOptions LangOpts; 30 31 public: 32 StoreDiagnostics(std::vector<StoredDiagnostic> &Out, bool ReportErrors) 33 : Out(Out), ReportErrors(ReportErrors) {} 34 35 void BeginSourceFile(const LangOptions &LangOpts, 36 const Preprocessor *) override { 37 this->LangOpts = LangOpts; 38 } 39 40 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 41 const Diagnostic &Info) override { 42 Out.emplace_back(DiagLevel, Info); 43 if (ReportErrors && DiagLevel >= DiagnosticsEngine::Error) { 44 std::string Text; 45 llvm::raw_string_ostream OS(Text); 46 TextDiagnostic Renderer(OS, LangOpts, 47 &Info.getDiags()->getDiagnosticOptions()); 48 Renderer.emitStoredDiagnostic(Out.back()); 49 ADD_FAILURE() << Text; 50 } 51 } 52 }; 53 54 // Fills in the bits of a CompilerInstance that weren't initialized yet. 55 // Provides "empty" ASTContext etc if we fail before parsing gets started. 56 void createMissingComponents(CompilerInstance &Clang) { 57 if (!Clang.hasDiagnostics()) 58 Clang.createDiagnostics(*llvm::vfs::getRealFileSystem()); 59 if (!Clang.hasFileManager()) 60 Clang.createFileManager(); 61 if (!Clang.hasSourceManager()) 62 Clang.createSourceManager(Clang.getFileManager()); 63 if (!Clang.hasTarget()) 64 Clang.createTarget(); 65 if (!Clang.hasPreprocessor()) 66 Clang.createPreprocessor(TU_Complete); 67 if (!Clang.hasASTConsumer()) 68 Clang.setASTConsumer(std::make_unique<ASTConsumer>()); 69 if (!Clang.hasASTContext()) 70 Clang.createASTContext(); 71 if (!Clang.hasSema()) 72 Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr); 73 } 74 75 } // namespace 76 77 TestAST::TestAST(const TestInputs &In) { 78 Clang = std::make_unique<CompilerInstance>( 79 std::make_shared<PCHContainerOperations>()); 80 // If we don't manage to finish parsing, create CompilerInstance components 81 // anyway so that the test will see an empty AST instead of crashing. 82 auto RecoverFromEarlyExit = 83 llvm::make_scope_exit([&] { createMissingComponents(*Clang); }); 84 85 std::string Filename = In.FileName; 86 if (Filename.empty()) 87 Filename = getFilenameForTesting(In.Language).str(); 88 89 // Set up a VFS with only the virtual file visible. 90 auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); 91 if (auto Err = VFS->setCurrentWorkingDirectory(In.WorkingDir)) 92 ADD_FAILURE() << "Failed to setWD: " << Err.message(); 93 VFS->addFile(Filename, /*ModificationTime=*/0, 94 llvm::MemoryBuffer::getMemBufferCopy(In.Code, Filename)); 95 for (const auto &Extra : In.ExtraFiles) 96 VFS->addFile( 97 Extra.getKey(), /*ModificationTime=*/0, 98 llvm::MemoryBuffer::getMemBufferCopy(Extra.getValue(), Extra.getKey())); 99 100 // Extra error conditions are reported through diagnostics, set that up first. 101 bool ErrorOK = In.ErrorOK || llvm::StringRef(In.Code).contains("error-ok"); 102 Clang->createDiagnostics(*VFS, new StoreDiagnostics(Diagnostics, !ErrorOK)); 103 104 // Parse cc1 argv, (typically [-std=c++20 input.cc]) into CompilerInvocation. 105 std::vector<const char *> Argv; 106 std::vector<std::string> LangArgs = getCC1ArgsForTesting(In.Language); 107 for (const auto &S : LangArgs) 108 Argv.push_back(S.c_str()); 109 for (const auto &S : In.ExtraArgs) 110 Argv.push_back(S.c_str()); 111 Argv.push_back(Filename.c_str()); 112 Clang->setInvocation(std::make_unique<CompilerInvocation>()); 113 if (!CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, 114 Clang->getDiagnostics(), "clang")) { 115 ADD_FAILURE() << "Failed to create invocation"; 116 return; 117 } 118 assert(!Clang->getInvocation().getFrontendOpts().DisableFree); 119 120 Clang->createFileManager(VFS); 121 122 // Running the FrontendAction creates the other components: SourceManager, 123 // Preprocessor, ASTContext, Sema. Preprocessor needs TargetInfo to be set. 124 EXPECT_TRUE(Clang->createTarget()); 125 Action = 126 In.MakeAction ? In.MakeAction() : std::make_unique<SyntaxOnlyAction>(); 127 const FrontendInputFile &Main = Clang->getFrontendOpts().Inputs.front(); 128 if (!Action->BeginSourceFile(*Clang, Main)) { 129 ADD_FAILURE() << "Failed to BeginSourceFile()"; 130 Action.reset(); // Don't call EndSourceFile if BeginSourceFile failed. 131 return; 132 } 133 if (auto Err = Action->Execute()) 134 ADD_FAILURE() << "Failed to Execute(): " << llvm::toString(std::move(Err)); 135 136 // Action->EndSourceFile() would destroy the ASTContext, we want to keep it. 137 // But notify the preprocessor we're done now. 138 Clang->getPreprocessor().EndSourceFile(); 139 // We're done gathering diagnostics, detach the consumer so we can destroy it. 140 Clang->getDiagnosticClient().EndSourceFile(); 141 Clang->getDiagnostics().setClient(new DiagnosticConsumer(), 142 /*ShouldOwnClient=*/true); 143 } 144 145 void TestAST::clear() { 146 if (Action) { 147 // We notified the preprocessor of EOF already, so detach it first. 148 // Sema needs the PP alive until after EndSourceFile() though. 149 auto PP = Clang->getPreprocessorPtr(); // Keep PP alive for now. 150 Clang->setPreprocessor(nullptr); // Detach so we don't send EOF twice. 151 Action->EndSourceFile(); // Destroy ASTContext and Sema. 152 // Now Sema is gone, PP can safely be destroyed. 153 } 154 Action.reset(); 155 Clang.reset(); 156 Diagnostics.clear(); 157 } 158 159 TestAST &TestAST::operator=(TestAST &&M) { 160 clear(); 161 Action = std::move(M.Action); 162 Clang = std::move(M.Clang); 163 Diagnostics = std::move(M.Diagnostics); 164 return *this; 165 } 166 167 TestAST::TestAST(TestAST &&M) { *this = std::move(M); } 168 169 TestAST::~TestAST() { clear(); } 170 171 } // end namespace clang 172