Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit f898539

Browse files
committed
Auto merge of #1300 - lijinpei:warn-unused-config, r=alexheretic
Warning on unused config This commit send a "windows/showMessage" notification, when some unknown or duplicated config is met during initialization request. see #1153
2 parents 3f367a7 + 888ed24 commit f898539

File tree

9 files changed

+300
-70
lines changed

9 files changed

+300
-70
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ rustc-serialize = "0.3"
4646
serde = "1.0"
4747
serde_json = "1.0"
4848
serde_derive = "1.0"
49+
serde_ignored = "0.0"
4950
url = "1"
5051
walkdir = "2"
5152
regex = "1"

src/actions/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ impl InitActionContext {
306306
let needs_inference = {
307307
let mut config = self.config.lock().unwrap();
308308

309-
if let Some(init_config) = init_options.settings.map(|s| s.rust.0) {
309+
if let Some(init_config) = init_options.settings.map(|s| s.rust) {
310310
config.update(init_config);
311311
}
312312
config.needs_inference()

src/actions/notifications.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use crate::actions::{FileWatch, InitActionContext, VersionOrdering};
1414
use crate::Span;
1515
use log::{debug, trace, warn};
1616
use rls_vfs::{Change, VfsSpan};
17-
use serde::Deserialize;
1817
use std::sync::atomic::Ordering;
1918

2019
use crate::build::*;
@@ -150,18 +149,22 @@ impl BlockingNotificationAction for Cancel {
150149

151150
impl BlockingNotificationAction for DidChangeConfiguration {
152151
fn handle<O: Output>(
153-
mut params: DidChangeConfigurationParams,
152+
params: DidChangeConfigurationParams,
154153
ctx: &mut InitActionContext,
155154
out: O,
156155
) -> Result<(), ()> {
157156
trace!("config change: {:?}", params.settings);
158-
params.settings = crate::lsp_data::json_key_to_snake_case(params.settings);
159-
let settings = ChangeConfigSettings::deserialize(&params.settings);
157+
use std::collections::HashMap;
158+
let mut dups = HashMap::new();
159+
let mut unknowns = vec![];
160+
let settings = ChangeConfigSettings::try_deserialize(&params.settings, &mut dups, &mut unknowns);
161+
crate::server::maybe_notify_unknown_configs(&out, &unknowns);
162+
crate::server::maybe_notify_duplicated_configs(&out, &dups);
160163

161164
let new_config = match settings {
162165
Ok(mut value) => {
163-
value.rust.0.normalise();
164-
value.rust.0
166+
value.rust.normalise();
167+
value.rust
165168
}
166169
Err(err) => {
167170
warn!(

src/config.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,50 @@ impl Default for Config {
201201
}
202202

203203
impl Config {
204+
/// try to deserialize a Config from a json value, val is expected to be a
205+
/// Value::Object, all first level keys of val are converted to snake_case,
206+
/// duplicated and unknown keys are reported
207+
pub fn try_deserialize(val: &serde_json::value::Value,
208+
dups:&mut std::collections::HashMap<String, Vec<String>>,
209+
unknowns: &mut Vec<(String)>)
210+
-> Result<Config, ()> {
211+
212+
#[derive(Clone)]
213+
struct JsonValue(serde_json::value::Value);
214+
215+
impl<'de> serde::de::IntoDeserializer<'de, serde_json::Error> for JsonValue {
216+
type Deserializer = serde_json::value::Value;
217+
fn into_deserializer(self) -> Self::Deserializer {
218+
self.0
219+
}
220+
}
221+
222+
if let serde_json::Value::Object(map) = val {
223+
let seq = serde::de::value::MapDeserializer::new(map.iter().filter_map(|(k, v)| {
224+
use heck::SnakeCase;
225+
let snake_case = k.to_snake_case();
226+
let (_, ref mut vec) = dups.raw_entry_mut().from_key(&snake_case).or_insert(snake_case.clone(), vec![]);
227+
vec.push(k.to_string());
228+
if vec.len() == 1 {
229+
Some((snake_case, JsonValue(v.to_owned().clone())))
230+
} else {
231+
None
232+
}
233+
}));
234+
match serde_ignored::deserialize(seq, |path|unknowns.push(path.to_string())) {
235+
Ok(conf) => {
236+
dups.retain(|_, v| v.len() > 1);
237+
return Ok(conf)
238+
239+
},
240+
_ => {
241+
dups.retain(|_, v| v.len() > 1);
242+
},
243+
}
244+
}
245+
Err(())
246+
}
247+
204248
/// Join this configuration with the new config.
205249
pub fn update(&mut self, mut new: Config) {
206250
new.normalise();

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//! functionality such as 'goto definition', symbol search, reformatting, and
66
//! code completion, and enables renaming and refactorings.
77
8-
#![feature(rustc_private, integer_atomics, drain_filter)]
8+
#![feature(rustc_private, integer_atomics, drain_filter, hash_raw_entry)]
99
#![allow(unknown_lints)]
1010
#![warn(clippy::all, rust_2018_idioms)]
1111
#![allow(

src/lsp_data.rs

Lines changed: 56 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -246,31 +246,38 @@ impl RangeExt for Range {
246246
}
247247
}
248248

249-
pub struct Config(pub config::Config);
250-
251-
impl<'de> serde::Deserialize<'de> for Config {
252-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
253-
where
254-
D: serde::Deserializer<'de> {
255-
use serde::de::Error;
256-
let val = json_key_to_snake_case(serde_json::Value::deserialize(deserializer)?);
257-
match serde_json::from_value(val) {
258-
Ok(config) => Ok(Config(config)),
259-
_ => Err(D::Error::custom("unable to deserialize Config")),
260-
}
261-
}
262-
}
263-
264-
impl fmt::Debug for Config {
265-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266-
fmt::Debug::fmt(&self.0, f)
267-
}
268-
}
269-
270249
/// `DidChangeConfigurationParams.settings` payload reading the { rust: {...} } bit.
271250
#[derive(Debug, Deserialize)]
272251
pub struct ChangeConfigSettings {
273-
pub rust: Config,
252+
pub rust: config::Config,
253+
}
254+
255+
impl ChangeConfigSettings {
256+
/// try to deserialize a ChangeConfigSettings from a json value, val is
257+
/// expected to be a Value::Object containing only one key "rust", all first
258+
/// level keys of rust's value are converted to snake_case, duplicated and
259+
/// unknown keys are reported
260+
pub fn try_deserialize(val: &serde_json::value::Value,
261+
dups:&mut std::collections::HashMap<String, Vec<String>>, unknowns: &mut Vec<String>)
262+
-> Result<ChangeConfigSettings, ()> {
263+
let mut ret = Err(());
264+
if let serde_json::Value::Object(map) = val {
265+
for (k, v) in map.iter() {
266+
if k != "rust" {
267+
unknowns.push(k.to_string());
268+
continue;
269+
}
270+
if let serde_json::Value::Object(_) = v {
271+
if let Ok(rust) = config::Config::try_deserialize(v, dups, unknowns) {
272+
ret = Ok(ChangeConfigSettings{rust: rust});
273+
}
274+
} else {
275+
return Err(());
276+
}
277+
}
278+
}
279+
ret
280+
}
274281
}
275282

276283
/* ----------------- JSON-RPC protocol types ----------------- */
@@ -287,6 +294,33 @@ pub struct InitializationOptions {
287294
pub settings: Option<ChangeConfigSettings>,
288295
}
289296

297+
impl InitializationOptions {
298+
/// try to deserialize a Initialization from a json value. If exists,
299+
/// val.settings is expected to be a Value::Object containing only one key,
300+
/// "rust", all first level keys of rust's value are converted to
301+
/// snake_case, duplicated and unknown keys are reported
302+
pub fn try_deserialize(val: &serde_json::value::Value,
303+
dups:&mut std::collections::HashMap<String, Vec<String>>, unknowns: &mut Vec<String>)
304+
-> Result<InitializationOptions, ()> {
305+
let mut val = val.to_owned();
306+
let mut set = None;
307+
if let Some(set1) = val.get_mut("settings") {
308+
set = Some(set1.take());
309+
}
310+
let mut ret:InitializationOptions =
311+
match serde_json::from_value(val) {
312+
Ok(ret) => ret,
313+
_ => return Err(()),
314+
};
315+
if let Some(set) = set {
316+
if let Ok(set) = ChangeConfigSettings::try_deserialize(&set, dups, unknowns) {
317+
ret.settings = Some(set);
318+
}
319+
}
320+
Ok(ret)
321+
}
322+
}
323+
290324
impl Default for InitializationOptions {
291325
fn default() -> Self {
292326
InitializationOptions {
@@ -417,28 +451,3 @@ pub struct ProgressParams {
417451
pub done: Option<bool>,
418452
}
419453

420-
pub fn json_key_to_snake_case(mut val: serde_json::Value) -> serde_json::Value {
421-
fn helper(val: &mut serde_json::Value) -> &mut serde_json::Value {
422-
use heck::SnakeCase;
423-
use serde_json::Value;
424-
match val {
425-
Value::Object(map) => {
426-
let mut map1 = serde_json::map::Map::<String, Value>::with_capacity(map.len());
427-
for kv in map.into_iter() {
428-
match map1.insert(kv.0.as_str().to_snake_case(), kv.1.clone()) {
429-
Some(val) => {
430-
log::error!("Multiple different case uses of `{}` config with value {} and value {}", kv.0, val, kv.1);
431-
}
432-
_ => (),
433-
}
434-
}
435-
std::mem::replace(map, map1);
436-
}
437-
_ => (),
438-
}
439-
val
440-
}
441-
helper(&mut val);
442-
val
443-
}
444-

src/server/mod.rs

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use crate::actions::{notifications, requests, ActionContext};
1616
use crate::config::Config;
1717
use crate::lsp_data;
18-
use crate::lsp_data::{InitializationOptions, LSPNotification, LSPRequest};
18+
use crate::lsp_data::{InitializationOptions, LSPNotification, LSPRequest, ShowMessageParams, MessageType};
1919
use crate::server::dispatch::Dispatcher;
2020
pub use crate::server::dispatch::{RequestAction, DEFAULT_REQUEST_TIMEOUT};
2121
pub use crate::server::io::{MessageReader, Output};
@@ -27,7 +27,7 @@ pub use crate::server::message::{
2727
};
2828
use crate::version;
2929
use jsonrpc_core::{self as jsonrpc, types::error::ErrorCode, Id};
30-
pub use lsp_types::notification::Exit as ExitNotification;
30+
pub use lsp_types::notification::{Exit as ExitNotification, ShowMessage};
3131
pub use lsp_types::request::Initialize as InitializeRequest;
3232
pub use lsp_types::request::Shutdown as ShutdownRequest;
3333
use lsp_types::{
@@ -38,7 +38,6 @@ use lsp_types::{
3838
use log::{debug, error, trace, warn};
3939
use rls_analysis::AnalysisHost;
4040
use rls_vfs::Vfs;
41-
use serde_json;
4241
use std::path::PathBuf;
4342
use std::sync::atomic::Ordering;
4443
use std::sync::{Arc, Mutex};
@@ -86,26 +85,56 @@ impl BlockingRequestAction for ShutdownRequest {
8685
}
8786
}
8887

88+
pub(crate) fn maybe_notify_unknown_configs<O: Output>(out: &O, unknowns: &[String]) {
89+
use std::fmt::Write;
90+
if unknowns.is_empty() {
91+
return;
92+
}
93+
let mut msg = "Unknown RLS configuration:".to_string();
94+
let mut first = true;
95+
for key in unknowns {
96+
write!(msg, "{}`{}` ", if first {' '} else {','}, key).unwrap();
97+
first = false;
98+
}
99+
out.notify(Notification::<ShowMessage>::new(ShowMessageParams {
100+
typ: MessageType::Warning,
101+
message: msg,
102+
}));
103+
}
104+
105+
pub(crate) fn maybe_notify_duplicated_configs<O: Output>(out: &O, dups: &std::collections::HashMap<String, Vec<String>>) {
106+
use std::fmt::Write;
107+
if dups.is_empty() {
108+
return;
109+
}
110+
let mut msg = String::new();
111+
for kv in dups {
112+
write!(msg, "{}:", kv.0).unwrap();
113+
let mut first = true;
114+
for v in kv.1 {
115+
write!(msg, "{}{}, ", if first {' '} else {','}, v).unwrap();
116+
first = false;
117+
}
118+
msg += "; ";
119+
}
120+
out.notify(Notification::<ShowMessage>::new(ShowMessageParams {
121+
typ: MessageType::Warning,
122+
message: format!("Duplicated RLS configuration: {}", msg.clone()),
123+
}));
124+
}
125+
89126
impl BlockingRequestAction for InitializeRequest {
90127
type Response = NoResponse;
91128

92129
fn handle<O: Output>(
93130
id: RequestId,
94-
params: Self::Params,
131+
mut params: Self::Params,
95132
ctx: &mut ActionContext,
96133
out: O,
97134
) -> Result<NoResponse, ResponseError> {
98-
let init_options: InitializationOptions = params
99-
.initialization_options
100-
.as_ref()
101-
.and_then(|options| {
102-
let de = serde_json::from_value(options.to_owned());
103-
if let Err(ref e) = de {
104-
warn!("initialization_options: {}", e);
105-
}
106-
de.ok()
107-
})
108-
.unwrap_or_default();
135+
let mut dups = std::collections::HashMap::new();
136+
let mut unknowns = Vec::new();
137+
let init_options = params.initialization_options.take().and_then(|opt| InitializationOptions::try_deserialize(&opt, &mut dups, &mut unknowns).ok()).unwrap_or_default();
109138

110139
trace!("init: {:?} -> {:?}", params.initialization_options, init_options);
111140

@@ -117,6 +146,9 @@ impl BlockingRequestAction for InitializeRequest {
117146
));
118147
}
119148

149+
maybe_notify_unknown_configs(&out, &unknowns);
150+
maybe_notify_duplicated_configs(&out, &dups);
151+
120152
let result = InitializeResult {
121153
capabilities: server_caps(ctx),
122154
};

0 commit comments

Comments
 (0)