From bcc4f1e8fae28c154ed2ad2878405ff1457fb792 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 15:18:24 +0900 Subject: [PATCH 01/19] chore: rebase --- .../src/categories.rs | 1 + .../src/semantic_analyzers/nursery.rs | 2 + .../nursery/no_misleading_character_class.rs | 73 +++++++++++++++++++ .../noMisleadingCharacterClass/invalid.js | 5 ++ .../noMisleadingCharacterClass/valid.js | 3 + .../src/configuration/linter/rules.rs | 30 ++++++-- .../src/configuration/parse/json/rules.rs | 8 ++ .../@biomejs/backend-jsonrpc/src/workspace.ts | 5 ++ .../@biomejs/biome/configuration_schema.json | 7 ++ .../src/content/docs/linter/rules/index.mdx | 1 + .../rules/no-misleading-character-class.md | 54 ++++++++++++++ 11 files changed, 181 insertions(+), 8 deletions(-) create mode 100644 crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js create mode 100644 website/src/content/docs/linter/rules/no-misleading-character-class.md diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index 3462ca8e2bf0..e0bcdc2abfc9 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -104,6 +104,7 @@ define_categories! { "lint/nursery/noDuplicateJsonKeys": "https://biomejs.dev/linter/rules/no-duplicate-json-keys", "lint/nursery/noEmptyBlockStatements": "https://biomejs.dev/linter/rules/no-empty-block-statements", "lint/nursery/noImplicitAnyLet": "https://biomejs.dev/lint/rules/no-implicit-any-let", + "lint/nursery/noMisleadingCharacterClass": "https://biomejs.dev/linter/rules/no-misleading-character-class", "lint/nursery/noUnusedImports": "https://biomejs.dev/linter/rules/no-unused-imports", "lint/nursery/noUnusedPrivateClassMembers": "https://biomejs.dev/linter/rules/no-unused-private-class-members", "lint/nursery/noUselessLoneBlockStatements": "https://biomejs.dev/linter/rules/no-useless-lone-block-statements", diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery.rs index 037aba6b8976..6d44f0a37463 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery.rs @@ -2,6 +2,7 @@ use biome_analyze::declare_group; +pub(crate) mod no_misleading_character_class; pub(crate) mod no_unused_imports; pub(crate) mod use_for_of; @@ -9,6 +10,7 @@ declare_group! { pub (crate) Nursery { name : "nursery" , rules : [ + self :: no_misleading_character_class :: NoMisleadingCharacterClass , self :: no_unused_imports :: NoUnusedImports , self :: use_for_of :: UseForOf , ] diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs new file mode 100644 index 000000000000..5948c8cac9c3 --- /dev/null +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -0,0 +1,73 @@ +use crate::semantic_services::Semantic; +use biome_analyze::{ + context::RuleContext, declare_rule, Rule, RuleDiagnostic, +}; +use biome_console::markup; +use biome_js_semantic::{Reference, ReferencesExtensions}; +use biome_js_syntax::JsIdentifierBinding; + +declare_rule! { + /// Succinct description of the rule. + /// + /// Put context and details about the rule. + /// As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). + /// + /// Try to stay consistent with the descriptions of implemented rules. + /// + /// Add a link to the corresponding ESLint rule (if any): + /// + /// Source: https://eslint.org/docs/latest/rules/rule-name + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// var a = 1; + /// a = 2; + /// ``` + /// + /// ## Valid + /// + /// ```js + /// var a = 1; + /// ``` + /// + pub(crate) NoMisleadingCharacterClass { + version: "next", + name: "noMisleadingCharacterClass", + recommended: false, + } +} + +impl Rule for NoMisleadingCharacterClass { + type Query = Semantic; + type State = Reference; + type Signals = Vec; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let binding = ctx.query(); + let model = ctx.model(); + binding.all_references(model).collect() + } + + fn diagnostic(_: &RuleContext, reference: &Self::State) -> Option { + // + // Read our guidelines to write great diagnostics: + // https://docs.rs/biome_analyze/latest/biome_analyze/#what-a-rule-should-say-to-the-user + // + Some( + RuleDiagnostic::new( + rule_category!(), + reference.range(), + markup! { + "Variable is read here." + }, + ) + .note(markup! { + "This note will give you more information." + }), + ) + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js new file mode 100644 index 000000000000..d5693999915c --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js @@ -0,0 +1,5 @@ + + + var a = 1; +a = 2; + a = 3; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js new file mode 100644 index 000000000000..b2cd6d0d1d01 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js @@ -0,0 +1,3 @@ +/* should not generate diagnostics */ + + var a = 1; \ No newline at end of file diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 347990076c9f..5ff339a076b7 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2786,6 +2786,15 @@ pub struct Nursery { #[bpaf(long("no-implicit-any-let"), argument("on|off|warn"), optional, hide)] #[serde(skip_serializing_if = "Option::is_none")] pub no_implicit_any_let: Option, + #[doc = "Succinct description of the rule."] + #[bpaf( + long("no-misleading-character-class"), + argument("on|off|warn"), + optional, + hide + )] + #[serde(skip_serializing_if = "Option::is_none")] + pub no_misleading_character_class: Option, #[doc = "Disallow unused imports."] #[bpaf(long("no-unused-imports"), argument("on|off|warn"), optional, hide)] #[serde(skip_serializing_if = "Option::is_none")] @@ -2860,6 +2869,9 @@ impl MergeWith for Nursery { if let Some(no_implicit_any_let) = other.no_implicit_any_let { self.no_implicit_any_let = Some(no_implicit_any_let); } + if let Some(no_misleading_character_class) = other.no_misleading_character_class { + self.no_misleading_character_class = Some(no_misleading_character_class); + } if let Some(no_unused_imports) = other.no_unused_imports { self.no_unused_imports = Some(no_unused_imports); } @@ -2905,6 +2917,7 @@ impl Nursery { "noDuplicateJsonKeys", "noEmptyBlockStatements", "noImplicitAnyLet", + "noMisleadingCharacterClass", "noUnusedImports", "noUnusedPrivateClassMembers", "noUselessLoneBlockStatements", @@ -2987,22 +3000,22 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4])); } } - if let Some(rule) = self.no_unused_imports.as_ref() { + if let Some(rule) = self.no_misleading_character_class.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5])); } } - if let Some(rule) = self.no_unused_private_class_members.as_ref() { + if let Some(rule) = self.no_unused_imports.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6])); } } - if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { + if let Some(rule) = self.no_unused_private_class_members.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7])); } } - if let Some(rule) = self.use_await.as_ref() { + if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } @@ -3061,22 +3074,22 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4])); } } - if let Some(rule) = self.no_unused_imports.as_ref() { + if let Some(rule) = self.no_misleading_character_class.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[5])); } } - if let Some(rule) = self.no_unused_private_class_members.as_ref() { + if let Some(rule) = self.no_unused_imports.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[6])); } } - if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { + if let Some(rule) = self.no_unused_private_class_members.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[7])); } } - if let Some(rule) = self.use_await.as_ref() { + if let Some(rule) = self.no_useless_lone_block_statements.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } @@ -3147,6 +3160,7 @@ impl Nursery { "noDuplicateJsonKeys" => self.no_duplicate_json_keys.as_ref(), "noEmptyBlockStatements" => self.no_empty_block_statements.as_ref(), "noImplicitAnyLet" => self.no_implicit_any_let.as_ref(), + "noMisleadingCharacterClass" => self.no_misleading_character_class.as_ref(), "noUnusedImports" => self.no_unused_imports.as_ref(), "noUnusedPrivateClassMembers" => self.no_unused_private_class_members.as_ref(), "noUselessLoneBlockStatements" => self.no_useless_lone_block_statements.as_ref(), diff --git a/crates/biome_service/src/configuration/parse/json/rules.rs b/crates/biome_service/src/configuration/parse/json/rules.rs index a3840d0fb923..86cc668cdd1a 100644 --- a/crates/biome_service/src/configuration/parse/json/rules.rs +++ b/crates/biome_service/src/configuration/parse/json/rules.rs @@ -937,6 +937,13 @@ impl Deserializable for Nursery { diagnostics, ); } + "noMisleadingCharacterClass" => { + result.no_misleading_character_class = Deserializable::deserialize( + &value, + "noMisleadingCharacterClass", + diagnostics, + ); + } "noUnusedImports" => { result.no_unused_imports = Deserializable::deserialize(&value, "noUnusedImports", diagnostics); @@ -1003,6 +1010,7 @@ impl Deserializable for Nursery { "noDuplicateJsonKeys", "noEmptyBlockStatements", "noImplicitAnyLet", + "noMisleadingCharacterClass", "noUnusedImports", "noUnusedPrivateClassMembers", "noUselessLoneBlockStatements", diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 650c77a60b4c..b98a03e4704e 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -797,6 +797,10 @@ export interface Nursery { * Disallow use of implicit any type on variable declarations. */ noImplicitAnyLet?: RuleConfiguration; + /** + * Succinct description of the rule. + */ + noMisleadingCharacterClass?: RuleConfiguration; /** * Disallow unused imports. */ @@ -1506,6 +1510,7 @@ export type Category = | "lint/nursery/noDuplicateJsonKeys" | "lint/nursery/noEmptyBlockStatements" | "lint/nursery/noImplicitAnyLet" + | "lint/nursery/noMisleadingCharacterClass" | "lint/nursery/noUnusedImports" | "lint/nursery/noUnusedPrivateClassMembers" | "lint/nursery/noUselessLoneBlockStatements" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 11133c005b1e..d60ed82a6fab 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1147,6 +1147,13 @@ { "type": "null" } ] }, + "noMisleadingCharacterClass": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "noUnusedImports": { "description": "Disallow unused imports.", "anyOf": [ diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index 1a4359c5b100..d23d3b323c5d 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -232,6 +232,7 @@ Rules that belong to this group are not subject to semantic versionany type on variable declarations. | | +| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Succinct description of the rule. | | | [noUnusedImports](/linter/rules/no-unused-imports) | Disallow unused imports. | ๐Ÿ”ง | | [noUnusedPrivateClassMembers](/linter/rules/no-unused-private-class-members) | Disallow unused private class members | โš ๏ธ | | [noUselessLoneBlockStatements](/linter/rules/no-useless-lone-block-statements) | Disallow unnecessary nested block statements. | โš ๏ธ | diff --git a/website/src/content/docs/linter/rules/no-misleading-character-class.md b/website/src/content/docs/linter/rules/no-misleading-character-class.md new file mode 100644 index 000000000000..fdf226bfce5e --- /dev/null +++ b/website/src/content/docs/linter/rules/no-misleading-character-class.md @@ -0,0 +1,54 @@ +--- +title: noMisleadingCharacterClass (since vnext) +--- + +**Diagnostic Category: `lint/nursery/noMisleadingCharacterClass`** + +:::caution +This rule is part of the [nursery](/linter/rules/#nursery) group. +::: + +Succinct description of the rule. + +Put context and details about the rule. +As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). + +Try to stay consistent with the descriptions of implemented rules. + +Add a link to the corresponding ESLint rule (if any): + +Source: https://eslint.org/docs/latest/rules/rule-name + +## Examples + +### Invalid + +```jsx +var a = 1; +a = 2; +``` + +
nursery/noMisleadingCharacterClass.js:1:11 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+  โš  Variable is read here.
+  
+  > 1 โ”‚ var a = 1;
+      โ”‚           
+  > 2 โ”‚ a = 2;
+      โ”‚ ^^
+    3 โ”‚ 
+  
+  โ„น This note will give you more information.
+  
+
+ +## Valid + +```jsx +var a = 1; +``` + +## Related links + +- [Disable a rule](/linter/#disable-a-lint-rule) +- [Rule options](/linter/#rule-options) From c5ea4296dd3d8a97c0c26878952c02510e52406a Mon Sep 17 00:00:00 2001 From: togami2864 Date: Wed, 29 Nov 2023 21:11:07 +0900 Subject: [PATCH 02/19] feat: handle regex literal --- Cargo.lock | 1 + crates/biome_js_analyze/Cargo.toml | 1 + .../nursery/no_misleading_character_class.rs | 375 ++++++++++++++++-- 3 files changed, 335 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e27789ea5bc2..2f09c284a734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,6 +401,7 @@ dependencies = [ "insta", "lazy_static", "natord", + "regex", "roaring", "rustc-hash", "schemars", diff --git a/crates/biome_js_analyze/Cargo.toml b/crates/biome_js_analyze/Cargo.toml index d2eeeb4fd447..fc70854e5996 100644 --- a/crates/biome_js_analyze/Cargo.toml +++ b/crates/biome_js_analyze/Cargo.toml @@ -27,6 +27,7 @@ biome_rowan = { workspace = true } bpaf.workspace = true lazy_static = { workspace = true } natord = "1.0.9" +regex = "1.5.5" roaring = "0.10.1" rustc-hash = { workspace = true } schemars = { workspace = true, optional = true } diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index 5948c8cac9c3..f03c6f7f849f 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -1,73 +1,364 @@ -use crate::semantic_services::Semantic; -use biome_analyze::{ - context::RuleContext, declare_rule, Rule, RuleDiagnostic, -}; +use crate::{semantic_services::Semantic, JsRuleAction}; +use biome_analyze::{context::RuleContext, declare_rule, ActionCategory, Rule, RuleDiagnostic}; use biome_console::markup; -use biome_js_semantic::{Reference, ReferencesExtensions}; -use biome_js_syntax::JsIdentifierBinding; - +use biome_diagnostics::Applicability; +use biome_js_syntax::{ + JsCallExpression, JsNewExpression, JsRegexLiteralExpression, JsSyntaxKind, JsSyntaxToken, +}; +use biome_rowan::{declare_node_union, AstNode, BatchMutationExt, TextRange}; +use regex::Regex; declare_rule! { - /// Succinct description of the rule. - /// - /// Put context and details about the rule. - /// As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). + /// Disallow characters which are made with multiple code points in character class syntax /// - /// Try to stay consistent with the descriptions of implemented rules. + /// Unicode includes the characters which are made with multiple code points. + /// RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as a character; those characters will be dissolved to each code point. For example, โ‡๏ธ is made by โ‡ (U+2747) and VARIATION SELECTOR-16 (U+FE0F). If this character is in RegExp character class, it will match to either โ‡ (U+2747) or VARIATION SELECTOR-16 (U+FE0F) rather than โ‡๏ธ. + /// This rule reports the regular expressions which include multiple code point characters in character class syntax. This rule considers the following characters as multiple code point characters. /// - /// Add a link to the corresponding ESLint rule (if any): - /// - /// Source: https://eslint.org/docs/latest/rules/rule-name + /// Source: https://eslint.org/docs/latest/rules/no-misleading-character-class/ /// /// ## Examples /// /// ### Invalid /// /// ```js,expect_diagnostic - /// var a = 1; - /// a = 2; + /// /^[Aฬ]$/u; + /// /^[โ‡๏ธ]$/u; + /// /^[๐Ÿ‘ถ๐Ÿป]$/u; + /// /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u; + /// /^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u; + /// /^[๐Ÿ‘]$/; /// ``` /// /// ## Valid /// /// ```js - /// var a = 1; + /// /^[abc]$/; + /// /^[๐Ÿ‘]$/u; + /// /^[\q{๐Ÿ‘ถ๐Ÿป}]$/v; /// ``` /// + pub(crate) NoMisleadingCharacterClass { - version: "next", + version: "1.3.0", name: "noMisleadingCharacterClass", recommended: false, } } +declare_node_union! { + pub(crate) AnyRegexExpression = JsNewExpression | JsCallExpression | JsRegexLiteralExpression +} + +pub enum Message { + SurrogatePairWithoutUFlag, + EmojiModifier, + RegionalIndicatorSymbol, + CombiningClassOrVs16, + JoinedCharSequence, +} + +impl Message { + fn as_str(&self) -> &str { + match self { + Self::CombiningClassOrVs16 => "Unexpected combined character in character class.", + Self::SurrogatePairWithoutUFlag => { + "Unexpected surrogate pair in character class. Use 'u' flag." + } + Self::EmojiModifier => "Unexpected modified Emoji in character class. ", + Self::RegionalIndicatorSymbol => { + "Regional indicator symbol characters should not be used in character class." + } + Self::JoinedCharSequence => "Unexpected joined character sequence in character class.", + } + } +} + +pub struct RuleState { + range: TextRange, + message: Message, + node: AnyRegexExpression, +} + impl Rule for NoMisleadingCharacterClass { - type Query = Semantic; - type State = Reference; - type Signals = Vec; + type Query = Semantic; + type State = RuleState; + type Signals = Option; type Options = (); fn run(ctx: &RuleContext) -> Self::Signals { - let binding = ctx.query(); - let model = ctx.model(); - binding.all_references(model).collect() + let regex = ctx.query(); + + match regex { + AnyRegexExpression::JsRegexLiteralExpression(expr) => { + let range = expr.syntax().text_range(); + + let Ok((pattern, flags)) = expr.decompose() else { + return None; + }; + + let has_v_flag = flags.text().contains('v'); + + if has_v_flag { + return None; + } + + let l = replace_escaped_unicode(pattern.text()); + + let has_u_flag = flags.text().contains('u'); + + if !has_u_flag && has_surrogate_pair(&l) { + return Some(RuleState { + range, + + message: Message::SurrogatePairWithoutUFlag, + + node: expr.clone().into(), + }); + } + + if has_combining_class_or_vs16(&l) { + return Some(RuleState { + range, + + message: Message::CombiningClassOrVs16, + + node: expr.clone().into(), + }); + } + + if has_regional_indicator_symbol(&l) { + return Some(RuleState { + range, + + message: Message::RegionalIndicatorSymbol, + + node: expr.clone().into(), + }); + } + + if has_emoji_modifier(&l) { + return Some(RuleState { + range, + + message: Message::EmojiModifier, + + node: expr.clone().into(), + }); + } + + if zwj(&l) { + return Some(RuleState { + range, + + message: Message::JoinedCharSequence, + + node: expr.clone().into(), + }); + } + } + + AnyRegexExpression::JsNewExpression(x) => { + // let regex = x.expression().ok()?.as_js_regex_literal_expression()?; + + // Self::check_regex(ctx, regex).map(|state| RuleState { + + // range: state.range, + + // message: state.message, + + // node: AnyRegexExpression::JsNewExpression(x.clone()), + + // }) + + todo!(); + } + + AnyRegexExpression::JsCallExpression(x) => { + // let regex = x.expression().ok()?.as_js_regex_literal_expression()?; + + // Self::check_regex(ctx, regex).map(|state| RuleState { + + // range: state.range, + + // message: state.message, + + // node: AnyRegexExpression::JsCallExpression(x.clone()), + + // }) + + todo!(); + } + } + + None } - fn diagnostic(_: &RuleContext, reference: &Self::State) -> Option { - // - // Read our guidelines to write great diagnostics: - // https://docs.rs/biome_analyze/latest/biome_analyze/#what-a-rule-should-say-to-the-user - // - Some( - RuleDiagnostic::new( - rule_category!(), - reference.range(), - markup! { - "Variable is read here." - }, - ) - .note(markup! { - "This note will give you more information." - }), - ) + fn diagnostic(_ctx: &RuleContext, state: &Self::State) -> Option { + Some(RuleDiagnostic::new( + rule_category!(), + state.range, + state.message.as_str(), + )) } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + match &state.node { + AnyRegexExpression::JsRegexLiteralExpression(expr) => { + let mut mutation = ctx.root().begin(); + + let prev_token = expr.value_token().ok()?; + + let text = prev_token.text(); + + let next_token = JsSyntaxToken::new_detached( + JsSyntaxKind::JS_REGEX_LITERAL, + &format!("{}u", text), + [], + [], + ); + + mutation.replace_token(prev_token, next_token); + + Some(JsRuleAction { + category: ActionCategory::QuickFix, + + applicability: Applicability::MaybeIncorrect, + + message: markup! { "Add unicode ""u"" flag to regex" } + .to_owned(), + + mutation, + }) + } + + AnyRegexExpression::JsNewExpression(_) => todo!(), + + AnyRegexExpression::JsCallExpression(_) => todo!(), + + _ => None, + } + + // if matches!(state.message, Message::SurrogatePairWithoutUFlag) { + + // let n = ctx.query(); + + // let mut mutation = ctx.root().begin(); + + // let prev_token = n.value_token().ok()?; + + // let text = n.text(); + + // let next_token = JsSyntaxToken::new_detached( + + // JsSyntaxKind::JS_REGEX_LITERAL, + + // &format!("{}u", text), + + // [], + + // [], + + // ); + + // mutation.replace_token(prev_token, next_token); + + // Some(JsRuleAction { + + // category: ActionCategory::QuickFix, + + // applicability: Applicability::MaybeIncorrect, + + // message: markup! { "Add unicode ""u"" flag to regex" } + + // .to_owned(), + + // mutation, + + // }) + + // } else { + + // None + + // } + } +} + +fn is_emoji_modifier(code: u32) -> bool { + (0x1F3FB..=0x1F3FF).contains(&code) +} + +fn has_emoji_modifier(chars: &str) -> bool { + let char_vec: Vec = chars.chars().collect(); + + char_vec.iter().enumerate().any(|(i, &c)| { + i != 0 && is_emoji_modifier(c as u32) && !is_emoji_modifier(char_vec[i - 1] as u32) + }) +} + +fn is_regional_indicator_symbol(code: u32) -> bool { + (0x1F1E6..=0x1F1FF).contains(&code) +} + +fn has_regional_indicator_symbol(chars: &str) -> bool { + let char_vec: Vec = chars.chars().collect(); + + char_vec.iter().enumerate().any(|(i, &c)| { + i != 0 + && is_regional_indicator_symbol(c as u32) + && is_regional_indicator_symbol(char_vec[i - 1] as u32) + }) +} + +fn is_combining_character(ch: char) -> bool { + match ch { + '\u{0300}'..='\u{036F}' | // Combining Diacritical Marks + '\u{1AB0}'..='\u{1AFF}' | // Combining Diacritical Marks Extended + '\u{1DC0}'..='\u{1DFF}' | // Combining Diacritical Marks Supplement + '\u{20D0}'..='\u{20FF}' | // Combining Diacritical Marks for Symbols + '\u{FE20}'..='\u{FE2F}' // Combining Half Marks + => true, + _ => false + } +} + +fn is_variation_selector_16(ch: char) -> bool { + ('\u{FE00}'..='\u{FE0F}').contains(&ch) +} + +fn has_combining_class_or_vs16(chars: &str) -> bool { + chars.chars().enumerate().any(|(i, c)| { + i != 0 + && (is_combining_character(c) || is_variation_selector_16(c)) + && !(is_combining_character(chars.chars().nth(i - 1).unwrap()) + || is_variation_selector_16(chars.chars().nth(i - 1).unwrap())) + }) +} + +fn zwj(chars: &str) -> bool { + let char_vec: Vec = chars.chars().collect(); + let last_index = char_vec.len() - 1; + char_vec.iter().enumerate().any(|(i, &c)| { + i != 0 + && i != last_index + && c as u32 == 0x200D + && char_vec[i - 1] as u32 != 0x200D + && char_vec[i + 1] as u32 != 0x200D + }) +} + +fn has_surrogate_pair(s: &str) -> bool { + s.chars().any(|c| c as u32 > 0xFFFF) +} + +fn replace_escaped_unicode(input: &str) -> String { + let re = Regex::new(r"\\u\{([0-9a-fA-F]+)\}").unwrap(); + + re.replace_all(input, |caps: ®ex::Captures| { + u32::from_str_radix(&caps[1], 16) + .ok() + .and_then(char::from_u32) + .map_or_else(String::new, |c| c.to_string()) + }) + .into_owned() } From 0bbff6067da228f9d40b8907d9261aaecd4c2f80 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Wed, 29 Nov 2023 21:43:17 +0900 Subject: [PATCH 03/19] feat: basic cases --- .../nursery/no_misleading_character_class.rs | 309 +++++++++--------- .../noMisleadingCharacterClass/invalid.js | 20 +- .../invalid.js.snap | 281 ++++++++++++++++ .../noMisleadingCharacterClass/valid.js | 13 +- .../noMisleadingCharacterClass/valid.js.snap | 22 ++ 5 files changed, 476 insertions(+), 169 deletions(-) create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index f03c6f7f849f..3a3a441d1ee2 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -76,7 +76,6 @@ impl Message { pub struct RuleState { range: TextRange, message: Message, - node: AnyRegexExpression, } impl Rule for NoMisleadingCharacterClass { @@ -90,106 +89,74 @@ impl Rule for NoMisleadingCharacterClass { match regex { AnyRegexExpression::JsRegexLiteralExpression(expr) => { - let range = expr.syntax().text_range(); - let Ok((pattern, flags)) = expr.decompose() else { return None; }; - - let has_v_flag = flags.text().contains('v'); - - if has_v_flag { - return None; - } - - let l = replace_escaped_unicode(pattern.text()); - + let regex_literal = v(pattern.text()); let has_u_flag = flags.text().contains('u'); - - if !has_u_flag && has_surrogate_pair(&l) { - return Some(RuleState { - range, - - message: Message::SurrogatePairWithoutUFlag, - - node: expr.clone().into(), - }); - } - - if has_combining_class_or_vs16(&l) { - return Some(RuleState { - range, - - message: Message::CombiningClassOrVs16, - - node: expr.clone().into(), - }); - } - - if has_regional_indicator_symbol(&l) { - return Some(RuleState { - range, - - message: Message::RegionalIndicatorSymbol, - - node: expr.clone().into(), - }); - } - - if has_emoji_modifier(&l) { - return Some(RuleState { - range, - - message: Message::EmojiModifier, - - node: expr.clone().into(), - }); - } - - if zwj(&l) { - return Some(RuleState { - range, - - message: Message::JoinedCharSequence, - - node: expr.clone().into(), - }); + let mut is_in_character_class = false; + let mut escape_next = false; + let mut char_iter = regex_literal.chars().peekable(); + + for (i, ch) in char_iter.enumerate() { + if escape_next { + escape_next = false; + continue; + } + + match ch { + '\\' => escape_next = true, + '[' => is_in_character_class = true, + ']' => is_in_character_class = false, + _ if is_in_character_class && i < regex_literal.len() => { + if !has_u_flag && has_surrogate_pair(®ex_literal[i..]) { + return Some(RuleState { + range: expr.syntax().text_range(), + message: Message::SurrogatePairWithoutUFlag, + }); + } + if has_combining_class_or_vs16(®ex_literal[i..]) { + return Some(RuleState { + range: expr.syntax().text_range(), + message: Message::CombiningClassOrVs16, + }); + } + + if has_regional_indicator_symbol(®ex_literal[i..]) { + return Some(RuleState { + range: expr.syntax().text_range(), + message: Message::RegionalIndicatorSymbol, + }); + } + + if has_emoji_modifier(®ex_literal[i..]) { + return Some(RuleState { + range: expr.syntax().text_range(), + message: Message::EmojiModifier, + }); + } + + if zwj(®ex_literal[i..]) { + return Some(RuleState { + range: expr.syntax().text_range(), + message: Message::JoinedCharSequence, + }); + } + } + _ => {} + } } + return None; } AnyRegexExpression::JsNewExpression(x) => { - // let regex = x.expression().ok()?.as_js_regex_literal_expression()?; - - // Self::check_regex(ctx, regex).map(|state| RuleState { - - // range: state.range, - - // message: state.message, - - // node: AnyRegexExpression::JsNewExpression(x.clone()), - - // }) - todo!(); } AnyRegexExpression::JsCallExpression(x) => { - // let regex = x.expression().ok()?.as_js_regex_literal_expression()?; - - // Self::check_regex(ctx, regex).map(|state| RuleState { - - // range: state.range, - - // message: state.message, - - // node: AnyRegexExpression::JsCallExpression(x.clone()), - - // }) - todo!(); } } - None } @@ -202,33 +169,32 @@ impl Rule for NoMisleadingCharacterClass { } fn action(ctx: &RuleContext, state: &Self::State) -> Option { - match &state.node { + let node = ctx.query(); + match node { AnyRegexExpression::JsRegexLiteralExpression(expr) => { - let mut mutation = ctx.root().begin(); - - let prev_token = expr.value_token().ok()?; - - let text = prev_token.text(); - - let next_token = JsSyntaxToken::new_detached( - JsSyntaxKind::JS_REGEX_LITERAL, - &format!("{}u", text), - [], - [], - ); - - mutation.replace_token(prev_token, next_token); - - Some(JsRuleAction { - category: ActionCategory::QuickFix, - - applicability: Applicability::MaybeIncorrect, - - message: markup! { "Add unicode ""u"" flag to regex" } - .to_owned(), - - mutation, - }) + if matches!(state.message, Message::SurrogatePairWithoutUFlag) { + let prev_token = expr.value_token().ok()?; + let text = prev_token.text(); + let next_token = JsSyntaxToken::new_detached( + JsSyntaxKind::JS_REGEX_LITERAL, + &format!("{}u", text), + [], + [], + ); + + let mut mutation = ctx.root().begin(); + mutation.replace_token(prev_token, next_token); + + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Add unicode ""u"" flag to regex" } + .to_owned(), + mutation, + }) + } else { + None + } } AnyRegexExpression::JsNewExpression(_) => todo!(), @@ -237,50 +203,6 @@ impl Rule for NoMisleadingCharacterClass { _ => None, } - - // if matches!(state.message, Message::SurrogatePairWithoutUFlag) { - - // let n = ctx.query(); - - // let mut mutation = ctx.root().begin(); - - // let prev_token = n.value_token().ok()?; - - // let text = n.text(); - - // let next_token = JsSyntaxToken::new_detached( - - // JsSyntaxKind::JS_REGEX_LITERAL, - - // &format!("{}u", text), - - // [], - - // [], - - // ); - - // mutation.replace_token(prev_token, next_token); - - // Some(JsRuleAction { - - // category: ActionCategory::QuickFix, - - // applicability: Applicability::MaybeIncorrect, - - // message: markup! { "Add unicode ""u"" flag to regex" } - - // .to_owned(), - - // mutation, - - // }) - - // } else { - - // None - - // } } } @@ -351,14 +273,77 @@ fn has_surrogate_pair(s: &str) -> bool { s.chars().any(|c| c as u32 > 0xFFFF) } -fn replace_escaped_unicode(input: &str) -> String { - let re = Regex::new(r"\\u\{([0-9a-fA-F]+)\}").unwrap(); +fn replace_surrogate_pairs(input: &str) -> String { + let re = Regex::new(r"\\u([Dd][89ABab][0-9a-fA-F]{2})\\u([Dd][Cc][0-9a-fA-F]{2})").unwrap(); re.replace_all(input, |caps: ®ex::Captures| { - u32::from_str_radix(&caps[1], 16) - .ok() - .and_then(char::from_u32) - .map_or_else(String::new, |c| c.to_string()) + let high = u32::from_str_radix(&caps[1], 16).unwrap(); + let low = u32::from_str_radix(&caps[2], 16).unwrap(); + + let codepoint = ((high - 0xD800) << 10) + (low - 0xDC00) + 0x10000; + + char::from_u32(codepoint).map_or_else(String::new, |c| c.to_string()) }) .into_owned() } + +fn v(input: &str) -> String { + let mut result = String::new(); + let mut chars_iter = input.chars().peekable(); + + while let Some(ch) = chars_iter.next() { + if ch == '\\' && chars_iter.peek() == Some(&'u') { + // '\' ใฎๅพŒใซ 'u' ใŒๆฅใ‚‹ๅ ดๅˆใ€Unicodeใ‚จใ‚นใ‚ฑใƒผใƒ—ใจใ—ใฆๅ‡ฆ็† + chars_iter.next(); // 'u' ใ‚’ๆถˆ่ฒป + + if chars_iter.peek() == Some(&'{') { + // '{' ใงๅง‹ใพใ‚‹ๅ ดๅˆใ€ใ‚ณใƒผใƒ‰ใƒใ‚คใƒณใƒˆใŒ {} ๅ†…ใซใ‚ใ‚‹ + chars_iter.next(); // '{' ใ‚’ๆถˆ่ฒป + let mut codepoint_str = String::new(); + while let Some(&next_char) = chars_iter.peek() { + if next_char == '}' { + chars_iter.next(); // '}' ใ‚’ๆถˆ่ฒป + break; + } else { + codepoint_str.push(next_char); + chars_iter.next(); + } + } + if let Ok(codepoint) = u32::from_str_radix(&codepoint_str, 16) { + if let Some(character) = char::from_u32(codepoint) { + result.push(character); + } + } + } else { + // ้€šๅธธใฎใ‚ตใƒญใ‚ฒใƒผใƒˆใƒšใ‚ข + let mut high_surrogate_str = String::new(); + for _ in 0..4 { + if let Some(next_char) = chars_iter.next() { + high_surrogate_str.push(next_char); + } + } + if chars_iter.next() == Some('\\') && chars_iter.next() == Some('u') { + let mut low_surrogate_str = String::new(); + for _ in 0..4 { + if let Some(next_char) = chars_iter.next() { + low_surrogate_str.push(next_char); + } + } + let high_surrogate = u32::from_str_radix(&high_surrogate_str, 16).unwrap(); + let low_surrogate = u32::from_str_radix(&low_surrogate_str, 16).unwrap(); + let codepoint = + ((high_surrogate - 0xD800) << 10) + (low_surrogate - 0xDC00) + 0x10000; + + if let Some(character) = char::from_u32(codepoint) { + result.push(character); + } + } + } + } else { + // Unicodeใ‚จใ‚นใ‚ฑใƒผใƒ—ใงใชใ„ๅ ดๅˆใ€ใใฎใพใพ่ฟฝๅŠ  + result.push(ch); + } + } + + result +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js index d5693999915c..7f697f22b5ca 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js @@ -1,5 +1,15 @@ - - - var a = 1; -a = 2; - a = 3; \ No newline at end of file +var r = /[๐Ÿ‘]/; +var r = /[\uD83D\uDC4D]/; +var r = /[๐Ÿ‘]\\a/; +var r = /(?<=[๐Ÿ‘])/; +var r = /[Aฬ]/; +var r = /[Aฬ]/u; +var r = /[โ‡๏ธ]/; +var r = /[โ‡๏ธ]/u; +var r = /[๐Ÿ‘ถ๐Ÿป]/; +var r = /[๐Ÿ‘ถ๐Ÿป]/u; +var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; +var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; +var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; +var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/; +var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap new file mode 100644 index 000000000000..f0b3b9ac6074 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap @@ -0,0 +1,281 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid.js +--- +# Input +```js +var r = /[๐Ÿ‘]/; +var r = /[\uD83D\uDC4D]/; +var r = /[๐Ÿ‘]\\a/; +var r = /(?<=[๐Ÿ‘])/; +var r = /[Aฬ]/; +var r = /[Aฬ]/u; +var r = /[โ‡๏ธ]/; +var r = /[โ‡๏ธ]/u; +var r = /[๐Ÿ‘ถ๐Ÿป]/; +var r = /[๐Ÿ‘ถ๐Ÿป]/u; +var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; +var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; +var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; +var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/; +var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; +``` + +# Diagnostics +``` +invalid.js:1:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + > 1 โ”‚ var r = /[๐Ÿ‘]/; + โ”‚ ^^^^^^ + 2 โ”‚ var r = /[\uD83D\uDC4D]/; + 3 โ”‚ var r = /[๐Ÿ‘]\\a/; + + i Unsafe fix: Add unicode u flag to regex + + 1 โ”‚ varยทrยท=ยท/[๐Ÿ‘]/u; + โ”‚ + + +``` + +``` +invalid.js:2:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 1 โ”‚ var r = /[๐Ÿ‘]/; + > 2 โ”‚ var r = /[\uD83D\uDC4D]/; + โ”‚ ^^^^^^^^^^^^^^^^ + 3 โ”‚ var r = /[๐Ÿ‘]\\a/; + 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; + + i Unsafe fix: Add unicode u flag to regex + + 2 โ”‚ varยทrยท=ยท/[\uD83D\uDC4D]/u; + โ”‚ + + +``` + +``` +invalid.js:3:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 1 โ”‚ var r = /[๐Ÿ‘]/; + 2 โ”‚ var r = /[\uD83D\uDC4D]/; + > 3 โ”‚ var r = /[๐Ÿ‘]\\a/; + โ”‚ ^^^^^^^^^ + 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; + 5 โ”‚ var r = /[A๏ฟฝ]/; + + i Unsafe fix: Add unicode u flag to regex + + 3 โ”‚ varยทrยท=ยท/[๐Ÿ‘]\\a/u; + โ”‚ + + +``` + +``` +invalid.js:4:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 2 โ”‚ var r = /[\uD83D\uDC4D]/; + 3 โ”‚ var r = /[๐Ÿ‘]\\a/; + > 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; + โ”‚ ^^^^^^^^^^^ + 5 โ”‚ var r = /[A๏ฟฝ]/; + 6 โ”‚ var r = /[A๏ฟฝ]/u; + + i Unsafe fix: Add unicode u flag to regex + + 4 โ”‚ varยทrยท=ยท/(?<=[๐Ÿ‘])/u; + โ”‚ + + +``` + +``` +invalid.js:5:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 3 โ”‚ var r = /[๐Ÿ‘]\\a/; + 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; + > 5 โ”‚ var r = /[A๏ฟฝ]/; + โ”‚ ^^^^^ + 6 โ”‚ var r = /[A๏ฟฝ]/u; + 7 โ”‚ var r = /[โ‡๏ฟฝ]/; + + +``` + +``` +invalid.js:6:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; + 5 โ”‚ var r = /[A๏ฟฝ]/; + > 6 โ”‚ var r = /[A๏ฟฝ]/u; + โ”‚ ^^^^^^ + 7 โ”‚ var r = /[โ‡๏ฟฝ]/; + 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + + +``` + +``` +invalid.js:7:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 5 โ”‚ var r = /[A๏ฟฝ]/; + 6 โ”‚ var r = /[A๏ฟฝ]/u; + > 7 โ”‚ var r = /[โ‡๏ฟฝ]/; + โ”‚ ^^^^^ + 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + + +``` + +``` +invalid.js:8:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 6 โ”‚ var r = /[A๏ฟฝ]/u; + 7 โ”‚ var r = /[โ‡๏ฟฝ]/; + > 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + โ”‚ ^^^^^^ + 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + + +``` + +``` +invalid.js:9:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 7 โ”‚ var r = /[โ‡๏ฟฝ]/; + 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + > 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + โ”‚ ^^^^^^^^ + 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + + i Unsafe fix: Add unicode u flag to regex + + 9 โ”‚ varยทrยท=ยท/[๐Ÿ‘ถ๐Ÿป]/u; + โ”‚ + + +``` + +``` +invalid.js:10:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + > 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + โ”‚ ^^^^^^^^^ + 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + + +``` + +``` +invalid.js:11:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + > 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + โ”‚ ^^^^^^ + 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + + i Unsafe fix: Add unicode u flag to regex + + 11 โ”‚ varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + โ”‚ + + +``` + +``` +invalid.js:12:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + > 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + โ”‚ ^^^^^^^ + 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + + i Unsafe fix: Add unicode u flag to regex + + 10 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 11 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 12 โ”‚ - varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 12 โ”‚ + varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/iu; + 13 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 14 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + + +``` + +``` +invalid.js:13:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Regional indicator symbol characters should not be used in character class. + + 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + > 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + โ”‚ ^^^^^^^ + 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + 15 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + + +``` + +``` +invalid.js:14:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + > 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + โ”‚ ^^^^^^^^^^ + 15 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + + i Unsafe fix: Add unicode u flag to regex + + 14 โ”‚ varยทrยท=ยท/[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + โ”‚ + + +``` + +``` +invalid.js:15:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + > 15 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + โ”‚ ^^^^^^^^^^^ + + +``` + + diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js index b2cd6d0d1d01..1cc20aa4ce3e 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js @@ -1,3 +1,12 @@ -/* should not generate diagnostics */ +var r = /[๐Ÿ‘]/u; +var r = /[\\uD83D\\uDC4D]/u; +var r = /[\\u{1F44D}]/u; +var r = /โ‡๏ธ/; +var r = /Aฬ/; +var r = /[โ‡]/; +var r = /๐Ÿ‘ถ๐Ÿป/; +var r = /[๐Ÿ‘ถ]/u; +var r = /๐Ÿ‡ฏ๐Ÿ‡ต/; +var r = /[JP]/; +var r = /๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ/; - var a = 1; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap new file mode 100644 index 000000000000..0045ad167d4f --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap @@ -0,0 +1,22 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid.js +--- +# Input +```js +var r = /[๐Ÿ‘]/u; +var r = /[\\uD83D\\uDC4D]/u; +var r = /[\\u{1F44D}]/u; +var r = /โ‡๏ธ/; +var r = /Aฬ/; +var r = /[โ‡]/; +var r = /๐Ÿ‘ถ๐Ÿป/; +var r = /[๐Ÿ‘ถ]/u; +var r = /๐Ÿ‡ฏ๐Ÿ‡ต/; +var r = /[JP]/; +var r = /๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ/; + + +``` + + From 6aefdfc7d98f5bdeb4559888c4deb33058b2539b Mon Sep 17 00:00:00 2001 From: togami2864 Date: Fri, 1 Dec 2023 02:27:54 +0900 Subject: [PATCH 04/19] feat: handle escaped unicode --- .../nursery/no_misleading_character_class.rs | 201 +++++++---- .../noMisleadingCharacterClass/invalid.js | 14 +- .../invalid.js.snap | 312 ++++++++++++++---- .../noMisleadingCharacterClass/valid.js | 23 ++ .../noMisleadingCharacterClass/valid.js.snap | 22 ++ 5 files changed, 448 insertions(+), 124 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index 3a3a441d1ee2..f1a64c81602c 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -6,7 +6,6 @@ use biome_js_syntax::{ JsCallExpression, JsNewExpression, JsRegexLiteralExpression, JsSyntaxKind, JsSyntaxToken, }; use biome_rowan::{declare_node_union, AstNode, BatchMutationExt, TextRange}; -use regex::Regex; declare_rule! { /// Disallow characters which are made with multiple code points in character class syntax /// @@ -92,18 +91,16 @@ impl Rule for NoMisleadingCharacterClass { let Ok((pattern, flags)) = expr.decompose() else { return None; }; - let regex_literal = v(pattern.text()); + let regex_literal = replace_escaped_unicode(pattern.text()); let has_u_flag = flags.text().contains('u'); let mut is_in_character_class = false; let mut escape_next = false; - let mut char_iter = regex_literal.chars().peekable(); - + let char_iter = regex_literal.chars().peekable(); for (i, ch) in char_iter.enumerate() { if escape_next { escape_next = false; continue; } - match ch { '\\' => escape_next = true, '[' => is_in_character_class = true, @@ -115,6 +112,7 @@ impl Rule for NoMisleadingCharacterClass { message: Message::SurrogatePairWithoutUFlag, }); } + if has_combining_class_or_vs16(®ex_literal[i..]) { return Some(RuleState { range: expr.syntax().text_range(), @@ -273,77 +271,154 @@ fn has_surrogate_pair(s: &str) -> bool { s.chars().any(|c| c as u32 > 0xFFFF) } -fn replace_surrogate_pairs(input: &str) -> String { - let re = Regex::new(r"\\u([Dd][89ABab][0-9a-fA-F]{2})\\u([Dd][Cc][0-9a-fA-F]{2})").unwrap(); - - re.replace_all(input, |caps: ®ex::Captures| { - let high = u32::from_str_radix(&caps[1], 16).unwrap(); - let low = u32::from_str_radix(&caps[2], 16).unwrap(); - - let codepoint = ((high - 0xD800) << 10) + (low - 0xDC00) + 0x10000; - - char::from_u32(codepoint).map_or_else(String::new, |c| c.to_string()) - }) - .into_owned() +fn is_unicode_char(ch: char) -> bool { + ch.is_ascii_digit() || ('a'..='f').contains(&ch) || ('A'..='F').contains(&ch) } -fn v(input: &str) -> String { +/// Convert unicode escape sequence string to unicode character +/// - unicode escape sequences: \u{XXXX} +/// - unicode escape sequences without parenthesis: \uXXXX +/// - surrogate pair: \uXXXX\uXXXX +/// If the unicode escape sequence is not valid, it will be treated as a simple string. +/// +/// ```example +/// \uD83D\uDC4D -> ๐Ÿ‘ +/// \u0041\u0301 -> Aฬ +/// \uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66 -> ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ +/// \u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466} -> ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ +/// \u899\uD83D\uDC4D -> \u899๐Ÿ‘ +/// ```` +fn replace_escaped_unicode(input: &str) -> String { let mut result = String::new(); let mut chars_iter = input.chars().peekable(); while let Some(ch) = chars_iter.next() { - if ch == '\\' && chars_iter.peek() == Some(&'u') { - // '\' ใฎๅพŒใซ 'u' ใŒๆฅใ‚‹ๅ ดๅˆใ€Unicodeใ‚จใ‚นใ‚ฑใƒผใƒ—ใจใ—ใฆๅ‡ฆ็† - chars_iter.next(); // 'u' ใ‚’ๆถˆ่ฒป - - if chars_iter.peek() == Some(&'{') { - // '{' ใงๅง‹ใพใ‚‹ๅ ดๅˆใ€ใ‚ณใƒผใƒ‰ใƒใ‚คใƒณใƒˆใŒ {} ๅ†…ใซใ‚ใ‚‹ - chars_iter.next(); // '{' ใ‚’ๆถˆ่ฒป - let mut codepoint_str = String::new(); - while let Some(&next_char) = chars_iter.peek() { - if next_char == '}' { - chars_iter.next(); // '}' ใ‚’ๆถˆ่ฒป - break; - } else { - codepoint_str.push(next_char); - chars_iter.next(); - } - } - if let Ok(codepoint) = u32::from_str_radix(&codepoint_str, 16) { - if let Some(character) = char::from_u32(codepoint) { - result.push(character); - } - } - } else { - // ้€šๅธธใฎใ‚ตใƒญใ‚ฒใƒผใƒˆใƒšใ‚ข - let mut high_surrogate_str = String::new(); + if ch == '\\' { + match handle_escape_sequence(&mut chars_iter) { + Some(unicode_char) => result.push_str(&unicode_char), + None => result.push(ch), + } + } else { + result.push(ch); + } + } + result +} + +fn handle_escape_sequence(chars_iter: &mut std::iter::Peekable) -> Option { + if chars_iter.peek() != Some(&'u') { + return None; + } + chars_iter.next(); + + if chars_iter.peek() == Some(&'{') { + handle_braced_escape_sequence(chars_iter) + } else { + handle_simple_or_surrogate_escape_sequence(chars_iter) + } +} + +fn handle_braced_escape_sequence( + chars_iter: &mut std::iter::Peekable, +) -> Option { + chars_iter.next(); + let mut codepoint_str = String::new(); + while let Some(&next_char) = chars_iter.peek() { + if next_char == '}' { + chars_iter.next(); + break; + } else { + codepoint_str.push(next_char); + chars_iter.next(); + } + } + u32::from_str_radix(&codepoint_str, 16) + .ok() + .and_then(char::from_u32) + .map(|c| c.to_string()) +} + +fn handle_simple_or_surrogate_escape_sequence( + chars_iter: &mut std::iter::Peekable, +) -> Option { + let mut invalid_pair = String::new(); + let mut high_surrogate_str = String::new(); + + for _ in 0..4 { + if let Some(next_char) = chars_iter.peek() { + if !is_unicode_char(*next_char) { + // Return as simple string. This is not unicode + return Some(format!("\\u{}", high_surrogate_str)); + } + high_surrogate_str.push(*next_char); + chars_iter.next(); + } else { + // Return as simple string. This is not unicode + return Some(format!("\\u{}", high_surrogate_str)); + } + } + + if let Ok(high_surrogate) = u32::from_str_radix(&high_surrogate_str, 16) { + // check if it is in the high surrogate range(0xD800-0xDBFF) in UTF-16. + if (0xD800..=0xDBFF).contains(&high_surrogate) { + // Handle surrogate pair + if chars_iter.next() == Some('\\') && chars_iter.next() == Some('u') { + let mut low_surrogate_str = String::new(); for _ in 0..4 { - if let Some(next_char) = chars_iter.next() { - high_surrogate_str.push(next_char); - } - } - if chars_iter.next() == Some('\\') && chars_iter.next() == Some('u') { - let mut low_surrogate_str = String::new(); - for _ in 0..4 { - if let Some(next_char) = chars_iter.next() { - low_surrogate_str.push(next_char); + if let Some(next_char) = chars_iter.peek() { + if !is_unicode_char(*next_char) { + // high_surrogate is the correct codepoint but treating low_surrogate_str a string because it is not a valid unicode codepoint + invalid_pair.push_str(&format!("{high_surrogate}")); + invalid_pair.push_str(&low_surrogate_str); + return Some(invalid_pair); } - } - let high_surrogate = u32::from_str_radix(&high_surrogate_str, 16).unwrap(); - let low_surrogate = u32::from_str_radix(&low_surrogate_str, 16).unwrap(); - let codepoint = - ((high_surrogate - 0xD800) << 10) + (low_surrogate - 0xDC00) + 0x10000; - - if let Some(character) = char::from_u32(codepoint) { - result.push(character); + low_surrogate_str.push(*next_char); + chars_iter.next(); } } + if let Ok(low_surrogate) = u32::from_str_radix(&low_surrogate_str, 16) { + // check if it is in the low surrogate range(0xDC00-0xDFFF) in UTF-16. + if (0xDC00..=0xDFFF).contains(&low_surrogate) { + // calculate the codepoint from the surrogate pair + let codepoint = + ((high_surrogate - 0xD800) << 10) + (low_surrogate - 0xDC00) + 0x10000; + return char::from_u32(codepoint).map(|c| c.to_string()); + }; + } else { + // high_surrogate is the correct codepoint but treating low_surrogate_str a string because it is not a valid unicode codepoint + invalid_pair.push_str(&format!("{}", high_surrogate)); + invalid_pair.push_str(&format!("\\u{}", low_surrogate_str)); + } } } else { - // Unicodeใ‚จใ‚นใ‚ฑใƒผใƒ—ใงใชใ„ๅ ดๅˆใ€ใใฎใพใพ่ฟฝๅŠ  - result.push(ch); + // Handle standard escape sequence + return char::from_u32(high_surrogate).map(|c| c.to_string()); } + } else { + invalid_pair.push_str(&format!("\\u{}", high_surrogate_str)); } + // return as simple string, not unicode + Some(invalid_pair) +} - result +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_replace_escaped_unicode() { + assert_eq!(replace_escaped_unicode(r#"/[\uD83D\uDC4D]/"#), "/[๐Ÿ‘]/"); + assert_eq!(replace_escaped_unicode(r#"/[\u0041\u0301]/"#), "/[Aฬ]/"); + assert_eq!( + replace_escaped_unicode("/[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u"), + "/[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u" + ); + assert_eq!( + replace_escaped_unicode(r#"/[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u"#), + "/[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u" + ); + assert_eq!( + replace_escaped_unicode(r#"/[\u899\uD83D\uDC4D]/"#), + r#"/[\u899๐Ÿ‘]/"# + ); + } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js index 7f697f22b5ca..8a0f944a288b 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js @@ -4,12 +4,24 @@ var r = /[๐Ÿ‘]\\a/; var r = /(?<=[๐Ÿ‘])/; var r = /[Aฬ]/; var r = /[Aฬ]/u; +var r = /[\u0041\u0301]/; +var r = /[\u0041\u0301]/u; +var r = /[\u{41}\u{301}]/u; var r = /[โ‡๏ธ]/; var r = /[โ‡๏ธ]/u; +var r = /[\u2747\uFE0F]/; +var r = /[\u2747\uFE0F]/u; +var r = /[\u{2747}\u{FE0F}]/u; var r = /[๐Ÿ‘ถ๐Ÿป]/; var r = /[๐Ÿ‘ถ๐Ÿป]/u; +var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; +var r = /[\u{1F476}\u{1F3FB}]/u; var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; +var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; +var r = /[\u{1F1EF}\u{1F1F5}]/u; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/; -var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; \ No newline at end of file +var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; +var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u +var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap index f0b3b9ac6074..f5a111e1992e 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap @@ -10,15 +10,27 @@ var r = /[๐Ÿ‘]\\a/; var r = /(?<=[๐Ÿ‘])/; var r = /[Aฬ]/; var r = /[Aฬ]/u; +var r = /[\u0041\u0301]/; +var r = /[\u0041\u0301]/u; +var r = /[\u{41}\u{301}]/u; var r = /[โ‡๏ธ]/; var r = /[โ‡๏ธ]/u; +var r = /[\u2747\uFE0F]/; +var r = /[\u2747\uFE0F]/u; +var r = /[\u{2747}\u{FE0F}]/u; var r = /[๐Ÿ‘ถ๐Ÿป]/; var r = /[๐Ÿ‘ถ๐Ÿป]/u; +var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; +var r = /[\u{1F476}\u{1F3FB}]/u; var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; +var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; +var r = /[\u{1F1EF}\u{1F1F5}]/u; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; +var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u +var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u ``` # Diagnostics @@ -105,7 +117,7 @@ invalid.js:5:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” > 5 โ”‚ var r = /[A๏ฟฝ]/; โ”‚ ^^^^^ 6 โ”‚ var r = /[A๏ฟฝ]/u; - 7 โ”‚ var r = /[โ‡๏ฟฝ]/; + 7 โ”‚ var r = /[\u0041\u0301]/; ``` @@ -119,8 +131,8 @@ invalid.js:6:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” 5 โ”‚ var r = /[A๏ฟฝ]/; > 6 โ”‚ var r = /[A๏ฟฝ]/u; โ”‚ ^^^^^^ - 7 โ”‚ var r = /[โ‡๏ฟฝ]/; - 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + 7 โ”‚ var r = /[\u0041\u0301]/; + 8 โ”‚ var r = /[\u0041\u0301]/u; ``` @@ -132,10 +144,10 @@ invalid.js:7:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” 5 โ”‚ var r = /[A๏ฟฝ]/; 6 โ”‚ var r = /[A๏ฟฝ]/u; - > 7 โ”‚ var r = /[โ‡๏ฟฝ]/; - โ”‚ ^^^^^ - 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; - 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + > 7 โ”‚ var r = /[\u0041\u0301]/; + โ”‚ ^^^^^^^^^^^^^^^^ + 8 โ”‚ var r = /[\u0041\u0301]/u; + 9 โ”‚ var r = /[\u{41}\u{301}]/u; ``` @@ -146,134 +158,314 @@ invalid.js:8:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected combined character in character class. 6 โ”‚ var r = /[A๏ฟฝ]/u; - 7 โ”‚ var r = /[โ‡๏ฟฝ]/; - > 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; + 7 โ”‚ var r = /[\u0041\u0301]/; + > 8 โ”‚ var r = /[\u0041\u0301]/u; + โ”‚ ^^^^^^^^^^^^^^^^^ + 9 โ”‚ var r = /[\u{41}\u{301}]/u; + 10 โ”‚ var r = /[โ‡๏ฟฝ]/; + + +``` + +``` +invalid.js:9:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 7 โ”‚ var r = /[\u0041\u0301]/; + 8 โ”‚ var r = /[\u0041\u0301]/u; + > 9 โ”‚ var r = /[\u{41}\u{301}]/u; + โ”‚ ^^^^^^^^^^^^^^^^^^ + 10 โ”‚ var r = /[โ‡๏ฟฝ]/; + 11 โ”‚ var r = /[โ‡๏ฟฝ]/u; + + +``` + +``` +invalid.js:10:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 8 โ”‚ var r = /[\u0041\u0301]/u; + 9 โ”‚ var r = /[\u{41}\u{301}]/u; + > 10 โ”‚ var r = /[โ‡๏ฟฝ]/; + โ”‚ ^^^^^ + 11 โ”‚ var r = /[โ‡๏ฟฝ]/u; + 12 โ”‚ var r = /[\u2747\uFE0F]/; + + +``` + +``` +invalid.js:11:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 9 โ”‚ var r = /[\u{41}\u{301}]/u; + 10 โ”‚ var r = /[โ‡๏ฟฝ]/; + > 11 โ”‚ var r = /[โ‡๏ฟฝ]/u; โ”‚ ^^^^^^ - 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; - 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 12 โ”‚ var r = /[\u2747\uFE0F]/; + 13 โ”‚ var r = /[\u2747\uFE0F]/u; + + +``` + +``` +invalid.js:12:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 10 โ”‚ var r = /[โ‡๏ฟฝ]/; + 11 โ”‚ var r = /[โ‡๏ฟฝ]/u; + > 12 โ”‚ var r = /[\u2747\uFE0F]/; + โ”‚ ^^^^^^^^^^^^^^^^ + 13 โ”‚ var r = /[\u2747\uFE0F]/u; + 14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u; + + +``` + +``` +invalid.js:13:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 11 โ”‚ var r = /[โ‡๏ฟฝ]/u; + 12 โ”‚ var r = /[\u2747\uFE0F]/; + > 13 โ”‚ var r = /[\u2747\uFE0F]/u; + โ”‚ ^^^^^^^^^^^^^^^^^ + 14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u; + 15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + + +``` + +``` +invalid.js:14:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 12 โ”‚ var r = /[\u2747\uFE0F]/; + 13 โ”‚ var r = /[\u2747\uFE0F]/u; + > 14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u; + โ”‚ ^^^^^^^^^^^^^^^^^^^^^ + 15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + 16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; ``` ``` -invalid.js:9:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:15:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. - 7 โ”‚ var r = /[โ‡๏ฟฝ]/; - 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; - > 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + 13 โ”‚ var r = /[\u2747\uFE0F]/u; + 14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u; + > 15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; โ”‚ ^^^^^^^^ - 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; - 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; i Unsafe fix: Add unicode u flag to regex - 9 โ”‚ varยทrยท=ยท/[๐Ÿ‘ถ๐Ÿป]/u; - โ”‚ + + 15 โ”‚ varยทrยท=ยท/[๐Ÿ‘ถ๐Ÿป]/u; + โ”‚ + ``` ``` -invalid.js:10:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:16:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected modified Emoji in character class. - 8 โ”‚ var r = /[โ‡๏ฟฝ]/u; - 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; - > 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u; + 15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + > 16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; โ”‚ ^^^^^^^^^ - 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; - 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; + 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; + + +``` + +``` +invalid.js:17:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; + 16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + > 17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; + 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + + +``` + +``` +invalid.js:18:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; + 17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; + > 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ + 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; ``` ``` -invalid.js:11:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:19:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. - 9 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/; - 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; - > 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; + 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; + > 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; โ”‚ ^^^^^^ - 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; - 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; i Unsafe fix: Add unicode u flag to regex - 11 โ”‚ varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 19 โ”‚ varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/u; โ”‚ + ``` ``` -invalid.js:12:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:20:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. - 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; - 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; - > 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; + 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + > 20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; โ”‚ ^^^^^^^ - 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; - 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; i Unsafe fix: Add unicode u flag to regex - 10 10 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; - 11 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; - 12 โ”‚ - varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/i; - 12 โ”‚ + varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/iu; - 13 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; - 14 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + 18 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; + 19 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 20 โ”‚ - varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 20 โ”‚ + varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/iu; + 21 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 22 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; ``` ``` -invalid.js:13:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:21:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Regional indicator symbol characters should not be used in character class. - 11 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; - 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; - > 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; + 20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + > 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; โ”‚ ^^^^^^^ - 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; - 15 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; + 23 โ”‚ var r = /[\u{1F1EF}\u{1F1F5}]/u; ``` ``` -invalid.js:14:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:22:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Regional indicator symbol characters should not be used in character class. + + 20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; + 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + > 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 23 โ”‚ var r = /[\u{1F1EF}\u{1F1F5}]/u; + 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + + +``` + +``` +invalid.js:23:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Regional indicator symbol characters should not be used in character class. + + 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; + 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; + > 23 โ”‚ var r = /[\u{1F1EF}\u{1F1F5}]/u; + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ + 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + + +``` + +``` +invalid.js:24:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. - 12 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; - 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; - > 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; + 23 โ”‚ var r = /[\u{1F1EF}\u{1F1F5}]/u; + > 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; โ”‚ ^^^^^^^^^^ - 15 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u i Unsafe fix: Add unicode u flag to regex - 14 โ”‚ varยทrยท=ยท/[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + 24 โ”‚ varยทrยท=ยท/[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; โ”‚ + ``` ``` -invalid.js:15:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:25:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected joined character sequence in character class. - 13 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; - 14 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; - > 15 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + 23 โ”‚ var r = /[\u{1F1EF}\u{1F1F5}]/u; + 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + > 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; โ”‚ ^^^^^^^^^^^ + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u + 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u + + +``` + +``` +invalid.js:26:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; + 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + > 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u + + +``` + +``` +invalid.js:27:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u + > 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js index 1cc20aa4ce3e..60dc08d13212 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js @@ -10,3 +10,26 @@ var r = /๐Ÿ‡ฏ๐Ÿ‡ต/; var r = /[JP]/; var r = /๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ/; +// Ignore solo lead/tail surrogate. +var r = /[\\uD83D]/; +var r = /[\\uDC4D]/; +var r = /[\\uD83D]/u; +var r = /[\\uDC4D]/u; + +// Ignore solo combining char. +var r = /[\\u0301]/; +var r = /[\\uFE0F]/; +var r = /[\\u0301]/u; +var r = /[\\uFE0F]/u; + +// Ignore solo emoji modifier. +var r = /[\\u{1F3FB}]/u; +var r = /[\u{1F3FB}]/u; + +// Ignore solo regional indicator symbol. +var r = /[๐Ÿ‡ฏ]/u; +var r = /[๐Ÿ‡ต]/u; + +// Ignore solo ZWJ. +var r = /[\\u200D]/; +var r = /[\\u200D]/u; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap index 0045ad167d4f..efc4cc183c40 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap @@ -16,7 +16,29 @@ var r = /๐Ÿ‡ฏ๐Ÿ‡ต/; var r = /[JP]/; var r = /๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ/; +// Ignore solo lead/tail surrogate. +var r = /[\\uD83D]/; +var r = /[\\uDC4D]/; +var r = /[\\uD83D]/u; +var r = /[\\uDC4D]/u; +// Ignore solo combining char. +var r = /[\\u0301]/; +var r = /[\\uFE0F]/; +var r = /[\\u0301]/u; +var r = /[\\uFE0F]/u; + +// Ignore solo emoji modifier. +var r = /[\\u{1F3FB}]/u; +var r = /[\u{1F3FB}]/u; + +// Ignore solo regional indicator symbol. +var r = /[๐Ÿ‡ฏ]/u; +var r = /[๐Ÿ‡ต]/u; + +// Ignore solo ZWJ. +var r = /[\\u200D]/; +var r = /[\\u200D]/u; ``` From c8b3e69743220136b3df93a975d6cbbf0d8b408b Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 02:04:58 +0900 Subject: [PATCH 05/19] feat: handle RegExp --- .../nursery/no_misleading_character_class.rs | 353 ++++++++--- .../noMisleadingCharacterClass/invalid.js | 35 +- .../invalid.js.snap | 595 +++++++++++++++++- 3 files changed, 877 insertions(+), 106 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index f1a64c81602c..63749392b1eb 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -2,10 +2,15 @@ use crate::{semantic_services::Semantic, JsRuleAction}; use biome_analyze::{context::RuleContext, declare_rule, ActionCategory, Rule, RuleDiagnostic}; use biome_console::markup; use biome_diagnostics::Applicability; +use biome_js_factory::make; use biome_js_syntax::{ - JsCallExpression, JsNewExpression, JsRegexLiteralExpression, JsSyntaxKind, JsSyntaxToken, + AnyJsCallArgument, AnyJsExpression, AnyJsLiteralExpression, AnyJsTemplateElement, + JsCallArguments, JsCallExpression, JsNewExpression, JsRegexLiteralExpression, + JsStringLiteralExpression, JsSyntaxKind, JsSyntaxToken, T, +}; +use biome_rowan::{ + declare_node_union, AstNode, AstNodeList, AstSeparatedList, BatchMutationExt, TextRange, }; -use biome_rowan::{declare_node_union, AstNode, BatchMutationExt, TextRange}; declare_rule! { /// Disallow characters which are made with multiple code points in character class syntax /// @@ -91,68 +96,64 @@ impl Rule for NoMisleadingCharacterClass { let Ok((pattern, flags)) = expr.decompose() else { return None; }; - let regex_literal = replace_escaped_unicode(pattern.text()); + let regex_pattern = replace_escaped_unicode(pattern.text()); let has_u_flag = flags.text().contains('u'); - let mut is_in_character_class = false; - let mut escape_next = false; - let char_iter = regex_literal.chars().peekable(); - for (i, ch) in char_iter.enumerate() { - if escape_next { - escape_next = false; - continue; - } - match ch { - '\\' => escape_next = true, - '[' => is_in_character_class = true, - ']' => is_in_character_class = false, - _ if is_in_character_class && i < regex_literal.len() => { - if !has_u_flag && has_surrogate_pair(®ex_literal[i..]) { - return Some(RuleState { - range: expr.syntax().text_range(), - message: Message::SurrogatePairWithoutUFlag, - }); - } - - if has_combining_class_or_vs16(®ex_literal[i..]) { - return Some(RuleState { - range: expr.syntax().text_range(), - message: Message::CombiningClassOrVs16, - }); - } - - if has_regional_indicator_symbol(®ex_literal[i..]) { - return Some(RuleState { - range: expr.syntax().text_range(), - message: Message::RegionalIndicatorSymbol, - }); - } - - if has_emoji_modifier(®ex_literal[i..]) { - return Some(RuleState { - range: expr.syntax().text_range(), - message: Message::EmojiModifier, - }); - } - - if zwj(®ex_literal[i..]) { - return Some(RuleState { - range: expr.syntax().text_range(), - message: Message::JoinedCharSequence, - }); - } - } - _ => {} - } - } - return None; + let range = expr.syntax().text_range(); + return diagnostic_regex_pattern(®ex_pattern, has_u_flag, range); } - AnyRegexExpression::JsNewExpression(x) => { - todo!(); + AnyRegexExpression::JsNewExpression(expr) => { + let callee = match expr.callee().ok()? { + AnyJsExpression::JsIdentifierExpression(callee) => callee, + _ => return None, + }; + if callee.name().ok()?.has_name("RegExp") { + let mut args = expr.arguments()?.args().iter(); + let raw_regex_pattern = args + .next() + .and_then(|arg| arg.ok()) + .and_then(|arg| JsStringLiteralExpression::cast_ref(arg.syntax())) + .and_then(|js_string_literal| js_string_literal.inner_string_text().ok())? + .to_string(); + + let regex_pattern = replace_escaped_unicode(raw_regex_pattern.as_str()); + let regexp_flags = args + .next() + .and_then(|arg| arg.ok()) + .and_then(|arg| JsStringLiteralExpression::cast_ref(arg.syntax())) + .map(|js_string_literal| js_string_literal.text()); + + let has_u_flag = regexp_flags.unwrap_or_default().contains('u'); + let range = expr.syntax().text_range(); + return diagnostic_regex_pattern(®ex_pattern, has_u_flag, range); + } } - - AnyRegexExpression::JsCallExpression(x) => { - todo!(); + AnyRegexExpression::JsCallExpression(expr) => { + let callee = match expr.callee().ok()? { + AnyJsExpression::JsIdentifierExpression(callee) => callee, + _ => return None, + }; + if callee.name().ok()?.has_name("RegExp") { + let mut args = expr.arguments().ok()?.args().iter(); + let raw_regex_pattern = args + .next() + .and_then(|arg| arg.ok()) + .and_then(|arg| JsStringLiteralExpression::cast_ref(arg.syntax())) + .and_then(|js_string_literal| js_string_literal.inner_string_text().ok())? + .to_string(); + + let regex_pattern = replace_escaped_unicode(raw_regex_pattern.as_str()); + + let regexp_flags = args + .next() + .and_then(|arg| arg.ok()) + .and_then(|arg| JsStringLiteralExpression::cast_ref(arg.syntax())) + .map(|js_string_literal| js_string_literal.text()); + + let has_u_flag = regexp_flags.unwrap_or_default().contains('u'); + let range = expr.syntax().text_range(); + return diagnostic_regex_pattern(®ex_pattern, has_u_flag, range); + } } } None @@ -168,9 +169,10 @@ impl Rule for NoMisleadingCharacterClass { fn action(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); - match node { - AnyRegexExpression::JsRegexLiteralExpression(expr) => { - if matches!(state.message, Message::SurrogatePairWithoutUFlag) { + let is_fixable = matches!(state.message, Message::SurrogatePairWithoutUFlag); + if is_fixable { + match node { + AnyRegexExpression::JsRegexLiteralExpression(expr) => { let prev_token = expr.value_token().ok()?; let text = prev_token.text(); let next_token = JsSyntaxToken::new_detached( @@ -190,18 +192,180 @@ impl Rule for NoMisleadingCharacterClass { .to_owned(), mutation, }) - } else { - None + } + + AnyRegexExpression::JsNewExpression(expr) => { + let prev_node = expr.arguments()?; + let mut prev_args = prev_node.args().iter(); + + let regex_pattern = prev_args.next().and_then(|a| a.ok())?; + let flag = prev_args.next().and_then(|a| a.ok())?; + + match make_suggestion(regex_pattern, flag) { + Some(suggest) => { + let mut mutation = ctx.root().begin(); + mutation.replace_node(prev_node, suggest); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Add unicode ""u"" flag to regex" } + .to_owned(), + mutation, + }) + } + None => None, + } + } + + AnyRegexExpression::JsCallExpression(expr) => { + let prev_node = expr.arguments().ok()?; + let mut prev_args = expr.arguments().ok()?.args().iter(); + + let regex_pattern = prev_args.next().and_then(|a| a.ok())?; + let flag = prev_args.next().and_then(|a| a.ok())?; + + match make_suggestion(regex_pattern, flag) { + Some(suggest) => { + let mut mutation = ctx.root().begin(); + mutation.replace_node(prev_node, suggest); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Add unicode ""u"" flag to regex" } + .to_owned(), + mutation, + }) + } + None => None, + } } } + } else { + None + } + } +} - AnyRegexExpression::JsNewExpression(_) => todo!(), +fn diagnostic_regex_pattern( + regex_pattern: &str, + has_u_flag: bool, + range: TextRange, +) -> Option { + let mut is_in_character_class = false; + let mut escape_next = false; + let char_iter = regex_pattern.chars().peekable(); + for (i, ch) in char_iter.enumerate() { + if escape_next { + escape_next = false; + continue; + } + match ch { + '\\' => escape_next = true, + '[' => is_in_character_class = true, + ']' => is_in_character_class = false, + _ if is_in_character_class && i < regex_pattern.len() => { + if !has_u_flag && has_surrogate_pair(®ex_pattern[i..]) { + return Some(RuleState { + range, + message: Message::SurrogatePairWithoutUFlag, + }); + } - AnyRegexExpression::JsCallExpression(_) => todo!(), + if has_combining_class_or_vs16(®ex_pattern[i..]) { + return Some(RuleState { + range, + message: Message::CombiningClassOrVs16, + }); + } - _ => None, + if has_regional_indicator_symbol(®ex_pattern[i..]) { + return Some(RuleState { + range, + message: Message::RegionalIndicatorSymbol, + }); + } + + if has_emoji_modifier(®ex_pattern[i..]) { + return Some(RuleState { + range, + message: Message::EmojiModifier, + }); + } + + if zwj(®ex_pattern[i..]) { + return Some(RuleState { + range, + message: Message::JoinedCharSequence, + }); + } + } + _ => {} } } + None +} + +fn make_suggestion(literal: AnyJsCallArgument, flag: AnyJsCallArgument) -> Option { + let suggest = match flag { + AnyJsCallArgument::AnyJsExpression(expr) => match expr { + AnyJsExpression::AnyJsLiteralExpression(e) => { + let text = e.text(); + if text.starts_with('\'') { + Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(make::js_string_literal( + &format!("'{}u'", text), + )), + ), + ), + )) + } else { + Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(make::js_string_literal( + &format!("{}u", text.replace('"', "")), + )), + ), + ), + )) + } + } + AnyJsExpression::JsTemplateExpression(expr) => { + let mut elements = expr + .elements() + .iter() + .collect::>(); + + let uflag = AnyJsTemplateElement::from(make::js_template_chunk_element( + make::js_template_chunk("u"), + )); + elements.push(uflag); + Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::JsTemplateExpression( + make::js_template_expression( + make::token(T!['`']), + make::js_template_element_list(elements), + make::token(T!['`']), + ) + .build(), + ), + )) + } + AnyJsExpression::JsIdentifierExpression(_) => None, + _ => None, + }, + AnyJsCallArgument::JsSpread(_) => None, + }; + + suggest.map(|s| { + make::js_call_arguments( + make::token(T!['(']), + make::js_call_argument_list([literal, s], [make::token(T![,])]), + make::token(T![')']), + ) + }) } fn is_emoji_modifier(code: u32) -> bool { @@ -271,10 +435,6 @@ fn has_surrogate_pair(s: &str) -> bool { s.chars().any(|c| c as u32 > 0xFFFF) } -fn is_unicode_char(ch: char) -> bool { - ch.is_ascii_digit() || ('a'..='f').contains(&ch) || ('A'..='F').contains(&ch) -} - /// Convert unicode escape sequence string to unicode character /// - unicode escape sequences: \u{XXXX} /// - unicode escape sequences without parenthesis: \uXXXX @@ -345,31 +505,35 @@ fn handle_simple_or_surrogate_escape_sequence( let mut high_surrogate_str = String::new(); for _ in 0..4 { - if let Some(next_char) = chars_iter.peek() { - if !is_unicode_char(*next_char) { - // Return as simple string. This is not unicode + if let Some(&next_char) = chars_iter.peek() { + if next_char.is_ascii_hexdigit() { + high_surrogate_str.push(next_char); + chars_iter.next(); + } else { + // If the character is not a valid Unicode char, return as simple string. return Some(format!("\\u{}", high_surrogate_str)); } - high_surrogate_str.push(*next_char); - chars_iter.next(); } else { - // Return as simple string. This is not unicode + // If not enough characters, return as if it were a simple string. return Some(format!("\\u{}", high_surrogate_str)); } } if let Ok(high_surrogate) = u32::from_str_radix(&high_surrogate_str, 16) { - // check if it is in the high surrogate range(0xD800-0xDBFF) in UTF-16. + // Check if it is in the high surrogate range(0xD800-0xDBFF) in UTF-16. if (0xD800..=0xDBFF).contains(&high_surrogate) { - // Handle surrogate pair + // If we have a high surrogate, expect a low surrogate next if chars_iter.next() == Some('\\') && chars_iter.next() == Some('u') { let mut low_surrogate_str = String::new(); for _ in 0..4 { if let Some(next_char) = chars_iter.peek() { - if !is_unicode_char(*next_char) { - // high_surrogate is the correct codepoint but treating low_surrogate_str a string because it is not a valid unicode codepoint - invalid_pair.push_str(&format!("{high_surrogate}")); - invalid_pair.push_str(&low_surrogate_str); + if !next_char.is_ascii_hexdigit() { + // Return as a simple string + // - high surrogate on its own doesn't make sense + // - low surrogate is not a valid unicode codepoint + // e.g \uD83D\u333 + invalid_pair.push_str(&format!("\\u{}", high_surrogate_str)); + invalid_pair.push_str(&format!("\\u{}", low_surrogate_str)); return Some(invalid_pair); } low_surrogate_str.push(*next_char); @@ -377,27 +541,22 @@ fn handle_simple_or_surrogate_escape_sequence( } } if let Ok(low_surrogate) = u32::from_str_radix(&low_surrogate_str, 16) { - // check if it is in the low surrogate range(0xDC00-0xDFFF) in UTF-16. + // Check if it is in the low surrogate range(0xDC00-0xDFFF) in UTF-16. if (0xDC00..=0xDFFF).contains(&low_surrogate) { - // calculate the codepoint from the surrogate pair + // Calculate the codepoint from the surrogate pair let codepoint = ((high_surrogate - 0xD800) << 10) + (low_surrogate - 0xDC00) + 0x10000; return char::from_u32(codepoint).map(|c| c.to_string()); }; - } else { - // high_surrogate is the correct codepoint but treating low_surrogate_str a string because it is not a valid unicode codepoint - invalid_pair.push_str(&format!("{}", high_surrogate)); - invalid_pair.push_str(&format!("\\u{}", low_surrogate_str)); } } } else { - // Handle standard escape sequence - return char::from_u32(high_surrogate).map(|c| c.to_string()); + match char::from_u32(high_surrogate) { + Some(c) => return Some(c.to_string()), + None => invalid_pair.push_str(&format!("\\u{}", high_surrogate_str)), + } } - } else { - invalid_pair.push_str(&format!("\\u{}", high_surrogate_str)); } - // return as simple string, not unicode Some(invalid_pair) } @@ -420,5 +579,9 @@ mod tests { replace_escaped_unicode(r#"/[\u899\uD83D\uDC4D]/"#), r#"/[\u899๐Ÿ‘]/"# ); + assert_eq!( + replace_escaped_unicode(r#"/[\u899\uD83D\u899\uDC4D]/"#), + r#"/[\u899\uD83D\u899\uDC4D]/"# + ); } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js index 8a0f944a288b..92dfa11c78cc 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js @@ -23,5 +23,36 @@ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; var r = /[\u{1F1EF}\u{1F1F5}]/u; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; -var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u -var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u \ No newline at end of file +var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; +var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; +var r = new RegExp("[๐Ÿ‘]", ""); +var r = new RegExp('[๐Ÿ‘]', ``); +var r = new RegExp("[๐Ÿ‘]", flags); +var r = new RegExp("[\uD83D\uDC4D]", ""); +var r = new RegExp("/(?<=[๐Ÿ‘])", ""); +var r = new RegExp("[Aฬ]", ""); +var r = new RegExp("[Aฬ]", "u"); +var r = new RegExp("[\u0041\u0301]", ""); +var r = new RegExp("[\u0041\u0301]", "u"); +var r = new RegExp("[\u{41}\u{301}]", "u"); +var r = new RegExp("[โ‡๏ธ]", ""); +var r = new RegExp("[โ‡๏ธ]", "u"); +var r = new RegExp("[\u2747\uFE0F]", ""); +var r = new RegExp("[\u2747\uFE0F]", "u"); +var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); +var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); +var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); +var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); +var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); +var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); +var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); +var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); +var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", ""); +var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", "u"); +var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); +var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap index f5a111e1992e..da4f63108554 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap @@ -29,8 +29,39 @@ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; var r = /[\u{1F1EF}\u{1F1F5}]/u; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/; var r = /[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]/u; -var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u -var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u +var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; +var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; +var r = new RegExp("[๐Ÿ‘]", ""); +var r = new RegExp('[๐Ÿ‘]', ``); +var r = new RegExp("[๐Ÿ‘]", flags); +var r = new RegExp("[\uD83D\uDC4D]", ""); +var r = new RegExp("/(?<=[๐Ÿ‘])", ""); +var r = new RegExp("[Aฬ]", ""); +var r = new RegExp("[Aฬ]", "u"); +var r = new RegExp("[\u0041\u0301]", ""); +var r = new RegExp("[\u0041\u0301]", "u"); +var r = new RegExp("[\u{41}\u{301}]", "u"); +var r = new RegExp("[โ‡๏ธ]", ""); +var r = new RegExp("[โ‡๏ธ]", "u"); +var r = new RegExp("[\u2747\uFE0F]", ""); +var r = new RegExp("[\u2747\uFE0F]", "u"); +var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); +var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); +var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); +var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); +var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); +var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); +var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); +var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); +var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); +var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", ""); +var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", "u"); +var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); +var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); ``` # Diagnostics @@ -419,7 +450,7 @@ invalid.js:24:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” > 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; โ”‚ ^^^^^^^^^^ 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; - 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; i Unsafe fix: Add unicode u flag to regex @@ -437,8 +468,8 @@ invalid.js:25:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; > 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; โ”‚ ^^^^^^^^^^^ - 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u - 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; + 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; ``` @@ -450,9 +481,10 @@ invalid.js:26:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” 24 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/; 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; - > 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u + > 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u + 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; + 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); ``` @@ -463,9 +495,554 @@ invalid.js:27:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected joined character sequence in character class. 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; - 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u - > 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; + > 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); + 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + + +``` + +``` +invalid.js:28:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; + 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; + > 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^ + 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + + i Unsafe fix: Add unicode u flag to regex + + 26 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; + 27 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; + 28 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‘]",ยท""); + 28 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‘]","u"); + 29 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + 30 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + + +``` + +``` +invalid.js:29:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; + 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); + > 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^ + 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + + i Unsafe fix: Add unicode u flag to regex + + 27 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; + 28 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); + 29 โ”‚ - varยทrยท=ยทnewยทRegExp('[๐Ÿ‘]',ยท``); + 29 โ”‚ + varยทrยท=ยทnewยทRegExp('[๐Ÿ‘]',`u`); + 30 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + 31 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + + +``` + +``` +invalid.js:30:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); + 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + > 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^ + 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); + + +``` + +``` +invalid.js:31:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + > 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); + 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + + i Unsafe fix: Add unicode u flag to regex + + 29 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); + 30 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + 31 โ”‚ - varยทrยท=ยทnewยทRegExp("[\uD83D\uDC4D]",ยท""); + 31 โ”‚ + varยทrยท=ยทnewยทRegExp("[\uD83D\uDC4D]","u"); + 32 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); + 33 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + + +``` + +``` +invalid.js:32:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + > 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); + + i Unsafe fix: Add unicode u flag to regex + + 30 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); + 31 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + 32 โ”‚ - varยทrยท=ยทnewยทRegExp("/(?<=[๐Ÿ‘])",ยท""); + 32 โ”‚ + varยทrยท=ยทnewยทRegExp("/(?<=[๐Ÿ‘])","u"); + 33 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + 34 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); + + +``` + +``` +invalid.js:33:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); + 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); + > 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^ + 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); + 35 โ”‚ var r = new RegExp("[\u0041\u0301]", ""); + + +``` + +``` +invalid.js:34:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); + 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + > 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^ + 35 โ”‚ var r = new RegExp("[\u0041\u0301]", ""); + 36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u"); + + +``` + +``` +invalid.js:35:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); + 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); + > 35 โ”‚ var r = new RegExp("[\u0041\u0301]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u"); + 37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u"); + + +``` + +``` +invalid.js:36:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); + 35 โ”‚ var r = new RegExp("[\u0041\u0301]", ""); + > 36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u"); + 38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", ""); + + +``` + +``` +invalid.js:37:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 35 โ”‚ var r = new RegExp("[\u0041\u0301]", ""); + 36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u"); + > 37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", ""); + 39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u"); + + +``` + +``` +invalid.js:38:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u"); + 37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u"); + > 38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^ + 39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u"); + 40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", ""); + + +``` + +``` +invalid.js:39:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u"); + 38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", ""); + > 39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^ + 40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", ""); + 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); + + +``` + +``` +invalid.js:40:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", ""); + 39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u"); + > 40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); + 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); + + +``` + +``` +invalid.js:41:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u"); + 40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", ""); + > 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); + 43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); + + +``` + +``` +invalid.js:42:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", ""); + 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); + > 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); + 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + + +``` + +``` +invalid.js:43:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); + 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); + > 43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^ + 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + + i Unsafe fix: Add unicode u flag to regex + + 41 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); + 42 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); + 43 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‘ถ๐Ÿป]",ยท""); + 43 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‘ถ๐Ÿป]","u"); + 44 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + 45 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + + +``` + +``` +invalid.js:44:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); + 43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); + > 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^ + 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + + +``` + +``` +invalid.js:45:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", ""); + 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + > 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + + +``` + +``` +invalid.js:46:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + > 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + + +``` + +``` +invalid.js:47:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + > 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^ + 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + + i Unsafe fix: Add unicode u flag to regex + + 45 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); + 46 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + 47 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",ยท""); + 47 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","u"); + 48 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + 49 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + + +``` + +``` +invalid.js:48:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + > 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ + 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + + i Unsafe fix: Add unicode u flag to regex + + 46 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); + 47 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + 48 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",ยท"i"); + 48 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","iu"); + 49 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + 50 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + + +``` + +``` +invalid.js:49:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + > 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ + 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + + i Unsafe fix: Add unicode u flag to regex + + 47 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + 48 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + 49 โ”‚ - varยทrยท=ยทnewยทRegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]',ยท`i`); + 49 โ”‚ + varยทrยท=ยทnewยทRegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]',`iu`); + 50 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 51 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + + +``` + +``` +invalid.js:50:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + > 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + โ”‚ ^^^^^^^^^^^^^^^^^^ + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + + +``` + +``` +invalid.js:51:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + > 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + โ”‚ ^^^^^^^^^^^^^^^^^^^ + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + + +``` + +``` +invalid.js:52:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Regional indicator symbol characters should not be used in character class. + + 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + > 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ + 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + + +``` + +``` +invalid.js:53:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Regional indicator symbol characters should not be used in character class. + + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + > 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + + +``` + +``` +invalid.js:54:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Regional indicator symbol characters should not be used in character class. + + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + > 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + + +``` + +``` +invalid.js:55:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + > 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^ + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + + i Unsafe fix: Add unicode u flag to regex + + 53 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 54 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 55 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]",ยท""); + 55 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]","u"); + 56 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 57 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + + +``` + +``` +invalid.js:56:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + > 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + 58 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + + +``` + +``` +invalid.js:57:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + > 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 58 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + + +``` + +``` +invalid.js:58:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + > 58 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` From 3945e6b3ff7a5376c134bad6ff83adf300543fb5 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 02:24:21 +0900 Subject: [PATCH 06/19] feat: handle trailing --- .../nursery/no_misleading_character_class.rs | 110 +++++++------ .../noMisleadingCharacterClass/invalid.js | 1 + .../invalid.js.snap | 155 +++++++++++------- 3 files changed, 156 insertions(+), 110 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index 63749392b1eb..b2f70813ec54 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -199,7 +199,7 @@ impl Rule for NoMisleadingCharacterClass { let mut prev_args = prev_node.args().iter(); let regex_pattern = prev_args.next().and_then(|a| a.ok())?; - let flag = prev_args.next().and_then(|a| a.ok())?; + let flag = prev_args.next().and_then(|a| a.ok()); match make_suggestion(regex_pattern, flag) { Some(suggest) => { @@ -222,7 +222,7 @@ impl Rule for NoMisleadingCharacterClass { let mut prev_args = expr.arguments().ok()?.args().iter(); let regex_pattern = prev_args.next().and_then(|a| a.ok())?; - let flag = prev_args.next().and_then(|a| a.ok())?; + let flag = prev_args.next().and_then(|a| a.ok()); match make_suggestion(regex_pattern, flag) { Some(suggest) => { @@ -305,61 +305,73 @@ fn diagnostic_regex_pattern( None } -fn make_suggestion(literal: AnyJsCallArgument, flag: AnyJsCallArgument) -> Option { - let suggest = match flag { - AnyJsCallArgument::AnyJsExpression(expr) => match expr { - AnyJsExpression::AnyJsLiteralExpression(e) => { - let text = e.text(); - if text.starts_with('\'') { - Some(AnyJsCallArgument::AnyJsExpression( - AnyJsExpression::AnyJsLiteralExpression( - AnyJsLiteralExpression::JsStringLiteralExpression( - make::js_string_literal_expression(make::js_string_literal( - &format!("'{}u'", text), - )), +fn make_suggestion( + literal: AnyJsCallArgument, + flag: Option, +) -> Option { + let suggestion = match flag { + None => Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(make::js_string_literal("u")), + ), + ), + )), + Some(f) => match f { + AnyJsCallArgument::AnyJsExpression(expr) => match expr { + AnyJsExpression::AnyJsLiteralExpression(e) => { + let text = e.text(); + if text.starts_with('\'') { + Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(make::js_string_literal( + &format!("'{}u'", text), + )), + ), ), - ), - )) - } else { - Some(AnyJsCallArgument::AnyJsExpression( - AnyJsExpression::AnyJsLiteralExpression( - AnyJsLiteralExpression::JsStringLiteralExpression( - make::js_string_literal_expression(make::js_string_literal( - &format!("{}u", text.replace('"', "")), - )), + )) + } else { + Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::AnyJsLiteralExpression( + AnyJsLiteralExpression::JsStringLiteralExpression( + make::js_string_literal_expression(make::js_string_literal( + &format!("{}u", text.replace('"', "")), + )), + ), ), + )) + } + } + AnyJsExpression::JsTemplateExpression(expr) => { + let mut elements = expr + .elements() + .iter() + .collect::>(); + + let uflag = AnyJsTemplateElement::from(make::js_template_chunk_element( + make::js_template_chunk("u"), + )); + elements.push(uflag); + Some(AnyJsCallArgument::AnyJsExpression( + AnyJsExpression::JsTemplateExpression( + make::js_template_expression( + make::token(T!['`']), + make::js_template_element_list(elements), + make::token(T!['`']), + ) + .build(), ), )) } - } - AnyJsExpression::JsTemplateExpression(expr) => { - let mut elements = expr - .elements() - .iter() - .collect::>(); - - let uflag = AnyJsTemplateElement::from(make::js_template_chunk_element( - make::js_template_chunk("u"), - )); - elements.push(uflag); - Some(AnyJsCallArgument::AnyJsExpression( - AnyJsExpression::JsTemplateExpression( - make::js_template_expression( - make::token(T!['`']), - make::js_template_element_list(elements), - make::token(T!['`']), - ) - .build(), - ), - )) - } - AnyJsExpression::JsIdentifierExpression(_) => None, - _ => None, + AnyJsExpression::JsIdentifierExpression(_) => None, + _ => None, + }, + AnyJsCallArgument::JsSpread(_) => None, }, - AnyJsCallArgument::JsSpread(_) => None, }; - suggest.map(|s| { + suggestion.map(|s| { make::js_call_arguments( make::token(T!['(']), make::js_call_argument_list([literal, s], [make::token(T![,])]), diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js index 92dfa11c78cc..c1ab35ebadf2 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js @@ -47,6 +47,7 @@ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); +var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap index da4f63108554..d0e4ad3ed961 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap @@ -53,6 +53,7 @@ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); +var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); @@ -868,7 +869,7 @@ invalid.js:48:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” > 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); - 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); i Unsafe fix: Add unicode u flag to regex @@ -877,7 +878,7 @@ invalid.js:48:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 48 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",ยท"i"); 48 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","iu"); 49 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); - 50 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 50 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); ``` @@ -891,8 +892,8 @@ invalid.js:49:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); > 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ - 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); - 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); i Unsafe fix: Add unicode u flag to regex @@ -900,148 +901,180 @@ invalid.js:49:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 48 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); 49 โ”‚ - varยทrยท=ยทnewยทRegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]',ยท`i`); 49 โ”‚ + varยทrยท=ยทnewยทRegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]',`iu`); - 50 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); - 51 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 50 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); + 51 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); ``` ``` -invalid.js:50:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:50:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); - > 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); - โ”‚ ^^^^^^^^^^^^^^^^^^ - 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); - 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + > 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + + i Unsafe fix: Add unicode u flag to regex + + 48 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); + 49 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); + 50 โ”‚ - varยทrยท=ยทnewยทRegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]',ยท`${foo}`); + 50 โ”‚ + varยทrยท=ยทnewยทRegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]',`${foo}u`); + 51 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 52 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); ``` ``` -invalid.js:51:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:51:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); - 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); - > 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); + > 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + โ”‚ ^^^^^^^^^^^^^^^^^^ + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + + i Unsafe fix: Add unicode u flag to regex + + 51 โ”‚ varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","u"); + โ”‚ ++++ + +``` + +``` +invalid.js:52:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + > 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); โ”‚ ^^^^^^^^^^^^^^^^^^^ - 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); - 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + i Unsafe fix: Add unicode u flag to regex + + 52 โ”‚ varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","u"); + โ”‚ +++ ``` ``` -invalid.js:52:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:53:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Regional indicator symbol characters should not be used in character class. - 50 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); - 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); - > 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + > 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^ - 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); - 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); ``` ``` -invalid.js:53:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:54:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Regional indicator symbol characters should not be used in character class. - 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); - 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); - > 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); + 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + > 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); - 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); ``` ``` -invalid.js:54:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:55:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Regional indicator symbol characters should not be used in character class. - 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); - 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); - > 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); + 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + > 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); - 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); ``` ``` -invalid.js:55:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:56:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected surrogate pair in character class. Use 'u' flag. - 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); - 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); - > 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + > 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^ - 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); - 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); i Unsafe fix: Add unicode u flag to regex - 53 53 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); - 54 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); - 55 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]",ยท""); - 55 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]","u"); - 56 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); - 57 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + 54 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); + 55 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 56 โ”‚ - varยทrยท=ยทnewยทRegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]",ยท""); + 56 โ”‚ + varยทrยท=ยทnewยทRegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]","u"); + 57 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 58 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); ``` ``` -invalid.js:56:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:57:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected joined character sequence in character class. - 54 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); - 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); - > 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + > 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); - 58 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); ``` ``` -invalid.js:57:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:58:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected joined character sequence in character class. - 55 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); - 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); - > 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", ""); + 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + > 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 58 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); ``` ``` -invalid.js:58:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” +invalid.js:59:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” ! Unexpected joined character sequence in character class. - 56 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); - 57 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); - > 58 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); + 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + > 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From f1cca3b9666c8d35043f55a24c82ff1ef56ad61f Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 02:46:23 +0900 Subject: [PATCH 07/19] feat: handle globalThis --- .../nursery/no_misleading_character_class.rs | 22 ++++-- .../noMisleadingCharacterClass/invalid.js | 6 +- .../invalid.js.snap | 72 +++++++++++++++++++ 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index b2f70813ec54..dd2febe61c2a 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -103,11 +103,25 @@ impl Rule for NoMisleadingCharacterClass { } AnyRegexExpression::JsNewExpression(expr) => { - let callee = match expr.callee().ok()? { - AnyJsExpression::JsIdentifierExpression(callee) => callee, - _ => return None, + let is_reg_exp = match expr.callee().ok()? { + AnyJsExpression::JsIdentifierExpression(callee) => { + callee.name().ok()?.has_name("RegExp") + } + AnyJsExpression::JsStaticMemberExpression(callee) => { + let is_global_this = match callee.object().ok()? { + AnyJsExpression::JsIdentifierExpression(e) => { + e.name().ok()?.has_name("globalThis") + } + _ => false, + }; + + is_global_this + && callee.member().ok()?.value_token().ok()?.text() == "RegExp" + } + _ => false, }; - if callee.name().ok()?.has_name("RegExp") { + + if is_reg_exp { let mut args = expr.arguments()?.args().iter(); let raw_regex_pattern = args .next() diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js index c1ab35ebadf2..9cce41c6e7fd 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js @@ -56,4 +56,8 @@ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", ""); var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", "u"); var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); -var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); \ No newline at end of file +var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); +var r = new globalThis.RegExp("[โ‡๏ธ]", ""); +var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); +var r = new globalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); +var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap index d0e4ad3ed961..1c72d0e5c530 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap @@ -63,6 +63,10 @@ var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", ""); var r = new RegExp("[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]", "u"); var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); +var r = new globalThis.RegExp("[โ‡๏ธ]", ""); +var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); +var r = new globalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); +var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); ``` # Diagnostics @@ -1063,6 +1067,7 @@ invalid.js:58:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” > 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); ``` @@ -1076,6 +1081,73 @@ invalid.js:59:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ” 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); > 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); + 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + + +``` + +``` +invalid.js:60:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected combined character in character class. + + 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); + 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + > 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + 62 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + + +``` + +``` +invalid.js:61:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected modified Emoji in character class. + + 59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); + > 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 62 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + 63 โ”‚ var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + + +``` + +``` +invalid.js:62:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected surrogate pair in character class. Use 'u' flag. + + 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); + 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + > 62 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 63 โ”‚ var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + + i Unsafe fix: Add unicode u flag to regex + + 60 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); + 61 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + 62 โ”‚ - varยทrยท=ยทnewยทglobalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",ยท""); + 62 โ”‚ + varยทrยท=ยทnewยทglobalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","u"); + 63 63 โ”‚ var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + + +``` + +``` +invalid.js:63:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ” + + ! Unexpected joined character sequence in character class. + + 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); + 62 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); + > 63 โ”‚ var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); + โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` From f7b9a097da4246c6b162668ce502f87da3307632 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 14:48:59 +0900 Subject: [PATCH 08/19] feat: handle vflag --- .../nursery/no_misleading_character_class.rs | 21 +++++++++++++++---- .../noMisleadingCharacterClass/valid.js | 9 +++++++- .../noMisleadingCharacterClass/valid.js.snap | 7 +++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index dd2febe61c2a..46b5fabac7de 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -96,6 +96,10 @@ impl Rule for NoMisleadingCharacterClass { let Ok((pattern, flags)) = expr.decompose() else { return None; }; + + if flags.text().contains('v') { + return None; + } let regex_pattern = replace_escaped_unicode(pattern.text()); let has_u_flag = flags.text().contains('u'); let range = expr.syntax().text_range(); @@ -135,9 +139,13 @@ impl Rule for NoMisleadingCharacterClass { .next() .and_then(|arg| arg.ok()) .and_then(|arg| JsStringLiteralExpression::cast_ref(arg.syntax())) - .map(|js_string_literal| js_string_literal.text()); + .map(|js_string_literal| js_string_literal.text()) + .unwrap_or_default(); - let has_u_flag = regexp_flags.unwrap_or_default().contains('u'); + if regexp_flags.contains('v') { + return None; + } + let has_u_flag = regexp_flags.contains('u'); let range = expr.syntax().text_range(); return diagnostic_regex_pattern(®ex_pattern, has_u_flag, range); } @@ -162,9 +170,14 @@ impl Rule for NoMisleadingCharacterClass { .next() .and_then(|arg| arg.ok()) .and_then(|arg| JsStringLiteralExpression::cast_ref(arg.syntax())) - .map(|js_string_literal| js_string_literal.text()); + .map(|js_string_literal| js_string_literal.text()) + .unwrap_or_default(); + + if regexp_flags.contains('v') { + return None; + } - let has_u_flag = regexp_flags.unwrap_or_default().contains('u'); + let has_u_flag = regexp_flags.contains('u'); let range = expr.syntax().text_range(); return diagnostic_regex_pattern(®ex_pattern, has_u_flag, range); } diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js index 60dc08d13212..710eb6e7ccaa 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js @@ -32,4 +32,11 @@ var r = /[๐Ÿ‡ต]/u; // Ignore solo ZWJ. var r = /[\\u200D]/; -var r = /[\\u200D]/u; \ No newline at end of file +var r = /[\\u200D]/u; + +// v flag +var r = /[๐Ÿ‘]/v; +var r = /^[\q{๐Ÿ‘ถ๐Ÿป}]$/v; +var r = /[๐Ÿ‡ฏ\q{abc}๐Ÿ‡ต]/v; +var r = /[๐Ÿ‡ฏ[A]๐Ÿ‡ต]/v; +var r = /[๐Ÿ‡ฏ[A--B]๐Ÿ‡ต]/v; \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap index efc4cc183c40..49a40e835ec7 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap @@ -39,6 +39,13 @@ var r = /[๐Ÿ‡ต]/u; // Ignore solo ZWJ. var r = /[\\u200D]/; var r = /[\\u200D]/u; + +// v flag +var r = /[๐Ÿ‘]/v; +var r = /^[\q{๐Ÿ‘ถ๐Ÿป}]$/v; +var r = /[๐Ÿ‡ฏ\q{abc}๐Ÿ‡ต]/v; +var r = /[๐Ÿ‡ฏ[A]๐Ÿ‡ต]/v; +var r = /[๐Ÿ‡ฏ[A--B]๐Ÿ‡ต]/v; ``` From 1c94e9ca2f57ad406578467f5b78d2e03f17576d Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 14:57:43 +0900 Subject: [PATCH 09/19] chore: add edge cases --- .../nursery/no_misleading_character_class.rs | 5 ----- .../specs/nursery/noMisleadingCharacterClass/valid.js | 7 +++++++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index 46b5fabac7de..833e2a7f7f76 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -14,11 +14,6 @@ use biome_rowan::{ declare_rule! { /// Disallow characters which are made with multiple code points in character class syntax /// - /// Unicode includes the characters which are made with multiple code points. - /// RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as a character; those characters will be dissolved to each code point. For example, โ‡๏ธ is made by โ‡ (U+2747) and VARIATION SELECTOR-16 (U+FE0F). If this character is in RegExp character class, it will match to either โ‡ (U+2747) or VARIATION SELECTOR-16 (U+FE0F) rather than โ‡๏ธ. - /// This rule reports the regular expressions which include multiple code point characters in character class syntax. This rule considers the following characters as multiple code point characters. - /// - /// Source: https://eslint.org/docs/latest/rules/no-misleading-character-class/ /// /// ## Examples /// diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js index 710eb6e7ccaa..4854e27c2b07 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js @@ -34,6 +34,13 @@ var r = /[๐Ÿ‡ต]/u; var r = /[\\u200D]/; var r = /[\\u200D]/u; +// don't report and don't crash on invalid regex +// FIXME: need to ecma regex parser to handle this case +// var r = new RegExp('[Aฬ] [ '); +// var r = RegExp('{ [Aฬ]', 'u'); +// var r = new globalThis.RegExp('[Aฬ] [ '); +// var r = globalThis.RegExp('{ [Aฬ]', 'u'); + // v flag var r = /[๐Ÿ‘]/v; var r = /^[\q{๐Ÿ‘ถ๐Ÿป}]$/v; From 0e69ea19016e08e86d51cdedf285e16f2f55bf92 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 15:15:22 +0900 Subject: [PATCH 10/19] chore: gen-lint --- .../nursery/no_misleading_character_class.rs | 4 ++ .../src/configuration/linter/rules.rs | 2 +- .../@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- .../@biomejs/biome/configuration_schema.json | 2 +- .../src/content/docs/linter/rules/index.mdx | 2 +- .../rules/no-misleading-character-class.md | 50 +++++++++++-------- 6 files changed, 38 insertions(+), 24 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index 833e2a7f7f76..e2db1bdd86b9 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -14,6 +14,10 @@ use biome_rowan::{ declare_rule! { /// Disallow characters which are made with multiple code points in character class syntax /// + /// Unicode includes the characters which are made with multiple code points. RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as + /// expected. This rule reports the regular expressions which include multiple code point characters in character class syntax. + /// + /// Source: https://eslint.org/docs/latest/rules/no-misleading-character-class /// /// ## Examples /// diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 5ff339a076b7..2fd17fbd4154 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2786,7 +2786,7 @@ pub struct Nursery { #[bpaf(long("no-implicit-any-let"), argument("on|off|warn"), optional, hide)] #[serde(skip_serializing_if = "Option::is_none")] pub no_implicit_any_let: Option, - #[doc = "Succinct description of the rule."] + #[doc = "Disallow characters which are made with multiple code points in character class syntax"] #[bpaf( long("no-misleading-character-class"), argument("on|off|warn"), diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index b98a03e4704e..4126a877a12f 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -798,7 +798,7 @@ export interface Nursery { */ noImplicitAnyLet?: RuleConfiguration; /** - * Succinct description of the rule. + * Disallow characters which are made with multiple code points in character class syntax */ noMisleadingCharacterClass?: RuleConfiguration; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index d60ed82a6fab..ba133304dab2 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1148,7 +1148,7 @@ ] }, "noMisleadingCharacterClass": { - "description": "Succinct description of the rule.", + "description": "Disallow characters which are made with multiple code points in character class syntax", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index d23d3b323c5d..924e49b4634e 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -232,7 +232,7 @@ Rules that belong to this group are not subject to semantic versionany type on variable declarations. | | -| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Succinct description of the rule. | | +| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Disallow characters which are made with multiple code points in character class syntax | | | [noUnusedImports](/linter/rules/no-unused-imports) | Disallow unused imports. | ๐Ÿ”ง | | [noUnusedPrivateClassMembers](/linter/rules/no-unused-private-class-members) | Disallow unused private class members | โš ๏ธ | | [noUselessLoneBlockStatements](/linter/rules/no-useless-lone-block-statements) | Disallow unnecessary nested block statements. | โš ๏ธ | diff --git a/website/src/content/docs/linter/rules/no-misleading-character-class.md b/website/src/content/docs/linter/rules/no-misleading-character-class.md index fdf226bfce5e..d7e5ae3a3105 100644 --- a/website/src/content/docs/linter/rules/no-misleading-character-class.md +++ b/website/src/content/docs/linter/rules/no-misleading-character-class.md @@ -1,5 +1,5 @@ --- -title: noMisleadingCharacterClass (since vnext) +title: noMisleadingCharacterClass (since v1.3.0) --- **Diagnostic Category: `lint/nursery/noMisleadingCharacterClass`** @@ -8,44 +8,54 @@ title: noMisleadingCharacterClass (since vnext) This rule is part of the [nursery](/linter/rules/#nursery) group. ::: -Succinct description of the rule. +Disallow characters which are made with multiple code points in character class syntax -Put context and details about the rule. -As a starting point, you can take the description of the corresponding _ESLint_ rule (if any). +Unicode includes the characters which are made with multiple code points. RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as +expected. This rule reports the regular expressions which include multiple code point characters in character class syntax. -Try to stay consistent with the descriptions of implemented rules. - -Add a link to the corresponding ESLint rule (if any): - -Source: https://eslint.org/docs/latest/rules/rule-name +Source: https://eslint.org/docs/latest/rules/no-misleading-character-class ## Examples ### Invalid ```jsx -var a = 1; -a = 2; +/^[Aฬ]$/u; +/^[โ‡๏ธ]$/u; +/^[๐Ÿ‘ถ๐Ÿป]$/u; +/^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u; +/^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u; +/^[๐Ÿ‘]$/; ``` -
nursery/noMisleadingCharacterClass.js:1:11 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  โš  Variable is read here.
+  โš  Unexpected combined character in character class.
   
-  > 1 โ”‚ var a = 1;
-      โ”‚           
-  > 2 โ”‚ a = 2;
-      โ”‚ ^^
-    3 โ”‚ 
+  > 1 โ”‚ /^[Aฬ]$/u;
+      โ”‚ ^^^^^^^^
+    2 โ”‚ /^[โ‡๏ธ]$/u;
+    3 โ”‚ /^[๐Ÿ‘ถ๐Ÿป]$/u;
+  
+nursery/noMisleadingCharacterClass.js:1:11 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+  โš  Unexpected combined character in character class.
   
-  โ„น This note will give you more information.
+  > 1 โ”‚ /^[Aฬ]$/u;
+      โ”‚          
+  > 2 โ”‚ /^[โ‡๏ธ]$/u;
+      โ”‚ ^^^^^^^^
+    3 โ”‚ /^[๐Ÿ‘ถ๐Ÿป]$/u;
+    4 โ”‚ /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u;
   
 
## Valid ```jsx -var a = 1; +/^[abc]$/; +/^[๐Ÿ‘]$/u; +/^[\q{๐Ÿ‘ถ๐Ÿป}]$/v; ``` ## Related links From ef42fee7f01e902b0edab0dbf11e8d56b85719b0 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 15:20:35 +0900 Subject: [PATCH 11/19] chore: remove unused dep --- Cargo.lock | 1 - crates/biome_js_analyze/Cargo.toml | 1 - .../nursery/no_misleading_character_class.rs | 8 +++++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f09c284a734..e27789ea5bc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,7 +401,6 @@ dependencies = [ "insta", "lazy_static", "natord", - "regex", "roaring", "rustc-hash", "schemars", diff --git a/crates/biome_js_analyze/Cargo.toml b/crates/biome_js_analyze/Cargo.toml index fc70854e5996..d2eeeb4fd447 100644 --- a/crates/biome_js_analyze/Cargo.toml +++ b/crates/biome_js_analyze/Cargo.toml @@ -27,7 +27,6 @@ biome_rowan = { workspace = true } bpaf.workspace = true lazy_static = { workspace = true } natord = "1.0.9" -regex = "1.5.5" roaring = "0.10.1" rustc-hash = { workspace = true } schemars = { workspace = true, optional = true } diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index e2db1bdd86b9..81f075d86527 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -14,8 +14,10 @@ use biome_rowan::{ declare_rule! { /// Disallow characters which are made with multiple code points in character class syntax /// - /// Unicode includes the characters which are made with multiple code points. RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as - /// expected. This rule reports the regular expressions which include multiple code point characters in character class syntax. + /// Unicode includes the characters which are made with multiple code points. e.g. Aฬ, ๐Ÿ‡ฏ๐Ÿ‡ต, ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ. + /// RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as + /// a character. + /// This rule reports the regular expressions which include multiple code point characters in character class syntax. /// /// Source: https://eslint.org/docs/latest/rules/no-misleading-character-class /// @@ -29,7 +31,7 @@ declare_rule! { /// /^[๐Ÿ‘ถ๐Ÿป]$/u; /// /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u; /// /^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u; - /// /^[๐Ÿ‘]$/; + /// /^[๐Ÿ‘]$/; // surrogate pair without u flag /// ``` /// /// ## Valid From e62eb45922a638f6d3ba466ae40861f34d92e668 Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 15:22:49 +0900 Subject: [PATCH 12/19] chore: lint-doc --- .../src/configuration/linter/rules.rs | 43 ++++++++++++------- .../components/generated/NumberOfRules.astro | 2 +- .../rules/no-misleading-character-class.md | 8 ++-- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 2fd17fbd4154..761b66317bc3 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2911,7 +2911,7 @@ impl MergeWith for Nursery { } impl Nursery { const GROUP_NAME: &'static str = "nursery"; - pub(crate) const GROUP_RULES: [&'static str; 14] = [ + pub(crate) const GROUP_RULES: [&'static str; 15] = [ "noAriaHiddenOnFocusable", "noDefaultExport", "noDuplicateJsonKeys", @@ -2940,11 +2940,11 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[4]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10]), - RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), ]; - const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 14] = [ + const ALL_RULES_AS_FILTERS: [RuleFilter<'static>; 15] = [ RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[0]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[1]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[2]), @@ -2959,6 +2959,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended(&self) -> bool { @@ -3020,31 +3021,36 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.use_for_of.as_ref() { + if let Some(rule) = self.use_await.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.use_grouped_type_import.as_ref() { + if let Some(rule) = self.use_for_of.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_grouped_type_import.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.use_regex_literals.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.use_valid_aria_role.as_ref() { + if let Some(rule) = self.use_regex_literals.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } + if let Some(rule) = self.use_valid_aria_role.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -3094,31 +3100,36 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[8])); } } - if let Some(rule) = self.use_for_of.as_ref() { + if let Some(rule) = self.use_await.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[9])); } } - if let Some(rule) = self.use_grouped_type_import.as_ref() { + if let Some(rule) = self.use_for_of.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[10])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_grouped_type_import.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[11])); } } - if let Some(rule) = self.use_regex_literals.as_ref() { + if let Some(rule) = self.use_import_restrictions.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[12])); } } - if let Some(rule) = self.use_valid_aria_role.as_ref() { + if let Some(rule) = self.use_regex_literals.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[13])); } } + if let Some(rule) = self.use_valid_aria_role.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[14])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3132,7 +3143,7 @@ impl Nursery { pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 6] { Self::RECOMMENDED_RULES_AS_FILTERS } - pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 14] { + pub(crate) fn all_rules_as_filters() -> [RuleFilter<'static>; 15] { Self::ALL_RULES_AS_FILTERS } #[doc = r" Select preset rules"] diff --git a/website/src/components/generated/NumberOfRules.astro b/website/src/components/generated/NumberOfRules.astro index 59aabab17f0b..7131a8124a29 100644 --- a/website/src/components/generated/NumberOfRules.astro +++ b/website/src/components/generated/NumberOfRules.astro @@ -1,2 +1,2 @@ -

Biome's linter has a total of 179 rules

\ No newline at end of file +

Biome's linter has a total of 180 rules

\ No newline at end of file diff --git a/website/src/content/docs/linter/rules/no-misleading-character-class.md b/website/src/content/docs/linter/rules/no-misleading-character-class.md index d7e5ae3a3105..36a2c99ab208 100644 --- a/website/src/content/docs/linter/rules/no-misleading-character-class.md +++ b/website/src/content/docs/linter/rules/no-misleading-character-class.md @@ -10,8 +10,10 @@ This rule is part of the [nursery](/linter/rules/#nursery) group. Disallow characters which are made with multiple code points in character class syntax -Unicode includes the characters which are made with multiple code points. RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as -expected. This rule reports the regular expressions which include multiple code point characters in character class syntax. +Unicode includes the characters which are made with multiple code points. e.g. Aฬ, ๐Ÿ‡ฏ๐Ÿ‡ต, ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ. +RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as +a character. +This rule reports the regular expressions which include multiple code point characters in character class syntax. Source: https://eslint.org/docs/latest/rules/no-misleading-character-class @@ -25,7 +27,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class /^[๐Ÿ‘ถ๐Ÿป]$/u; /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u; /^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u; -/^[๐Ÿ‘]$/; +/^[๐Ÿ‘]$/; // surrogate pair without u flag ```

nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”

From 5ccf0fa538abf635b87b61f0ec98780f828d6f99 Mon Sep 17 00:00:00 2001
From: togami2864 
Date: Sat, 2 Dec 2023 15:56:57 +0900
Subject: [PATCH 13/19] chore: review snap

---
 .../specs/nursery/noMisleadingCharacterClass/valid.js.snap | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap
index 49a40e835ec7..575f07754a4b 100644
--- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap
+++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/valid.js.snap
@@ -40,6 +40,13 @@ var r = /[๐Ÿ‡ต]/u;
 var r = /[\\u200D]/;
 var r = /[\\u200D]/u;
 
+// don't report and don't crash on invalid regex
+// FIXME: need to ecma regex parser to handle this case
+// var r = new RegExp('[Aฬ] [ ');
+// var r = RegExp('{ [Aฬ]', 'u');
+// var r = new globalThis.RegExp('[Aฬ] [ ');
+// var r = globalThis.RegExp('{ [Aฬ]', 'u');
+
 // v flag
 var r = /[๐Ÿ‘]/v;
 var r = /^[\q{๐Ÿ‘ถ๐Ÿป}]$/v;

From 7578eb9fa9c723dc904812518586cfecabef37fa Mon Sep 17 00:00:00 2001
From: togami2864 
Date: Sat, 2 Dec 2023 22:33:37 +0900
Subject: [PATCH 14/19] chore: review

---
 .../nursery/no_misleading_character_class.rs  | 25 +++++-
 .../src/content/docs/linter/rules/index.mdx   |  2 +-
 .../rules/no-misleading-character-class.md    | 88 +++++++++++++++----
 3 files changed, 96 insertions(+), 19 deletions(-)

diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs
index 81f075d86527..efb180b0fec3 100644
--- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs
+++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs
@@ -1,5 +1,7 @@
 use crate::{semantic_services::Semantic, JsRuleAction};
-use biome_analyze::{context::RuleContext, declare_rule, ActionCategory, Rule, RuleDiagnostic};
+use biome_analyze::{
+    context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic,
+};
 use biome_console::markup;
 use biome_diagnostics::Applicability;
 use biome_js_factory::make;
@@ -15,8 +17,9 @@ declare_rule! {
     /// Disallow characters which are made with multiple code points in character class syntax
     ///
     /// Unicode includes the characters which are made with multiple code points. e.g. Aฬ, ๐Ÿ‡ฏ๐Ÿ‡ต, ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ.
-    /// RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as
-    /// a character.
+    /// A RegExp character class `/[abc]/` cannot handle characters that consists of multiple code points.
+    /// For example, the character `โ‡๏ธ` consists of two code points: `โ‡` (U+2747) and `VARIATION SELECTOR-16` (U+FE0F).
+    /// If this character is in a RegExp character class, it will match to either `โ‡` or `VARIATION SELECTOR-16` rather than `โ‡๏ธ`.
     /// This rule reports the regular expressions which include multiple code point characters in character class syntax.
     ///
     /// Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
@@ -27,10 +30,25 @@ declare_rule! {
     ///
     /// ```js,expect_diagnostic
     /// /^[Aฬ]$/u;
+    /// ```
+    ///
+    /// ```js,expect_diagnostic
     /// /^[โ‡๏ธ]$/u;
+    /// ```
+    ///
+    /// ```js,expect_diagnostic
     /// /^[๐Ÿ‘ถ๐Ÿป]$/u;
+    /// ```
+    ///
+    /// ```js,expect_diagnostic
     /// /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u;
+    /// ```
+    ///
+    /// ```js,expect_diagnostic
     /// /^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u;
+    /// ```
+    ///
+    /// ```js,expect_diagnostic
     /// /^[๐Ÿ‘]$/; // surrogate pair without u flag
     /// ```
     ///
@@ -47,6 +65,7 @@ declare_rule! {
         version: "1.3.0",
         name: "noMisleadingCharacterClass",
         recommended: false,
+        fix_kind: FixKind::Safe,
     }
 }
 
diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx
index 924e49b4634e..443b3b7058e6 100644
--- a/website/src/content/docs/linter/rules/index.mdx
+++ b/website/src/content/docs/linter/rules/index.mdx
@@ -232,7 +232,7 @@ Rules that belong to this group are not subject to semantic versionany type on variable declarations. |  |
-| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Disallow characters which are made with multiple code points in character class syntax |  |
+| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Disallow characters which are made with multiple code points in character class syntax | ๐Ÿ”ง  |
 | [noUnusedImports](/linter/rules/no-unused-imports) | Disallow unused imports. | ๐Ÿ”ง  |
 | [noUnusedPrivateClassMembers](/linter/rules/no-unused-private-class-members) | Disallow unused private class members | โš ๏ธ  |
 | [noUselessLoneBlockStatements](/linter/rules/no-useless-lone-block-statements) | Disallow unnecessary nested block statements. | โš ๏ธ  |
diff --git a/website/src/content/docs/linter/rules/no-misleading-character-class.md b/website/src/content/docs/linter/rules/no-misleading-character-class.md
index 36a2c99ab208..12e269d631b9 100644
--- a/website/src/content/docs/linter/rules/no-misleading-character-class.md
+++ b/website/src/content/docs/linter/rules/no-misleading-character-class.md
@@ -11,8 +11,9 @@ This rule is part of the [nursery](/linter/rules/#nursery) group.
 Disallow characters which are made with multiple code points in character class syntax
 
 Unicode includes the characters which are made with multiple code points. e.g. Aฬ, ๐Ÿ‡ฏ๐Ÿ‡ต, ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ.
-RegExp character class syntax (/[abc]/) cannot handle characters which are made by multiple code points as
-a character.
+A RegExp character class `/[abc]/` cannot handle characters that consists of multiple code points.
+For example, the character `โ‡๏ธ` consists of two code points: `โ‡` (U+2747) and `VARIATION SELECTOR-16` (U+FE0F).
+If this character is in a RegExp character class, it will match to either `โ‡` or `VARIATION SELECTOR-16` rather than `โ‡๏ธ`.
 This rule reports the regular expressions which include multiple code point characters in character class syntax.
 
 Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
@@ -23,11 +24,6 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
 
 ```jsx
 /^[Aฬ]$/u;
-/^[โ‡๏ธ]$/u;
-/^[๐Ÿ‘ถ๐Ÿป]$/u;
-/^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u;
-/^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u;
-/^[๐Ÿ‘]$/; // surrogate pair without u flag
 ```
 
 
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
@@ -36,20 +32,82 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
   
   > 1 โ”‚ /^[Aฬ]$/u;
       โ”‚ ^^^^^^^^
-    2 โ”‚ /^[โ‡๏ธ]$/u;
-    3 โ”‚ /^[๐Ÿ‘ถ๐Ÿป]$/u;
+    2 โ”‚ 
   
-nursery/noMisleadingCharacterClass.js:1:11 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+ +```jsx +/^[โ‡๏ธ]$/u; +``` + +
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
   โš  Unexpected combined character in character class.
   
-  > 1 โ”‚ /^[Aฬ]$/u;
-      โ”‚          
-  > 2 โ”‚ /^[โ‡๏ธ]$/u;
+  > 1 โ”‚ /^[โ‡๏ธ]$/u;
+      โ”‚ ^^^^^^^^
+    2 โ”‚ 
+  
+
+ +```jsx +/^[๐Ÿ‘ถ๐Ÿป]$/u; +``` + +
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+  โš  Unexpected modified Emoji in character class. 
+  
+  > 1 โ”‚ /^[๐Ÿ‘ถ๐Ÿป]$/u;
+      โ”‚ ^^^^^^^^^^^
+    2 โ”‚ 
+  
+
+ +```jsx +/^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u; +``` + +
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+  โš  Regional indicator symbol characters should not be used in character class.
+  
+  > 1 โ”‚ /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u;
+      โ”‚ ^^^^^^^^^
+    2 โ”‚ 
+  
+
+ +```jsx +/^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u; +``` + +
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+  โš  Unexpected joined character sequence in character class.
+  
+  > 1 โ”‚ /^[๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ]$/u;
+      โ”‚ ^^^^^^^^^^^^^
+    2 โ”‚ 
+  
+
+ +```jsx +/^[๐Ÿ‘]$/; // surrogate pair without u flag +``` + +
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
+
+  โš  Unexpected surrogate pair in character class. Use 'u' flag.
+  
+  > 1 โ”‚ /^[๐Ÿ‘]$/; // surrogate pair without u flag
       โ”‚ ^^^^^^^^
-    3 โ”‚ /^[๐Ÿ‘ถ๐Ÿป]$/u;
-    4 โ”‚ /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u;
+    2 โ”‚ 
+  
+  โ„น Unsafe fix: Add unicode u flag to regex
   
+    1 โ”‚ /^[๐Ÿ‘]$/u;ยท//ยทsurrogateยทpairยทwithoutยทuยทflag
+      โ”‚         +                                  
 
## Valid From f703339ca1d670e3088e605e8c84f3ac4514907f Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sat, 2 Dec 2023 22:36:21 +0900 Subject: [PATCH 15/19] fix: change safe fix --- .../nursery/no_misleading_character_class.rs | 6 +-- .../invalid.js.snap | 42 +++++++++---------- .../rules/no-misleading-character-class.md | 2 +- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index efb180b0fec3..ae04d35ce3c5 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -234,7 +234,7 @@ impl Rule for NoMisleadingCharacterClass { Some(JsRuleAction { category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, + applicability: Applicability::Always, message: markup! { "Add unicode ""u"" flag to regex" } .to_owned(), mutation, @@ -254,7 +254,7 @@ impl Rule for NoMisleadingCharacterClass { mutation.replace_node(prev_node, suggest); Some(JsRuleAction { category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, + applicability: Applicability::Always, message: markup! { "Add unicode ""u"" flag to regex" } .to_owned(), mutation, @@ -277,7 +277,7 @@ impl Rule for NoMisleadingCharacterClass { mutation.replace_node(prev_node, suggest); Some(JsRuleAction { category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, + applicability: Applicability::Always, message: markup! { "Add unicode ""u"" flag to regex" } .to_owned(), mutation, diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap index 1c72d0e5c530..351b7f980794 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap @@ -80,7 +80,7 @@ invalid.js:1:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ” 2 โ”‚ var r = /[\uD83D\uDC4D]/; 3 โ”‚ var r = /[๐Ÿ‘]\\a/; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 1 โ”‚ varยทrยท=ยท/[๐Ÿ‘]/u; โ”‚ + @@ -98,7 +98,7 @@ invalid.js:2:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ” 3 โ”‚ var r = /[๐Ÿ‘]\\a/; 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 2 โ”‚ varยทrยท=ยท/[\uD83D\uDC4D]/u; โ”‚ + @@ -117,7 +117,7 @@ invalid.js:3:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ” 4 โ”‚ var r = /(?<=[๐Ÿ‘])/; 5 โ”‚ var r = /[A๏ฟฝ]/; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 3 โ”‚ varยทrยท=ยท/[๐Ÿ‘]\\a/u; โ”‚ + @@ -136,7 +136,7 @@ invalid.js:4:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ”โ” 5 โ”‚ var r = /[A๏ฟฝ]/; 6 โ”‚ var r = /[A๏ฟฝ]/u; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 4 โ”‚ varยทrยท=ยท/(?<=[๐Ÿ‘])/u; โ”‚ + @@ -305,7 +305,7 @@ invalid.js:15:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u; 17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 15 โ”‚ varยทrยท=ยท/[๐Ÿ‘ถ๐Ÿป]/u; โ”‚ + @@ -369,7 +369,7 @@ invalid.js:19:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i; 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 19 โ”‚ varยทrยท=ยท/[๐Ÿ‡ฏ๐Ÿ‡ต]/u; โ”‚ + @@ -388,7 +388,7 @@ invalid.js:20:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u; 22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 18 18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u; 19 19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/; @@ -457,7 +457,7 @@ invalid.js:24:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 25 โ”‚ var r = /[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 24 โ”‚ varยทrยท=ยท/[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]/u; โ”‚ + @@ -521,7 +521,7 @@ invalid.js:28:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 26 26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u; 27 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; @@ -545,7 +545,7 @@ invalid.js:29:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 27 27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u; 28 28 โ”‚ var r = new RegExp("[๐Ÿ‘]", ""); @@ -584,7 +584,7 @@ invalid.js:31:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", ""); 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 29 29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``); 30 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); @@ -608,7 +608,7 @@ invalid.js:32:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 33 โ”‚ var r = new RegExp("[A๏ฟฝ]", ""); 34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 30 30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags); 31 31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", ""); @@ -782,7 +782,7 @@ invalid.js:43:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 41 41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u"); 42 42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u"); @@ -851,7 +851,7 @@ invalid.js:47:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 45 45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u"); 46 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); @@ -875,7 +875,7 @@ invalid.js:48:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 46 46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u"); 47 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); @@ -899,7 +899,7 @@ invalid.js:49:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`); 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 47 47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", ""); 48 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); @@ -923,7 +923,7 @@ invalid.js:50:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]"); 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 48 48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i"); 49 49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`); @@ -947,7 +947,7 @@ invalid.js:51:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",); 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 51 โ”‚ varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","u"); โ”‚ ++++ @@ -966,7 +966,7 @@ invalid.js:52:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u"); 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 52 โ”‚ varยทrยท=ยทnewยทRegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]","u"); โ”‚ +++ @@ -1030,7 +1030,7 @@ invalid.js:56:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” 57 โ”‚ var r = new RegExp("[๐Ÿ‘จ๏ฟฝ๐Ÿ‘ฉ๏ฟฝ๐Ÿ‘ฆ]", "u"); 58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 54 54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u"); 55 55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u"); @@ -1128,7 +1128,7 @@ invalid.js:62:9 lint/nursery/noMisleadingCharacterClass FIXABLE โ”โ”โ”โ” โ”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 63 โ”‚ var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u"); - i Unsafe fix: Add unicode u flag to regex + i Safe fix: Add unicode u flag to regex 60 60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", ""); 61 61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u"); diff --git a/website/src/content/docs/linter/rules/no-misleading-character-class.md b/website/src/content/docs/linter/rules/no-misleading-character-class.md index 12e269d631b9..351c2646509d 100644 --- a/website/src/content/docs/linter/rules/no-misleading-character-class.md +++ b/website/src/content/docs/linter/rules/no-misleading-character-class.md @@ -104,7 +104,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class โ”‚ ^^^^^^^^ 2 โ”‚ - โ„น Unsafe fix: Add unicode u flag to regex + โ„น Safe fix: Add unicode u flag to regex 1 โ”‚ /^[๐Ÿ‘]$/u;ยท//ยทsurrogateยทpairยทwithoutยทuยทflag โ”‚ + From e047f9af4d361b7d07e2d2b142d91497b98aca6a Mon Sep 17 00:00:00 2001 From: togami2864 Date: Sun, 3 Dec 2023 20:54:31 +0900 Subject: [PATCH 16/19] chore: fix version --- .../semantic_analyzers/nursery/no_misleading_character_class.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index ae04d35ce3c5..323a5d6a1fc9 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -62,7 +62,7 @@ declare_rule! { /// pub(crate) NoMisleadingCharacterClass { - version: "1.3.0", + version: "next", name: "noMisleadingCharacterClass", recommended: false, fix_kind: FixKind::Safe, From a0f388c996059c5b2b1705f2cc27a54232ea11ea Mon Sep 17 00:00:00 2001 From: togami2864 Date: Mon, 4 Dec 2023 23:25:46 +0900 Subject: [PATCH 17/19] chore: doc --- .../nursery/no_misleading_character_class.rs | 12 ++++++------ .../src/configuration/linter/rules.rs | 2 +- .../@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- .../@biomejs/biome/configuration_schema.json | 2 +- website/src/content/docs/linter/rules/index.mdx | 2 +- .../rules/no-misleading-character-class.md | 16 ++++++++-------- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs index 323a5d6a1fc9..d0e8cffba819 100644 --- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs +++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/no_misleading_character_class.rs @@ -14,10 +14,10 @@ use biome_rowan::{ declare_node_union, AstNode, AstNodeList, AstSeparatedList, BatchMutationExt, TextRange, }; declare_rule! { - /// Disallow characters which are made with multiple code points in character class syntax + /// Disallow characters made with multiple code points in character class syntax. /// /// Unicode includes the characters which are made with multiple code points. e.g. Aฬ, ๐Ÿ‡ฏ๐Ÿ‡ต, ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ. - /// A RegExp character class `/[abc]/` cannot handle characters that consists of multiple code points. + /// A RegExp character class `/[abc]/` cannot handle characters with multiple code points. /// For example, the character `โ‡๏ธ` consists of two code points: `โ‡` (U+2747) and `VARIATION SELECTOR-16` (U+FE0F). /// If this character is in a RegExp character class, it will match to either `โ‡` or `VARIATION SELECTOR-16` rather than `โ‡๏ธ`. /// This rule reports the regular expressions which include multiple code point characters in character class syntax. @@ -84,13 +84,13 @@ pub enum Message { impl Message { fn as_str(&self) -> &str { match self { - Self::CombiningClassOrVs16 => "Unexpected combined character in character class.", + Self::CombiningClassOrVs16 => "Unexpected combined character in the character class.", Self::SurrogatePairWithoutUFlag => { - "Unexpected surrogate pair in character class. Use 'u' flag." + "Unexpected surrogate pair in character class. Use the 'u' flag." } - Self::EmojiModifier => "Unexpected modified Emoji in character class. ", + Self::EmojiModifier => "Unexpected modified Emoji in the character class. ", Self::RegionalIndicatorSymbol => { - "Regional indicator symbol characters should not be used in character class." + "Regional indicator symbol characters should not be used in the character class." } Self::JoinedCharSequence => "Unexpected joined character sequence in character class.", } diff --git a/crates/biome_service/src/configuration/linter/rules.rs b/crates/biome_service/src/configuration/linter/rules.rs index 761b66317bc3..7568fb6ed104 100644 --- a/crates/biome_service/src/configuration/linter/rules.rs +++ b/crates/biome_service/src/configuration/linter/rules.rs @@ -2786,7 +2786,7 @@ pub struct Nursery { #[bpaf(long("no-implicit-any-let"), argument("on|off|warn"), optional, hide)] #[serde(skip_serializing_if = "Option::is_none")] pub no_implicit_any_let: Option, - #[doc = "Disallow characters which are made with multiple code points in character class syntax"] + #[doc = "Disallow characters made with multiple code points in character class syntax."] #[bpaf( long("no-misleading-character-class"), argument("on|off|warn"), diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 4126a877a12f..e4834ae8af8d 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -798,7 +798,7 @@ export interface Nursery { */ noImplicitAnyLet?: RuleConfiguration; /** - * Disallow characters which are made with multiple code points in character class syntax + * Disallow characters made with multiple code points in character class syntax. */ noMisleadingCharacterClass?: RuleConfiguration; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index ba133304dab2..fcdc498a74f4 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1148,7 +1148,7 @@ ] }, "noMisleadingCharacterClass": { - "description": "Disallow characters which are made with multiple code points in character class syntax", + "description": "Disallow characters made with multiple code points in character class syntax.", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } diff --git a/website/src/content/docs/linter/rules/index.mdx b/website/src/content/docs/linter/rules/index.mdx index 443b3b7058e6..40033ff85811 100644 --- a/website/src/content/docs/linter/rules/index.mdx +++ b/website/src/content/docs/linter/rules/index.mdx @@ -232,7 +232,7 @@ Rules that belong to this group are not subject to semantic versionany
type on variable declarations. | | -| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Disallow characters which are made with multiple code points in character class syntax | ๐Ÿ”ง | +| [noMisleadingCharacterClass](/linter/rules/no-misleading-character-class) | Disallow characters made with multiple code points in character class syntax. | ๐Ÿ”ง | | [noUnusedImports](/linter/rules/no-unused-imports) | Disallow unused imports. | ๐Ÿ”ง | | [noUnusedPrivateClassMembers](/linter/rules/no-unused-private-class-members) | Disallow unused private class members | โš ๏ธ | | [noUselessLoneBlockStatements](/linter/rules/no-useless-lone-block-statements) | Disallow unnecessary nested block statements. | โš ๏ธ | diff --git a/website/src/content/docs/linter/rules/no-misleading-character-class.md b/website/src/content/docs/linter/rules/no-misleading-character-class.md index 351c2646509d..9d932840d116 100644 --- a/website/src/content/docs/linter/rules/no-misleading-character-class.md +++ b/website/src/content/docs/linter/rules/no-misleading-character-class.md @@ -1,5 +1,5 @@ --- -title: noMisleadingCharacterClass (since v1.3.0) +title: noMisleadingCharacterClass (since vnext) --- **Diagnostic Category: `lint/nursery/noMisleadingCharacterClass`** @@ -8,10 +8,10 @@ title: noMisleadingCharacterClass (since v1.3.0) This rule is part of the [nursery](/linter/rules/#nursery) group. ::: -Disallow characters which are made with multiple code points in character class syntax +Disallow characters made with multiple code points in character class syntax. Unicode includes the characters which are made with multiple code points. e.g. Aฬ, ๐Ÿ‡ฏ๐Ÿ‡ต, ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ. -A RegExp character class `/[abc]/` cannot handle characters that consists of multiple code points. +A RegExp character class `/[abc]/` cannot handle characters with multiple code points. For example, the character `โ‡๏ธ` consists of two code points: `โ‡` (U+2747) and `VARIATION SELECTOR-16` (U+FE0F). If this character is in a RegExp character class, it will match to either `โ‡` or `VARIATION SELECTOR-16` rather than `โ‡๏ธ`. This rule reports the regular expressions which include multiple code point characters in character class syntax. @@ -28,7 +28,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  โš  Unexpected combined character in character class.
+  โš  Unexpected combined character in the character class.
   
   > 1 โ”‚ /^[Aฬ]$/u;
       โ”‚ ^^^^^^^^
@@ -42,7 +42,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
 
 
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  โš  Unexpected combined character in character class.
+  โš  Unexpected combined character in the character class.
   
   > 1 โ”‚ /^[โ‡๏ธ]$/u;
       โ”‚ ^^^^^^^^
@@ -56,7 +56,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
 
 
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  โš  Unexpected modified Emoji in character class. 
+  โš  Unexpected modified Emoji in the character class. 
   
   > 1 โ”‚ /^[๐Ÿ‘ถ๐Ÿป]$/u;
       โ”‚ ^^^^^^^^^^^
@@ -70,7 +70,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
 
 
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  โš  Regional indicator symbol characters should not be used in character class.
+  โš  Regional indicator symbol characters should not be used in the character class.
   
   > 1 โ”‚ /^[๐Ÿ‡ฏ๐Ÿ‡ต]$/u;
       โ”‚ ^^^^^^^^^
@@ -98,7 +98,7 @@ Source: https://eslint.org/docs/latest/rules/no-misleading-character-class
 
 
nursery/noMisleadingCharacterClass.js:1:1 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  โš  Unexpected surrogate pair in character class. Use 'u' flag.
+  โš  Unexpected surrogate pair in character class. Use the 'u' flag.
   
   > 1 โ”‚ /^[๐Ÿ‘]$/; // surrogate pair without u flag
       โ”‚ ^^^^^^^^

From b5d8d6cc4f851ee56d138ba7a33aae976a639852 Mon Sep 17 00:00:00 2001
From: togami2864 
Date: Tue, 5 Dec 2023 00:04:23 +0900
Subject: [PATCH 18/19] chore: update snapshot

---
 .../invalid.js.snap                           | 112 +++++++++---------
 1 file changed, 56 insertions(+), 56 deletions(-)

diff --git a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap
index 351b7f980794..a6f24a38253e 100644
--- a/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap
+++ b/crates/biome_js_analyze/tests/specs/nursery/noMisleadingCharacterClass/invalid.js.snap
@@ -73,7 +73,7 @@ var r = new globalThis.RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "
 ```
 invalid.js:1:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
   > 1 โ”‚ var r = /[๐Ÿ‘]/;
       โ”‚         ^^^^^^
@@ -90,7 +90,7 @@ invalid.js:1:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”
 ```
 invalid.js:2:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     1 โ”‚ var r = /[๐Ÿ‘]/;
   > 2 โ”‚ var r = /[\uD83D\uDC4D]/;
@@ -108,7 +108,7 @@ invalid.js:2:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”
 ```
 invalid.js:3:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     1 โ”‚ var r = /[๐Ÿ‘]/;
     2 โ”‚ var r = /[\uD83D\uDC4D]/;
@@ -127,7 +127,7 @@ invalid.js:3:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”
 ```
 invalid.js:4:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     2 โ”‚ var r = /[\uD83D\uDC4D]/;
     3 โ”‚ var r = /[๐Ÿ‘]\\a/;
@@ -146,7 +146,7 @@ invalid.js:4:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”
 ```
 invalid.js:5:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     3 โ”‚ var r = /[๐Ÿ‘]\\a/;
     4 โ”‚ var r = /(?<=[๐Ÿ‘])/;
@@ -161,7 +161,7 @@ invalid.js:5:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:6:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     4 โ”‚ var r = /(?<=[๐Ÿ‘])/;
     5 โ”‚ var r = /[A๏ฟฝ]/;
@@ -176,7 +176,7 @@ invalid.js:6:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:7:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     5 โ”‚ var r = /[A๏ฟฝ]/;
     6 โ”‚ var r = /[A๏ฟฝ]/u;
@@ -191,7 +191,7 @@ invalid.js:7:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:8:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
      6 โ”‚ var r = /[A๏ฟฝ]/u;
      7 โ”‚ var r = /[\u0041\u0301]/;
@@ -206,7 +206,7 @@ invalid.js:8:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:9:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
      7 โ”‚ var r = /[\u0041\u0301]/;
      8 โ”‚ var r = /[\u0041\u0301]/u;
@@ -221,7 +221,7 @@ invalid.js:9:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:10:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
      8 โ”‚ var r = /[\u0041\u0301]/u;
      9 โ”‚ var r = /[\u{41}\u{301}]/u;
@@ -236,7 +236,7 @@ invalid.js:10:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:11:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
      9 โ”‚ var r = /[\u{41}\u{301}]/u;
     10 โ”‚ var r = /[โ‡๏ฟฝ]/;
@@ -251,7 +251,7 @@ invalid.js:11:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:12:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     10 โ”‚ var r = /[โ‡๏ฟฝ]/;
     11 โ”‚ var r = /[โ‡๏ฟฝ]/u;
@@ -266,7 +266,7 @@ invalid.js:12:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:13:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     11 โ”‚ var r = /[โ‡๏ฟฝ]/u;
     12 โ”‚ var r = /[\u2747\uFE0F]/;
@@ -281,7 +281,7 @@ invalid.js:13:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:14:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     12 โ”‚ var r = /[\u2747\uFE0F]/;
     13 โ”‚ var r = /[\u2747\uFE0F]/u;
@@ -296,7 +296,7 @@ invalid.js:14:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:15:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     13 โ”‚ var r = /[\u2747\uFE0F]/u;
     14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u;
@@ -315,7 +315,7 @@ invalid.js:15:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:16:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     14 โ”‚ var r = /[\u{2747}\u{FE0F}]/u;
     15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/;
@@ -330,7 +330,7 @@ invalid.js:16:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:17:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     15 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/;
     16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u;
@@ -345,7 +345,7 @@ invalid.js:17:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:18:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     16 โ”‚ var r = /[๐Ÿ‘ถ๐Ÿป]/u;
     17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u;
@@ -360,7 +360,7 @@ invalid.js:18:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:19:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     17 โ”‚ var r = /[\uD83D\uDC76\uD83C\uDFFB]/u;
     18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u;
@@ -379,7 +379,7 @@ invalid.js:19:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:20:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     18 โ”‚ var r = /[\u{1F476}\u{1F3FB}]/u;
     19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/;
@@ -403,7 +403,7 @@ invalid.js:20:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:21:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Regional indicator symbol characters should not be used in character class.
+  ! Regional indicator symbol characters should not be used in the character class.
   
     19 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/;
     20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i;
@@ -418,7 +418,7 @@ invalid.js:21:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:22:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Regional indicator symbol characters should not be used in character class.
+  ! Regional indicator symbol characters should not be used in the character class.
   
     20 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/i;
     21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u;
@@ -433,7 +433,7 @@ invalid.js:22:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:23:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Regional indicator symbol characters should not be used in character class.
+  ! Regional indicator symbol characters should not be used in the character class.
   
     21 โ”‚ var r = /[๐Ÿ‡ฏ๐Ÿ‡ต]/u;
     22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u;
@@ -448,7 +448,7 @@ invalid.js:23:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:24:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     22 โ”‚ var r = /[\uD83C\uDDEF\uD83C\uDDF5]/u;
     23 โ”‚ var r = /[\u{1F1EF}\u{1F1F5}]/u;
@@ -512,7 +512,7 @@ invalid.js:27:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:28:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     26 โ”‚ var r = /[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]/u;
     27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u;
@@ -536,7 +536,7 @@ invalid.js:28:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:29:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     27 โ”‚ var r = /[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]/u;
     28 โ”‚ var r = new RegExp("[๐Ÿ‘]", "");
@@ -560,7 +560,7 @@ invalid.js:29:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:30:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     28 โ”‚ var r = new RegExp("[๐Ÿ‘]", "");
     29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``);
@@ -575,7 +575,7 @@ invalid.js:30:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:31:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     29 โ”‚ var r = new RegExp('[๐Ÿ‘]', ``);
     30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags);
@@ -599,7 +599,7 @@ invalid.js:31:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:32:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     30 โ”‚ var r = new RegExp("[๐Ÿ‘]", flags);
     31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", "");
@@ -623,7 +623,7 @@ invalid.js:32:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:33:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     31 โ”‚ var r = new RegExp("[\uD83D\uDC4D]", "");
     32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", "");
@@ -638,7 +638,7 @@ invalid.js:33:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:34:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     32 โ”‚ var r = new RegExp("/(?<=[๐Ÿ‘])", "");
     33 โ”‚ var r = new RegExp("[A๏ฟฝ]", "");
@@ -653,7 +653,7 @@ invalid.js:34:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:35:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     33 โ”‚ var r = new RegExp("[A๏ฟฝ]", "");
     34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u");
@@ -668,7 +668,7 @@ invalid.js:35:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:36:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     34 โ”‚ var r = new RegExp("[A๏ฟฝ]", "u");
     35 โ”‚ var r = new RegExp("[\u0041\u0301]", "");
@@ -683,7 +683,7 @@ invalid.js:36:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:37:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     35 โ”‚ var r = new RegExp("[\u0041\u0301]", "");
     36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u");
@@ -698,7 +698,7 @@ invalid.js:37:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:38:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     36 โ”‚ var r = new RegExp("[\u0041\u0301]", "u");
     37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u");
@@ -713,7 +713,7 @@ invalid.js:38:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:39:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     37 โ”‚ var r = new RegExp("[\u{41}\u{301}]", "u");
     38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "");
@@ -728,7 +728,7 @@ invalid.js:39:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:40:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     38 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "");
     39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u");
@@ -743,7 +743,7 @@ invalid.js:40:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:41:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     39 โ”‚ var r = new RegExp("[โ‡๏ฟฝ]", "u");
     40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "");
@@ -758,7 +758,7 @@ invalid.js:41:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:42:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     40 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "");
     41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u");
@@ -773,7 +773,7 @@ invalid.js:42:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:43:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     41 โ”‚ var r = new RegExp("[\u2747\uFE0F]", "u");
     42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u");
@@ -797,7 +797,7 @@ invalid.js:43:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:44:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     42 โ”‚ var r = new RegExp("[\u{2747}\u{FE0F}]", "u");
     43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "");
@@ -812,7 +812,7 @@ invalid.js:44:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:45:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     43 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "");
     44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u");
@@ -827,7 +827,7 @@ invalid.js:45:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:46:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     44 โ”‚ var r = new RegExp("[๐Ÿ‘ถ๐Ÿป]", "u");
     45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u");
@@ -842,7 +842,7 @@ invalid.js:46:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:47:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     45 โ”‚ var r = new RegExp("[\uD83D\uDC76\uD83C\uDFFB]", "u");
     46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u");
@@ -866,7 +866,7 @@ invalid.js:47:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:48:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     46 โ”‚ var r = new RegExp("[\u{1F476}\u{1F3FB}]", "u");
     47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "");
@@ -890,7 +890,7 @@ invalid.js:48:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:49:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     47 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "");
     48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i");
@@ -914,7 +914,7 @@ invalid.js:49:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:50:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     48 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "i");
     49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`);
@@ -938,7 +938,7 @@ invalid.js:50:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:51:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     49 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `i`);
     50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`);
@@ -957,7 +957,7 @@ invalid.js:51:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:52:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     50 โ”‚ var r = new RegExp('[๐Ÿ‡ฏ๐Ÿ‡ต]', `${foo}`);
     51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]");
@@ -976,7 +976,7 @@ invalid.js:52:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”
 ```
 invalid.js:53:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Regional indicator symbol characters should not be used in character class.
+  ! Regional indicator symbol characters should not be used in the character class.
   
     51 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]");
     52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",);
@@ -991,7 +991,7 @@ invalid.js:53:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:54:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Regional indicator symbol characters should not be used in character class.
+  ! Regional indicator symbol characters should not be used in the character class.
   
     52 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]",);
     53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u");
@@ -1006,7 +1006,7 @@ invalid.js:54:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:55:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Regional indicator symbol characters should not be used in character class.
+  ! Regional indicator symbol characters should not be used in the character class.
   
     53 โ”‚ var r = new RegExp("[๐Ÿ‡ฏ๐Ÿ‡ต]", "u");
     54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u");
@@ -1021,7 +1021,7 @@ invalid.js:55:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:56:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     54 โ”‚ var r = new RegExp("[\uD83C\uDDEF\uD83C\uDDF5]", "u");
     55 โ”‚ var r = new RegExp("[\u{1F1EF}\u{1F1F5}]", "u");
@@ -1090,7 +1090,7 @@ invalid.js:59:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:60:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected combined character in character class.
+  ! Unexpected combined character in the character class.
   
     58 โ”‚ var r = new RegExp("[\uD83D\uDC68\u200D\uD83D\uDC69\u200D\uD83D\uDC66]", "u");
     59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u");
@@ -1105,7 +1105,7 @@ invalid.js:60:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:61:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected modified Emoji in character class. 
+  ! Unexpected modified Emoji in the character class. 
   
     59 โ”‚ var r = new RegExp("[\u{1F468}\u{200D}\u{1F469}\u{200D}\u{1F466}]", "u");
     60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", "");
@@ -1120,7 +1120,7 @@ invalid.js:61:9 lint/nursery/noMisleadingCharacterClass โ”โ”โ”โ”โ”โ”โ”โ”
 ```
 invalid.js:62:9 lint/nursery/noMisleadingCharacterClass  FIXABLE  โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
 
-  ! Unexpected surrogate pair in character class. Use 'u' flag.
+  ! Unexpected surrogate pair in character class. Use the 'u' flag.
   
     60 โ”‚ var r = new globalThis.RegExp("[โ‡๏ฟฝ]", "");
     61 โ”‚ var r = new globalThis.RegExp("[๐Ÿ‘ถ๐Ÿป]", "u");

From 72099e03089d06c35cda6db7399218e6006024e6 Mon Sep 17 00:00:00 2001
From: Emanuele Stoppa 
Date: Mon, 4 Dec 2023 15:34:38 +0000
Subject: [PATCH 19/19] update snapshots

---
 .../tests/invalid/hooks_incorrect_options.json.snap              | 1 +
 crates/biome_service/tests/invalid/hooks_missing_name.json.snap  | 1 +
 2 files changed, 2 insertions(+)

diff --git a/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap b/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap
index b2ae35370b28..b26aafc7dd78 100644
--- a/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap
+++ b/crates/biome_service/tests/invalid/hooks_incorrect_options.json.snap
@@ -22,6 +22,7 @@ hooks_incorrect_options.json:6:5 deserialize โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
   - noDuplicateJsonKeys
   - noEmptyBlockStatements
   - noImplicitAnyLet
+  - noMisleadingCharacterClass
   - noUnusedImports
   - noUnusedPrivateClassMembers
   - noUselessLoneBlockStatements
diff --git a/crates/biome_service/tests/invalid/hooks_missing_name.json.snap b/crates/biome_service/tests/invalid/hooks_missing_name.json.snap
index 479320a69bf3..695c5e1d6229 100644
--- a/crates/biome_service/tests/invalid/hooks_missing_name.json.snap
+++ b/crates/biome_service/tests/invalid/hooks_missing_name.json.snap
@@ -22,6 +22,7 @@ hooks_missing_name.json:6:5 deserialize โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”
   - noDuplicateJsonKeys
   - noEmptyBlockStatements
   - noImplicitAnyLet
+  - noMisleadingCharacterClass
   - noUnusedImports
   - noUnusedPrivateClassMembers
   - noUselessLoneBlockStatements