xref: /freebsd-src/contrib/llvm-project/clang/lib/Testing/TestAST.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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