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 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. 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; 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