xref: /llvm-project/clang/tools/clang-repl/ClangRepl.cpp (revision 53a87b4a6903c0c47337ca18697bb6355cc087d4)
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/CodeCompletion.h"
17 #include "clang/Interpreter/Interpreter.h"
18 
19 #include "llvm/ExecutionEngine/Orc/LLJIT.h"
20 #include "llvm/LineEditor/LineEditor.h"
21 #include "llvm/Support/CommandLine.h"
22 #include "llvm/Support/ManagedStatic.h" // llvm_shutdown
23 #include "llvm/Support/Signals.h"
24 #include "llvm/Support/TargetSelect.h"
25 #include <optional>
26 
27 // Disable LSan for this test.
28 // FIXME: Re-enable once we can assume GCC 13.2 or higher.
29 // https://llvm.org/github.com/llvm/llvm-project/issues/67586.
30 #if LLVM_ADDRESS_SANITIZER_BUILD || LLVM_HWADDRESS_SANITIZER_BUILD
31 #include <sanitizer/lsan_interface.h>
32 LLVM_ATTRIBUTE_USED int __lsan_is_turned_off() { return 1; }
33 #endif
34 
35 static llvm::cl::opt<bool> CudaEnabled("cuda", llvm::cl::Hidden);
36 static llvm::cl::opt<std::string> CudaPath("cuda-path", llvm::cl::Hidden);
37 static llvm::cl::opt<std::string> OffloadArch("offload-arch", llvm::cl::Hidden);
38 
39 static llvm::cl::list<std::string>
40     ClangArgs("Xcc",
41               llvm::cl::desc("Argument to pass to the CompilerInvocation"),
42               llvm::cl::CommaSeparated);
43 static llvm::cl::opt<bool> OptHostSupportsJit("host-supports-jit",
44                                               llvm::cl::Hidden);
45 static llvm::cl::list<std::string> OptInputs(llvm::cl::Positional,
46                                              llvm::cl::desc("[code to run]"));
47 
48 static void LLVMErrorHandler(void *UserData, const char *Message,
49                              bool GenCrashDiag) {
50   auto &Diags = *static_cast<clang::DiagnosticsEngine *>(UserData);
51 
52   Diags.Report(clang::diag::err_fe_error_backend) << Message;
53 
54   // Run the interrupt handlers to make sure any special cleanups get done, in
55   // particular that we remove files registered with RemoveFileOnSignal.
56   llvm::sys::RunInterruptHandlers();
57 
58   // We cannot recover from llvm errors.  When reporting a fatal error, exit
59   // with status 70 to generate crash diagnostics.  For BSD systems this is
60   // defined as an internal software error. Otherwise, exit with status 1.
61 
62   exit(GenCrashDiag ? 70 : 1);
63 }
64 
65 // If we are running with -verify a reported has to be returned as unsuccess.
66 // This is relevant especially for the test suite.
67 static int checkDiagErrors(const clang::CompilerInstance *CI, bool HasError) {
68   unsigned Errs = CI->getDiagnostics().getClient()->getNumErrors();
69   if (CI->getDiagnosticOpts().VerifyDiagnostics) {
70     // If there was an error that came from the verifier we must return 1 as
71     // an exit code for the process. This will make the test fail as expected.
72     clang::DiagnosticConsumer *Client = CI->getDiagnostics().getClient();
73     Client->EndSourceFile();
74     Errs = Client->getNumErrors();
75 
76     // The interpreter expects BeginSourceFile/EndSourceFiles to be balanced.
77     Client->BeginSourceFile(CI->getLangOpts(), &CI->getPreprocessor());
78   }
79   return (Errs || HasError) ? EXIT_FAILURE : EXIT_SUCCESS;
80 }
81 
82 struct ReplListCompleter {
83   clang::IncrementalCompilerBuilder &CB;
84   clang::Interpreter &MainInterp;
85   ReplListCompleter(clang::IncrementalCompilerBuilder &CB,
86                     clang::Interpreter &Interp)
87       : CB(CB), MainInterp(Interp){};
88 
89   std::vector<llvm::LineEditor::Completion> operator()(llvm::StringRef Buffer,
90                                                        size_t Pos) const;
91   std::vector<llvm::LineEditor::Completion>
92   operator()(llvm::StringRef Buffer, size_t Pos, llvm::Error &ErrRes) const;
93 };
94 
95 std::vector<llvm::LineEditor::Completion>
96 ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos) const {
97   auto Err = llvm::Error::success();
98   auto res = (*this)(Buffer, Pos, Err);
99   if (Err)
100     llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
101   return res;
102 }
103 
104 std::vector<llvm::LineEditor::Completion>
105 ReplListCompleter::operator()(llvm::StringRef Buffer, size_t Pos,
106                               llvm::Error &ErrRes) const {
107   std::vector<llvm::LineEditor::Completion> Comps;
108   std::vector<std::string> Results;
109 
110   auto CI = CB.CreateCpp();
111   if (auto Err = CI.takeError()) {
112     ErrRes = std::move(Err);
113     return {};
114   }
115 
116   size_t Lines =
117       std::count(Buffer.begin(), std::next(Buffer.begin(), Pos), '\n') + 1;
118   auto Interp = clang::Interpreter::create(std::move(*CI));
119 
120   if (auto Err = Interp.takeError()) {
121     // log the error and returns an empty vector;
122     ErrRes = std::move(Err);
123 
124     return {};
125   }
126 
127   codeComplete(
128       const_cast<clang::CompilerInstance *>((*Interp)->getCompilerInstance()),
129       Buffer, Lines, Pos + 1, MainInterp.getCompilerInstance(), Results);
130 
131   size_t space_pos = Buffer.rfind(" ");
132   llvm::StringRef Prefix;
133   if (space_pos == llvm::StringRef::npos) {
134     Prefix = Buffer;
135   } else {
136     Prefix = Buffer.substr(space_pos + 1);
137   }
138 
139   for (auto c : Results) {
140     if (c.find(Prefix) == 0)
141       Comps.push_back(llvm::LineEditor::Completion(c.substr(Prefix.size()), c));
142   }
143   return Comps;
144 }
145 
146 llvm::ExitOnError ExitOnErr;
147 int main(int argc, const char **argv) {
148   ExitOnErr.setBanner("clang-repl: ");
149   llvm::cl::ParseCommandLineOptions(argc, argv);
150 
151   llvm::llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
152 
153   std::vector<const char *> ClangArgv(ClangArgs.size());
154   std::transform(ClangArgs.begin(), ClangArgs.end(), ClangArgv.begin(),
155                  [](const std::string &s) -> const char * { return s.data(); });
156   // Initialize all targets (required for device offloading)
157   llvm::InitializeAllTargetInfos();
158   llvm::InitializeAllTargets();
159   llvm::InitializeAllTargetMCs();
160   llvm::InitializeAllAsmPrinters();
161 
162   if (OptHostSupportsJit) {
163     auto J = llvm::orc::LLJITBuilder().create();
164     if (J)
165       llvm::outs() << "true\n";
166     else {
167       llvm::consumeError(J.takeError());
168       llvm::outs() << "false\n";
169     }
170     return 0;
171   }
172 
173   clang::IncrementalCompilerBuilder CB;
174   CB.SetCompilerArgs(ClangArgv);
175 
176   std::unique_ptr<clang::CompilerInstance> DeviceCI;
177   if (CudaEnabled) {
178     if (!CudaPath.empty())
179       CB.SetCudaSDK(CudaPath);
180 
181     if (OffloadArch.empty()) {
182       OffloadArch = "sm_35";
183     }
184     CB.SetOffloadArch(OffloadArch);
185 
186     DeviceCI = ExitOnErr(CB.CreateCudaDevice());
187   }
188 
189   // FIXME: Investigate if we could use runToolOnCodeWithArgs from tooling. It
190   // can replace the boilerplate code for creation of the compiler instance.
191   std::unique_ptr<clang::CompilerInstance> CI;
192   if (CudaEnabled) {
193     CI = ExitOnErr(CB.CreateCudaHost());
194   } else {
195     CI = ExitOnErr(CB.CreateCpp());
196   }
197 
198   // Set an error handler, so that any LLVM backend diagnostics go through our
199   // error handler.
200   llvm::install_fatal_error_handler(LLVMErrorHandler,
201                                     static_cast<void *>(&CI->getDiagnostics()));
202 
203   // Load any requested plugins.
204   CI->LoadRequestedPlugins();
205   if (CudaEnabled)
206     DeviceCI->LoadRequestedPlugins();
207 
208   std::unique_ptr<clang::Interpreter> Interp;
209 
210   if (CudaEnabled) {
211     Interp = ExitOnErr(
212         clang::Interpreter::createWithCUDA(std::move(CI), std::move(DeviceCI)));
213 
214     if (CudaPath.empty()) {
215       ExitOnErr(Interp->LoadDynamicLibrary("libcudart.so"));
216     } else {
217       auto CudaRuntimeLibPath = CudaPath + "/lib/libcudart.so";
218       ExitOnErr(Interp->LoadDynamicLibrary(CudaRuntimeLibPath.c_str()));
219     }
220   } else
221     Interp = ExitOnErr(clang::Interpreter::create(std::move(CI)));
222 
223   for (const std::string &input : OptInputs) {
224     if (auto Err = Interp->ParseAndExecute(input))
225       llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
226   }
227 
228   bool HasError = false;
229 
230   if (OptInputs.empty()) {
231     llvm::LineEditor LE("clang-repl");
232     std::string Input;
233     LE.setListCompleter(ReplListCompleter(CB, *Interp));
234     while (std::optional<std::string> Line = LE.readLine()) {
235       llvm::StringRef L = *Line;
236       L = L.trim();
237       if (L.endswith("\\")) {
238         // FIXME: Support #ifdef X \ ...
239         Input += L.drop_back(1);
240         LE.setPrompt("clang-repl...   ");
241         continue;
242       }
243 
244       Input += L;
245       if (Input == R"(%quit)") {
246         break;
247       }
248       if (Input == R"(%undo)") {
249         if (auto Err = Interp->Undo()) {
250           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
251           HasError = true;
252         }
253       } else if (Input.rfind("%lib ", 0) == 0) {
254         if (auto Err = Interp->LoadDynamicLibrary(Input.data() + 5)) {
255           llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
256           HasError = true;
257         }
258       } else if (auto Err = Interp->ParseAndExecute(Input)) {
259         llvm::logAllUnhandledErrors(std::move(Err), llvm::errs(), "error: ");
260         HasError = true;
261       }
262 
263       Input = "";
264       LE.setPrompt("clang-repl> ");
265     }
266   }
267 
268   // Our error handler depends on the Diagnostics object, which we're
269   // potentially about to delete. Uninstall the handler now so that any
270   // later errors use the default handling behavior instead.
271   llvm::remove_fatal_error_handler();
272 
273   return checkDiagErrors(Interp->getCompilerInstance(), HasError);
274 }
275