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
31 changes: 31 additions & 0 deletions .changeset/yellow-crews-guess.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"@biomejs/biome": patch
---

Added the nursery rule [`noContinue`](https://biomejs.dev/linter/rules/no-continue/). Disallowing the usage of the `continue` statement, structured control flow statements such as `if` should be used instead.

**Invalid:**
```js
let sum = 0,
i;

for(i = 0; i < 10; i++) {
if(i >= 5) {
continue;
}

sum += i;
}
```

**Valid:**
```js
let sum = 0,
i;

for(i = 0; i < 10; i++) {
if(i < 5) {
sum += i;
}
}
```
12 changes: 12 additions & 0 deletions crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs

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

151 changes: 86 additions & 65 deletions crates/biome_configuration/src/analyzer/linter/rules.rs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/biome_diagnostics_categories/src/categories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ define_categories! {
"lint/correctness/useValidTypeof": "https://biomejs.dev/linter/rules/use-valid-typeof",
"lint/correctness/useYield": "https://biomejs.dev/linter/rules/use-yield",
"lint/nursery/noColorInvalidHex": "https://biomejs.dev/linter/rules/no-color-invalid-hex",
"lint/nursery/noContinue": "https://biomejs.dev/linter/rules/no-continue",
"lint/nursery/noDeprecatedImports": "https://biomejs.dev/linter/rules/no-deprecated-imports",
"lint/nursery/noDuplicateDependencies": "https://biomejs.dev/linter/rules/no-duplicate-dependencies",
"lint/nursery/noEmptySource": "https://biomejs.dev/linter/rules/no-empty-source",
Expand Down
3 changes: 2 additions & 1 deletion crates/biome_js_analyze/src/lint/nursery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
//! Generated file, do not edit by hand, see `xtask/codegen`

use biome_analyze::declare_lint_group;
pub mod no_continue;
pub mod no_deprecated_imports;
pub mod no_empty_source;
pub mod no_floating_promises;
Expand Down Expand Up @@ -33,4 +34,4 @@ pub mod use_qwik_valid_lexical_scope;
pub mod use_sorted_classes;
pub mod use_vue_define_macros_order;
pub mod use_vue_multi_word_component_names;
declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_deprecated_imports :: NoDeprecatedImports , self :: no_empty_source :: NoEmptySource , self :: no_floating_promises :: NoFloatingPromises , self :: no_import_cycles :: NoImportCycles , self :: no_increment_decrement :: NoIncrementDecrement , self :: no_jsx_literals :: NoJsxLiterals , self :: no_misused_promises :: NoMisusedPromises , self :: no_next_async_client_component :: NoNextAsyncClientComponent , self :: no_parameters_only_used_in_recursion :: NoParametersOnlyUsedInRecursion , self :: no_react_forward_ref :: NoReactForwardRef , self :: no_shadow :: NoShadow , self :: no_unknown_attribute :: NoUnknownAttribute , self :: no_unnecessary_conditions :: NoUnnecessaryConditions , self :: no_unresolved_imports :: NoUnresolvedImports , self :: no_unused_expressions :: NoUnusedExpressions , self :: no_useless_catch_binding :: NoUselessCatchBinding , self :: no_useless_undefined :: NoUselessUndefined , self :: no_vue_data_object_declaration :: NoVueDataObjectDeclaration , self :: no_vue_duplicate_keys :: NoVueDuplicateKeys , self :: no_vue_reserved_keys :: NoVueReservedKeys , self :: no_vue_reserved_props :: NoVueReservedProps , self :: use_consistent_arrow_return :: UseConsistentArrowReturn , self :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCases , self :: use_explicit_type :: UseExplicitType , self :: use_max_params :: UseMaxParams , self :: use_qwik_method_usage :: UseQwikMethodUsage , self :: use_qwik_valid_lexical_scope :: UseQwikValidLexicalScope , self :: use_sorted_classes :: UseSortedClasses , self :: use_vue_define_macros_order :: UseVueDefineMacrosOrder , self :: use_vue_multi_word_component_names :: UseVueMultiWordComponentNames ,] } }
declare_lint_group! { pub Nursery { name : "nursery" , rules : [self :: no_continue :: NoContinue , self :: no_deprecated_imports :: NoDeprecatedImports , self :: no_empty_source :: NoEmptySource , self :: no_floating_promises :: NoFloatingPromises , self :: no_import_cycles :: NoImportCycles , self :: no_increment_decrement :: NoIncrementDecrement , self :: no_jsx_literals :: NoJsxLiterals , self :: no_misused_promises :: NoMisusedPromises , self :: no_next_async_client_component :: NoNextAsyncClientComponent , self :: no_parameters_only_used_in_recursion :: NoParametersOnlyUsedInRecursion , self :: no_react_forward_ref :: NoReactForwardRef , self :: no_shadow :: NoShadow , self :: no_unknown_attribute :: NoUnknownAttribute , self :: no_unnecessary_conditions :: NoUnnecessaryConditions , self :: no_unresolved_imports :: NoUnresolvedImports , self :: no_unused_expressions :: NoUnusedExpressions , self :: no_useless_catch_binding :: NoUselessCatchBinding , self :: no_useless_undefined :: NoUselessUndefined , self :: no_vue_data_object_declaration :: NoVueDataObjectDeclaration , self :: no_vue_duplicate_keys :: NoVueDuplicateKeys , self :: no_vue_reserved_keys :: NoVueReservedKeys , self :: no_vue_reserved_props :: NoVueReservedProps , self :: use_consistent_arrow_return :: UseConsistentArrowReturn , self :: use_exhaustive_switch_cases :: UseExhaustiveSwitchCases , self :: use_explicit_type :: UseExplicitType , self :: use_max_params :: UseMaxParams , self :: use_qwik_method_usage :: UseQwikMethodUsage , self :: use_qwik_valid_lexical_scope :: UseQwikValidLexicalScope , self :: use_sorted_classes :: UseSortedClasses , self :: use_vue_define_macros_order :: UseVueDefineMacrosOrder , self :: use_vue_multi_word_component_names :: UseVueMultiWordComponentNames ,] } }
80 changes: 80 additions & 0 deletions crates/biome_js_analyze/src/lint/nursery/no_continue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use biome_analyze::{
Ast, Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule,
};
use biome_console::markup;
use biome_js_syntax::JsContinueStatement;
use biome_rowan::AstNode;
use biome_rule_options::no_continue::NoContinueOptions;

