clang  15.0.0git
TestAST.cpp
Go to the documentation of this file.
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 
10 #include "clang/Basic/Diagnostic.h"
15 #include "llvm/ADT/ScopeExit.h"
16 #include "llvm/Support/VirtualFileSystem.h"
17 
18 #include "gtest/gtest.h"
19 
20 namespace clang {
21 namespace {
22 
23 // Captures diagnostics into a vector, optionally reporting errors to gtest.
24 class StoreDiagnostics : public DiagnosticConsumer {
25  std::vector<StoredDiagnostic> &Out;
26  bool ReportErrors;
27  LangOptions LangOpts;
28 
29 public:
30  StoreDiagnostics(std::vector<StoredDiagnostic> &Out, bool ReportErrors)
31  : Out(Out), ReportErrors(ReportErrors) {}
32 
33  void BeginSourceFile(const LangOptions &LangOpts,
34  const Preprocessor *) override {
35  this->LangOpts = LangOpts;
36  }
37 
38  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
39  const Diagnostic &Info) override {
40  Out.emplace_back(DiagLevel, Info);
41  if (ReportErrors && DiagLevel >= DiagnosticsEngine::Error) {
43  llvm::raw_string_ostream OS(Text);
44  TextDiagnostic Renderer(OS, LangOpts,
45  &Info.getDiags()->getDiagnosticOptions());
46  Renderer.emitStoredDiagnostic(Out.back());
47  ADD_FAILURE() << Text;
48  }
49  }
50 };
51 
52 // Fills in the bits of a CompilerInstance that weren't initialized yet.
53 // Provides "empty" ASTContext etc if we fail before parsing gets started.
54 void createMissingComponents(CompilerInstance &Clang) {
55  if (!Clang.hasDiagnostics())
56  Clang.createDiagnostics();
57  if (!Clang.hasFileManager())
58  Clang.createFileManager();
59  if (!Clang.hasSourceManager())
60  Clang.createSourceManager(Clang.getFileManager());
61  if (!Clang.hasTarget())
62  Clang.createTarget();
63  if (!Clang.hasPreprocessor())
64  Clang.createPreprocessor(TU_Complete);
65  if (!Clang.hasASTConsumer())
66  Clang.setASTConsumer(std::make_unique<ASTConsumer>());
67  if (!Clang.hasASTContext())
68  Clang.createASTContext();
69  if (!Clang.hasSema())
70  Clang.createSema(TU_Complete, /*CodeCompleteConsumer=*/nullptr);
71 }
72 
73 } // namespace
74 
76  Clang = std::make_unique<CompilerInstance>(
77  std::make_shared<PCHContainerOperations>());
78  // If we don't manage to finish parsing, create CompilerInstance components
79  // anyway so that the test will see an empty AST instead of crashing.
80  auto RecoverFromEarlyExit =
81  llvm::make_scope_exit([&] { createMissingComponents(*Clang); });
82 
83  // Extra error conditions are reported through diagnostics, set that up first.
84  bool ErrorOK = In.ErrorOK || llvm::StringRef(In.Code).contains("error-ok");
85  Clang->createDiagnostics(new StoreDiagnostics(Diagnostics, !ErrorOK));
86 
87  // Parse cc1 argv, (typically [-std=c++20 input.cc]) into CompilerInvocation.
88  std::vector<const char *> Argv;
89  std::vector<std::string> LangArgs = getCC1ArgsForTesting(In.Language);
90  for (const auto &S : LangArgs)
91  Argv.push_back(S.c_str());
92  for (const auto &S : In.ExtraArgs)
93  Argv.push_back(S.c_str());
95  Argv.push_back(Filename.c_str());
96  Clang->setInvocation(std::make_unique<CompilerInvocation>());
97  if (!CompilerInvocation::CreateFromArgs(Clang->getInvocation(), Argv,
98  Clang->getDiagnostics(), "clang")) {
99  ADD_FAILURE() << "Failed to create invocation";
100  return;
101  }
102  assert(!Clang->getInvocation().getFrontendOpts().DisableFree);
103 
104  // Set up a VFS with only the virtual file visible.
105  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
106  VFS->addFile(Filename, /*ModificationTime=*/0,
107  llvm::MemoryBuffer::getMemBufferCopy(In.Code, Filename));
108  for (const auto &Extra : In.ExtraFiles)
109  VFS->addFile(
110  Extra.getKey(), /*ModificationTime=*/0,
111  llvm::MemoryBuffer::getMemBufferCopy(Extra.getValue(), Extra.getKey()));
112  Clang->createFileManager(VFS);
113 
114  // Running the FrontendAction creates the other components: SourceManager,
115  // Preprocessor, ASTContext, Sema. Preprocessor needs TargetInfo to be set.
116  EXPECT_TRUE(Clang->createTarget());
117  Action = std::make_unique<SyntaxOnlyAction>();
118  const FrontendInputFile &Main = Clang->getFrontendOpts().Inputs.front();
119  if (!Action->BeginSourceFile(*Clang, Main)) {
120  ADD_FAILURE() << "Failed to BeginSourceFile()";
121  Action.reset(); // Don't call EndSourceFile if BeginSourceFile failed.
122  return;
123  }
124  if (auto Err = Action->Execute())
125  ADD_FAILURE() << "Failed to Execute(): " << llvm::toString(std::move(Err));
126 
127  // Action->EndSourceFile() would destroy the ASTContext, we want to keep it.
128  // But notify the preprocessor we're done now.
129  Clang->getPreprocessor().EndSourceFile();
130  // We're done gathering diagnostics, detach the consumer so we can destroy it.
131  Clang->getDiagnosticClient().EndSourceFile();
132  Clang->getDiagnostics().setClient(new DiagnosticConsumer(),
133  /*ShouldOwnClient=*/true);
134 }
135 
136 void TestAST::clear() {
137  if (Action) {
138  // We notified the preprocessor of EOF already, so detach it first.
139  // Sema needs the PP alive until after EndSourceFile() though.
140  auto PP = Clang->getPreprocessorPtr(); // Keep PP alive for now.
141  Clang->setPreprocessor(nullptr); // Detach so we don't send EOF twice.
142  Action->EndSourceFile(); // Destroy ASTContext and Sema.
143  // Now Sema is gone, PP can safely be destroyed.
144  }
145  Action.reset();
146  Clang.reset();
147  Diagnostics.clear();
148 }
149 
151  clear();
152  Action = std::move(M.Action);
153  Clang = std::move(M.Clang);
154  Diagnostics = std::move(M.Diagnostics);
155  return *this;
156 }
157 
158 TestAST::TestAST(TestAST &&M) { *this = std::move(M); }
159 
160 TestAST::~TestAST() { clear(); }
161 
162 } // end namespace clang
clang::DiagnosticsEngine::Level
Level
The level of the diagnostic, after it has been through mapping.
Definition: Diagnostic.h:195
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
TextDiagnostic.h
clang::TestInputs
Specifies a virtual source file to be parsed as part of a test.
Definition: TestAST.h:34
clang::TestAST
The result of parsing a file specified by TestInputs.
Definition: TestAST.h:61
clang::DiagnosticConsumer
Abstract interface, implemented by clients of the front-end, which formats and prints fully processed...
Definition: Diagnostic.h:1739
Filename
StringRef Filename
Definition: Format.cpp:2577
clang::FrontendInputFile
An input file for the front end.
Definition: FrontendOptions.h:221
clang::TestInputs::ExtraArgs
std::vector< std::string > ExtraArgs
Extra argv to pass to clang -cc1.
Definition: TestAST.h:46
clang::getCC1ArgsForTesting
std::vector< std::string > getCC1ArgsForTesting(TestLanguage Lang)
Definition: CommandLineArgs.cpp:51
clang::TestAST::TestAST
TestAST(const TestInputs &)
Constructing a TestAST parses the virtual file.
Definition: TestAST.cpp:75
clang::getFilenameForTesting
StringRef getFilenameForTesting(TestLanguage Lang)
Definition: CommandLineArgs.cpp:87
clang::TestInputs::ErrorOK
bool ErrorOK
By default, error diagnostics during parsing are reported as gtest errors.
Definition: TestAST.h:55
clang::threadSafety::sx::toString
std::string toString(const til::SExpr *E)
Definition: ThreadSafetyCommon.h:90
LangOptions.h
Diagnostic.h
clang::DiagnosticsEngine::Error
@ Error
Definition: Diagnostic.h:200
clang::TestInputs::Language
TestLanguage Language
The language to parse as.
Definition: TestAST.h:43
TestAST.h
clang::TestInputs::ExtraFiles
llvm::StringMap< std::string > ExtraFiles
Extra virtual files that are available to be #included.
Definition: TestAST.h:50
FrontendActions.h
clang::TestAST::~TestAST
~TestAST()
Definition: TestAST.cpp:160
clang
Definition: CalledOnceCheck.h:17
Text
StringRef Text
Definition: Format.cpp:2578
CommandLineArgs.h
clang::TestAST::operator=
TestAST & operator=(TestAST &&)
Definition: TestAST.cpp:150
clang::TU_Complete
@ TU_Complete
The translation unit is a complete translation unit.
Definition: LangOptions.h:889
clang::TestInputs::Code
std::string Code
The source code of the input file to be parsed.
Definition: TestAST.h:39
clang::CompilerInvocation::CreateFromArgs
static bool CreateFromArgs(CompilerInvocation &Res, ArrayRef< const char * > CommandLineArgs, DiagnosticsEngine &Diags, const char *Argv0=nullptr)
Create a compiler invocation from a list of input options.
Definition: CompilerInvocation.cpp:4529
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...