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