Skip to content

Commit 2b934cd

Browse files
committed
[ty] Warn users if server received unknown options
1 parent 1f29a04 commit 2b934cd

File tree

4 files changed

+90
-2
lines changed

4 files changed

+90
-2
lines changed

crates/ty_server/src/server.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ impl Server {
9898
let (main_loop_sender, main_loop_receiver) = crossbeam::channel::bounded(32);
9999
let client = Client::new(main_loop_sender.clone(), connection.sender.clone());
100100

101+
if !initialization_options.options.unknown.is_empty() {
102+
let options = serde_json::to_string_pretty(&initialization_options.options.unknown)
103+
.unwrap_or_else(|_| "<invalid JSON>".to_string());
104+
tracing::warn!("Received unknown options during initialization: {options}");
105+
client.show_warning_message(format_args!(
106+
"Received unknown options during initialization: {options}"
107+
));
108+
}
109+
101110
// Get workspace URLs without settings - settings will come from workspace/configuration
102111
let workspace_urls = workspace_folders
103112
.filter(|folders| !folders.is_empty())

crates/ty_server/src/session.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,12 +450,23 @@ impl Session {
450450

451451
// Combine the global options specified during initialization with the
452452
// workspace-specific options to create the final workspace options.
453-
let ClientOptions { global, workspace } = self
453+
let ClientOptions {
454+
global, workspace, ..
455+
} = self
454456
.initialization_options
455457
.options
456458
.clone()
457459
.combine(options.clone());
458460

461+
if !options.unknown.is_empty() {
462+
let options = serde_json::to_string_pretty(&options.unknown)
463+
.unwrap_or_else(|_| "<invalid JSON>".to_string());
464+
tracing::warn!("Received unknown options for workspace `{url}`: {options}");
465+
client.show_warning_message(format!(
466+
"Received unknown options for workspace `{url}`: {options}",
467+
));
468+
}
469+
459470
combined_global_options.combine_with(Some(global));
460471

461472
let workspace_settings = workspace.into_settings();

crates/ty_server/src/session/options.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use lsp_types::Url;
24
use ruff_db::system::SystemPathBuf;
35
use ruff_macros::Combine;
@@ -84,6 +86,11 @@ pub struct ClientOptions {
8486

8587
#[serde(flatten)]
8688
pub(crate) workspace: WorkspaceOptions,
89+
90+
/// Additional options that aren't valid as per the schema but we accept it to provide better
91+
/// error message to the user.
92+
#[serde(flatten)]
93+
pub(crate) unknown: HashMap<String, Value>,
8794
}
8895

8996
impl ClientOptions {
@@ -98,6 +105,12 @@ impl ClientOptions {
98105
self.workspace.disable_language_services = Some(disable_language_services);
99106
self
100107
}
108+
109+
#[must_use]
110+
pub fn with_unknown(mut self, unknown: HashMap<String, Value>) -> Self {
111+
self.unknown = unknown;
112+
self
113+
}
101114
}
102115

103116
/// Options that are global to the language server.

crates/ty_server/tests/e2e/initialize.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::Result;
2-
use lsp_types::{Position, request::RegisterCapability};
2+
use lsp_types::{Position, notification::ShowMessage, request::RegisterCapability};
33
use ruff_db::system::SystemPath;
4+
use serde_json::Value;
45
use ty_server::{ClientOptions, DiagnosticMode};
56

67
use crate::TestServerBuilder;
@@ -382,3 +383,57 @@ def bar() -> str:
382383

383384
Ok(())
384385
}
386+
387+
/// Tests that the server sends a warning notification if user provided unknown options during
388+
/// initialization.
389+
#[test]
390+
fn unknown_initialization_options() -> Result<()> {
391+
let workspace_root = SystemPath::new("foo");
392+
let mut server = TestServerBuilder::new()?
393+
.with_workspace(workspace_root, None)?
394+
.with_initialization_options(
395+
ClientOptions::default()
396+
.with_unknown([("foo".to_string(), Value::String("bar".to_string()))].into()),
397+
)
398+
.build()?
399+
.wait_until_workspaces_are_initialized()?;
400+
401+
let show_message_params = server.await_notification::<ShowMessage>()?;
402+
403+
insta::assert_json_snapshot!(show_message_params, @r#"
404+
{
405+
"type": 2,
406+
"message": "Received unknown options during initialization: {\n /"foo/": /"bar/"\n}"
407+
}
408+
"#);
409+
410+
Ok(())
411+
}
412+
413+
/// Tests that the server sends a warning notification if user provided unknown options in the
414+
/// workspace configuration.
415+
#[test]
416+
fn unknown_options_in_workspace_configuration() -> Result<()> {
417+
let workspace_root = SystemPath::new("foo");
418+
let mut server = TestServerBuilder::new()?
419+
.with_workspace(
420+
workspace_root,
421+
Some(
422+
ClientOptions::default()
423+
.with_unknown([("foo".to_string(), Value::String("bar".to_string()))].into()),
424+
),
425+
)?
426+
.build()?
427+
.wait_until_workspaces_are_initialized()?;
428+
429+
let show_message_params = server.await_notification::<ShowMessage>()?;
430+
431+
insta::assert_json_snapshot!(show_message_params, @r#"
432+
{
433+
"type": 2,
434+
"message": "Received unknown options for workspace `file://<temp_dir>/foo`: {\n /"foo/": /"bar/"\n}"
435+
}
436+
"#);
437+
438+
Ok(())
439+
}

0 commit comments

Comments
 (0)