declare_lint_rule! {
/// Disallow continue statements.
///
/// The continue statement terminates execution of the statements in the current iteration of the current or labeled loop, and continues execution of the loop with the next iteration.
/// When used incorrectly it makes code less testable, less readable and less maintainable.
/// Structured control flow statements such as if should be used instead.
///
/// ## Examples
///
/// ### Invalid
///
/// ```js,expect_diagnostic
/// let sum = 0,
/// i;
///
/// for(i = 0; i < 10; i++) {
/// if(i >= 5) {
/// continue;
/// }
///
/// sum += i;
/// }
/// ```
///
/// ### Valid
///
/// ```js
/// let sum = 0,
/// i;
///
/// for(i = 0; i < 10; i++) {
/// if(i < 5) {
/// sum += i;
/// }
/// }
/// ```
///
pub NoContinue {
version: "next",
name: "noContinue",
language: "js",
recommended: false,
sources: &[RuleSource::Eslint("no-continue").same()],
}
}

impl Rule for NoContinue {
type Query = Ast<JsContinueStatement>;
type State = ();
type Signals = Option<Self::State>;
type Options = NoContinueOptions;

fn run(_ctx: &RuleContext<Self>) -> Self::Signals {
Some(())
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we check if this is inside a loop? That's what the docs say

Copy link
Member Author

@Netail Netail Oct 25, 2025

Choose a reason for hiding this comment

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

Mhm, the source code of Eslint doesn't either 🤔.
Could also be confusing to use in switch statements

Copy link
Member

Choose a reason for hiding this comment

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

Well, the docs and the rule must match. Docs are usually in higher order, and the code follows

Copy link
Member Author

Choose a reason for hiding this comment

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

Will add a mention of switch statements in the docs :)

Copy link
Member Author

@Netail Netail Oct 30, 2025

Choose a reason for hiding this comment

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

Actually, what am I saying lol. Switch statements don't use continue 🤦, confused with break for a sec

Copy link
Member Author

Choose a reason for hiding this comment

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

The continue statement is only valid syntax in a loop to begin with

}

fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> {
let node = ctx.query();
Some(
RuleDiagnostic::new(
rule_category!(),
node.range(),
markup! {
"Unexpected use of continue statement."
},
)
.note(markup! {
"The continue statement terminates execution of the statements in the current iteration, when used incorrectly it makes code less testable, less readable and less maintainable. Structured control flow statements such as if should be used instead."
}),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var sum = 0, i; for (i = 0; i < 10; i++) { if (i <= 5) { continue; } sum += i; }
var sum = 0, i; myLabel: for (i = 0; i < 10; i++) { if (i <= 5) { continue myLabel; } sum += i; }
var sum = 0, i = 0; while (i < 10) { if (i <= 5) { i++; continue; } sum += i; i++; }
var sum = 0, i = 0; myLabel: while (i < 10) { if (i <= 5) { i++; continue myLabel; } sum += i; i++; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: invalid.js
---
# Input
```js
var sum = 0, i; for (i = 0; i < 10; i++) { if (i <= 5) { continue; } sum += i; }
var sum = 0, i; myLabel: for (i = 0; i < 10; i++) { if (i <= 5) { continue myLabel; } sum += i; }
var sum = 0, i = 0; while (i < 10) { if (i <= 5) { i++; continue; } sum += i; i++; }
var sum = 0, i = 0; myLabel: while (i < 10) { if (i <= 5) { i++; continue myLabel; } sum += i; i++; }

```

# Diagnostics
```
invalid.js:1:58 lint/nursery/noContinue ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Unexpected use of continue statement.

> 1 │ var sum = 0, i; for (i = 0; i < 10; i++) { if (i <= 5) { continue; } sum += i; }
│ ^^^^^^^^^
2 │ var sum = 0, i; myLabel: for (i = 0; i < 10; i++) { if (i <= 5) { continue myLabel; } sum += i; }
3 │ var sum = 0, i = 0; while (i < 10) { if (i <= 5) { i++; continue; } sum += i; i++; }

i The continue statement terminates execution of the statements in the current iteration, when used incorrectly it makes code less testable, less readable and less maintainable. Structured control flow statements such as if should be used instead.


```

```
invalid.js:2:67 lint/nursery/noContinue ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Unexpected use of continue statement.

1 │ var sum = 0, i; for (i = 0; i < 10; i++) { if (i <= 5) { continue; } sum += i; }
> 2 │ var sum = 0, i; myLabel: for (i = 0; i < 10; i++) { if (i <= 5) { continue myLabel; } sum += i; }
│ ^^^^^^^^^^^^^^^^^
3 │ var sum = 0, i = 0; while (i < 10) { if (i <= 5) { i++; continue; } sum += i; i++; }
4 │ var sum = 0, i = 0; myLabel: while (i < 10) { if (i <= 5) { i++; continue myLabel; } sum += i; i++; }

i The continue statement terminates execution of the statements in the current iteration, when used incorrectly it makes code less testable, less readable and less maintainable. Structured control flow statements such as if should be used instead.


```

```
invalid.js:3:57 lint/nursery/noContinue ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Unexpected use of continue statement.

1 │ var sum = 0, i; for (i = 0; i < 10; i++) { if (i <= 5) { continue; } sum += i; }
2 │ var sum = 0, i; myLabel: for (i = 0; i < 10; i++) { if (i <= 5) { continue myLabel; } sum += i; }
> 3 │ var sum = 0, i = 0; while (i < 10) { if (i <= 5) { i++; continue; } sum += i; i++; }
│ ^^^^^^^^^
4 │ var sum = 0, i = 0; myLabel: while (i < 10) { if (i <= 5) { i++; continue myLabel; } sum += i; i++; }
5 │

i The continue statement terminates execution of the statements in the current iteration, when used incorrectly it makes code less testable, less readable and less maintainable. Structured control flow statements such as if should be used instead.


```

```
invalid.js:4:66 lint/nursery/noContinue ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

i Unexpected use of continue statement.

2 │ var sum = 0, i; myLabel: for (i = 0; i < 10; i++) { if (i <= 5) { continue myLabel; } sum += i; }
3 │ var sum = 0, i = 0; while (i < 10) { if (i <= 5) { i++; continue; } sum += i; i++; }
> 4 │ var sum = 0, i = 0; myLabel: while (i < 10) { if (i <= 5) { i++; continue myLabel; } sum += i; i++; }
│ ^^^^^^^^^^^^^^^^^
5 │

i The continue statement terminates execution of the statements in the current iteration, when used incorrectly it makes code less testable, less readable and less maintainable. Structured control flow statements such as if should be used instead.


```
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* should not generate diagnostics */
var sum = 0, i; for (i = 0; i < 10; i++) { if (i > 5) { sum += i; } }
var sum = 0, i = 0; while (i < 10) { if (i > 5) { sum += i; } i++; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/biome_js_analyze/tests/spec_tests.rs
expression: valid.js
---
# Input
```js
/* should not generate diagnostics */
var sum = 0, i; for (i = 0; i < 10; i++) { if (i > 5) { sum += i; } }
var sum = 0, i = 0; while (i < 10) { if (i > 5) { sum += i; } i++; }

```
1 change: 1 addition & 0 deletions crates/biome_rule_options/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub mod no_constant_binary_expressions;
pub mod no_constant_condition;
pub mod no_constant_math_min_max_clamp;
pub mod no_constructor_return;
pub mod no_continue;
pub mod no_control_characters_in_regex;
pub mod no_dangerously_set_inner_html;
pub mod no_dangerously_set_inner_html_with_children;
Expand Down
6 changes: 6 additions & 0 deletions crates/biome_rule_options/src/no_continue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use biome_deserialize_macros::Deserializable;
use serde::{Deserialize, Serialize};
#[derive(Default, Clone, Debug, Deserialize, Deserializable, Eq, PartialEq, Serialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields, default)]
pub struct NoContinueOptions {}
19 changes: 19 additions & 0 deletions packages/@biomejs/backend-jsonrpc/src/workspace.ts

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

29 changes: 29 additions & 0 deletions packages/@biomejs/biome/configuration_schema.json

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