xref: /openbsd-src/gnu/llvm/clang/tools/clang-repl/ClangRepl.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1a9ac8606Spatrick //===--- tools/clang-repl/ClangRepl.cpp - clang-repl - the Clang REPL -----===//
2a9ac8606Spatrick //
3a9ac8606Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a9ac8606Spatrick // See https://llvm.org/LICENSE.txt for license information.
5a9ac8606Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a9ac8606Spatrick //
7a9ac8606Spatrick //===----------------------------------------------------------------------===//
8a9ac8606Spatrick //
9a9ac8606Spatrick //  This file implements a REPL tool on top of clang.
10a9ac8606Spatrick //
11a9ac8606Spatrick //===----------------------------------------------------------------------===//
12a9ac8606Spatrick 
13a9ac8606Spatrick #include "clang/Basic/Diagnostic.h"
14a9ac8606Spatrick #include "clang/Frontend/CompilerInstance.h"
15a9ac8606Spatrick #include "clang/Frontend/FrontendDiagnostic.h"
16a9ac8606Spatrick #include "clang/Interpreter/Interpreter.h"
17a9ac8606Spatrick 
18a9ac8606Spatrick #include "llvm/ExecutionEngine/Orc/LLJIT.h"
19a9ac8606Spatrick #include "llvm/LineEditor/LineEditor.h"
20a9ac8606Spatrick #include "llvm/Support/CommandLine.h"
21a9ac8606Spatrick #include "llvm/Support/ManagedStatic.h" // llvm_shutdown
22a9ac8606Spatrick #include "llvm/Support/Signals.h"
23a9ac8606Spatrick #include "llvm/Support/TargetSelect.h" // llvm::Initialize*
24*12c85518Srobert #include <optional>
25a9ac8606Spatrick 
26a9ac8606Spatrick static llvm::cl::list<std::string>
27*12c85518Srobert     ClangArgs("Xcc",
28a9ac8606Spatrick               llvm::cl::desc("Argument to pass to the CompilerInvocation"),
29a9ac8606Spatrick               llvm::cl::CommaSeparated);
30a9ac8606Spatrick static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
31a9ac8606Spatrick                                               llvm::cl::Hidden);
32a9ac8606Spatrick static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
33a9ac8606Spatrick                                              llvm::cl::desc("[code to run]"));
34a9ac8606Spatrick 
LLVMErrorHandler(void * UserData,const char * Message,bool GenCrashDiag)35*12c85518Srobert static void LLVMErrorHandler(void *UserData, const char *Message,
36a9ac8606Spatrick                              bool GenCrashDiag) {
37a9ac8606Spatrick   auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
38a9ac8606Spatrick 
39a9ac8606Spatrick   Diags.Report(clang::diag::err_fe_error_backend) << Message;
40a9ac8606Spatrick 
41a9ac8606Spatrick   // Run the interrupt handlers to make sure any special cleanups get done, in
42a9ac8606Spatrick   // particular that we remove files registered with RemoveFileOnSignal.
43a9ac8606Spatrick   llvm::sys::RunInterruptHandlers();
44a9ac8606Spatrick 
45a9ac8606Spatrick   // We cannot recover from llvm errors.  When reporting a fatal error, exit
46a9ac8606Spatrick   // with status 70 to generate crash diagnostics.  For BSD systems this is
47a9ac8606Spatrick   // defined as an internal software error. Otherwise, exit with status 1.
48a9ac8606Spatrick 
49a9ac8606Spatrick   exit(GenCrashDiag ? 70 : 1);
50a9ac8606Spatrick }
51a9ac8606Spatrick 
52*12c85518Srobert // If we are running with -verify a reported has to be returned as unsuccess.
53*12c85518Srobert // This is relevant especially for the test suite.
checkDiagErrors(const clang::CompilerInstance * CI,bool HasError)54*12c85518Srobert static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) {
55*12c85518Srobert   unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors();
56*12c85518Srobert   if (CI->getDiagnosticOpts().VerifyDiagnostics) {
57*12c85518Srobert     // If there was an error that came from the verifier we must return 1 as
58*12c85518Srobert     // an exit code for the process. This will make the test fail as expected.
59*12c85518Srobert     clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient();
60*12c85518Srobert     Client->EndSourceFile();
61*12c85518Srobert     Errs = Client->getNumErrors();
62*12c85518Srobert 
63*12c85518Srobert     // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.
64*12c85518Srobert     Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor());
65*12c85518Srobert   }
66*12c85518Srobert   return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;
67*12c85518Srobert }
68*12c85518Srobert 
69a9ac8606Spatrick llvm::ExitOnError ExitOnErr;
main(int argc,const char ** argv)70a9ac8606Spatrick int main(int argc, const char **argv) {
71a9ac8606Spatrick   ExitOnErr.setBanner("clang-repl: ");
72a9ac8606Spatrick   llvm::cl::ParseCommandLineOptions(argc, argv);
73a9ac8606Spatrick 
74*12c85518Srobert   llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
75*12c85518Srobert 
76a9ac8606Spatrick   std::vector<const char *> ClangArgv(ClangArgs.size());
77a9ac8606Spatrick   std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
78a9ac8606Spatrick                  [](const std::string &s) -> const char * { return s.data(); });
79a9ac8606Spatrick   llvm::InitializeNativeTarget();
80a9ac8606Spatrick   llvm::InitializeNativeTargetAsmPrinter();
81a9ac8606Spatrick 
82a9ac8606Spatrick   if (OptHostSupportsJit) {
83a9ac8606Spatrick     auto J = llvm::orc::LLJITBuilder().create();
84a9ac8606Spatrick     if (J)
85a9ac8606Spatrick       llvm::outs() << "true\n";
86a9ac8606Spatrick     else {
87a9ac8606Spatrick       llvm::consumeError(J.takeError());
88a9ac8606Spatrick       llvm::outs() << "false\n";
89a9ac8606Spatrick     }
90a9ac8606Spatrick     return 0;
91a9ac8606Spatrick   }
92a9ac8606Spatrick 
93a9ac8606Spatrick   // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
94a9ac8606Spatrick   // can replace the boilerplate code for creation of the compiler instance.
95a9ac8606Spatrick   auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create(ClangArgv));
96a9ac8606Spatrick 
97a9ac8606Spatrick   // Set an error handler, so that any LLVM backend diagnostics go through our
98a9ac8606Spatrick   // error handler.
99a9ac8606Spatrick   llvm::install_fatal_error_handler(LLVMErrorHandler,
100a9ac8606Spatrick                                     static_cast<void *>(&CI->getDiagnostics()));
101a9ac8606Spatrick 
102*12c85518Srobert   // Load any requested plugins.
103*12c85518Srobert   CI->LoadRequestedPlugins();
104*12c85518Srobert 
105a9ac8606Spatrick   auto Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
106a9ac8606Spatrick   for (const std::string &input : OptInputs) {
107a9ac8606Spatrick     if (auto Err = Interp->ParseAndExecute(input))
108a9ac8606Spatrick       llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
109a9ac8606Spatrick   }
110a9ac8606Spatrick 
111*12c85518Srobert   bool HasError = false;
112*12c85518Srobert 
113a9ac8606Spatrick   if (OptInputs.empty()) {
114a9ac8606Spatrick     llvm::LineEditor LE("clang-repl");
115a9ac8606Spatrick     // FIXME: Add LE.setListCompleter
116*12c85518Srobert     while (std::optional<std::string> Line = LE.readLine()) {
117*12c85518Srobert       if (*Line == R"(%quit)")
118a9ac8606Spatrick         break;
119*12c85518Srobert       if (*Line == R"(%undo)") {
120*12c85518Srobert         if (auto Err = Interp->Undo()) {
121a9ac8606Spatrick           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
122*12c85518Srobert           HasError = true;
123*12c85518Srobert         }
124*12c85518Srobert         continue;
125*12c85518Srobert       }
126*12c85518Srobert 
127*12c85518Srobert       if (auto Err = Interp->ParseAndExecute(*Line)) {
128*12c85518Srobert         llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
129*12c85518Srobert         HasError = true;
130*12c85518Srobert       }
131a9ac8606Spatrick     }
132a9ac8606Spatrick   }
133a9ac8606Spatrick 
134a9ac8606Spatrick   // Our error handler depends on the Diagnostics object, which we're
135a9ac8606Spatrick   // potentially about to delete. Uninstall the handler now so that any
136a9ac8606Spatrick   // later errors use the default handling behavior instead.
137a9ac8606Spatrick   llvm::remove_fatal_error_handler();
138a9ac8606Spatrick 
139*12c85518Srobert   return checkDiagErrors(Interp->getCompilerInstance(), HasError);
140a9ac8606Spatrick }
141