From 9b17d44201c66a15bd0860b30fd2c19aa8419590 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Mon, 16 Feb 2026 18:59:59 +0000 Subject: [PATCH] feat(linter): add typescript/strict-void-return rule (#19441) --- apps/oxlint/fixtures/tsgolint/.oxlintrc.json | 1 + .../fixtures/tsgolint/strict-void-return.ts | 4 ++ .../tsgolint_rule_options/.oxlintrc.json | 6 ++ .../fixtures/tsgolint_rule_options/test.ts | 8 +++ ...tsgolint_--type-aware --silent@oxlint.snap | 4 +- ...type-aware -c config-test.json@oxlint.snap | 2 +- ...olint_--type-aware test.svelte@oxlint.snap | 2 +- ...ixtures__tsgolint_--type-aware@oxlint.snap | 12 +++- ...lint_rule_options_--type-aware@oxlint.snap | 12 +++- .../src/generated/rule_runner_impls.rs | 5 ++ crates/oxc_linter/src/generated/rules_enum.rs | 29 ++++++++- crates/oxc_linter/src/rules.rs | 1 + .../rules/typescript/strict_void_return.rs | 61 +++++++++++++++++++ 13 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 apps/oxlint/fixtures/tsgolint/strict-void-return.ts create mode 100644 crates/oxc_linter/src/rules/typescript/strict_void_return.rs diff --git a/apps/oxlint/fixtures/tsgolint/.oxlintrc.json b/apps/oxlint/fixtures/tsgolint/.oxlintrc.json index c705dccd7d9b8..02b1b30c968fb 100644 --- a/apps/oxlint/fixtures/tsgolint/.oxlintrc.json +++ b/apps/oxlint/fixtures/tsgolint/.oxlintrc.json @@ -46,6 +46,7 @@ "typescript/restrict-template-expressions": "error", "typescript/return-await": "error", "typescript/strict-boolean-expressions": "error", + "typescript/strict-void-return": "error", "typescript/switch-exhaustiveness-check": "error", "typescript/unbound-method": "error", "typescript/use-unknown-in-catch-callback-variable": "error", diff --git a/apps/oxlint/fixtures/tsgolint/strict-void-return.ts b/apps/oxlint/fixtures/tsgolint/strict-void-return.ts new file mode 100644 index 0000000000000..c0d3fbd17e8b1 --- /dev/null +++ b/apps/oxlint/fixtures/tsgolint/strict-void-return.ts @@ -0,0 +1,4 @@ +declare function foo(cb: () => void): void; +foo(() => null); + +export {}; diff --git a/apps/oxlint/fixtures/tsgolint_rule_options/.oxlintrc.json b/apps/oxlint/fixtures/tsgolint_rule_options/.oxlintrc.json index 74e8d85bf0e5e..42f0750a89950 100644 --- a/apps/oxlint/fixtures/tsgolint_rule_options/.oxlintrc.json +++ b/apps/oxlint/fixtures/tsgolint_rule_options/.oxlintrc.json @@ -57,6 +57,12 @@ "fixMixedExportsWithInlineTypeSpecifier": true } ], + "typescript/strict-void-return": [ + "error", + { + "allowReturnAny": true + } + ], "typescript/only-throw-error": [ "error", { diff --git a/apps/oxlint/fixtures/tsgolint_rule_options/test.ts b/apps/oxlint/fixtures/tsgolint_rule_options/test.ts index c7e187f12f2ee..c02697a47c14e 100644 --- a/apps/oxlint/fixtures/tsgolint_rule_options/test.ts +++ b/apps/oxlint/fixtures/tsgolint_rule_options/test.ts @@ -80,6 +80,14 @@ const exportOnlyValue = 1; // This SHOULD error because ExportOnlyType is only used as a type. export { ExportOnlyType, exportOnlyValue }; +// Test strict-void-return with allowReturnAny option +declare function takesVoidCallback(cb: () => void): void; +declare const anyReturnValue: any; +// This should NOT error because allowReturnAny is true +takesVoidCallback(() => anyReturnValue); +// This SHOULD error because returning string is not allowed in a void callback +takesVoidCallback(() => 'not-void'); + // Test only-throw-error with allowRethrowing option // When allowRethrowing is false, rethrowing a caught error SHOULD error try { diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap index 0f635ab81fc53..06190367cc866 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap @@ -6,8 +6,8 @@ arguments: --type-aware --silent working directory: fixtures/tsgolint ---------- -Found 0 warnings and 58 errors. -Finished in ms on 48 files with 47 rules using 1 threads. +Found 0 warnings and 59 errors. +Finished in ms on 49 files with 48 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware -c config-test.json@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware -c config-test.json@oxlint.snap index 51206f940d71a..5d938b21ea1bf 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware -c config-test.json@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware -c config-test.json@oxlint.snap @@ -40,7 +40,7 @@ working directory: fixtures/tsgolint help: Remove the debugger statement Found 2 warnings and 2 errors. -Finished in ms on 48 files with 1 rules using 1 threads. +Finished in ms on 49 files with 1 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware test.svelte@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware test.svelte@oxlint.snap index 3224fef6af439..99861c62cba02 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware test.svelte@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware test.svelte@oxlint.snap @@ -16,7 +16,7 @@ working directory: fixtures/tsgolint help: Remove the debugger statement Found 0 warnings and 1 error. -Finished in ms on 1 file with 47 rules using 1 threads. +Finished in ms on 1 file with 48 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap index 2370cd80daf04..c9ac7428eb11f 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap @@ -430,6 +430,14 @@ working directory: fixtures/tsgolint 3 | } `---- + x typescript-eslint(strict-void-return): Value returned in a context where a void return is expected. + ,-[strict-void-return.ts:2:11] + 1 | declare function foo(cb: () => void): void; + 2 | foo(() => null); + : ^^^^ + 3 | + `---- + x typescript-eslint(switch-exhaustiveness-check): Switch is not exhaustive ,-[switch-exhaustiveness-check.ts:3:11] 2 | function handleStatus(status: Status) { @@ -455,8 +463,8 @@ working directory: fixtures/tsgolint `---- help: If your function does not access `this`, you can annotate it with `this: void`, or consider using an arrow function instead. -Found 0 warnings and 58 errors. -Finished in ms on 48 files with 47 rules using 1 threads. +Found 0 warnings and 59 errors. +Finished in ms on 49 files with 48 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_rule_options_--type-aware@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_rule_options_--type-aware@oxlint.snap index ca763cc72e6e3..46c7f1e614e14 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_rule_options_--type-aware@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_rule_options_--type-aware@oxlint.snap @@ -56,8 +56,16 @@ working directory: fixtures/tsgolint_rule_options 82 | `---- -Found 0 warnings and 6 errors. -Finished in ms on 1 file with 9 rules using 1 threads. + x typescript-eslint(strict-void-return): Value returned in a context where a void return is expected. + ,-[test.ts:89:25] + 88 | // This SHOULD error because returning string is not allowed in a void callback + 89 | takesVoidCallback(() => 'not-void'); + : ^^^^^^^^^^ + 90 | + `---- + +Found 0 warnings and 7 errors. +Finished in ms on 1 file with 10 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/crates/oxc_linter/src/generated/rule_runner_impls.rs b/crates/oxc_linter/src/generated/rule_runner_impls.rs index 35a6255d0b17c..2b74eb72f9a94 100644 --- a/crates/oxc_linter/src/generated/rule_runner_impls.rs +++ b/crates/oxc_linter/src/generated/rule_runner_impls.rs @@ -1950,6 +1950,11 @@ impl RuleRunner for crate::rules::typescript::strict_boolean_expressions::Strict const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown; } +impl RuleRunner for crate::rules::typescript::strict_void_return::StrictVoidReturn { + const NODE_TYPES: Option<&AstTypesBitset> = None; + const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown; +} + impl RuleRunner for crate::rules::typescript::switch_exhaustiveness_check::SwitchExhaustivenessCheck { diff --git a/crates/oxc_linter/src/generated/rules_enum.rs b/crates/oxc_linter/src/generated/rules_enum.rs index 82997307e7a1f..80e5ec6106bef 100644 --- a/crates/oxc_linter/src/generated/rules_enum.rs +++ b/crates/oxc_linter/src/generated/rules_enum.rs @@ -527,6 +527,7 @@ pub use crate::rules::typescript::restrict_plus_operands::RestrictPlusOperands a pub use crate::rules::typescript::restrict_template_expressions::RestrictTemplateExpressions as TypescriptRestrictTemplateExpressions; pub use crate::rules::typescript::return_await::ReturnAwait as TypescriptReturnAwait; pub use crate::rules::typescript::strict_boolean_expressions::StrictBooleanExpressions as TypescriptStrictBooleanExpressions; +pub use crate::rules::typescript::strict_void_return::StrictVoidReturn as TypescriptStrictVoidReturn; pub use crate::rules::typescript::switch_exhaustiveness_check::SwitchExhaustivenessCheck as TypescriptSwitchExhaustivenessCheck; pub use crate::rules::typescript::triple_slash_reference::TripleSlashReference as TypescriptTripleSlashReference; pub use crate::rules::typescript::unbound_method::UnboundMethod as TypescriptUnboundMethod; @@ -990,6 +991,7 @@ pub enum RuleEnum { TypescriptRestrictTemplateExpressions(TypescriptRestrictTemplateExpressions), TypescriptReturnAwait(TypescriptReturnAwait), TypescriptStrictBooleanExpressions(TypescriptStrictBooleanExpressions), + TypescriptStrictVoidReturn(TypescriptStrictVoidReturn), TypescriptSwitchExhaustivenessCheck(TypescriptSwitchExhaustivenessCheck), TypescriptTripleSlashReference(TypescriptTripleSlashReference), TypescriptUnboundMethod(TypescriptUnboundMethod), @@ -1708,8 +1710,8 @@ const TYPESCRIPT_RESTRICT_TEMPLATE_EXPRESSIONS_ID: usize = TYPESCRIPT_RESTRICT_PLUS_OPERANDS_ID + 1usize; const TYPESCRIPT_RETURN_AWAIT_ID: usize = TYPESCRIPT_RESTRICT_TEMPLATE_EXPRESSIONS_ID + 1usize; const TYPESCRIPT_STRICT_BOOLEAN_EXPRESSIONS_ID: usize = TYPESCRIPT_RETURN_AWAIT_ID + 1usize; -const TYPESCRIPT_SWITCH_EXHAUSTIVENESS_CHECK_ID: usize = - TYPESCRIPT_STRICT_BOOLEAN_EXPRESSIONS_ID + 1usize; +const TYPESCRIPT_STRICT_VOID_RETURN_ID: usize = TYPESCRIPT_STRICT_BOOLEAN_EXPRESSIONS_ID + 1usize; +const TYPESCRIPT_SWITCH_EXHAUSTIVENESS_CHECK_ID: usize = TYPESCRIPT_STRICT_VOID_RETURN_ID + 1usize; const TYPESCRIPT_TRIPLE_SLASH_REFERENCE_ID: usize = TYPESCRIPT_SWITCH_EXHAUSTIVENESS_CHECK_ID + 1usize; const TYPESCRIPT_UNBOUND_METHOD_ID: usize = TYPESCRIPT_TRIPLE_SLASH_REFERENCE_ID + 1usize; @@ -2486,6 +2488,7 @@ impl RuleEnum { } Self::TypescriptReturnAwait(_) => TYPESCRIPT_RETURN_AWAIT_ID, Self::TypescriptStrictBooleanExpressions(_) => TYPESCRIPT_STRICT_BOOLEAN_EXPRESSIONS_ID, + Self::TypescriptStrictVoidReturn(_) => TYPESCRIPT_STRICT_VOID_RETURN_ID, Self::TypescriptSwitchExhaustivenessCheck(_) => { TYPESCRIPT_SWITCH_EXHAUSTIVENESS_CHECK_ID } @@ -3262,6 +3265,7 @@ impl RuleEnum { } Self::TypescriptReturnAwait(_) => TypescriptReturnAwait::NAME, Self::TypescriptStrictBooleanExpressions(_) => TypescriptStrictBooleanExpressions::NAME, + Self::TypescriptStrictVoidReturn(_) => TypescriptStrictVoidReturn::NAME, Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::NAME } @@ -4052,6 +4056,7 @@ impl RuleEnum { Self::TypescriptStrictBooleanExpressions(_) => { TypescriptStrictBooleanExpressions::CATEGORY } + Self::TypescriptStrictVoidReturn(_) => TypescriptStrictVoidReturn::CATEGORY, Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::CATEGORY } @@ -4845,6 +4850,7 @@ impl RuleEnum { } Self::TypescriptReturnAwait(_) => TypescriptReturnAwait::FIX, Self::TypescriptStrictBooleanExpressions(_) => TypescriptStrictBooleanExpressions::FIX, + Self::TypescriptStrictVoidReturn(_) => TypescriptStrictVoidReturn::FIX, Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::FIX } @@ -5696,6 +5702,7 @@ impl RuleEnum { Self::TypescriptStrictBooleanExpressions(_) => { TypescriptStrictBooleanExpressions::documentation() } + Self::TypescriptStrictVoidReturn(_) => TypescriptStrictVoidReturn::documentation(), Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::documentation() } @@ -7063,6 +7070,10 @@ impl RuleEnum { TypescriptStrictBooleanExpressions::config_schema(generator) .or_else(|| TypescriptStrictBooleanExpressions::schema(generator)) } + Self::TypescriptStrictVoidReturn(_) => { + TypescriptStrictVoidReturn::config_schema(generator) + .or_else(|| TypescriptStrictVoidReturn::schema(generator)) + } Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::config_schema(generator) .or_else(|| TypescriptSwitchExhaustivenessCheck::schema(generator)) @@ -8449,6 +8460,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(_) => "typescript", Self::TypescriptReturnAwait(_) => "typescript", Self::TypescriptStrictBooleanExpressions(_) => "typescript", + Self::TypescriptStrictVoidReturn(_) => "typescript", Self::TypescriptSwitchExhaustivenessCheck(_) => "typescript", Self::TypescriptTripleSlashReference(_) => "typescript", Self::TypescriptUnboundMethod(_) => "typescript", @@ -9788,6 +9800,9 @@ impl RuleEnum { TypescriptStrictBooleanExpressions::from_configuration(value)?, )) } + Self::TypescriptStrictVoidReturn(_) => Ok(Self::TypescriptStrictVoidReturn( + TypescriptStrictVoidReturn::from_configuration(value)?, + )), Self::TypescriptSwitchExhaustivenessCheck(_) => { Ok(Self::TypescriptSwitchExhaustivenessCheck( TypescriptSwitchExhaustivenessCheck::from_configuration(value)?, @@ -11306,6 +11321,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(rule) => rule.to_configuration(), Self::TypescriptReturnAwait(rule) => rule.to_configuration(), Self::TypescriptStrictBooleanExpressions(rule) => rule.to_configuration(), + Self::TypescriptStrictVoidReturn(rule) => rule.to_configuration(), Self::TypescriptSwitchExhaustivenessCheck(rule) => rule.to_configuration(), Self::TypescriptTripleSlashReference(rule) => rule.to_configuration(), Self::TypescriptUnboundMethod(rule) => rule.to_configuration(), @@ -11990,6 +12006,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(rule) => rule.run(node, ctx), Self::TypescriptReturnAwait(rule) => rule.run(node, ctx), Self::TypescriptStrictBooleanExpressions(rule) => rule.run(node, ctx), + Self::TypescriptStrictVoidReturn(rule) => rule.run(node, ctx), Self::TypescriptSwitchExhaustivenessCheck(rule) => rule.run(node, ctx), Self::TypescriptTripleSlashReference(rule) => rule.run(node, ctx), Self::TypescriptUnboundMethod(rule) => rule.run(node, ctx), @@ -12672,6 +12689,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(rule) => rule.run_once(ctx), Self::TypescriptReturnAwait(rule) => rule.run_once(ctx), Self::TypescriptStrictBooleanExpressions(rule) => rule.run_once(ctx), + Self::TypescriptStrictVoidReturn(rule) => rule.run_once(ctx), Self::TypescriptSwitchExhaustivenessCheck(rule) => rule.run_once(ctx), Self::TypescriptTripleSlashReference(rule) => rule.run_once(ctx), Self::TypescriptUnboundMethod(rule) => rule.run_once(ctx), @@ -13408,6 +13426,7 @@ impl RuleEnum { } Self::TypescriptReturnAwait(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptStrictBooleanExpressions(rule) => rule.run_on_jest_node(jest_node, ctx), + Self::TypescriptStrictVoidReturn(rule) => rule.run_on_jest_node(jest_node, ctx), Self::TypescriptSwitchExhaustivenessCheck(rule) => { rule.run_on_jest_node(jest_node, ctx) } @@ -14126,6 +14145,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(rule) => rule.should_run(ctx), Self::TypescriptReturnAwait(rule) => rule.should_run(ctx), Self::TypescriptStrictBooleanExpressions(rule) => rule.should_run(ctx), + Self::TypescriptStrictVoidReturn(rule) => rule.should_run(ctx), Self::TypescriptSwitchExhaustivenessCheck(rule) => rule.should_run(ctx), Self::TypescriptTripleSlashReference(rule) => rule.should_run(ctx), Self::TypescriptUnboundMethod(rule) => rule.should_run(ctx), @@ -14940,6 +14960,7 @@ impl RuleEnum { Self::TypescriptStrictBooleanExpressions(_) => { TypescriptStrictBooleanExpressions::IS_TSGOLINT_RULE } + Self::TypescriptStrictVoidReturn(_) => TypescriptStrictVoidReturn::IS_TSGOLINT_RULE, Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::IS_TSGOLINT_RULE } @@ -15853,6 +15874,7 @@ impl RuleEnum { Self::TypescriptStrictBooleanExpressions(_) => { TypescriptStrictBooleanExpressions::HAS_CONFIG } + Self::TypescriptStrictVoidReturn(_) => TypescriptStrictVoidReturn::HAS_CONFIG, Self::TypescriptSwitchExhaustivenessCheck(_) => { TypescriptSwitchExhaustivenessCheck::HAS_CONFIG } @@ -16607,6 +16629,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(rule) => rule.types_info(), Self::TypescriptReturnAwait(rule) => rule.types_info(), Self::TypescriptStrictBooleanExpressions(rule) => rule.types_info(), + Self::TypescriptStrictVoidReturn(rule) => rule.types_info(), Self::TypescriptSwitchExhaustivenessCheck(rule) => rule.types_info(), Self::TypescriptTripleSlashReference(rule) => rule.types_info(), Self::TypescriptUnboundMethod(rule) => rule.types_info(), @@ -17289,6 +17312,7 @@ impl RuleEnum { Self::TypescriptRestrictTemplateExpressions(rule) => rule.run_info(), Self::TypescriptReturnAwait(rule) => rule.run_info(), Self::TypescriptStrictBooleanExpressions(rule) => rule.run_info(), + Self::TypescriptStrictVoidReturn(rule) => rule.run_info(), Self::TypescriptSwitchExhaustivenessCheck(rule) => rule.run_info(), Self::TypescriptTripleSlashReference(rule) => rule.run_info(), Self::TypescriptUnboundMethod(rule) => rule.run_info(), @@ -18043,6 +18067,7 @@ pub static RULES: std::sync::LazyLock> = std::sync::LazyLock::new( ), RuleEnum::TypescriptReturnAwait(TypescriptReturnAwait::default()), RuleEnum::TypescriptStrictBooleanExpressions(TypescriptStrictBooleanExpressions::default()), + RuleEnum::TypescriptStrictVoidReturn(TypescriptStrictVoidReturn::default()), RuleEnum::TypescriptSwitchExhaustivenessCheck( TypescriptSwitchExhaustivenessCheck::default(), ), diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 4d504876794ff..9bd75eb1d07b7 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -304,6 +304,7 @@ pub(crate) mod typescript { pub mod restrict_template_expressions; pub mod return_await; pub mod strict_boolean_expressions; + pub mod strict_void_return; pub mod switch_exhaustiveness_check; pub mod triple_slash_reference; pub mod unbound_method; diff --git a/crates/oxc_linter/src/rules/typescript/strict_void_return.rs b/crates/oxc_linter/src/rules/typescript/strict_void_return.rs new file mode 100644 index 0000000000000..d6d4b84452952 --- /dev/null +++ b/crates/oxc_linter/src/rules/typescript/strict_void_return.rs @@ -0,0 +1,61 @@ +use oxc_macros::declare_oxc_lint; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +use crate::rule::{DefaultRuleConfig, Rule}; + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct StrictVoidReturn(Box); + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)] +#[serde(rename_all = "camelCase", default, deny_unknown_fields)] +pub struct StrictVoidReturnConfig { + /// Allow callbacks that return `any` in places that expect a `void` callback. + pub allow_return_any: bool, +} + +declare_oxc_lint!( + /// ### What it does + /// + /// Disallow returning non-void values where a `void` return is expected. + /// + /// ### Why is this bad? + /// + /// Returning values from `void` contexts can hide logic errors and make callback APIs + /// behave unexpectedly. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```ts + /// declare function run(cb: () => void): void; + /// + /// run(() => 'value'); + /// run(async () => 123); + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```ts + /// declare function run(cb: () => void): void; + /// + /// run(() => { + /// doWork(); + /// }); + /// + /// run(() => undefined); + /// ``` + StrictVoidReturn(tsgolint), + typescript, + nursery, + config = StrictVoidReturnConfig, +); + +impl Rule for StrictVoidReturn { + fn from_configuration(value: serde_json::Value) -> Result { + serde_json::from_value::>(value).map(DefaultRuleConfig::into_inner) + } + + fn to_configuration(&self) -> Option> { + Some(serde_json::to_value(&*self.0)) + } +}