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
25 static llvm::cl::list<std::string>
26 ClangArgs("Xcc", llvm::cl::ZeroOrMore,
27 llvm::cl::desc("Argument to pass to the CompilerInvocation"),
28 llvm::cl::CommaSeparated);
29 static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
30 llvm::cl::Hidden);
31
LLVMErrorHandler(void * UserData,const std::string & Message,bool GenCrashDiag)32 static void LLVMErrorHandler(void *UserData, const std::string &Message,
33 bool GenCrashDiag) {
34 auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
35
36 Diags.Report(clang::diag::err_fe_error_backend) << Message;
37
38 // Run the interrupt handlers to make sure any special cleanups get done, in
39 // particular that we remove files registered with RemoveFileOnSignal.
40 llvm::sys::RunInterruptHandlers();
41
42 // We cannot recover from llvm errors. When reporting a fatal error, exit
43 // with status 70 to generate crash diagnostics. For BSD systems this is
44 // defined as an internal software error. Otherwise, exit with status 1.
45
46 exit(GenCrashDiag ? 70 : 1);
47 }
48
49 llvm::ExitOnError ExitOnErr;
main(int argc,const char ** argv)50 int main(int argc, const char **argv) {
51 ExitOnErr.setBanner("clang-repl: ");
52 llvm::cl::ParseCommandLineOptions(argc, argv);
53
54 std::vector<const char *> ClangArgv(ClangArgs.size());
55 std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
56 [](const std::string &s) -> const char * { return s.data(); });
57 llvm::InitializeNativeTarget();
58 llvm::InitializeNativeTargetAsmPrinter();
59
60 if (OptHostSupportsJit) {
61 auto J = llvm::orc::LLJITBuilder().create();
62 if (J)
63 llvm::outs() << "true\n";
64 else {
65 llvm::consumeError(J.takeError());
66 llvm::outs() << "false\n";
67 }
68 return 0;
69 }
70
71 // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
72 // can replace the boilerplate code for creation of the compiler instance.
73 auto CI = ExitOnErr(clang::IncrementalCompilerBuilder::create(ClangArgv));
74
75 // Set an error handler, so that any LLVM backend diagnostics go through our
76 // error handler.
77 llvm::install_fatal_error_handler(LLVMErrorHandler,
78 static_cast<void *>(&CI->getDiagnostics()));
79
80 auto Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
81 llvm::LineEditor LE("clang-repl");
82 // FIXME: Add LE.setListCompleter
83 while (llvm::Optional<std::string> Line = LE.readLine()) {
84 if (*Line == "quit")
85 break;
86 if (auto Err = Interp->ParseAndExecute(*Line))
87 llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
88 }
89
90 // Our error handler depends on the Diagnostics object, which we're
91 // potentially about to delete. Uninstall the handler now so that any
92 // later errors use the default handling behavior instead.
93 llvm::remove_fatal_error_handler();
94
95 llvm::llvm_shutdown();
96
97 return 0;
98 }
99