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