181ad6265SDimitry Andric //===--- TestAST.cpp ------------------------------------------------------===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric 981ad6265SDimitry Andric #include "clang/Testing/TestAST.h" 1081ad6265SDimitry Andric #include "clang/Basic/Diagnostic.h" 1181ad6265SDimitry Andric #include "clang/Basic/LangOptions.h" 1281ad6265SDimitry Andric #include "clang/Frontend/FrontendActions.h" 1381ad6265SDimitry Andric #include "clang/Frontend/TextDiagnostic.h" 1481ad6265SDimitry Andric #include "clang/Testing/CommandLineArgs.h" 1581ad6265SDimitry Andric #include "llvm/ADT/ScopeExit.h" 16*0fca6ea1SDimitry Andric #include "llvm/Support/Error.h" 1781ad6265SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 1881ad6265SDimitry Andric 1981ad6265SDimitry Andric #include "gtest/gtest.h" 2006c3fb27SDimitry Andric #include <string> 2181ad6265SDimitry Andric 2281ad6265SDimitry Andric namespace clang { 2381ad6265SDimitry Andric namespace { 2481ad6265SDimitry Andric 2581ad6265SDimitry Andric // Captures diagnostics into a vector, optionally reporting errors to gtest. 2681ad6265SDimitry Andric class StoreDiagnostics : public DiagnosticConsumer { 2781ad6265SDimitry Andric std::vector<StoredDiagnostic> &Out; 2881ad6265SDimitry Andric bool ReportErrors; 2981ad6265SDimitry Andric LangOptions LangOpts; 3081ad6265SDimitry Andric 3181ad6265SDimitry Andric public: 3281ad6265SDimitry Andric StoreDiagnostics(std::vector<StoredDiagnostic> &Out, bool ReportErrors) 3381ad6265SDimitry Andric : Out(Out), ReportErrors(ReportErrors) {} 3481ad6265SDimitry Andric 3581ad6265SDimitry Andric void BeginSourceFile(const LangOptions &LangOpts, 3681ad6265SDimitry Andric const Preprocessor *) override { 3781ad6265SDimitry Andric this->LangOpts = LangOpts; 3881ad6265SDimitry Andric } 3981ad6265SDimitry Andric 4081ad6265SDimitry Andric void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 4181ad6265SDimitry Andric const Diagnostic &Info) override { 4281ad6265SDimitry Andric Out.emplace_back(DiagLevel, Info); 4381ad6265SDimitry Andric if (ReportErrors && DiagLevel >= DiagnosticsEngine::Error) { 4481ad6265SDimitry Andric std::string Text; 4581ad6265SDimitry Andric llvm::raw_string_ostream OS(Text); 4681ad6265SDimitry Andric TextDiagnostic Renderer(OS, LangOpts, 4781ad6265SDimitry Andric &Info.getDiags()->getDiagnosticOptions()); 4881ad6265SDimitry Andric Renderer.emitStoredDiagnostic(Out.back()); 4981ad6265SDimitry Andric ADD_FAILURE() << Text; 5081ad6265SDimitry Andric } 5181ad6265SDimitry Andric } 5281ad6265SDimitry Andric }; 5381ad6265SDimitry Andric 5481ad6265SDimitry Andric // Fills in the bits of a CompilerInstance that weren't initialized yet. 5581ad6265SDimitry Andric // Provides "empty" ASTContext etc if we fail before parsing gets started. 5681ad6265SDimitry Andric void createMissingComponents(CompilerInstance &Clang) { 5781ad6265SDimitry Andric if (!Clang.hasDiagnostics()) 5881ad6265SDimitry Andric Clang.createDiagnostics(); 5981ad6265SDimitry Andric if (!Clang.hasFileManager()) 6081ad6265SDimitry Andric Clang.createFileManager(); 6181ad6265SDimitry Andric if (!Clang.hasSourceManager()) 6281ad6265SDimitry Andric Clang.createSourceManager(Clang.getFileManager()); 6381ad6265SDimitry Andric if (!Clang.hasTarget()) 6481ad6265SDimitry Andric Clang.createTarget(); 6581ad6265SDimitry Andric if (!Clang.hasPreprocessor()) 6681ad6265SDimitry Andric Clang.createPreprocessor(TU_Complete); 6781ad6265SDimitry Andric if (!Clang.hasASTConsumer()) 6881ad6265SDimitry Andric Clang.setASTConsumer(std::make_unique<ASTConsumer>()); 6981ad6265SDimitry Andric if (!Clang.hasASTContext()) 7081ad6265SDimitry Andric Clang.createASTContext(); 7181ad6265SDimitry Andric if (!Clang.hasSema()) 7281ad6265SDimitry Andric Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr); 7381ad6265SDimitry Andric } 7481ad6265SDimitry Andric 7581ad6265SDimitry Andric } // namespace 7681ad6265SDimitry Andric 7781ad6265SDimitry Andric TestAST::TestAST(const TestInputs &In) { 7881ad6265SDimitry Andric Clang = std::make_unique<CompilerInstance>( 7981ad6265SDimitry Andric std::make_shared<PCHContainerOperations>()); 8081ad6265SDimitry Andric // If we don't manage to finish parsing, create CompilerInstance components 8181ad6265SDimitry Andric // anyway so that the test will see an empty AST instead of crashing. 8281ad6265SDimitry Andric auto RecoverFromEarlyExit = 8381ad6265SDimitry Andric llvm::make_scope_exit([&] { createMissingComponents(*Clang); }); 8481ad6265SDimitry Andric 8581ad6265SDimitry Andric // Extra error conditions are reported through diagnostics, set that up first. 8681ad6265SDimitry Andric bool ErrorOK = In.ErrorOK || llvm::StringRef(In.Code).contains("error-ok"); 8781ad6265SDimitry Andric Clang->createDiagnostics(new StoreDiagnostics(Diagnostics, !ErrorOK)); 8881ad6265SDimitry Andric 8981ad6265SDimitry Andric // Parse cc1 argv, (typically [-std=c++20 input.cc]) into CompilerInvocation. 9081ad6265SDimitry Andric std::vector<const char *> Argv; 9181ad6265SDimitry Andric std::vector<std::string> LangArgs = getCC1ArgsForTesting(In.Language); 9281ad6265SDimitry Andric for (const auto &S : LangArgs) 9381ad6265SDimitry Andric Argv.push_back(S.c_str()); 9481ad6265SDimitry Andric for (const auto &S : In.ExtraArgs) 9581ad6265SDimitry Andric Argv.push_back(S.c_str()); 9606c3fb27SDimitry Andric std::string Filename = In.FileName; 9706c3fb27SDimitry Andric if (Filename.empty()) 9806c3fb27SDimitry Andric Filename = getFilenameForTesting(In.Language).str(); 9981ad6265SDimitry Andric Argv.push_back(Filename.c_str()); 10081ad6265SDimitry Andric Clang->setInvocation(std::make_unique<CompilerInvocation>()); 10181ad6265SDimitry Andric if (!CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv, 10281ad6265SDimitry Andric Clang->getDiagnostics(), "clang")) { 10381ad6265SDimitry Andric ADD_FAILURE() << "Failed to create invocation"; 10481ad6265SDimitry Andric return; 10581ad6265SDimitry Andric } 10681ad6265SDimitry Andric assert(!Clang->getInvocation().getFrontendOpts().DisableFree); 10781ad6265SDimitry Andric 10881ad6265SDimitry Andric // Set up a VFS with only the virtual file visible. 10981ad6265SDimitry Andric auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>(); 110*0fca6ea1SDimitry Andric if (auto Err = VFS->setCurrentWorkingDirectory(In.WorkingDir)) 111*0fca6ea1SDimitry Andric ADD_FAILURE() << "Failed to setWD: " << Err.message(); 11281ad6265SDimitry Andric VFS->addFile(Filename, /*ModificationTime=*/0, 11381ad6265SDimitry Andric llvm::MemoryBuffer::getMemBufferCopy(In.Code, Filename)); 11481ad6265SDimitry Andric for (const auto &Extra : In.ExtraFiles) 11581ad6265SDimitry Andric VFS->addFile( 11681ad6265SDimitry Andric Extra.getKey(), /*ModificationTime=*/0, 11781ad6265SDimitry Andric llvm::MemoryBuffer::getMemBufferCopy(Extra.getValue(), Extra.getKey())); 11881ad6265SDimitry Andric Clang->createFileManager(VFS); 11981ad6265SDimitry Andric 12081ad6265SDimitry Andric // Running the FrontendAction creates the other components: SourceManager, 12181ad6265SDimitry Andric // Preprocessor, ASTContext, Sema. Preprocessor needs TargetInfo to be set. 12281ad6265SDimitry Andric EXPECT_TRUE(Clang->createTarget()); 123bdd1243dSDimitry Andric Action = 124bdd1243dSDimitry Andric In.MakeAction ? In.MakeAction() : std::make_unique<SyntaxOnlyAction>(); 12581ad6265SDimitry Andric const FrontendInputFile &Main = Clang->getFrontendOpts().Inputs.front(); 12681ad6265SDimitry Andric if (!Action->BeginSourceFile(*Clang, Main)) { 12781ad6265SDimitry Andric ADD_FAILURE() << "Failed to BeginSourceFile()"; 12881ad6265SDimitry Andric Action.reset(); // Don't call EndSourceFile if BeginSourceFile failed. 12981ad6265SDimitry Andric return; 13081ad6265SDimitry Andric } 13181ad6265SDimitry Andric if (auto Err = Action->Execute()) 13281ad6265SDimitry Andric ADD_FAILURE() << "Failed to Execute(): " << llvm::toString(std::move(Err)); 13381ad6265SDimitry Andric 13481ad6265SDimitry Andric // Action->EndSourceFile() would destroy the ASTContext, we want to keep it. 13581ad6265SDimitry Andric // But notify the preprocessor we're done now. 13681ad6265SDimitry Andric Clang->getPreprocessor().EndSourceFile(); 13781ad6265SDimitry Andric // We're done gathering diagnostics, detach the consumer so we can destroy it. 13881ad6265SDimitry Andric Clang->getDiagnosticClient().EndSourceFile(); 13981ad6265SDimitry Andric Clang->getDiagnostics().setClient(new DiagnosticConsumer(), 14081ad6265SDimitry Andric /*ShouldOwnClient=*/true); 14181ad6265SDimitry Andric } 14281ad6265SDimitry Andric 14381ad6265SDimitry Andric void TestAST::clear() { 14481ad6265SDimitry Andric if (Action) { 14581ad6265SDimitry Andric // We notified the preprocessor of EOF already, so detach it first. 14681ad6265SDimitry Andric // Sema needs the PP alive until after EndSourceFile() though. 14781ad6265SDimitry Andric auto PP = Clang->getPreprocessorPtr(); // Keep PP alive for now. 14881ad6265SDimitry Andric Clang->setPreprocessor(nullptr); // Detach so we don't send EOF twice. 14981ad6265SDimitry Andric Action->EndSourceFile(); // Destroy ASTContext and Sema. 15081ad6265SDimitry Andric // Now Sema is gone, PP can safely be destroyed. 15181ad6265SDimitry Andric } 15281ad6265SDimitry Andric Action.reset(); 15381ad6265SDimitry Andric Clang.reset(); 15481ad6265SDimitry Andric Diagnostics.clear(); 15581ad6265SDimitry Andric } 15681ad6265SDimitry Andric 15781ad6265SDimitry Andric TestAST &TestAST::operator=(TestAST &&M) { 15881ad6265SDimitry Andric clear(); 15981ad6265SDimitry Andric Action = std::move(M.Action); 16081ad6265SDimitry Andric Clang = std::move(M.Clang); 16181ad6265SDimitry Andric Diagnostics = std::move(M.Diagnostics); 16281ad6265SDimitry Andric return *this; 16381ad6265SDimitry Andric } 16481ad6265SDimitry Andric 16581ad6265SDimitry Andric TestAST::TestAST(TestAST &&M) { *this = std::move(M); } 16681ad6265SDimitry Andric 16781ad6265SDimitry Andric TestAST::~TestAST() { clear(); } 16881ad6265SDimitry Andric 16981ad6265SDimitry Andric } // end namespace clang 170