From dd20a6f70145da0adb660a3ba8881b97521ae3c0 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 6 Jan 2022 14:23:35 +0100 Subject: [PATCH] feat: poke user when supplying faulty configurations --- crates/rust-analyzer/src/bin/main.rs | 2 +- crates/rust-analyzer/src/config.rs | 31 +++++++++++++------ crates/rust-analyzer/src/main_loop.rs | 13 +++++++- .../rust-analyzer/tests/slow-tests/support.rs | 2 +- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 567cdc48ef..9a2e435591 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -150,7 +150,7 @@ fn run_server() -> Result<()> { let mut config = Config::new(root_path, initialize_params.capabilities); if let Some(json) = initialize_params.initialization_options { - config.update(json); + let _ = config.update(json); } let server_capabilities = rust_analyzer::server_capabilities(&config); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index a704139255..de0aba0caa 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -341,7 +341,7 @@ config_data! { impl Default for ConfigData { fn default() -> Self { - ConfigData::from_json(serde_json::Value::Null) + ConfigData::from_json(serde_json::Value::Null, &mut Vec::new()) } } @@ -492,16 +492,21 @@ impl Config { snippets: Default::default(), } } - pub fn update(&mut self, mut json: serde_json::Value) { + pub fn update( + &mut self, + mut json: serde_json::Value, + ) -> Result<(), Vec<(String, serde_json::Error)>> { tracing::info!("updating config from JSON: {:#}", json); if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { - return; + return Ok(()); } - self.detached_files = get_field::>(&mut json, "detachedFiles", None, "[]") - .into_iter() - .map(AbsPathBuf::assert) - .collect(); - self.data = ConfigData::from_json(json); + let mut errors = Vec::new(); + self.detached_files = + get_field::>(&mut json, &mut errors, "detachedFiles", None, "[]") + .into_iter() + .map(AbsPathBuf::assert) + .collect(); + self.data = ConfigData::from_json(json, &mut errors); self.snippets.clear(); for (name, def) in self.data.completion_snippets.iter() { if def.prefix.is_empty() && def.postfix.is_empty() { @@ -524,6 +529,11 @@ impl Config { None => tracing::info!("Invalid snippet {}", name), } } + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } } pub fn json_schema() -> serde_json::Value { @@ -1116,10 +1126,11 @@ macro_rules! _config_data { #[derive(Debug, Clone)] struct $name { $($field: $ty,)* } impl $name { - fn from_json(mut json: serde_json::Value) -> $name { + fn from_json(mut json: serde_json::Value, error_sink: &mut Vec<(String, serde_json::Error)>) -> $name { $name {$( $field: get_field( &mut json, + error_sink, stringify!($field), None$(.or(Some(stringify!($alias))))*, $default, @@ -1156,6 +1167,7 @@ use _config_data as config_data; fn get_field( json: &mut serde_json::Value, + error_sink: &mut Vec<(String, serde_json::Error)>, field: &'static str, alias: Option<&'static str>, default: &str, @@ -1174,6 +1186,7 @@ fn get_field( Ok(it) => Some(it), Err(e) => { tracing::warn!("Failed to deserialize config field at {}: {:?}", pointer, e); + error_sink.push((pointer, e)); None } }) diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 65f9c87367..af987230de 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -9,6 +9,7 @@ use std::{ use always_assert::always; use crossbeam_channel::{select, Receiver}; use ide_db::base_db::{SourceDatabaseExt, VfsPath}; +use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; use lsp_types::notification::Notification as _; use vfs::{ChangeKind, FileId}; @@ -731,7 +732,17 @@ impl GlobalState { // Note that json can be null according to the spec if the client can't // provide a configuration. This is handled in Config::update below. let mut config = Config::clone(&*this.config); - config.update(json.take()); + if let Err(errors) = config.update(json.take()) { + let errors = errors + .iter() + .format_with("\n", |(key, e),f| { + f(key)?; + f(&": ")?; + f(e) + }); + let msg= format!("Failed to deserialize config key(s):\n{}", errors); + this.show_message(lsp_types::MessageType::WARNING, msg); + } this.update_configuration(config); } } diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 6e7ecec5b2..78aa411915 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -137,7 +137,7 @@ impl<'a> Project<'a> { }, ); config.discovered_projects = Some(discovered_projects); - config.update(self.config); + let _ = config.update(self.config); Server::new(tmp_dir, config) }