]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/rust-analyzer/src/dispatch.rs
Auto merge of #101212 - eholk:dyn-star, r=compiler-errors
[rust.git] / src / tools / rust-analyzer / crates / rust-analyzer / src / dispatch.rs
1 //! See [RequestDispatcher].
2 use std::{fmt, panic, thread};
3
4 use ide::Cancelled;
5 use lsp_server::ExtractError;
6 use serde::{de::DeserializeOwned, Serialize};
7
8 use crate::{
9     global_state::{GlobalState, GlobalStateSnapshot},
10     main_loop::Task,
11     version::version,
12     LspError, Result,
13 };
14
15 /// A visitor for routing a raw JSON request to an appropriate handler function.
16 ///
17 /// Most requests are read-only and async and are handled on the threadpool
18 /// (`on` method).
19 ///
20 /// Some read-only requests are latency sensitive, and are immediately handled
21 /// on the main loop thread (`on_sync`). These are typically typing-related
22 /// requests.
23 ///
24 /// Some requests modify the state, and are run on the main thread to get
25 /// `&mut` (`on_sync_mut`).
26 ///
27 /// Read-only requests are wrapped into `catch_unwind` -- they don't modify the
28 /// state, so it's OK to recover from their failures.
29 pub(crate) struct RequestDispatcher<'a> {
30     pub(crate) req: Option<lsp_server::Request>,
31     pub(crate) global_state: &'a mut GlobalState,
32 }
33
34 impl<'a> RequestDispatcher<'a> {
35     /// Dispatches the request onto the current thread, given full access to
36     /// mutable global state. Unlike all other methods here, this one isn't
37     /// guarded by `catch_unwind`, so, please, don't make bugs :-)
38     pub(crate) fn on_sync_mut<R>(
39         &mut self,
40         f: fn(&mut GlobalState, R::Params) -> Result<R::Result>,
41     ) -> &mut Self
42     where
43         R: lsp_types::request::Request,
44         R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
45         R::Result: Serialize,
46     {
47         let (req, params, panic_context) = match self.parse::<R>() {
48             Some(it) => it,
49             None => return self,
50         };
51         let result = {
52             let _pctx = stdx::panic_context::enter(panic_context);
53             f(self.global_state, params)
54         };
55         if let Ok(response) = result_to_response::<R>(req.id.clone(), result) {
56             self.global_state.respond(response);
57         }
58
59         self
60     }
61
62     /// Dispatches the request onto the current thread.
63     pub(crate) fn on_sync<R>(
64         &mut self,
65         f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
66     ) -> &mut Self
67     where
68         R: lsp_types::request::Request,
69         R::Params: DeserializeOwned + panic::UnwindSafe + fmt::Debug,
70         R::Result: Serialize,
71     {
72         let (req, params, panic_context) = match self.parse::<R>() {
73             Some(it) => it,
74             None => return self,
75         };
76         let global_state_snapshot = self.global_state.snapshot();
77
78         let result = panic::catch_unwind(move || {
79             let _pctx = stdx::panic_context::enter(panic_context);
80             f(global_state_snapshot, params)
81         });
82
83         if let Ok(response) = thread_result_to_response::<R>(req.id.clone(), result) {
84             self.global_state.respond(response);
85         }
86
87         self
88     }
89
90     /// Dispatches the request onto thread pool
91     pub(crate) fn on<R>(
92         &mut self,
93         f: fn(GlobalStateSnapshot, R::Params) -> Result<R::Result>,
94     ) -> &mut Self
95     where
96         R: lsp_types::request::Request + 'static,
97         R::Params: DeserializeOwned + panic::UnwindSafe + Send + fmt::Debug,
98         R::Result: Serialize,
99     {
100         let (req, params, panic_context) = match self.parse::<R>() {
101             Some(it) => it,
102             None => return self,
103         };
104
105         self.global_state.task_pool.handle.spawn({
106             let world = self.global_state.snapshot();
107             move || {
108                 let result = panic::catch_unwind(move || {
109                     let _pctx = stdx::panic_context::enter(panic_context);
110                     f(world, params)
111                 });
112                 match thread_result_to_response::<R>(req.id.clone(), result) {
113                     Ok(response) => Task::Response(response),
114                     Err(_) => Task::Retry(req),
115                 }
116             }
117         });
118
119         self
120     }
121
122     pub(crate) fn finish(&mut self) {
123         if let Some(req) = self.req.take() {
124             tracing::error!("unknown request: {:?}", req);
125             let response = lsp_server::Response::new_err(
126                 req.id,
127                 lsp_server::ErrorCode::MethodNotFound as i32,
128                 "unknown request".to_string(),
129             );
130             self.global_state.respond(response);
131         }
132     }
133
134     fn parse<R>(&mut self) -> Option<(lsp_server::Request, R::Params, String)>
135     where
136         R: lsp_types::request::Request,
137         R::Params: DeserializeOwned + fmt::Debug,
138     {
139         let req = match &self.req {
140             Some(req) if req.method == R::METHOD => self.req.take()?,
141             _ => return None,
142         };
143
144         let res = crate::from_json(R::METHOD, &req.params);
145         match res {
146             Ok(params) => {
147                 let panic_context =
148                     format!("\nversion: {}\nrequest: {} {:#?}", version(), R::METHOD, params);
149                 Some((req, params, panic_context))
150             }
151             Err(err) => {
152                 let response = lsp_server::Response::new_err(
153                     req.id,
154                     lsp_server::ErrorCode::InvalidParams as i32,
155                     err.to_string(),
156                 );
157                 self.global_state.respond(response);
158                 None
159             }
160         }
161     }
162 }
163
164 fn thread_result_to_response<R>(
165     id: lsp_server::RequestId,
166     result: thread::Result<Result<R::Result>>,
167 ) -> Result<lsp_server::Response, Cancelled>
168 where
169     R: lsp_types::request::Request,
170     R::Params: DeserializeOwned,
171     R::Result: Serialize,
172 {
173     match result {
174         Ok(result) => result_to_response::<R>(id, result),
175         Err(panic) => {
176             let panic_message = panic
177                 .downcast_ref::<String>()
178                 .map(String::as_str)
179                 .or_else(|| panic.downcast_ref::<&str>().copied());
180
181             let mut message = "request handler panicked".to_string();
182             if let Some(panic_message) = panic_message {
183                 message.push_str(": ");
184                 message.push_str(panic_message)
185             };
186
187             Ok(lsp_server::Response::new_err(
188                 id,
189                 lsp_server::ErrorCode::InternalError as i32,
190                 message,
191             ))
192         }
193     }
194 }
195
196 fn result_to_response<R>(
197     id: lsp_server::RequestId,
198     result: Result<R::Result>,
199 ) -> Result<lsp_server::Response, Cancelled>
200 where
201     R: lsp_types::request::Request,
202     R::Params: DeserializeOwned,
203     R::Result: Serialize,
204 {
205     let res = match result {
206         Ok(resp) => lsp_server::Response::new_ok(id, &resp),
207         Err(e) => match e.downcast::<LspError>() {
208             Ok(lsp_error) => lsp_server::Response::new_err(id, lsp_error.code, lsp_error.message),
209             Err(e) => match e.downcast::<Cancelled>() {
210                 Ok(cancelled) => return Err(*cancelled),
211                 Err(e) => lsp_server::Response::new_err(
212                     id,
213                     lsp_server::ErrorCode::InternalError as i32,
214                     e.to_string(),
215                 ),
216             },
217         },
218     };
219     Ok(res)
220 }
221
222 pub(crate) struct NotificationDispatcher<'a> {
223     pub(crate) not: Option<lsp_server::Notification>,
224     pub(crate) global_state: &'a mut GlobalState,
225 }
226
227 impl<'a> NotificationDispatcher<'a> {
228     pub(crate) fn on<N>(
229         &mut self,
230         f: fn(&mut GlobalState, N::Params) -> Result<()>,
231     ) -> Result<&mut Self>
232     where
233         N: lsp_types::notification::Notification,
234         N::Params: DeserializeOwned + Send,
235     {
236         let not = match self.not.take() {
237             Some(it) => it,
238             None => return Ok(self),
239         };
240         let params = match not.extract::<N::Params>(N::METHOD) {
241             Ok(it) => it,
242             Err(ExtractError::JsonError { method, error }) => {
243                 panic!("Invalid request\nMethod: {method}\n error: {error}",)
244             }
245             Err(ExtractError::MethodMismatch(not)) => {
246                 self.not = Some(not);
247                 return Ok(self);
248             }
249         };
250         let _pctx = stdx::panic_context::enter(format!(
251             "\nversion: {}\nnotification: {}",
252             version(),
253             N::METHOD
254         ));
255         f(self.global_state, params)?;
256         Ok(self)
257     }
258
259     pub(crate) fn finish(&mut self) {
260         if let Some(not) = &self.not {
261             if !not.method.starts_with("$/") {
262                 tracing::error!("unhandled notification: {:?}", not);
263             }
264         }
265     }
266 }