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