From 97aef5891ae4bde257b8b860df94197238905254 Mon Sep 17 00:00:00 2001 From: copilot-swe-agent <198982749+copilot-swe-agent@users.noreply.github.com> Date: Sun, 28 Dec 2025 14:13:02 +0000 Subject: [PATCH] fix(language_server): treat empty string config path as None in language server (#17415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VS Code's settings UI auto-generates `"oxc.configPath": ""` when users click the setting field, causing the language server to fail initializing oxlintrc config instead of falling back to `.oxlintrc.json`. ## Changes - **Config path initialization**: Match `Some("")` alongside `None` to use default config file - **Watcher patterns**: Apply same logic when determining file watchers ```rust // Before let config_path = options.config_path.as_ref().map_or(LINT_CONFIG_FILE, |v| v); // After let config_path = match options.config_path.as_deref() { Some("") | None => LINT_CONFIG_FILE, Some(v) => v, }; ``` Applied at both config initialization and watcher pattern registration to ensure consistent behavior.
Original prompt > > ---- > > *This section details on the original issue you should resolve* > > linter: [vscode] Failed to initialize oxlintrc config > ### What version of Oxlint are you using? > > 1.35.0 > > ### What command did you run? > > save changes > > ### What does your `.oxlintrc.json` config file look like? > > ```jsonc > { > "plugins": ["typescript", "unicorn", "oxc", "react"], > "rules": { > "no-unused-vars": "off", > "no-unused-expressions": "off", > "react/jsx-no-undef": "off", > "react/jsx-no-target-blank": "off", > "react/exhaustive-deps": "off", > "react/rules-of-hooks": "error" > } > } > ``` > > > ### What happened? > > in vscode with settings.json `oxc.configPath` set to empty string > ```json > { > "oxc.configPath": "" > } > ``` > > will cause `WARN oxc_language_server::linter::server_linter] Failed to initialize oxlintrc config` > hence oxc config not recognized. > > I looked into the code and found > https://github.com/oxc-project/oxc/blob/oxlint_v1.35.0/crates/oxc_language_server/src/linter/server_linter.rs#L58 > > empty string is used and not fallback to default `.oxlintrc.json`, so resulting `Failed to initialize oxlintrc config` error > > but I was not meant to config `oxc.configPath` to an empty string, it's auto generated by a click on vscode settings UI > ![Image](https://github.com/user-attachments/assets/3ac7eed6-787c-4d3a-b335-a5a06f493e74) > > > ## Code > > https://github.com/oxc-project/oxc/blob/oxlint_v1.35.0/crates/oxc_language_server/src/linter/server_linter.rs#L58 > > I confirmed the behavior with ChatGPT. > Image > > empty string should use default fallback too: > ```rust > let config_path = match options.config_path.as_deref() { > Some("") | None => LINT_CONFIG_FILE, > Some(v) => v, > }; > ``` > > > > match on Some("") | None in the language server > > ## Comments on the Issue (you are @copilot in this section) > > > >
- Fixes oxc-project/oxc#17384 --- ✨ Let Copilot coding agent [set things up for you](https://github.com/oxc-project/oxc/issues/new?title=✨+Set+up+Copilot+instructions&body=Configure%20instructions%20for%20this%20repository%20as%20documented%20in%20%5BBest%20practices%20for%20Copilot%20coding%20agent%20in%20your%20repository%5D%28https://gh.io/copilot-coding-agent-tips%29%2E%0A%0A%3COnboard%20this%20repo%3E&assignees=copilot) — coding agent works faster and does higher quality work when set up for your repo. --- .../src/linter/server_linter.rs | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/crates/oxc_language_server/src/linter/server_linter.rs b/crates/oxc_language_server/src/linter/server_linter.rs index 03bf7aa1a3dc1..5b774afd93383 100644 --- a/crates/oxc_language_server/src/linter/server_linter.rs +++ b/crates/oxc_language_server/src/linter/server_linter.rs @@ -57,7 +57,10 @@ impl ServerLinterBuilder { let mut nested_ignore_patterns = Vec::new(); let (nested_configs, mut extended_paths) = Self::create_nested_configs(&root_path, &options, &mut nested_ignore_patterns); - let config_path = options.config_path.as_ref().map_or(LINT_CONFIG_FILE, |v| v); + let config_path = match options.config_path.as_deref() { + Some("") | None => LINT_CONFIG_FILE, + Some(v) => v, + }; let config = normalize_path(root_path.join(config_path)); let oxlintrc = if config.try_exists().is_ok_and(|exists| exists) { if let Ok(oxlintrc) = Oxlintrc::from_file(&config) { @@ -383,9 +386,11 @@ impl Tool for ServerLinter { LSPLintOptions::default() } }; - let mut watchers = vec![ - options.config_path.as_ref().unwrap_or(&"**/.oxlintrc.json".to_string()).to_owned(), - ]; + let config_pattern = match options.config_path.as_deref() { + Some("") | None => "**/.oxlintrc.json".to_string(), + Some(v) => v.to_string(), + }; + let mut watchers = vec![config_pattern]; for path in &self.extended_paths { // ignore .oxlintrc.json files when using nested configs @@ -820,6 +825,20 @@ mod test_watchers { assert_eq!(patterns[0], "**/.oxlintrc.json".to_string()); } + #[test] + fn test_empty_string_config_path() { + let patterns = Tester::new( + "fixtures/linter/watchers/default", + json!({ + "configPath": "" + }), + ) + .get_watcher_patterns(); + + assert_eq!(patterns.len(), 1); + assert_eq!(patterns[0], "**/.oxlintrc.json".to_string()); + } + #[test] fn test_custom_config_path() { let patterns = Tester::new(