diff --git a/apps/oxlint/src/lsp/options.rs b/apps/oxlint/src/lsp/options.rs index fcb40d17df349..53de9ce1f2707 100644 --- a/apps/oxlint/src/lsp/options.rs +++ b/apps/oxlint/src/lsp/options.rs @@ -29,7 +29,7 @@ pub struct LintOptions { pub config_path: Option, #[serde(skip_serializing_if = "Option::is_none")] pub ts_config_path: Option, - pub unused_disable_directives: UnusedDisableDirectives, + pub unused_disable_directives: Option, pub type_aware: Option, pub disable_nested_config: bool, pub fix_kind: LintFixKindFlag, @@ -103,13 +103,9 @@ impl TryFrom for LintOptions { .get("run") .map(|run| serde_json::from_value::(run.clone()).unwrap_or_default()) .unwrap_or_default(), - unused_disable_directives: object - .get("unusedDisableDirectives") - .map(|key| { - serde_json::from_value::(key.clone()) - .unwrap_or_default() - }) - .unwrap_or_default(), + unused_disable_directives: object.get("unusedDisableDirectives").and_then(|key| { + serde_json::from_value::(key.clone()).ok() + }), config_path: object .get("configPath") .and_then(|config_path| serde_json::from_value::(config_path.clone()).ok()), @@ -161,7 +157,7 @@ mod test { let options = LintOptions::try_from(json).unwrap(); assert_eq!(options.run, Run::OnSave); assert_eq!(options.config_path, Some("./custom.json".into())); - assert_eq!(options.unused_disable_directives, UnusedDisableDirectives::Warn); + assert_eq!(options.unused_disable_directives, Some(UnusedDisableDirectives::Warn)); assert_eq!(options.type_aware, Some(true)); assert!(options.disable_nested_config); assert_eq!(options.fix_kind, super::LintFixKindFlag::DangerousFix); @@ -174,7 +170,7 @@ mod test { let options = LintOptions::try_from(json).unwrap(); assert_eq!(options.run, Run::OnType); assert_eq!(options.config_path, None); - assert_eq!(options.unused_disable_directives, UnusedDisableDirectives::Allow); + assert_eq!(options.unused_disable_directives, None); assert_eq!(options.type_aware, None); assert!(!options.disable_nested_config); assert_eq!(options.fix_kind, super::LintFixKindFlag::SafeFix); @@ -192,6 +188,19 @@ mod test { assert_eq!(options.config_path, Some("./custom.json".into())); } + #[test] + fn test_null_options_json() { + let json = json!({ + "configPath": null, + "tsConfigPath": null, + "typeAware": null, + "unusedDisableDirectives": null + }); + + let options = LintOptions::try_from(json).unwrap(); + assert_eq!(options.type_aware, None); // null should be treated as None + } + #[test] fn test_use_nested_configs() { let options = LintOptions::default(); @@ -228,7 +237,7 @@ mod test { let options = LintOptions::try_from(json).unwrap(); assert_eq!(options.run, Run::OnSave); assert_eq!(options.config_path, Some("./custom.json".into())); - assert_eq!(options.unused_disable_directives, UnusedDisableDirectives::Warn); + assert_eq!(options.unused_disable_directives, Some(UnusedDisableDirectives::Warn)); assert_eq!(options.type_aware, Some(true)); assert!(options.disable_nested_config); assert_eq!(options.fix_kind, LintFixKindFlag::DangerousFix); diff --git a/apps/oxlint/src/lsp/server_linter.rs b/apps/oxlint/src/lsp/server_linter.rs index 74d3c964ffce5..850f668c8a31d 100644 --- a/apps/oxlint/src/lsp/server_linter.rs +++ b/apps/oxlint/src/lsp/server_linter.rs @@ -151,20 +151,25 @@ impl ServerLinterBuilder { ConfigStoreBuilder::empty().build(&mut ExternalPluginStore::new(false)).unwrap() }); + if external_plugin_store.is_empty() { + external_linter = None; + } + let config_store = ConfigStore::new(base_config, nested_configs, external_plugin_store); + let lint_options = LintOptions { fix: fix_kind, report_unused_directive: match options.unused_disable_directives { - UnusedDisableDirectives::Allow => None, // or AllowWarnDeny::Allow, should be the same? - UnusedDisableDirectives::Warn => Some(AllowWarnDeny::Warn), - UnusedDisableDirectives::Deny => Some(AllowWarnDeny::Deny), + Some(UnusedDisableDirectives::Allow) => Some(AllowWarnDeny::Allow), + Some(UnusedDisableDirectives::Warn) => Some(AllowWarnDeny::Warn), + Some(UnusedDisableDirectives::Deny) => Some(AllowWarnDeny::Deny), + None => match config_store.report_unused_disable_directives() { + Some(severity) if severity.is_warn_deny() => Some(severity), + _ => None, + }, }, ..Default::default() }; - if external_plugin_store.is_empty() { - external_linter = None; - } - let config_store = ConfigStore::new(base_config, nested_configs, external_plugin_store); let type_aware = options.type_aware.unwrap_or(config_store.type_aware_enabled()); let config_store_clone = config_store.clone(); diff --git a/apps/oxlint/test/lsp/lint/__snapshots__/lint.test.ts.snap b/apps/oxlint/test/lsp/lint/__snapshots__/lint.test.ts.snap index ebbd9e7af1402..607b827b4f889 100644 --- a/apps/oxlint/test/lsp/lint/__snapshots__/lint.test.ts.snap +++ b/apps/oxlint/test/lsp/lint/__snapshots__/lint.test.ts.snap @@ -129,6 +129,17 @@ help: The promise must end with a call to .catch, or end with a call to .then wi --------------------" `; +exports[`LSP linting > config options > should apply config from unused-disable-directive-from-config/test.ts 1`] = ` +"--- FILE ----------- +unused-disable-directive-from-config/test.ts +--- Diagnostics --------- +> 1 | // oxlint-disable-next-line debugger + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error: Unused eslint-disable directive (no problems were reported). + 2 | console.log("This directive is not used"); + 3 | +--------------------" +`; + exports[`LSP linting > initializationOptions > should use custom config path from configPath 1`] = ` "--- FILE ----------- custom-config-path/test.ts diff --git a/apps/oxlint/test/lsp/lint/fixtures/unused-disable-directive-from-config/.oxlintrc.json b/apps/oxlint/test/lsp/lint/fixtures/unused-disable-directive-from-config/.oxlintrc.json new file mode 100644 index 0000000000000..8e97239778650 --- /dev/null +++ b/apps/oxlint/test/lsp/lint/fixtures/unused-disable-directive-from-config/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "options": { + "reportUnusedDisableDirectives": "error" + }, + "rules": { + "no-debugger": "warn" + } +} diff --git a/apps/oxlint/test/lsp/lint/fixtures/unused-disable-directive-from-config/test.ts b/apps/oxlint/test/lsp/lint/fixtures/unused-disable-directive-from-config/test.ts new file mode 100644 index 0000000000000..fce9fc21e53cd --- /dev/null +++ b/apps/oxlint/test/lsp/lint/fixtures/unused-disable-directive-from-config/test.ts @@ -0,0 +1,2 @@ +// oxlint-disable-next-line debugger +console.log("This directive is not used"); diff --git a/apps/oxlint/test/lsp/lint/lint.test.ts b/apps/oxlint/test/lsp/lint/lint.test.ts index d0e90ca0bd00d..9018594bbf45d 100644 --- a/apps/oxlint/test/lsp/lint/lint.test.ts +++ b/apps/oxlint/test/lsp/lint/lint.test.ts @@ -23,6 +23,7 @@ describe("LSP linting", () => { ["config-ts-config/test.js", "javascript"], ["config-ts-type-aware/test.ts", "typescript"], ["config-ts-nested-type-aware-invalid/nested/test.ts", "typescript"], + ["unused-disable-directive-from-config/test.ts", "typescript"], ])("should apply config from %s", async (path, languageId) => { expect(await lintFixture(FIXTURES_DIR, path, languageId)).toMatchSnapshot(); });