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