Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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
5 changes: 5 additions & 0 deletions .changeset/warm-clouds-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Added `allowImplicit` option to [`useIterableCallbackReturn`](https://biomejs.dev/linter/rules/use-iterable-callback-return/). When set to `true`, callbacks for methods like `map` and `filter` can use `return;` to implicitly return `undefined`. This matches ESLint's `allowImplicit` option for `array-callback-return`.
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ declare_lint_rule! {
///
/// ### `checkForEach`
///
/// **Since `v2.4.0**
/// **Since `v2.4.0`**
///
/// Default: `true`
///
Expand All @@ -107,7 +107,37 @@ declare_lint_rule! {
/// });
/// ```
///
/// When `checkForEach` is `false` (default), the above code will not trigger any diagnostic.
/// When `checkForEach` is `false`, the above code will not trigger any diagnostic.
///
/// ### `allowImplicit`
///
/// Default: `false`
///
/// When set to `true`, allows callbacks of methods that expect a return value
/// (such as `map` or `filter`) to implicitly return `undefined` using `return;`.
/// This is useful for patterns like mapping with early returns where some paths
/// intentionally return `undefined`.
///
/// This matches ESLint's [`allowImplicit`](https://eslint.org/docs/latest/rules/array-callback-return#allowimplicit) option.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This information isn't needed

This comment was marked as low quality.

///
/// ### Examples
///
/// ```json,options
/// {
/// "options": {
/// "allowImplicit": true
/// }
/// }
/// ```
///
/// ```js,use_options
/// [1, 2, 3].map((x) => {
/// if (x > 2) return x;
/// return;
/// });

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a snippet that is more real?

For example we should a .filter(Boolean)

This comment was marked as low quality.

/// ```
///
/// When `allowImplicit` is `true`, the above code will not trigger any diagnostic.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// When `allowImplicit` is `true`, the above code will not trigger any diagnostic.

That's implied by the infrastructure

This comment was marked as low quality.

///
pub UseIterableCallbackReturn {
version: "2.0.0",
Expand Down Expand Up @@ -180,22 +210,34 @@ impl Rule for UseIterableCallbackReturn {

let returns_info = get_function_returns_info(cfg);

let allow_implicit = ctx.options().allow_implicit();
let mut problems: Vec<RuleProblemKind> = Vec::new();
let member_range = member_expression.member().ok()?.range();
if method_config.return_value_required {
if returns_info.has_paths_without_returns {
if returns_info.returns_with_value.is_empty() {
if allow_implicit {
// When allowImplicit is true, `return;` is accepted.
// Only report if there are paths that fall through without any return at all.
if returns_info.has_paths_without_returns
&& returns_info.returns_with_value.is_empty()
&& returns_info.returns_without_value.is_empty()
{
problems.push(RuleProblemKind::MissingReturnWithValue);
} else {
problems.push(RuleProblemKind::NotAllPathsReturnValue);
}
} else if !returns_info.returns_without_value.is_empty() {
if !returns_info.returns_with_value.is_empty() {
for return_range in returns_info.returns_without_value {
problems.push(RuleProblemKind::UnexpectedEmptyReturn(return_range));
} else {
if returns_info.has_paths_without_returns {
if returns_info.returns_with_value.is_empty() {
problems.push(RuleProblemKind::MissingReturnWithValue);
} else {
problems.push(RuleProblemKind::NotAllPathsReturnValue);
}
} else if !returns_info.returns_without_value.is_empty() {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if !returns_info.returns_with_value.is_empty() {
for return_range in returns_info.returns_without_value {
problems.push(RuleProblemKind::UnexpectedEmptyReturn(return_range));
}
} else {
problems.push(RuleProblemKind::MissingReturnWithValue);
}
} else {
problems.push(RuleProblemKind::MissingReturnWithValue);
}
}
} else {
Expand Down
13 changes: 13 additions & 0 deletions crates/biome_rule_options/src/use_iterable_callback_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,27 @@ pub struct UseIterableCallbackReturnOptions {
/// When `false` or unset, such callbacks are ignored.
#[serde(skip_serializing_if = "Option::is_none")]
pub check_for_each: Option<bool>,

/// When `true`, allows callbacks in methods that require a return value
/// (e.g. `map`, `filter`) to implicitly return `undefined` via `return;`.
/// This matches ESLint's `allowImplicit` option for `array-callback-return`.
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_implicit: Option<bool>,
}

impl UseIterableCallbackReturnOptions {
pub const DEFAULT_CHECK_FOR_EACH: bool = true;
pub const DEFAULT_ALLOW_IMPLICIT: bool = false;

/// Returns [`Self::check_for_each`] if it is set.
/// Otherwise, returns [`Self::DEFAULT_CHECK_FOR_EACH`].
pub fn check_for_each(&self) -> bool {
self.check_for_each.unwrap_or(Self::DEFAULT_CHECK_FOR_EACH)
}

/// Returns [`Self::allow_implicit`] if it is set.
/// Otherwise, returns [`Self::DEFAULT_ALLOW_IMPLICIT`].
pub fn allow_implicit(&self) -> bool {
self.allow_implicit.unwrap_or(Self::DEFAULT_ALLOW_IMPLICIT)
}
}