Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/oxlint/fixtures/tsgolint/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions apps/oxlint/fixtures/tsgolint/strict-void-return.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
declare function foo(cb: () => void): void;
foo(() => null);

export {};
6 changes: 6 additions & 0 deletions apps/oxlint/fixtures/tsgolint_rule_options/.oxlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@
"fixMixedExportsWithInlineTypeSpecifier": true
}
],
"typescript/strict-void-return": [
"error",
{
"allowReturnAny": true
}
],
"typescript/only-throw-error": [
"error",
{
Expand Down
8 changes: 8 additions & 0 deletions apps/oxlint/fixtures/tsgolint_rule_options/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ arguments: --type-aware --silent
working directory: fixtures/tsgolint
----------

Found 0 warnings and 58 errors.
Finished in <variable>ms on 48 files with 47 rules using 1 threads.
Found 0 warnings and 59 errors.
Finished in <variable>ms on 49 files with 48 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ working directory: fixtures/tsgolint
help: Remove the debugger statement

Found 2 warnings and 2 errors.
Finished in <variable>ms on 48 files with 1 rules using 1 threads.
Finished in <variable>ms on 49 files with 1 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ working directory: fixtures/tsgolint
help: Remove the debugger statement

Found 0 warnings and 1 error.
Finished in <variable>ms on 1 file with 47 rules using 1 threads.
Finished in <variable>ms on 1 file with 48 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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 <variable>ms on 48 files with 47 rules using 1 threads.
Found 0 warnings and 59 errors.
Finished in <variable>ms on 49 files with 48 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,16 @@ working directory: fixtures/tsgolint_rule_options
82 |
`----

Found 0 warnings and 6 errors.
Finished in <variable>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 <variable>ms on 1 file with 10 rules using 1 threads.
----------
CLI result: LintFoundErrors
----------
5 changes: 5 additions & 0 deletions crates/oxc_linter/src/generated/rule_runner_impls.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 27 additions & 2 deletions crates/oxc_linter/src/generated/rules_enum.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
61 changes: 61 additions & 0 deletions crates/oxc_linter/src/rules/typescript/strict_void_return.rs
Original file line number Diff line number Diff line change
@@ -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<StrictVoidReturnConfig>);

#[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<Self, serde_json::error::Error> {
serde_json::from_value::<DefaultRuleConfig<Self>>(value).map(DefaultRuleConfig::into_inner)
}

fn to_configuration(&self) -> Option<Result<serde_json::Value, serde_json::Error>> {
Some(serde_json::to_value(&*self.0))
}
}
Loading