From bb848719a94cfae15d31bcf9b9626b0f59784631 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Fri, 22 Sep 2023 23:59:23 +0200 Subject: [PATCH] fix(lint/noMultipleSpacesInRegularExpressionLiterals): correctly handle spaces followed by a quantifier (#384) --- CHANGELOG.md | 2 + ...e_spaces_in_regular_expression_literals.rs | 181 +++++--- .../invalid.jsonc | 21 +- .../invalid.jsonc.snap | 433 +++++++++++++++++- .../src/configuration/linter/rules.rs | 2 +- editors/vscode/configuration_schema.json | 2 +- .../@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- .../@biomejs/biome/configuration_schema.json | 2 +- .../src/content/docs/internals/changelog.mdx | 2 + .../src/content/docs/linter/rules/index.mdx | 2 +- ...e-spaces-in-regular-expression-literals.md | 92 ++-- 11 files changed, 598 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82cc6a2c49f3..6d8c3ac484dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Fix [#294](https://github.com/biomejs/biome/issues/294). [noConfusingVoidType](https://biomejs.dev/linter/rules/no-confusing-void-type/) no longer reports false positives for return types. Contributed by @b4s36t4 +- Fix [#383](https://github.com/biomejs/biome/issues/383). [noMultipleSpacesInRegularExpressionLiterals](https://biomejs.dev/linter/rules/no-multiple-spaces-in-regular-expression-literals) now provides correct code fixes when consecutive spaces are followed by a quantifier. Contributed by @Conaclos + ### Parser - Enhance diagnostic for infer type handling in the parser. The 'infer' keyword can only be utilized within the 'extends' clause of a conditional type. Using it outside of this context will result in an error. Ensure that any type declarations using 'infer' are correctly placed within the conditional type structure to avoid parsing issues. Contributed by @denbezrukov diff --git a/crates/biome_js_analyze/src/analyzers/complexity/no_multiple_spaces_in_regular_expression_literals.rs b/crates/biome_js_analyze/src/analyzers/complexity/no_multiple_spaces_in_regular_expression_literals.rs index 691ec1a8b6d7..140f5cb37a7f 100644 --- a/crates/biome_js_analyze/src/analyzers/complexity/no_multiple_spaces_in_regular_expression_literals.rs +++ b/crates/biome_js_analyze/src/analyzers/complexity/no_multiple_spaces_in_regular_expression_literals.rs @@ -5,12 +5,14 @@ use biome_console::markup; use biome_diagnostics::Applicability; use biome_js_syntax::{JsRegexLiteralExpression, JsSyntaxKind, JsSyntaxToken, TextRange, TextSize}; use biome_rowan::BatchMutationExt; -use std::fmt::Write; +use std::{fmt::Write, ops::Range}; use crate::JsRuleAction; declare_rule! { - /// Disallow unclear usage of multiple space characters in regular expression literals + /// Disallow unclear usage of consecutive space characters in regular expression literals + /// + /// Source: https://eslint.org/docs/latest/rules/no-regex-spaces/ /// /// ## Examples /// @@ -21,19 +23,11 @@ declare_rule! { /// ``` /// /// ```js,expect_diagnostic - /// / foo/ - /// ``` - /// - /// ```js,expect_diagnostic - /// /foo / - /// ``` - /// - /// ```js,expect_diagnostic - /// /foo bar/ + /// /foo */ /// ``` /// /// ```js,expect_diagnostic - /// /foo bar baz/ + /// /foo {2,}bar {3,5}baz/ /// ``` /// /// ```js,expect_diagnostic @@ -47,16 +41,12 @@ declare_rule! { ///``` /// /// ```js - /// /foo bar baz/ + /// / foo bar baz / ///``` /// /// ```js /// /foo bar baz/ ///``` - /// - /// ```js - /// /foo / - ///``` pub(crate) NoMultipleSpacesInRegularExpressionLiterals { version: "1.0.0", name: "noMultipleSpacesInRegularExpressionLiterals", @@ -66,7 +56,7 @@ declare_rule! { impl Rule for NoMultipleSpacesInRegularExpressionLiterals { type Query = Ast; - type State = Vec<(usize, usize)>; + type State = Vec>; type Signals = Option; type Options = (); @@ -74,19 +64,19 @@ impl Rule for NoMultipleSpacesInRegularExpressionLiterals { let value_token = ctx.query().value_token().ok()?; let trimmed_text = value_token.text_trimmed(); let mut range_list = vec![]; - let mut continue_white_space = false; - let mut last_white_index = 0; + let mut previous_is_space = false; + let mut first_consecutive_space_index = 0; for (i, ch) in trimmed_text.chars().enumerate() { if ch == ' ' { - if !continue_white_space { - continue_white_space = true; - last_white_index = i; + if !previous_is_space { + previous_is_space = true; + first_consecutive_space_index = i; } - } else if continue_white_space { - if i - last_white_index > 1 { - range_list.push((last_white_index, i)); + } else if previous_is_space { + if i - first_consecutive_space_index > 1 { + range_list.push(first_consecutive_space_index..i); } - continue_white_space = false; + previous_is_space = false; } } if !range_list.is_empty() { @@ -101,50 +91,117 @@ impl Rule for NoMultipleSpacesInRegularExpressionLiterals { let value_token_range = value_token.text_trimmed_range(); // SAFETY: We know diagnostic will be sended only if the `range_list` is not empty // first and last continuous whitespace range of `range_list` - let (first_start, _) = state[0]; - let (_, last_end) = state[state.len() - 1]; - - Some(RuleDiagnostic::new( - rule_category!(), - TextRange::new( - value_token_range.start() + TextSize::from(first_start as u32), - value_token_range.start() + TextSize::from(last_end as u32), - ), - markup! { - "This regular expression contains unclear uses of multiple spaces." - }, - )) + let Range { + start: first_start, .. + } = state[0]; + let Range { end: last_end, .. } = state[state.len() - 1]; + Some( + RuleDiagnostic::new( + rule_category!(), + TextRange::new( + value_token_range.start() + TextSize::from(first_start as u32), + value_token_range.start() + TextSize::from(last_end as u32), + ), + markup! { + "This regular expression contains unclear uses of consecutive spaces." + }, + ) + .note(markup! { "It's hard to visually count the amount of spaces." }), + ) } fn action(ctx: &RuleContext, state: &Self::State) -> Option { - let mut mutation = ctx.root().begin(); - - let trimmed_token = ctx.query().value_token().ok()?; - let trimmed_token_string = trimmed_token.text_trimmed(); - let mut normalized_string_token = String::new(); + let token = ctx.query().value_token().ok()?; + let text = token.text_trimmed(); + let mut normalized_text = String::with_capacity(text.len()); let mut previous_start = 0; - - let mut eg_length = 0; - - for (start, end) in state.iter() { - normalized_string_token += &trimmed_token_string[previous_start..*start]; - write!(normalized_string_token, " {{{}}}", *end - *start).unwrap(); - previous_start = *end; - eg_length += *end - *start; + for range in state { + // copy previous characters and the first space + normalized_text += &text[previous_start..range.start + 1]; + let n = match text.chars().nth(range.end) { + Some('?') => { + write!(normalized_text, "{{{},{}}}", range.len() - 1, range.len()).unwrap(); + 1 + } + Some('+') => { + write!(normalized_text, "{{{},}}", range.len()).unwrap(); + 1 + } + Some('*') => { + if range.len() == 2 { + write!(normalized_text, "+").unwrap(); + } else { + write!(normalized_text, "{{{},}}", range.len() - 1).unwrap(); + } + 1 + } + Some('{') => { + let (quantifier, n) = parse_range_quantifier(&text[range.end..])?; + match quantifier { + RegexQuantifier::Amount(amount) => { + write!(normalized_text, "{{{}}}", amount + range.len() - 1).unwrap(); + } + RegexQuantifier::OpenRange(start) => { + write!(normalized_text, "{{{},}}", start + range.len() - 1).unwrap(); + } + RegexQuantifier::InclusiveRange((start, end)) => { + let extra = range.len() - 1; + write!(normalized_text, "{{{},{}}}", start + extra, end + extra) + .unwrap(); + } + } + n + } + _ => { + write!(normalized_text, "{{{}}}", range.len()).unwrap(); + 0 + } + }; + previous_start = range.end + n; } - normalized_string_token += &trimmed_token_string[previous_start..]; - let next_trimmed_token = JsSyntaxToken::new_detached( - JsSyntaxKind::JS_REGEX_LITERAL, - &normalized_string_token, - [], - [], - ); - mutation.replace_token(trimmed_token, next_trimmed_token); + normalized_text += &text[previous_start..]; + let next_trimmed_token = + JsSyntaxToken::new_detached(JsSyntaxKind::JS_REGEX_LITERAL, &normalized_text, [], []); + let mut mutation = ctx.root().begin(); + mutation.replace_token(token, next_trimmed_token); Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, - message: markup! { "It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {"{eg_length}"}/" }.to_owned(), + message: markup! { "Use a quantifier instead." }.to_owned(), mutation, }) } } + +#[derive(Debug)] +enum RegexQuantifier { + /// `{n}` + Amount(usize), + /// `{n,}` + OpenRange(usize), + /// `{n,m}` + InclusiveRange((usize, usize)), +} + +/// Returns the quantifier and the number of consumed characters, +/// if `source` starts with a well-formed range quantifier such as `{1,2}`. +fn parse_range_quantifier(source: &str) -> Option<(RegexQuantifier, usize)> { + debug_assert!(source.starts_with('{')); + let quantifier_end = source.find('}')?; + let comma = source[..quantifier_end].find(','); + // A range quantifier must include at least one number. + // If a comma is present, a number must precede the comma. + let quantifier_start: usize = source[1..comma.unwrap_or(quantifier_end)].parse().ok()?; + let quantifier = if let Some(comma) = comma { + debug_assert!(comma < quantifier_end); + let quantifier_end = source[comma + 1..quantifier_end].parse::(); + if let Ok(quantifier_end) = quantifier_end { + RegexQuantifier::InclusiveRange((quantifier_start, quantifier_end)) + } else { + RegexQuantifier::OpenRange(quantifier_start) + } + } else { + RegexQuantifier::Amount(quantifier_start) + }; + Some((quantifier, quantifier_end + 1)) +} diff --git a/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc b/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc index be8f5f1f0de9..27868682768f 100644 --- a/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc +++ b/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc @@ -4,5 +4,24 @@ "/foo /;", "/foo bar/;", "/foo bar baz/;", - "/foo [ba]r b(a|z)/;" + "/foo [ba]r b(a|z)/;", + "/foo +/;", + "/foo +?/;", + "/foo */;", + "/foo *?/;", + "/foo */;", + "/foo ?/;", + "/foo {2}/;", + "/foo {2}a{1,2}/;", + "/foo {2,}/;", + "/foo {,2}/;", + "/foo {2,3}/;", + "/foo + * * {2,}/;", + // Malformed regexes + "/foo {}/;", + "/foo {,}/;", + "/foo {,2}/;", + "/foo {1 2}/;", + "/foo {1/;", + "/foo {1,2/;" ] diff --git a/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc.snap b/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc.snap index 546e800a8f9b..0c73361b40ce 100644 --- a/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc.snap +++ b/crates/biome_js_analyze/tests/specs/complexity/noMultipleSpacesInRegularExpressionLiterals/invalid.jsonc.snap @@ -11,12 +11,14 @@ expression: invalid.jsonc ``` invalid.jsonc:1:2 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ - ! This regular expression contains unclear uses of multiple spaces. + ! This regular expression contains unclear uses of consecutive spaces. > 1 │ / /; │ ^^^ - i Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {3}/ + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. - /···/; + /·{3}/; @@ -33,12 +35,14 @@ invalid.jsonc:1:2 lint/complexity/noMultipleSpacesInRegularExpressionLiterals F ``` invalid.jsonc:1:2 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ - ! This regular expression contains unclear uses of multiple spaces. + ! This regular expression contains unclear uses of consecutive spaces. > 1 │ / foo/; │ ^^ - i Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {2}/ + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. - /··foo/; + /·{2}foo/; @@ -55,12 +59,14 @@ invalid.jsonc:1:2 lint/complexity/noMultipleSpacesInRegularExpressionLiterals F ``` invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ - ! This regular expression contains unclear uses of multiple spaces. + ! This regular expression contains unclear uses of consecutive spaces. > 1 │ /foo /; │ ^^^ - i Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {3}/ + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. - /foo···/; + /foo·{3}/; @@ -77,12 +83,14 @@ invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals F ``` invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ - ! This regular expression contains unclear uses of multiple spaces. + ! This regular expression contains unclear uses of consecutive spaces. > 1 │ /foo bar/; │ ^^ - i Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {2}/ + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. - /foo··bar/; + /foo·{2}bar/; @@ -99,12 +107,14 @@ invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals F ``` invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ - ! This regular expression contains unclear uses of multiple spaces. + ! This regular expression contains unclear uses of consecutive spaces. > 1 │ /foo bar baz/; │ ^^^^^^^^^^ - i Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {7}/ + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. - /foo···bar····baz/; + /foo·{3}bar·{4}baz/; @@ -121,12 +131,14 @@ invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals F ``` invalid.jsonc:1:11 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━ - ! This regular expression contains unclear uses of multiple spaces. + ! This regular expression contains unclear uses of consecutive spaces. > 1 │ /foo [ba]r b(a|z)/; │ ^^ - i Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {2}/ + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. - /foo·[ba]r··b(a|z)/; + /foo·[ba]r·{2}b(a|z)/; @@ -134,4 +146,401 @@ invalid.jsonc:1:11 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ``` +# Input +```js +/foo +/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo +/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··+/; + + /foo·{2,}/; + + +``` + +# Input +```js +/foo +?/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo +?/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··+?/; + + /foo·{2,}?/; + + +``` + +# Input +```js +/foo */; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo */; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··*/; + + /foo·+/; + + +``` + +# Input +```js +/foo *?/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo *?/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··*?/; + + /foo·+?/; + + +``` + +# Input +```js +/foo */; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo */; + │ ^^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo···*/; + + /foo·{2,}/; + + +``` + +# Input +```js +/foo ?/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo ?/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··?/; + + /foo·{1,2}/; + + +``` + +# Input +```js +/foo {2}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {2}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··{2}/; + + /foo·{3}/; + + +``` + +# Input +```js +/foo {2}a{1,2}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {2}a{1,2}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··{2}a{1,2}/; + + /foo·{3}a{1,2}/; + + +``` + +# Input +```js +/foo {2,}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {2,}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··{2,}/; + + /foo·{3,}/; + + +``` + +# Input +```js +/foo {,2}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {,2}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + +# Input +```js +/foo {2,3}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {2,3}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··{2,3}/; + + /foo·{3,4}/; + + +``` + +# Input +```js +/foo + * * {2,}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals FIXABLE ━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo + * * {2,}/; + │ ^^^^^^^^^^^^^ + + i It's hard to visually count the amount of spaces. + + i Suggested fix: Use a quantifier instead. + + - /foo··+··*···*···{2,}/; + + /foo·{2,}·+·{2,}·{4,}/; + + +``` + +# Input +```js +/foo {}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + +# Input +```js +/foo {,}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {,}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + +# Input +```js +/foo {,2}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {,2}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + +# Input +```js +/foo {1 2}/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {1 2}/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + +# Input +```js +/foo {1/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {1/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + +# Input +```js +/foo {1,2/; +``` + +# Diagnostics +``` +invalid.jsonc:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals ━━━━━━━━━━━━━━━━━━━━━━ + + ! This regular expression contains unclear uses of consecutive spaces. + + > 1 │ /foo {1,2/; + │ ^^ + + i It's hard to visually count the amount of spaces. + + +``` + diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 4ef2087a08f6..1a8b8718b026 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -975,7 +975,7 @@ pub struct Complexity { #[bpaf(long("no-for-each"), argument("on|off|warn"), optional, hide)] #[serde(skip_serializing_if = "Option::is_none")] pub no_for_each: Option, - #[doc = "Disallow unclear usage of multiple space characters in regular expression literals"] + #[doc = "Disallow unclear usage of consecutive space characters in regular expression literals"] #[bpaf( long("no-multiple-spaces-in-regular-expression-literals"), argument("on|off|warn"), diff --git a/editors/vscode/configuration_schema.json b/editors/vscode/configuration_schema.json index 7e2dbd9184f0..d131e8a6718d 100644 --- a/editors/vscode/configuration_schema.json +++ b/editors/vscode/configuration_schema.json @@ -288,7 +288,7 @@ ] }, "noMultipleSpacesInRegularExpressionLiterals": { - "description": "Disallow unclear usage of multiple space characters in regular expression literals", + "description": "Disallow unclear usage of consecutive space characters in regular expression literals", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 8678984d208c..73874cfcbdc3 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -444,7 +444,7 @@ export interface Complexity { */ noForEach?: RuleConfiguration; /** - * Disallow unclear usage of multiple space characters in regular expression literals + * Disallow unclear usage of consecutive space characters in regular expression literals */ noMultipleSpacesInRegularExpressionLiterals?: RuleConfiguration; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 7e2dbd9184f0..d131e8a6718d 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -288,7 +288,7 @@ ] }, "noMultipleSpacesInRegularExpressionLiterals": { - "description": "Disallow unclear usage of multiple space characters in regular expression literals", + "description": "Disallow unclear usage of consecutive space characters in regular expression literals", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } diff --git a/website/src/content/docs/internals/changelog.mdx b/website/src/content/docs/internals/changelog.mdx index c86202a308e9..e69bbe99b07b 100644 --- a/website/src/content/docs/internals/changelog.mdx +++ b/website/src/content/docs/internals/changelog.mdx @@ -65,6 +65,8 @@ Read our [guidelines for writing a good changelog entry](https://github.com/biom - Fix [#294](https://github.com/biomejs/biome/issues/294). [noConfusingVoidType](https://biomejs.dev/linter/rules/no-confusing-void-type/) no longer reports false positives for return types. Contributed by @b4s36t4 +- Fix [#383](https://github.com/biomejs/biome/issues/383). [noMultipleSpacesInRegularExpressionLiterals](https://biomejs.dev/linter/rules/no-multiple-spaces-in-regular-expression-literals) now provides correct code fixes when consecutive spaces are followed by a quantifier. Contributed by @Conaclos + ### Parser - Enhance diagnostic for infer type handling in the parser. The 'infer' keyword can only be utilized within the 'extends' clause of a conditional type. Using it outside of this context will result in an error. Ensure that any type declarations using 'infer' are correctly placed within the conditional type structure to avoid parsing issues. Contributed by @denbezrukov diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index 0e836335c99e..33c8f64082ff 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -72,7 +72,7 @@ Disallow unnecessary boolean casts ### [noForEach](/linter/rules/no-for-each) Prefer for...of statement instead of Array.forEach. ### [noMultipleSpacesInRegularExpressionLiterals](/linter/rules/no-multiple-spaces-in-regular-expression-literals) -Disallow unclear usage of multiple space characters in regular expression literals +Disallow unclear usage of consecutive space characters in regular expression literals ### [noStaticOnlyClass](/linter/rules/no-static-only-class) This rule reports when a class has no non-static members, such as for a class used exclusively as a static namespace. ### [noUselessCatch](/linter/rules/no-useless-catch) diff --git a/website/src/content/docs/linter/rules/no-multiple-spaces-in-regular-expression-literals.md b/website/src/content/docs/linter/rules/no-multiple-spaces-in-regular-expression-literals.md index 48a8717752c1..c11d262b0154 100644 --- a/website/src/content/docs/linter/rules/no-multiple-spaces-in-regular-expression-literals.md +++ b/website/src/content/docs/linter/rules/no-multiple-spaces-in-regular-expression-literals.md @@ -8,7 +8,9 @@ title: noMultipleSpacesInRegularExpressionLiterals (since v1.0.0) This rule is recommended by Biome. A diagnostic error will appear when linting your code. ::: -Disallow unclear usage of multiple space characters in regular expression literals +Disallow unclear usage of consecutive space characters in regular expression literals + +Source: https://eslint.org/docs/latest/rules/no-regex-spaces/ ## Examples @@ -20,13 +22,15 @@ Disallow unclear usage of multiple space characters in regular expression litera
complexity/noMultipleSpacesInRegularExpressionLiterals.js:1:2 lint/complexity/noMultipleSpacesInRegularExpressionLiterals  FIXABLE  ━━━━━━━━━━
 
-   This regular expression contains unclear uses of multiple spaces.
+   This regular expression contains unclear uses of consecutive spaces.
   
   > 1 │ /   /
     ^^^
     2 │ 
   
-   Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {3}/
+   It's hard to visually count the amount of spaces.
+  
+   Suggested fix: Use a quantifier instead.
   
     1  - /···/
       1+ /·{3}/
@@ -35,81 +39,45 @@ Disallow unclear usage of multiple space characters in regular expression litera
 
```jsx -/ foo/ -``` - -
complexity/noMultipleSpacesInRegularExpressionLiterals.js:1:2 lint/complexity/noMultipleSpacesInRegularExpressionLiterals  FIXABLE  ━━━━━━━━━━
-
-   This regular expression contains unclear uses of multiple spaces.
-  
-  > 1 │ /  foo/
-    ^^
-    2 │ 
-  
-   Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {2}/
-  
-    1  - /··foo/
-      1+ /·{2}foo/
-    2 2  
-  
-
- -```jsx -/foo / -``` - -
complexity/noMultipleSpacesInRegularExpressionLiterals.js:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals  FIXABLE  ━━━━━━━━━━
-
-   This regular expression contains unclear uses of multiple spaces.
-  
-  > 1 │ /foo   /
-       ^^^
-    2 │ 
-  
-   Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {3}/
-  
-    1  - /foo···/
-      1+ /foo·{3}/
-    2 2  
-  
-
- -```jsx -/foo bar/ +/foo */ ```
complexity/noMultipleSpacesInRegularExpressionLiterals.js:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals  FIXABLE  ━━━━━━━━━━
 
-   This regular expression contains unclear uses of multiple spaces.
+   This regular expression contains unclear uses of consecutive spaces.
   
-  > 1 │ /foo  bar/
+  > 1 │ /foo  */
        ^^
     2 │ 
   
-   Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {2}/
+   It's hard to visually count the amount of spaces.
   
-    1  - /foo··bar/
-      1+ /foo·{2}bar/
+   Suggested fix: Use a quantifier instead.
+  
+    1  - /foo··*/
+      1+ /foo·+/
     2 2  
   
 
```jsx -/foo bar baz/ +/foo {2,}bar {3,5}baz/ ```
complexity/noMultipleSpacesInRegularExpressionLiterals.js:1:5 lint/complexity/noMultipleSpacesInRegularExpressionLiterals  FIXABLE  ━━━━━━━━━━
 
-   This regular expression contains unclear uses of multiple spaces.
+   This regular expression contains unclear uses of consecutive spaces.
   
-  > 1 │ /foo   bar    baz/
-       ^^^^^^^^^^
+  > 1 │ /foo  {2,}bar   {3,5}baz/
+       ^^^^^^^^^^^^
     2 │ 
   
-   Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {7}/
+   It's hard to visually count the amount of spaces.
+  
+   Suggested fix: Use a quantifier instead.
   
-    1  - /foo···bar····baz/
-      1+ /foo·{3}bar·{4}baz/
+    1  - /foo··{2,}bar···{3,5}baz/
+      1+ /foo·{3,}bar·{5,7}baz/
     2 2  
   
 
@@ -120,13 +88,15 @@ Disallow unclear usage of multiple space characters in regular expression litera
complexity/noMultipleSpacesInRegularExpressionLiterals.js:1:11 lint/complexity/noMultipleSpacesInRegularExpressionLiterals  FIXABLE  ━━━━━━━━━━
 
-   This regular expression contains unclear uses of multiple spaces.
+   This regular expression contains unclear uses of consecutive spaces.
   
   > 1 │ /foo [ba]r  b(a|z)/
              ^^
     2 │ 
   
-   Suggested fix: It's hard to visually count the amount of spaces, it's clearer if you use a quantifier instead. eg / {2}/
+   It's hard to visually count the amount of spaces.
+  
+   Suggested fix: Use a quantifier instead.
   
     1  - /foo·[ba]r··b(a|z)/
       1+ /foo·[ba]r·{2}b(a|z)/
@@ -141,17 +111,13 @@ Disallow unclear usage of multiple space characters in regular expression litera
 ```
 
 ```jsx
-/foo bar baz/
+/ foo bar baz /
 ```
 
 ```jsx
 /foo bar	baz/
 ```
 
-```jsx
-/foo /
-```
-
 ## Related links
 
 - [Disable a rule](/linter/#disable-a-lint-rule)