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