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(); 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 // Extra error conditions are reported through diagnostics, set that up first. 86 bool ErrorOK = In.ErrorOK || llvm::StringRef(In.Code).contains("error-ok"); 87 Clang->createDiagnostics(new StoreDiagnostics(Diagnostics, !ErrorOK)); 88 89 // Parse cc1 argv, (typically [-std=c++20 input.cc]) into CompilerInvocation. 90 std::vector<const char *> Argv; 91 std::vector<std::string> LangArgs = getCC1ArgsForTesting(In.Language); 92 for (const auto &S : LangArgs) 93 Argv.push_back(S.c_str()); 94 for (const auto &S : In.ExtraArgs) 95 Argv.push_back(S.c_str()); 96 std::string Filename = In.FileName; 97 if (Filename.empty()) 98 Filename = getFilenameForTesting(In.Language).str(); 99 Argv.push_back(Filename.c_str()); 100 Clang->setInvocation(std::make_unique<CompilerInvocation>()); 101 if (!CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, 102 Clang->getDiagnostics(), "clang")) { 103 ADD_FAILURE() << "Failed to create invocation"; 104 return; 105 } 106 assert(!Clang->getInvocation().getFrontendOpts().DisableFree); 107 108 // Set up a VFS with only the virtual file visible. 109 auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); 110 if (auto Err = VFS->setCurrentWorkingDirectory(In.WorkingDir)) 111 ADD_FAILURE() << "Failed to setWD: " << Err.message(); 112 VFS->addFile(Filename, /*ModificationTime=*/0, 113 llvm::MemoryBuffer::getMemBufferCopy(In.Code, Filename)); 114 for (const auto &Extra : In.ExtraFiles) 115 VFS->addFile( 116 Extra.getKey(), /*ModificationTime=*/0, 117 llvm::MemoryBuffer::getMemBufferCopy(Extra.getValue(), Extra.getKey())); 118 Clang->createFileManager(VFS); 119 120 // Running the FrontendAction creates the other components: SourceManager, 121 // Preprocessor, ASTContext, Sema. Preprocessor needs TargetInfo to be set. 122 EXPECT_TRUE(Clang->createTarget()); 123 Action = 124 In.MakeAction ? In.MakeAction() : std::make_unique<SyntaxOnlyAction>(); 125 const FrontendInputFile &Main = Clang->getFrontendOpts().Inputs.front(); 126 if (!Action->BeginSourceFile(*Clang, Main)) { 127 ADD_FAILURE() << "Failed to BeginSourceFile()"; 128 Action.reset(); // Don't call EndSourceFile if BeginSourceFile failed. 129 return; 130 } 131 if (auto Err = Action->Execute()) 132 ADD_FAILURE() << "Failed to Execute(): " << llvm::toString(std::move(Err)); 133 134 // Action->EndSourceFile() would destroy the ASTContext, we want to keep it. 135 // But notify the preprocessor we're done now. 136 Clang->getPreprocessor().EndSourceFile(); 137 // We're done gathering diagnostics, detach the consumer so we can destroy it. 138 Clang->getDiagnosticClient().EndSourceFile(); 139 Clang->getDiagnostics().setClient(new DiagnosticConsumer(), 140 /*ShouldOwnClient=*/true); 141 } 142 143 void TestAST::clear() { 144 if (Action) { 145 // We notified the preprocessor of EOF already, so detach it first. 146 // Sema needs the PP alive until after EndSourceFile() though. 147 auto PP = Clang->getPreprocessorPtr(); // Keep PP alive for now. 148 Clang->setPreprocessor(nullptr); // Detach so we don't send EOF twice. 149 Action->EndSourceFile(); // Destroy ASTContext and Sema. 150 // Now Sema is gone, PP can safely be destroyed. 151 } 152 Action.reset(); 153 Clang.reset(); 154 Diagnostics.clear(); 155 } 156 157 TestAST &TestAST::operator=(TestAST &&M) { 158 clear(); 159 Action = std::move(M.Action); 160 Clang = std::move(M.Clang); 161 Diagnostics = std::move(M.Diagnostics); 162 return *this; 163 } 164 165 TestAST::TestAST(TestAST &&M) { *this = std::move(M); } 166 167 TestAST::~TestAST() { clear(); } 168 169 } // end namespace clang 170