From 5c410da3a583d36e360aaa5dc66d0d057a84c1d6 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Sat, 20 Apr 2024 22:23:03 +0300 Subject: [PATCH 01/11] Implement useNewForBuiltins Implement initial useForBuiltins Update snapshots Changes --- .../biome_configuration/src/linter/rules.rs | 23 +- .../src/categories.rs | 1 + crates/biome_js_analyze/src/lint/nursery.rs | 2 + .../src/lint/nursery/use_new_for_builtins.rs | 190 ++++++ crates/biome_js_analyze/src/options.rs | 2 + .../nursery/useNewForBuiltins/invalid.js | 44 ++ .../nursery/useNewForBuiltins/invalid.js.snap | 632 ++++++++++++++++++ .../specs/nursery/useNewForBuiltins/valid.js | 52 ++ .../nursery/useNewForBuiltins/valid.js.snap | 60 ++ .../@biomejs/backend-jsonrpc/src/workspace.ts | 5 + .../@biomejs/biome/configuration_schema.json | 7 + 11 files changed, 1016 insertions(+), 2 deletions(-) create mode 100644 crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index bf0ce4e26aea..964c949e7847 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2699,6 +2699,9 @@ pub struct Nursery { #[doc = "Disallows package private imports."] #[serde(skip_serializing_if = "Option::is_none")] pub use_import_restrictions: Option>, + #[doc = "Succinct description of the rule."] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_new_for_builtins: Option>, #[doc = "Enforce the sorting of CSS utility classes."] #[serde(skip_serializing_if = "Option::is_none")] pub use_sorted_classes: Option>, @@ -2737,6 +2740,7 @@ impl Nursery { "noRestrictedImports", "noUndeclaredDependencies", "useImportRestrictions", + "useNewForBuiltins", "useSortedClasses", ]; const RECOMMENDED_RULES: &'static [&'static str] = &[ @@ -2778,6 +2782,7 @@ impl Nursery { RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16]), RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17]), + RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18]), ]; #[doc = r" Retrieves the recommended rules"] pub(crate) fn is_recommended_true(&self) -> bool { @@ -2879,11 +2884,16 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_new_for_builtins.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_enabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); + } + } index_set } pub(crate) fn get_disabled_rules(&self) -> IndexSet { @@ -2973,11 +2983,16 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_sorted_classes.as_ref() { + if let Some(rule) = self.use_new_for_builtins.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[17])); } } + if let Some(rule) = self.use_sorted_classes.as_ref() { + if rule.is_disabled() { + index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[18])); + } + } index_set } #[doc = r" Checks if, given a rule name, matches one of the rules contained in this category"] @@ -3082,6 +3097,10 @@ impl Nursery { .use_import_restrictions .as_ref() .map(|conf| (conf.level(), conf.get_options())), + "useNewForBuiltins" => self + .use_new_for_builtins + .as_ref() + .map(|conf| (conf.level(), conf.get_options())), "useSortedClasses" => self .use_sorted_classes .as_ref() diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index ce22b36987a8..c381eec02165 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -129,6 +129,7 @@ define_categories! { "lint/nursery/noUndeclaredDependencies": "https://biomejs.dev/linter/rules/no-undeclared-dependencies", "lint/nursery/useBiomeSuppressionComment": "https://biomejs.dev/linter/rules/use-biome-suppression-comment", "lint/nursery/useImportRestrictions": "https://biomejs.dev/linter/rules/use-import-restrictions", + "lint/nursery/useNewForBuiltins": "https://biomejs.dev/linter/rules/use-new-for-builtins", "lint/nursery/useSortedClasses": "https://biomejs.dev/linter/rules/use-sorted-classes", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index b850281e2548..0f45d3e373f2 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -14,6 +14,7 @@ pub mod no_react_specific_props; pub mod no_restricted_imports; pub mod no_undeclared_dependencies; pub mod use_import_restrictions; +pub mod use_new_for_builtins; pub mod use_sorted_classes; declare_group! { @@ -32,6 +33,7 @@ declare_group! { self :: no_restricted_imports :: NoRestrictedImports , self :: no_undeclared_dependencies :: NoUndeclaredDependencies , self :: use_import_restrictions :: UseImportRestrictions , + self :: use_new_for_builtins :: UseNewForBuiltins , self :: use_sorted_classes :: UseSortedClasses , ] } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs new file mode 100644 index 000000000000..a450b32ffda5 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs @@ -0,0 +1,190 @@ +use crate::{services::semantic::Semantic, JsRuleAction}; +use biome_analyze::{ + context::RuleContext, declare_rule, ActionCategory, Rule, RuleDiagnostic, RuleSource, +}; +use biome_console::markup; +use biome_diagnostics::Applicability; +use biome_js_factory::make; +use biome_js_syntax::{global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression}; +use biome_rowan::{chain_trivia_pieces, declare_node_union, AstNode, BatchMutationExt}; + +declare_rule! { + /// Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. + /// + /// TODO. 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): + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// var a = 1; + /// a = 2; + /// ``` + /// + /// ### Valid + /// + /// ```js + /// // var a = 1; + /// ``` + /// + pub UseNewForBuiltins { + version: "next", + name: "useNewForBuiltins", + sources: &[RuleSource::EslintUnicorn("new-for-builtins")], + recommended: false, + } +} + +const BUILTINS_REQUIRING_NEW: &[&str] = &[ + "Object", + "Array", + "ArrayBuffer", + "BigInt64Array", + "BigUint64Array", + "DataView", + "Date", + "Error", + "Float32Array", + "Float64Array", + "Function", + "Int8Array", + "Int16Array", + "Int32Array", + "Map", + "WeakMap", + "Set", + "WeakSet", + "Promise", + "RegExp", + "Uint8Array", + "Uint16Array", + "Uint32Array", + "Uint8ClampedArray", + "SharedArrayBuffer", + "Proxy", + "WeakRef", + "FinalizationRegistry", +]; + +const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["String", "Number", "Boolean", "Symbol", "BigInt"]; + +enum BuiltinCreationRule { + MustUseNew, + MustNotUseNew, +} + +impl BuiltinCreationRule { + fn forbidden_builtins_list(&self) -> &[&str] { + match self { + BuiltinCreationRule::MustUseNew => BUILTINS_REQUIRING_NEW, + BuiltinCreationRule::MustNotUseNew => BUILTINS_NOT_REQUIRING_NEW, + } + } +} + +pub struct UseNewForBuiltinsState { + name: String, + creation_rule: BuiltinCreationRule, +} + +declare_node_union! { + pub JsNewOrCallExpression = JsNewExpression | JsCallExpression +} + +impl Rule for UseNewForBuiltins { + type Query = Semantic; + type State = UseNewForBuiltinsState; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let node = ctx.query(); + let (callee, creation_rule) = extract_callee_and_rule(node)?; + + let (reference, name) = global_identifier(&callee.omit_parentheses())?; + let name_text = name.text(); + + if creation_rule.forbidden_builtins_list().contains(&name_text) { + return ctx.model() + .binding(&reference) + .is_none() + .then_some(UseNewForBuiltinsState { + name: name_text.to_string(), + creation_rule: creation_rule, + }); + } + + None + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + + let name = &state.name; + + let (use_this, instead_of) = match state.creation_rule { + BuiltinCreationRule::MustUseNew => ("new ", ""), + BuiltinCreationRule::MustNotUseNew => ("", "new "), + }; + + Some(RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { + "Use "{use_this}{name}"()"" instead of "{instead_of}{name}"()""." + }, + )) + } + + fn action(ctx: &RuleContext, _: &Self::State) -> Option { + let node: &JsNewOrCallExpression = ctx.query(); + let call_expression = convert_new_expression_to_call_expression(node)?; + let mut mutation = ctx.root().begin(); + mutation.replace_node_discard_trivia::( + node.clone().into(), + call_expression.into(), + ); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Remove ""new""." }.to_owned(), + mutation, + }) + } +} + +fn extract_callee_and_rule( + node: &JsNewOrCallExpression, +) -> Option<(AnyJsExpression, BuiltinCreationRule)> { + match node { + JsNewOrCallExpression::JsNewExpression(node) => { + let callee = node.callee().ok()?; + + Some((callee, BuiltinCreationRule::MustNotUseNew)) + } + JsNewOrCallExpression::JsCallExpression(node) => { + let callee = node.callee().ok()?; + + Some((callee, BuiltinCreationRule::MustUseNew)) + } + } +} + +fn convert_new_expression_to_call_expression(expr: &JsNewExpression) -> Option { + let new_token = expr.new_token().ok()?; + let mut callee = expr.callee().ok()?; + if new_token.has_leading_comments() || new_token.has_trailing_comments() { + callee = callee.prepend_trivia_pieces(chain_trivia_pieces( + new_token.leading_trivia().pieces(), + new_token.trailing_trivia().pieces(), + ))?; + } + Some(make::js_call_expression(callee, expr.arguments()?).build()) +} + diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index eabce4134d18..916c5cd9ad02 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -301,6 +301,8 @@ pub type UseNamespaceKeyword = ::Options; pub type UseNamingConvention = ::Options; +pub type UseNewForBuiltins = + ::Options; pub type UseNodeAssertStrict = ::Options; pub type UseNodejsImportProtocol = < lint :: style :: use_nodejs_import_protocol :: UseNodejsImportProtocol as biome_analyze :: Rule > :: Options ; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js new file mode 100644 index 000000000000..98f44075f5d3 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js @@ -0,0 +1,44 @@ +Object() +Array() +ArrayBuffer() +BigInt64Array() +BigUint64Array() +DataView() +Date() +Error() +Float32Array() +Float64Array() +Function() +Int8Array() +Int16Array() +Int32Array() +Map() +WeakMap() +Set() +WeakSet() +Promise() +RegExp() +Uint8Array() +Uint16Array() +Uint32Array() +Uint8ClampedArray() +SharedArrayBuffer() +Proxy() +WeakRef() +FinalizationRegistry() +window.Object() +globalThis.Object() +function foo() { + return globalThis.Object({ foo: 'bar' }) +} + +new String() +new Number() +new Boolean() +new Symbol() +new BigInt() +new window.String() +new globalThis.String() +function foo() { + return new globalThis.String("foo") +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap new file mode 100644 index 000000000000..45b5b832dfd1 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap @@ -0,0 +1,632 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid.js +--- +# Input +```jsx +Object() +Array() +ArrayBuffer() +BigInt64Array() +BigUint64Array() +DataView() +Date() +Error() +Float32Array() +Float64Array() +Function() +Int8Array() +Int16Array() +Int32Array() +Map() +WeakMap() +Set() +WeakSet() +Promise() +RegExp() +Uint8Array() +Uint16Array() +Uint32Array() +Uint8ClampedArray() +SharedArrayBuffer() +Proxy() +WeakRef() +FinalizationRegistry() +window.Object() +globalThis.Object() +function foo() { + return globalThis.Object({ foo: 'bar' }) +} + +new String() +new Number() +new Boolean() +new Symbol() +new BigInt() +new window.String() +new globalThis.String() +function foo() { + return new globalThis.String("foo") +} +``` + +# Diagnostics +``` +invalid.js:1:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + > 1 │ Object() + │ ^^^^^^^^ + 2 │ Array() + 3 │ ArrayBuffer() + + +``` + +``` +invalid.js:2:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Array() instead of Array(). + + 1 │ Object() + > 2 │ Array() + │ ^^^^^^^ + 3 │ ArrayBuffer() + 4 │ BigInt64Array() + + +``` + +``` +invalid.js:3:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new ArrayBuffer() instead of ArrayBuffer(). + + 1 │ Object() + 2 │ Array() + > 3 │ ArrayBuffer() + │ ^^^^^^^^^^^^^ + 4 │ BigInt64Array() + 5 │ BigUint64Array() + + +``` + +``` +invalid.js:4:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new BigInt64Array() instead of BigInt64Array(). + + 2 │ Array() + 3 │ ArrayBuffer() + > 4 │ BigInt64Array() + │ ^^^^^^^^^^^^^^^ + 5 │ BigUint64Array() + 6 │ DataView() + + +``` + +``` +invalid.js:5:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new BigUint64Array() instead of BigUint64Array(). + + 3 │ ArrayBuffer() + 4 │ BigInt64Array() + > 5 │ BigUint64Array() + │ ^^^^^^^^^^^^^^^^ + 6 │ DataView() + 7 │ Date() + + +``` + +``` +invalid.js:6:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new DataView() instead of DataView(). + + 4 │ BigInt64Array() + 5 │ BigUint64Array() + > 6 │ DataView() + │ ^^^^^^^^^^ + 7 │ Date() + 8 │ Error() + + +``` + +``` +invalid.js:7:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Date() instead of Date(). + + 5 │ BigUint64Array() + 6 │ DataView() + > 7 │ Date() + │ ^^^^^^ + 8 │ Error() + 9 │ Float32Array() + + +``` + +``` +invalid.js:8:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Error() instead of Error(). + + 6 │ DataView() + 7 │ Date() + > 8 │ Error() + │ ^^^^^^^ + 9 │ Float32Array() + 10 │ Float64Array() + + +``` + +``` +invalid.js:9:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Float32Array() instead of Float32Array(). + + 7 │ Date() + 8 │ Error() + > 9 │ Float32Array() + │ ^^^^^^^^^^^^^^ + 10 │ Float64Array() + 11 │ Function() + + +``` + +``` +invalid.js:10:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Float64Array() instead of Float64Array(). + + 8 │ Error() + 9 │ Float32Array() + > 10 │ Float64Array() + │ ^^^^^^^^^^^^^^ + 11 │ Function() + 12 │ Int8Array() + + +``` + +``` +invalid.js:11:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Function() instead of Function(). + + 9 │ Float32Array() + 10 │ Float64Array() + > 11 │ Function() + │ ^^^^^^^^^^ + 12 │ Int8Array() + 13 │ Int16Array() + + +``` + +``` +invalid.js:12:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Int8Array() instead of Int8Array(). + + 10 │ Float64Array() + 11 │ Function() + > 12 │ Int8Array() + │ ^^^^^^^^^^^ + 13 │ Int16Array() + 14 │ Int32Array() + + +``` + +``` +invalid.js:13:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Int16Array() instead of Int16Array(). + + 11 │ Function() + 12 │ Int8Array() + > 13 │ Int16Array() + │ ^^^^^^^^^^^^ + 14 │ Int32Array() + 15 │ Map() + + +``` + +``` +invalid.js:14:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Int32Array() instead of Int32Array(). + + 12 │ Int8Array() + 13 │ Int16Array() + > 14 │ Int32Array() + │ ^^^^^^^^^^^^ + 15 │ Map() + 16 │ WeakMap() + + +``` + +``` +invalid.js:15:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Map() instead of Map(). + + 13 │ Int16Array() + 14 │ Int32Array() + > 15 │ Map() + │ ^^^^^ + 16 │ WeakMap() + 17 │ Set() + + +``` + +``` +invalid.js:16:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new WeakMap() instead of WeakMap(). + + 14 │ Int32Array() + 15 │ Map() + > 16 │ WeakMap() + │ ^^^^^^^^^ + 17 │ Set() + 18 │ WeakSet() + + +``` + +``` +invalid.js:17:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Set() instead of Set(). + + 15 │ Map() + 16 │ WeakMap() + > 17 │ Set() + │ ^^^^^ + 18 │ WeakSet() + 19 │ Promise() + + +``` + +``` +invalid.js:18:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new WeakSet() instead of WeakSet(). + + 16 │ WeakMap() + 17 │ Set() + > 18 │ WeakSet() + │ ^^^^^^^^^ + 19 │ Promise() + 20 │ RegExp() + + +``` + +``` +invalid.js:19:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Promise() instead of Promise(). + + 17 │ Set() + 18 │ WeakSet() + > 19 │ Promise() + │ ^^^^^^^^^ + 20 │ RegExp() + 21 │ Uint8Array() + + +``` + +``` +invalid.js:20:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new RegExp() instead of RegExp(). + + 18 │ WeakSet() + 19 │ Promise() + > 20 │ RegExp() + │ ^^^^^^^^ + 21 │ Uint8Array() + 22 │ Uint16Array() + + +``` + +``` +invalid.js:21:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint8Array() instead of Uint8Array(). + + 19 │ Promise() + 20 │ RegExp() + > 21 │ Uint8Array() + │ ^^^^^^^^^^^^ + 22 │ Uint16Array() + 23 │ Uint32Array() + + +``` + +``` +invalid.js:22:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint16Array() instead of Uint16Array(). + + 20 │ RegExp() + 21 │ Uint8Array() + > 22 │ Uint16Array() + │ ^^^^^^^^^^^^^ + 23 │ Uint32Array() + 24 │ Uint8ClampedArray() + + +``` + +``` +invalid.js:23:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint32Array() instead of Uint32Array(). + + 21 │ Uint8Array() + 22 │ Uint16Array() + > 23 │ Uint32Array() + │ ^^^^^^^^^^^^^ + 24 │ Uint8ClampedArray() + 25 │ SharedArrayBuffer() + + +``` + +``` +invalid.js:24:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint8ClampedArray() instead of Uint8ClampedArray(). + + 22 │ Uint16Array() + 23 │ Uint32Array() + > 24 │ Uint8ClampedArray() + │ ^^^^^^^^^^^^^^^^^^^ + 25 │ SharedArrayBuffer() + 26 │ Proxy() + + +``` + +``` +invalid.js:25:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new SharedArrayBuffer() instead of SharedArrayBuffer(). + + 23 │ Uint32Array() + 24 │ Uint8ClampedArray() + > 25 │ SharedArrayBuffer() + │ ^^^^^^^^^^^^^^^^^^^ + 26 │ Proxy() + 27 │ WeakRef() + + +``` + +``` +invalid.js:26:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Proxy() instead of Proxy(). + + 24 │ Uint8ClampedArray() + 25 │ SharedArrayBuffer() + > 26 │ Proxy() + │ ^^^^^^^ + 27 │ WeakRef() + 28 │ FinalizationRegistry() + + +``` + +``` +invalid.js:27:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new WeakRef() instead of WeakRef(). + + 25 │ SharedArrayBuffer() + 26 │ Proxy() + > 27 │ WeakRef() + │ ^^^^^^^^^ + 28 │ FinalizationRegistry() + 29 │ window.Object() + + +``` + +``` +invalid.js:28:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new FinalizationRegistry() instead of FinalizationRegistry(). + + 26 │ Proxy() + 27 │ WeakRef() + > 28 │ FinalizationRegistry() + │ ^^^^^^^^^^^^^^^^^^^^^^ + 29 │ window.Object() + 30 │ globalThis.Object() + + +``` + +``` +invalid.js:29:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + 27 │ WeakRef() + 28 │ FinalizationRegistry() + > 29 │ window.Object() + │ ^^^^^^^^^^^^^^^ + 30 │ globalThis.Object() + 31 │ function foo() { + + +``` + +``` +invalid.js:30:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + 28 │ FinalizationRegistry() + 29 │ window.Object() + > 30 │ globalThis.Object() + │ ^^^^^^^^^^^^^^^^^^^ + 31 │ function foo() { + 32 │ return globalThis.Object({ foo: 'bar' }) + + +``` + +``` +invalid.js:32:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + 30 │ globalThis.Object() + 31 │ function foo() { + > 32 │ return globalThis.Object({ foo: 'bar' }) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 33 │ } + 34 │ + + +``` + +``` +invalid.js:35:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 33 │ } + 34 │ + > 35 │ new String() + │ ^^^^^^^^^^^^ + 36 │ new Number() + 37 │ new Boolean() + + +``` + +``` +invalid.js:36:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use Number() instead of new Number(). + + 35 │ new String() + > 36 │ new Number() + │ ^^^^^^^^^^^^ + 37 │ new Boolean() + 38 │ new Symbol() + + +``` + +``` +invalid.js:37:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use Boolean() instead of new Boolean(). + + 35 │ new String() + 36 │ new Number() + > 37 │ new Boolean() + │ ^^^^^^^^^^^^^ + 38 │ new Symbol() + 39 │ new BigInt() + + +``` + +``` +invalid.js:38:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use Symbol() instead of new Symbol(). + + 36 │ new Number() + 37 │ new Boolean() + > 38 │ new Symbol() + │ ^^^^^^^^^^^^ + 39 │ new BigInt() + 40 │ new window.String() + + +``` + +``` +invalid.js:39:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use BigInt() instead of new BigInt(). + + 37 │ new Boolean() + 38 │ new Symbol() + > 39 │ new BigInt() + │ ^^^^^^^^^^^^ + 40 │ new window.String() + 41 │ new globalThis.String() + + +``` + +``` +invalid.js:40:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 38 │ new Symbol() + 39 │ new BigInt() + > 40 │ new window.String() + │ ^^^^^^^^^^^^^^^^^^^ + 41 │ new globalThis.String() + 42 │ function foo() { + + +``` + +``` +invalid.js:41:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 39 │ new BigInt() + 40 │ new window.String() + > 41 │ new globalThis.String() + │ ^^^^^^^^^^^^^^^^^^^^^^^ + 42 │ function foo() { + 43 │ return new globalThis.String("foo") + + +``` + +``` +invalid.js:43:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 41 │ new globalThis.String() + 42 │ function foo() { + > 43 │ return new globalThis.String("foo") + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 44 │ } + + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js new file mode 100644 index 000000000000..f14fdd33e09e --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js @@ -0,0 +1,52 @@ +new Object() +new Array() +new ArrayBuffer() +new BigInt64Array() +new BigUint64Array() +new DataView() +new Date() +new Error() +new Float32Array() +new Float64Array() +new Function() +new Int8Array() +new Int16Array() +new Int32Array() +new Map() +new WeakMap() +new Set() +new WeakSet() +new Promise() +new RegExp() +new Uint8Array() +new Uint16Array() +new Uint32Array() +new Uint8ClampedArray() +new SharedArrayBuffer() +new Proxy() +new WeakRef() +new FinalizationRegistry() +new window.Object() +new globalThis.Object() +function foo() { + return new globalThis.Object() +} + +String() +Number() +Boolean() +Symbol() +BigInt() +window.String() +globalThis.String() +function foo() { + return globalThis.String() +} + +function varCheck() { + { + var String = class {} + } + // This should not reported + return new String() +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap new file mode 100644 index 000000000000..80aeef7713b9 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap @@ -0,0 +1,60 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: valid.js +--- +# Input +```jsx +new Object() +new Array() +new ArrayBuffer() +new BigInt64Array() +new BigUint64Array() +new DataView() +new Date() +new Error() +new Float32Array() +new Float64Array() +new Function() +new Int8Array() +new Int16Array() +new Int32Array() +new Map() +new WeakMap() +new Set() +new WeakSet() +new Promise() +new RegExp() +new Uint8Array() +new Uint16Array() +new Uint32Array() +new Uint8ClampedArray() +new SharedArrayBuffer() +new Proxy() +new WeakRef() +new FinalizationRegistry() +new window.Object() +new globalThis.Object() +function foo() { + return new globalThis.Object() +} + +String() +Number() +Boolean() +Symbol() +BigInt() +window.String() +globalThis.String() +function foo() { + return globalThis.String() +} + +function varCheck() { + { + var String = class {} + } + // This should not reported + return new String() +} + +``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index 00e25c38cb25..db6ccf8af2e3 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -980,6 +980,10 @@ export interface Nursery { * Disallows package private imports. */ useImportRestrictions?: RuleConfiguration_for_Null; + /** + * Succinct description of the rule. + */ + useNewForBuiltins?: RuleConfiguration_for_Null; /** * Enforce the sorting of CSS utility classes. */ @@ -1963,6 +1967,7 @@ export type Category = | "lint/nursery/noUndeclaredDependencies" | "lint/nursery/useBiomeSuppressionComment" | "lint/nursery/useImportRestrictions" + | "lint/nursery/useNewForBuiltins" | "lint/nursery/useSortedClasses" | "lint/performance/noAccumulatingSpread" | "lint/performance/noBarrelFile" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index 8179aed4c82a..ba450ec2ffa8 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1556,6 +1556,13 @@ { "type": "null" } ] }, + "useNewForBuiltins": { + "description": "Succinct description of the rule.", + "anyOf": [ + { "$ref": "#/definitions/RuleConfiguration" }, + { "type": "null" } + ] + }, "useSortedClasses": { "description": "Enforce the sorting of CSS utility classes.", "anyOf": [ From dac61e99e0951d922d47de80e5a3209ffbfcd9d8 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Sun, 21 Apr 2024 00:54:41 +0300 Subject: [PATCH 02/11] Implement remove new action --- .../src/lint/nursery/use_new_for_builtins.rs | 75 ++++++++++++++----- .../nursery/useNewForBuiltins/invalid.js | 6 +- .../nursery/useNewForBuiltins/invalid.js.snap | 74 +++++++++++++----- .../specs/nursery/useNewForBuiltins/valid.js | 4 +- .../nursery/useNewForBuiltins/valid.js.snap | 4 +- 5 files changed, 118 insertions(+), 45 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs index a450b32ffda5..6fd87b4e86f9 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs @@ -4,8 +4,8 @@ use biome_analyze::{ }; use biome_console::markup; use biome_diagnostics::Applicability; -use biome_js_factory::make; -use biome_js_syntax::{global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression}; +use biome_js_factory::make::{self, token_decorated_with_space}; +use biome_js_syntax::{global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind}; use biome_rowan::{chain_trivia_pieces, declare_node_union, AstNode, BatchMutationExt}; declare_rule! { @@ -111,7 +111,8 @@ impl Rule for UseNewForBuiltins { let name_text = name.text(); if creation_rule.forbidden_builtins_list().contains(&name_text) { - return ctx.model() + return ctx + .model() .binding(&reference) .is_none() .then_some(UseNewForBuiltinsState { @@ -124,7 +125,7 @@ impl Rule for UseNewForBuiltins { } fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { - let node = ctx.query(); + let node: &JsNewOrCallExpression = ctx.query(); let name = &state.name; @@ -143,19 +144,12 @@ impl Rule for UseNewForBuiltins { } fn action(ctx: &RuleContext, _: &Self::State) -> Option { - let node: &JsNewOrCallExpression = ctx.query(); - let call_expression = convert_new_expression_to_call_expression(node)?; - let mut mutation = ctx.root().begin(); - mutation.replace_node_discard_trivia::( - node.clone().into(), - call_expression.into(), - ); - Some(JsRuleAction { - category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, - message: markup! { "Remove ""new""." }.to_owned(), - mutation, - }) + let node = ctx.query(); + + match node { + JsNewOrCallExpression::JsNewExpression(node) => action_remove_new(ctx, node), + JsNewOrCallExpression::JsCallExpression(_) => None, + } } } @@ -169,7 +163,7 @@ fn extract_callee_and_rule( Some((callee, BuiltinCreationRule::MustNotUseNew)) } JsNewOrCallExpression::JsCallExpression(node) => { - let callee = node.callee().ok()?; + let callee: AnyJsExpression = node.callee().ok()?; Some((callee, BuiltinCreationRule::MustUseNew)) } @@ -188,3 +182,48 @@ fn convert_new_expression_to_call_expression(expr: &JsNewExpression) -> Option, node: &JsNewExpression) -> Option { + let call_expression = convert_new_expression_to_call_expression(node)?; + let mut mutation = ctx.root().begin(); + mutation.replace_node::( + node.clone().into(), + call_expression.into(), + ); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Remove ""new""." }.to_owned(), + mutation, + }) +} + +fn _convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { + let new_token = token_decorated_with_space(JsSyntaxKind::NEW_KW); + + let mut callee = expr.callee().ok()?; + if new_token.has_leading_comments() || new_token.has_trailing_comments() { + callee = callee.prepend_trivia_pieces(chain_trivia_pieces( + new_token.leading_trivia().pieces(), + new_token.trailing_trivia().pieces(), + ))?; + } + + // TODO. make::js_new_expression currently does not accept arguments. + // To fix that js.ungram needs to be updated, but that breaks a lot of rules. + Some(make::js_new_expression(new_token, callee).build()) +} + +fn _action_add_new(ctx: &RuleContext, node: &JsCallExpression) -> Option { + let new_expression = _convert_call_expression_to_new_expression(node)?; + let mut mutation = ctx.root().begin(); + mutation.replace_node::( + node.clone().into(), + new_expression.into(), + ); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Add ""new""." }.to_owned(), + mutation, + }) +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js index 98f44075f5d3..0a50fb5d1868 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js @@ -26,7 +26,7 @@ SharedArrayBuffer() Proxy() WeakRef() FinalizationRegistry() -window.Object() +window.Object({}) globalThis.Object() function foo() { return globalThis.Object({ foo: 'bar' }) @@ -37,8 +37,8 @@ new Number() new Boolean() new Symbol() new BigInt() -new window.String() +new window.String(123) new globalThis.String() function foo() { return new globalThis.String("foo") -} \ No newline at end of file +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap index 45b5b832dfd1..f7bb0edc8e5f 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap @@ -32,7 +32,7 @@ SharedArrayBuffer() Proxy() WeakRef() FinalizationRegistry() -window.Object() +window.Object({}) globalThis.Object() function foo() { return globalThis.Object({ foo: 'bar' }) @@ -43,11 +43,12 @@ new Number() new Boolean() new Symbol() new BigInt() -new window.String() +new window.String(123) new globalThis.String() function foo() { return new globalThis.String("foo") } + ``` # Diagnostics @@ -448,7 +449,7 @@ invalid.js:27:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ > 27 │ WeakRef() │ ^^^^^^^^^ 28 │ FinalizationRegistry() - 29 │ window.Object() + 29 │ window.Object({}) ``` @@ -462,7 +463,7 @@ invalid.js:28:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 27 │ WeakRef() > 28 │ FinalizationRegistry() │ ^^^^^^^^^^^^^^^^^^^^^^ - 29 │ window.Object() + 29 │ window.Object({}) 30 │ globalThis.Object() @@ -475,8 +476,8 @@ invalid.js:29:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 27 │ WeakRef() 28 │ FinalizationRegistry() - > 29 │ window.Object() - │ ^^^^^^^^^^^^^^^ + > 29 │ window.Object({}) + │ ^^^^^^^^^^^^^^^^^ 30 │ globalThis.Object() 31 │ function foo() { @@ -489,7 +490,7 @@ invalid.js:30:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ ! Use new Object() instead of Object(). 28 │ FinalizationRegistry() - 29 │ window.Object() + 29 │ window.Object({}) > 30 │ globalThis.Object() │ ^^^^^^^^^^^^^^^^^^^ 31 │ function foo() { @@ -514,7 +515,7 @@ invalid.js:32:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━ ``` ``` -invalid.js:35:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:35:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use String() instead of new String(). @@ -525,11 +526,15 @@ invalid.js:35:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 36 │ new Number() 37 │ new Boolean() + i Unsafe fix: Remove new. + + 35 │ new·String() + │ ---- ``` ``` -invalid.js:36:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:36:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use Number() instead of new Number(). @@ -539,11 +544,15 @@ invalid.js:36:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 37 │ new Boolean() 38 │ new Symbol() + i Unsafe fix: Remove new. + + 36 │ new·Number() + │ ---- ``` ``` -invalid.js:37:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:37:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use Boolean() instead of new Boolean(). @@ -554,11 +563,15 @@ invalid.js:37:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 38 │ new Symbol() 39 │ new BigInt() + i Unsafe fix: Remove new. + + 37 │ new·Boolean() + │ ---- ``` ``` -invalid.js:38:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:38:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use Symbol() instead of new Symbol(). @@ -567,13 +580,17 @@ invalid.js:38:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ > 38 │ new Symbol() │ ^^^^^^^^^^^^ 39 │ new BigInt() - 40 │ new window.String() + 40 │ new window.String(123) + + i Unsafe fix: Remove new. + 38 │ new·Symbol() + │ ---- ``` ``` -invalid.js:39:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:39:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use BigInt() instead of new BigInt(). @@ -581,44 +598,56 @@ invalid.js:39:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 38 │ new Symbol() > 39 │ new BigInt() │ ^^^^^^^^^^^^ - 40 │ new window.String() + 40 │ new window.String(123) 41 │ new globalThis.String() + i Unsafe fix: Remove new. + + 39 │ new·BigInt() + │ ---- ``` ``` -invalid.js:40:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:40:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use String() instead of new String(). 38 │ new Symbol() 39 │ new BigInt() - > 40 │ new window.String() - │ ^^^^^^^^^^^^^^^^^^^ + > 40 │ new window.String(123) + │ ^^^^^^^^^^^^^^^^^^^^^^ 41 │ new globalThis.String() 42 │ function foo() { + i Unsafe fix: Remove new. + + 40 │ new·window.String(123) + │ ---- ``` ``` -invalid.js:41:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:41:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use String() instead of new String(). 39 │ new BigInt() - 40 │ new window.String() + 40 │ new window.String(123) > 41 │ new globalThis.String() │ ^^^^^^^^^^^^^^^^^^^^^^^ 42 │ function foo() { 43 │ return new globalThis.String("foo") + i Unsafe fix: Remove new. + + 41 │ new·globalThis.String() + │ ---- ``` ``` -invalid.js:43:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:43:12 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use String() instead of new String(). @@ -627,6 +656,11 @@ invalid.js:43:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━ > 43 │ return new globalThis.String("foo") │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 44 │ } + 45 │ + + i Unsafe fix: Remove new. + 43 │ ····return·new·globalThis.String("foo") + │ ---- ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js index f14fdd33e09e..de0cd727eb2d 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js @@ -26,7 +26,7 @@ new SharedArrayBuffer() new Proxy() new WeakRef() new FinalizationRegistry() -new window.Object() +new window.Object({}) new globalThis.Object() function foo() { return new globalThis.Object() @@ -38,7 +38,7 @@ Boolean() Symbol() BigInt() window.String() -globalThis.String() +globalThis.String(123) function foo() { return globalThis.String() } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap index 80aeef7713b9..78104912c58a 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap @@ -32,7 +32,7 @@ new SharedArrayBuffer() new Proxy() new WeakRef() new FinalizationRegistry() -new window.Object() +new window.Object({}) new globalThis.Object() function foo() { return new globalThis.Object() @@ -44,7 +44,7 @@ Boolean() Symbol() BigInt() window.String() -globalThis.String() +globalThis.String(123) function foo() { return globalThis.String() } From bcef6acd1c831a874e6120a2f6945a2ecca87f5b Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Sun, 21 Apr 2024 02:17:02 +0300 Subject: [PATCH 03/11] Add documentation --- .../src/lint/nursery/use_new_for_builtins.rs | 60 ++++++++++++++++--- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs index 6fd87b4e86f9..67bfbee60653 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs @@ -11,26 +11,68 @@ use biome_rowan::{chain_trivia_pieces, declare_node_union, AstNode, BatchMutatio declare_rule! { /// Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. /// - /// TODO. 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): + /// They work the same, but new should be preferred for consistency with other constructors. + /// Enforces the use of new for following builtins: + /// + /// - Object + /// - Array + /// - ArrayBuffer + /// - BigInt64Array + /// - BigUint64Array + /// - DataView + /// - Date + /// - Error + /// - Float32Array + /// - Float64Array + /// - Function + /// - Int8Array + /// - Int16Array + /// - Int32Array + /// - Map + /// - WeakMap + /// - Set + /// - WeakSet + /// - Promise + /// - RegExp + /// - Uint8Array + /// - Uint16Array + /// - Uint32Array + /// - Uint8ClampedArray + /// - SharedArrayBuffer + /// - Proxy + /// - WeakRef + /// - FinalizationRegistry + /// + /// Disallows the use of new for following builtins. /// + /// - String + /// - Number + /// - Boolean + /// - Symbol + /// - BigInt + /// + /// > These should not use new as that would create object wrappers for the primitive values, which is not what you want. However, without new they can be useful for coercing a value to that type. + /// /// ## Examples /// /// ### Invalid /// /// ```js,expect_diagnostic - /// var a = 1; - /// a = 2; + /// const list = Array(10); + /// const now = Date(); + /// const map = Map([ + /// ['foo', 'bar'] + /// ]); /// ``` /// /// ### Valid /// /// ```js - /// // var a = 1; + /// const list = new Array(10); + /// const now = new Date(); + /// const map = new Map([ + /// ['foo', 'bar'] + /// ]); /// ``` /// pub UseNewForBuiltins { From 1e5e3b97911c42260bae563455c0402e9b261e99 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Sun, 21 Apr 2024 02:24:08 +0300 Subject: [PATCH 04/11] chore: codegen --- .../migrate/eslint_any_rule_to_biome.rs | 8 +++++ .../biome_configuration/src/linter/rules.rs | 2 +- .../src/lint/nursery/use_new_for_builtins.rs | 36 ++++++++++--------- .../@biomejs/backend-jsonrpc/src/workspace.ts | 2 +- .../@biomejs/biome/configuration_schema.json | 2 +- 5 files changed, 30 insertions(+), 20 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index e33f3f9a9124..94ee34d43291 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1218,6 +1218,14 @@ pub(crate) fn migrate_eslint_any_rule( .get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } + "unicorn/new-for-builtins" => { + if !options.include_nursery { + return false; + } + let group = rules.nursery.get_or_insert_with(Default::default); + let rule = group.use_new_for_builtins.get_or_insert(Default::default()); + rule.set_level(rule_severity.into()); + } "unicorn/no-array-for-each" => { let group = rules.complexity.get_or_insert_with(Default::default); let rule = group.no_for_each.get_or_insert(Default::default()); diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 964c949e7847..3ac3654dd410 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2699,7 +2699,7 @@ pub struct Nursery { #[doc = "Disallows package private imports."] #[serde(skip_serializing_if = "Option::is_none")] pub use_import_restrictions: Option>, - #[doc = "Succinct description of the rule."] + #[doc = "Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt."] #[serde(skip_serializing_if = "Option::is_none")] pub use_new_for_builtins: Option>, #[doc = "Enforce the sorting of CSS utility classes."] diff --git a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs index 67bfbee60653..69c3512794ac 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs @@ -5,7 +5,9 @@ use biome_analyze::{ use biome_console::markup; use biome_diagnostics::Applicability; use biome_js_factory::make::{self, token_decorated_with_space}; -use biome_js_syntax::{global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind}; +use biome_js_syntax::{ + global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind, +}; use biome_rowan::{chain_trivia_pieces, declare_node_union, AstNode, BatchMutationExt}; declare_rule! { @@ -13,7 +15,7 @@ declare_rule! { /// /// They work the same, but new should be preferred for consistency with other constructors. /// Enforces the use of new for following builtins: - /// + /// /// - Object /// - Array /// - ArrayBuffer @@ -42,7 +44,7 @@ declare_rule! { /// - Proxy /// - WeakRef /// - FinalizationRegistry - /// + /// /// Disallows the use of new for following builtins. /// /// - String @@ -50,9 +52,9 @@ declare_rule! { /// - Boolean /// - Symbol /// - BigInt - /// + /// /// > These should not use new as that would create object wrappers for the primitive values, which is not what you want. However, without new they can be useful for coercing a value to that type. - /// + /// /// ## Examples /// /// ### Invalid @@ -224,13 +226,13 @@ fn convert_new_expression_to_call_expression(expr: &JsNewExpression) -> Option, node: &JsNewExpression) -> Option { +fn action_remove_new( + ctx: &RuleContext, + node: &JsNewExpression, +) -> Option { let call_expression = convert_new_expression_to_call_expression(node)?; let mut mutation = ctx.root().begin(); - mutation.replace_node::( - node.clone().into(), - call_expression.into(), - ); + mutation.replace_node::(node.clone().into(), call_expression.into()); Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, @@ -252,20 +254,20 @@ fn _convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option // TODO. make::js_new_expression currently does not accept arguments. // To fix that js.ungram needs to be updated, but that breaks a lot of rules. - Some(make::js_new_expression(new_token, callee).build()) + Some(make::js_new_expression(new_token, callee).build()) } -fn _action_add_new(ctx: &RuleContext, node: &JsCallExpression) -> Option { +fn _action_add_new( + ctx: &RuleContext, + node: &JsCallExpression, +) -> Option { let new_expression = _convert_call_expression_to_new_expression(node)?; let mut mutation = ctx.root().begin(); - mutation.replace_node::( - node.clone().into(), - new_expression.into(), - ); + mutation.replace_node::(node.clone().into(), new_expression.into()); Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, message: markup! { "Add ""new""." }.to_owned(), mutation, }) -} \ No newline at end of file +} diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index db6ccf8af2e3..c7dc6e0b8620 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -981,7 +981,7 @@ export interface Nursery { */ useImportRestrictions?: RuleConfiguration_for_Null; /** - * Succinct description of the rule. + * Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. */ useNewForBuiltins?: RuleConfiguration_for_Null; /** diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index ba450ec2ffa8..a14801cff07e 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1557,7 +1557,7 @@ ] }, "useNewForBuiltins": { - "description": "Succinct description of the rule.", + "description": "Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt.", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } From 8472cdd9a56ecc6af99cc26cae785ad59e6d815f Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Sun, 21 Apr 2024 14:33:21 +0300 Subject: [PATCH 05/11] feat: cleanup impl --- .../src/lint/nursery/use_new_for_builtins.rs | 61 ++-- .../nursery/useNewForBuiltins/invalid.js.snap | 301 ++++++++++++++++-- 2 files changed, 310 insertions(+), 52 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs index 69c3512794ac..27ebdd4b9af9 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs @@ -1,6 +1,6 @@ use crate::{services::semantic::Semantic, JsRuleAction}; use biome_analyze::{ - context::RuleContext, declare_rule, ActionCategory, Rule, RuleDiagnostic, RuleSource, + context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic, RuleSource, }; use biome_console::markup; use biome_diagnostics::Applicability; @@ -82,11 +82,12 @@ declare_rule! { name: "useNewForBuiltins", sources: &[RuleSource::EslintUnicorn("new-for-builtins")], recommended: false, + fix_kind: FixKind::Unsafe, } } +/// Sorted array of builtins that require new keyword. const BUILTINS_REQUIRING_NEW: &[&str] = &[ - "Object", "Array", "ArrayBuffer", "BigInt64Array", @@ -94,29 +95,31 @@ const BUILTINS_REQUIRING_NEW: &[&str] = &[ "DataView", "Date", "Error", + "FinalizationRegistry", "Float32Array", "Float64Array", "Function", - "Int8Array", "Int16Array", "Int32Array", + "Int8Array", "Map", - "WeakMap", - "Set", - "WeakSet", + "Object", "Promise", + "Proxy", "RegExp", - "Uint8Array", + "Set", + "SharedArrayBuffer", "Uint16Array", "Uint32Array", + "Uint8Array", "Uint8ClampedArray", - "SharedArrayBuffer", - "Proxy", + "WeakMap", "WeakRef", - "FinalizationRegistry", + "WeakSet", ]; -const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["String", "Number", "Boolean", "Symbol", "BigInt"]; +/// Sorted array of builtins that should not use new keyword. +const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["BigInt", "Boolean", "Number", "String", "Symbol"]; enum BuiltinCreationRule { MustUseNew, @@ -154,14 +157,18 @@ impl Rule for UseNewForBuiltins { let (reference, name) = global_identifier(&callee.omit_parentheses())?; let name_text = name.text(); - if creation_rule.forbidden_builtins_list().contains(&name_text) { + if creation_rule + .forbidden_builtins_list() + .binary_search(&name_text) + .is_ok() + { return ctx .model() .binding(&reference) .is_none() .then_some(UseNewForBuiltinsState { name: name_text.to_string(), - creation_rule: creation_rule, + creation_rule, }); } @@ -192,7 +199,7 @@ impl Rule for UseNewForBuiltins { match node { JsNewOrCallExpression::JsNewExpression(node) => action_remove_new(ctx, node), - JsNewOrCallExpression::JsCallExpression(_) => None, + JsNewOrCallExpression::JsCallExpression(node) => action_add_new(ctx, node), } } } @@ -231,7 +238,7 @@ fn action_remove_new( node: &JsNewExpression, ) -> Option { let call_expression = convert_new_expression_to_call_expression(node)?; - let mut mutation = ctx.root().begin(); + let mut mutation: biome_rowan::BatchMutation = ctx.root().begin(); mutation.replace_node::(node.clone().into(), call_expression.into()); Some(JsRuleAction { category: ActionCategory::QuickFix, @@ -241,7 +248,7 @@ fn action_remove_new( }) } -fn _convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { +fn convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { let new_token = token_decorated_with_space(JsSyntaxKind::NEW_KW); let mut callee = expr.callee().ok()?; @@ -252,16 +259,18 @@ fn _convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option ))?; } - // TODO. make::js_new_expression currently does not accept arguments. - // To fix that js.ungram needs to be updated, but that breaks a lot of rules. - Some(make::js_new_expression(new_token, callee).build()) + Some( + make::js_new_expression(new_token, callee) + .with_arguments(expr.arguments().ok()?) + .build(), + ) } -fn _action_add_new( +fn action_add_new( ctx: &RuleContext, node: &JsCallExpression, ) -> Option { - let new_expression = _convert_call_expression_to_new_expression(node)?; + let new_expression = convert_call_expression_to_new_expression(node)?; let mut mutation = ctx.root().begin(); mutation.replace_node::(node.clone().into(), new_expression.into()); Some(JsRuleAction { @@ -271,3 +280,13 @@ fn _action_add_new( mutation, }) } + +#[test] +fn test_order() { + for items in BUILTINS_REQUIRING_NEW.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } + for items in BUILTINS_NOT_REQUIRING_NEW.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } +} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap index f7bb0edc8e5f..9acacb6e6dff 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap @@ -53,7 +53,7 @@ function foo() { # Diagnostics ``` -invalid.js:1:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:1:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Object() instead of Object(). @@ -62,11 +62,15 @@ invalid.js:1:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 2 │ Array() 3 │ ArrayBuffer() + i Unsafe fix: Add new. + + 1 │ new·Object() + │ ++++ ``` ``` -invalid.js:2:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:2:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Array() instead of Array(). @@ -76,11 +80,18 @@ invalid.js:2:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 3 │ ArrayBuffer() 4 │ BigInt64Array() + i Unsafe fix: Add new. + + 1 1 │ Object() + 2 │ + new· + 2 3 │ Array() + 3 4 │ ArrayBuffer() + ``` ``` -invalid.js:3:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:3:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new ArrayBuffer() instead of ArrayBuffer(). @@ -91,11 +102,19 @@ invalid.js:3:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 4 │ BigInt64Array() 5 │ BigUint64Array() + i Unsafe fix: Add new. + + 1 1 │ Object() + 2 2 │ Array() + 3 │ + new· + 3 4 │ ArrayBuffer() + 4 5 │ BigInt64Array() + ``` ``` -invalid.js:4:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:4:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new BigInt64Array() instead of BigInt64Array(). @@ -106,11 +125,19 @@ invalid.js:4:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 5 │ BigUint64Array() 6 │ DataView() + i Unsafe fix: Add new. + + 2 2 │ Array() + 3 3 │ ArrayBuffer() + 4 │ + new· + 4 5 │ BigInt64Array() + 5 6 │ BigUint64Array() + ``` ``` -invalid.js:5:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:5:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new BigUint64Array() instead of BigUint64Array(). @@ -121,11 +148,19 @@ invalid.js:5:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 6 │ DataView() 7 │ Date() + i Unsafe fix: Add new. + + 3 3 │ ArrayBuffer() + 4 4 │ BigInt64Array() + 5 │ + new· + 5 6 │ BigUint64Array() + 6 7 │ DataView() + ``` ``` -invalid.js:6:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:6:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new DataView() instead of DataView(). @@ -136,11 +171,19 @@ invalid.js:6:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 7 │ Date() 8 │ Error() + i Unsafe fix: Add new. + + 4 4 │ BigInt64Array() + 5 5 │ BigUint64Array() + 6 │ + new· + 6 7 │ DataView() + 7 8 │ Date() + ``` ``` -invalid.js:7:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:7:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Date() instead of Date(). @@ -151,11 +194,19 @@ invalid.js:7:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 8 │ Error() 9 │ Float32Array() + i Unsafe fix: Add new. + + 5 5 │ BigUint64Array() + 6 6 │ DataView() + 7 │ + new· + 7 8 │ Date() + 8 9 │ Error() + ``` ``` -invalid.js:8:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:8:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Error() instead of Error(). @@ -166,11 +217,19 @@ invalid.js:8:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 9 │ Float32Array() 10 │ Float64Array() + i Unsafe fix: Add new. + + 6 6 │ DataView() + 7 7 │ Date() + 8 │ + new· + 8 9 │ Error() + 9 10 │ Float32Array() + ``` ``` -invalid.js:9:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:9:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Float32Array() instead of Float32Array(). @@ -181,11 +240,19 @@ invalid.js:9:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 10 │ Float64Array() 11 │ Function() + i Unsafe fix: Add new. + + 7 7 │ Date() + 8 8 │ Error() + 9 │ + new· + 9 10 │ Float32Array() + 10 11 │ Float64Array() + ``` ``` -invalid.js:10:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:10:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Float64Array() instead of Float64Array(). @@ -196,11 +263,19 @@ invalid.js:10:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 11 │ Function() 12 │ Int8Array() + i Unsafe fix: Add new. + + 8 8 │ Error() + 9 9 │ Float32Array() + 10 │ + new· + 10 11 │ Float64Array() + 11 12 │ Function() + ``` ``` -invalid.js:11:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:11:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Function() instead of Function(). @@ -211,11 +286,19 @@ invalid.js:11:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 12 │ Int8Array() 13 │ Int16Array() + i Unsafe fix: Add new. + + 9 9 │ Float32Array() + 10 10 │ Float64Array() + 11 │ + new· + 11 12 │ Function() + 12 13 │ Int8Array() + ``` ``` -invalid.js:12:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:12:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Int8Array() instead of Int8Array(). @@ -226,11 +309,19 @@ invalid.js:12:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 13 │ Int16Array() 14 │ Int32Array() + i Unsafe fix: Add new. + + 10 10 │ Float64Array() + 11 11 │ Function() + 12 │ + new· + 12 13 │ Int8Array() + 13 14 │ Int16Array() + ``` ``` -invalid.js:13:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:13:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Int16Array() instead of Int16Array(). @@ -241,11 +332,19 @@ invalid.js:13:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 14 │ Int32Array() 15 │ Map() + i Unsafe fix: Add new. + + 11 11 │ Function() + 12 12 │ Int8Array() + 13 │ + new· + 13 14 │ Int16Array() + 14 15 │ Int32Array() + ``` ``` -invalid.js:14:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:14:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Int32Array() instead of Int32Array(). @@ -256,11 +355,19 @@ invalid.js:14:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 15 │ Map() 16 │ WeakMap() + i Unsafe fix: Add new. + + 12 12 │ Int8Array() + 13 13 │ Int16Array() + 14 │ + new· + 14 15 │ Int32Array() + 15 16 │ Map() + ``` ``` -invalid.js:15:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:15:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Map() instead of Map(). @@ -271,11 +378,19 @@ invalid.js:15:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 16 │ WeakMap() 17 │ Set() + i Unsafe fix: Add new. + + 13 13 │ Int16Array() + 14 14 │ Int32Array() + 15 │ + new· + 15 16 │ Map() + 16 17 │ WeakMap() + ``` ``` -invalid.js:16:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:16:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new WeakMap() instead of WeakMap(). @@ -286,11 +401,19 @@ invalid.js:16:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 17 │ Set() 18 │ WeakSet() + i Unsafe fix: Add new. + + 14 14 │ Int32Array() + 15 15 │ Map() + 16 │ + new· + 16 17 │ WeakMap() + 17 18 │ Set() + ``` ``` -invalid.js:17:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:17:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Set() instead of Set(). @@ -301,11 +424,19 @@ invalid.js:17:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 18 │ WeakSet() 19 │ Promise() + i Unsafe fix: Add new. + + 15 15 │ Map() + 16 16 │ WeakMap() + 17 │ + new· + 17 18 │ Set() + 18 19 │ WeakSet() + ``` ``` -invalid.js:18:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:18:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new WeakSet() instead of WeakSet(). @@ -316,11 +447,19 @@ invalid.js:18:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 19 │ Promise() 20 │ RegExp() + i Unsafe fix: Add new. + + 16 16 │ WeakMap() + 17 17 │ Set() + 18 │ + new· + 18 19 │ WeakSet() + 19 20 │ Promise() + ``` ``` -invalid.js:19:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:19:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Promise() instead of Promise(). @@ -331,11 +470,19 @@ invalid.js:19:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 20 │ RegExp() 21 │ Uint8Array() + i Unsafe fix: Add new. + + 17 17 │ Set() + 18 18 │ WeakSet() + 19 │ + new· + 19 20 │ Promise() + 20 21 │ RegExp() + ``` ``` -invalid.js:20:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:20:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new RegExp() instead of RegExp(). @@ -346,11 +493,19 @@ invalid.js:20:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 21 │ Uint8Array() 22 │ Uint16Array() + i Unsafe fix: Add new. + + 18 18 │ WeakSet() + 19 19 │ Promise() + 20 │ + new· + 20 21 │ RegExp() + 21 22 │ Uint8Array() + ``` ``` -invalid.js:21:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:21:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Uint8Array() instead of Uint8Array(). @@ -361,11 +516,19 @@ invalid.js:21:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 22 │ Uint16Array() 23 │ Uint32Array() + i Unsafe fix: Add new. + + 19 19 │ Promise() + 20 20 │ RegExp() + 21 │ + new· + 21 22 │ Uint8Array() + 22 23 │ Uint16Array() + ``` ``` -invalid.js:22:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:22:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Uint16Array() instead of Uint16Array(). @@ -376,11 +539,19 @@ invalid.js:22:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 23 │ Uint32Array() 24 │ Uint8ClampedArray() + i Unsafe fix: Add new. + + 20 20 │ RegExp() + 21 21 │ Uint8Array() + 22 │ + new· + 22 23 │ Uint16Array() + 23 24 │ Uint32Array() + ``` ``` -invalid.js:23:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:23:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Uint32Array() instead of Uint32Array(). @@ -391,11 +562,19 @@ invalid.js:23:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 24 │ Uint8ClampedArray() 25 │ SharedArrayBuffer() + i Unsafe fix: Add new. + + 21 21 │ Uint8Array() + 22 22 │ Uint16Array() + 23 │ + new· + 23 24 │ Uint32Array() + 24 25 │ Uint8ClampedArray() + ``` ``` -invalid.js:24:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:24:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Uint8ClampedArray() instead of Uint8ClampedArray(). @@ -406,11 +585,19 @@ invalid.js:24:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 25 │ SharedArrayBuffer() 26 │ Proxy() + i Unsafe fix: Add new. + + 22 22 │ Uint16Array() + 23 23 │ Uint32Array() + 24 │ + new· + 24 25 │ Uint8ClampedArray() + 25 26 │ SharedArrayBuffer() + ``` ``` -invalid.js:25:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:25:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new SharedArrayBuffer() instead of SharedArrayBuffer(). @@ -421,11 +608,19 @@ invalid.js:25:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 26 │ Proxy() 27 │ WeakRef() + i Unsafe fix: Add new. + + 23 23 │ Uint32Array() + 24 24 │ Uint8ClampedArray() + 25 │ + new· + 25 26 │ SharedArrayBuffer() + 26 27 │ Proxy() + ``` ``` -invalid.js:26:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:26:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Proxy() instead of Proxy(). @@ -436,11 +631,19 @@ invalid.js:26:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 27 │ WeakRef() 28 │ FinalizationRegistry() + i Unsafe fix: Add new. + + 24 24 │ Uint8ClampedArray() + 25 25 │ SharedArrayBuffer() + 26 │ + new· + 26 27 │ Proxy() + 27 28 │ WeakRef() + ``` ``` -invalid.js:27:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:27:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new WeakRef() instead of WeakRef(). @@ -451,11 +654,19 @@ invalid.js:27:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 28 │ FinalizationRegistry() 29 │ window.Object({}) + i Unsafe fix: Add new. + + 25 25 │ SharedArrayBuffer() + 26 26 │ Proxy() + 27 │ + new· + 27 28 │ WeakRef() + 28 29 │ FinalizationRegistry() + ``` ``` -invalid.js:28:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:28:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new FinalizationRegistry() instead of FinalizationRegistry(). @@ -466,11 +677,19 @@ invalid.js:28:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 29 │ window.Object({}) 30 │ globalThis.Object() + i Unsafe fix: Add new. + + 26 26 │ Proxy() + 27 27 │ WeakRef() + 28 │ + new· + 28 29 │ FinalizationRegistry() + 29 30 │ window.Object({}) + ``` ``` -invalid.js:29:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:29:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Object() instead of Object(). @@ -481,11 +700,19 @@ invalid.js:29:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 30 │ globalThis.Object() 31 │ function foo() { + i Unsafe fix: Add new. + + 27 27 │ WeakRef() + 28 28 │ FinalizationRegistry() + 29 │ + new· + 29 30 │ window.Object({}) + 30 31 │ globalThis.Object() + ``` ``` -invalid.js:30:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:30:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Object() instead of Object(). @@ -496,11 +723,19 @@ invalid.js:30:1 lint/nursery/useNewForBuiltins ━━━━━━━━━━━ 31 │ function foo() { 32 │ return globalThis.Object({ foo: 'bar' }) + i Unsafe fix: Add new. + + 28 28 │ FinalizationRegistry() + 29 29 │ window.Object({}) + 30 │ + new· + 30 31 │ globalThis.Object() + 31 32 │ function foo() { + ``` ``` -invalid.js:32:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:32:12 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Object() instead of Object(). @@ -511,6 +746,10 @@ invalid.js:32:12 lint/nursery/useNewForBuiltins ━━━━━━━━━━ 33 │ } 34 │ + i Unsafe fix: Add new. + + 32 │ ····return·new·globalThis.Object({·foo:·'bar'·}) + │ ++++ ``` From 51bf3e0609c3fbafa5a4a3104051161f1125b85b Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Mon, 22 Apr 2024 02:19:14 +0300 Subject: [PATCH 06/11] feat(linter): implement use_consistent_new_builtin --- .../migrate/eslint_any_rule_to_biome.rs | 4 +- .../biome_configuration/src/linter/rules.rs | 24 +- .../src/categories.rs | 2 +- crates/biome_js_analyze/src/lint/nursery.rs | 4 +- .../nursery/use_consistent_new_builtin.rs | 196 ++++ .../src/lint/nursery/use_new_for_builtins.rs | 292 ------ crates/biome_js_analyze/src/options.rs | 3 +- .../invalid.js | 13 +- .../useConsistentNewBuiltin/invalid.js.snap | 628 ++++++++++++ .../valid.js | 19 - .../valid.js.snap | 19 - .../nursery/useNewForBuiltins/invalid.js.snap | 905 ------------------ .../@biomejs/backend-jsonrpc/src/workspace.ts | 10 +- .../@biomejs/biome/configuration_schema.json | 8 +- 14 files changed, 853 insertions(+), 1274 deletions(-) create mode 100644 crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs delete mode 100644 crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs rename crates/biome_js_analyze/tests/specs/nursery/{useNewForBuiltins => useConsistentNewBuiltin}/invalid.js (64%) create mode 100644 crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap rename crates/biome_js_analyze/tests/specs/nursery/{useNewForBuiltins => useConsistentNewBuiltin}/valid.js (68%) rename crates/biome_js_analyze/tests/specs/nursery/{useNewForBuiltins => useConsistentNewBuiltin}/valid.js.snap (71%) delete mode 100644 crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index 94ee34d43291..54ee88e5188d 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -1223,7 +1223,9 @@ pub(crate) fn migrate_eslint_any_rule( return false; } let group = rules.nursery.get_or_insert_with(Default::default); - let rule = group.use_new_for_builtins.get_or_insert(Default::default()); + let rule = group + .use_consistent_new_builtin + .get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } "unicorn/no-array-for-each" => { diff --git a/crates/biome_configuration/src/linter/rules.rs b/crates/biome_configuration/src/linter/rules.rs index 3ac3654dd410..a22901c38720 100644 --- a/crates/biome_configuration/src/linter/rules.rs +++ b/crates/biome_configuration/src/linter/rules.rs @@ -2696,12 +2696,12 @@ pub struct Nursery { #[doc = "Disallow the use of dependencies that aren't specified in the package.json."] #[serde(skip_serializing_if = "Option::is_none")] pub no_undeclared_dependencies: Option>, + #[doc = "Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt."] + #[serde(skip_serializing_if = "Option::is_none")] + pub use_consistent_new_builtin: Option>, #[doc = "Disallows package private imports."] #[serde(skip_serializing_if = "Option::is_none")] pub use_import_restrictions: Option>, - #[doc = "Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt."] - #[serde(skip_serializing_if = "Option::is_none")] - pub use_new_for_builtins: Option>, #[doc = "Enforce the sorting of CSS utility classes."] #[serde(skip_serializing_if = "Option::is_none")] pub use_sorted_classes: Option>, @@ -2739,8 +2739,8 @@ impl Nursery { "noReactSpecificProps", "noRestrictedImports", "noUndeclaredDependencies", + "useConsistentNewBuiltin", "useImportRestrictions", - "useNewForBuiltins", "useSortedClasses", ]; const RECOMMENDED_RULES: &'static [&'static str] = &[ @@ -2879,12 +2879,12 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_consistent_new_builtin.as_ref() { if rule.is_enabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_new_for_builtins.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[17])); } @@ -2978,12 +2978,12 @@ impl Nursery { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[15])); } } - if let Some(rule) = self.use_import_restrictions.as_ref() { + if let Some(rule) = self.use_consistent_new_builtin.as_ref() { if rule.is_disabled() { index_set.insert(RuleFilter::Rule(Self::GROUP_NAME, Self::GROUP_RULES[16])); } } - if let Some(rule) = self.use_new_for_builtins.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[17])); } @@ -3093,12 +3093,12 @@ impl Nursery { .no_undeclared_dependencies .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "useImportRestrictions" => self - .use_import_restrictions + "useConsistentNewBuiltin" => self + .use_consistent_new_builtin .as_ref() .map(|conf| (conf.level(), conf.get_options())), - "useNewForBuiltins" => self - .use_new_for_builtins + "useImportRestrictions" => self + .use_import_restrictions .as_ref() .map(|conf| (conf.level(), conf.get_options())), "useSortedClasses" => self diff --git a/crates/biome_diagnostics_categories/src/categories.rs b/crates/biome_diagnostics_categories/src/categories.rs index c381eec02165..76b8af99962c 100644 --- a/crates/biome_diagnostics_categories/src/categories.rs +++ b/crates/biome_diagnostics_categories/src/categories.rs @@ -128,8 +128,8 @@ define_categories! { "lint/nursery/noTypeOnlyImportAttributes": "https://biomejs.dev/linter/rules/no-type-only-import-attributes", "lint/nursery/noUndeclaredDependencies": "https://biomejs.dev/linter/rules/no-undeclared-dependencies", "lint/nursery/useBiomeSuppressionComment": "https://biomejs.dev/linter/rules/use-biome-suppression-comment", + "lint/nursery/useConsistentNewBuiltin": "https://biomejs.dev/linter/rules/use-consistent-new-builtin", "lint/nursery/useImportRestrictions": "https://biomejs.dev/linter/rules/use-import-restrictions", - "lint/nursery/useNewForBuiltins": "https://biomejs.dev/linter/rules/use-new-for-builtins", "lint/nursery/useSortedClasses": "https://biomejs.dev/linter/rules/use-sorted-classes", "lint/performance/noAccumulatingSpread": "https://biomejs.dev/linter/rules/no-accumulating-spread", "lint/performance/noBarrelFile": "https://biomejs.dev/linter/rules/no-barrel-file", diff --git a/crates/biome_js_analyze/src/lint/nursery.rs b/crates/biome_js_analyze/src/lint/nursery.rs index 0f45d3e373f2..c7c98b6932e6 100644 --- a/crates/biome_js_analyze/src/lint/nursery.rs +++ b/crates/biome_js_analyze/src/lint/nursery.rs @@ -13,8 +13,8 @@ pub mod no_nodejs_modules; pub mod no_react_specific_props; pub mod no_restricted_imports; pub mod no_undeclared_dependencies; +pub mod use_consistent_new_builtin; pub mod use_import_restrictions; -pub mod use_new_for_builtins; pub mod use_sorted_classes; declare_group! { @@ -32,8 +32,8 @@ declare_group! { self :: no_react_specific_props :: NoReactSpecificProps , self :: no_restricted_imports :: NoRestrictedImports , self :: no_undeclared_dependencies :: NoUndeclaredDependencies , + self :: use_consistent_new_builtin :: UseConsistentNewBuiltin , self :: use_import_restrictions :: UseImportRestrictions , - self :: use_new_for_builtins :: UseNewForBuiltins , self :: use_sorted_classes :: UseSortedClasses , ] } diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs new file mode 100644 index 000000000000..782f7d434b05 --- /dev/null +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs @@ -0,0 +1,196 @@ +use crate::{services::semantic::Semantic, JsRuleAction}; +use biome_analyze::{ + context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic, RuleSource, +}; +use biome_console::markup; +use biome_diagnostics::Applicability; +use biome_js_factory::make; +use biome_js_syntax::{ + global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind, +}; +use biome_rowan::{AstNode, BatchMutationExt, TriviaPieceKind}; + +declare_rule! { + /// Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. + /// + /// They work the same, but new should be preferred for consistency with other constructors. + /// Enforces the use of new for following builtins: + /// + /// - Object + /// - Array + /// - ArrayBuffer + /// - BigInt64Array + /// - BigUint64Array + /// - DataView + /// - Date + /// - Error + /// - Float32Array + /// - Float64Array + /// - Function + /// - Int8Array + /// - Int16Array + /// - Int32Array + /// - Map + /// - WeakMap + /// - Set + /// - WeakSet + /// - Promise + /// - RegExp + /// - Uint8Array + /// - Uint16Array + /// - Uint32Array + /// - Uint8ClampedArray + /// - SharedArrayBuffer + /// - Proxy + /// - WeakRef + /// - FinalizationRegistry + /// + /// Following builtins are handled by [noInvalidBuiltin](https://biomejs.dev/linter/rules/no-invalid-new-builtin/): + /// + /// - String + /// - Number + /// - Boolean + /// - Symbol + /// - BigInt + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// const list = Array(10); + /// const now = Date(); + /// const map = Map([ + /// ['foo', 'bar'] + /// ]); + /// ``` + /// + /// ### Valid + /// + /// ```js + /// const list = new Array(10); + /// const now = new Date(); + /// const map = new Map([ + /// ['foo', 'bar'] + /// ]); + /// ``` + /// + pub UseConsistentNewBuiltin { + version: "next", + name: "useConsistentNewBuiltin", + sources: &[RuleSource::EslintUnicorn("new-for-builtins")], + recommended: false, + fix_kind: FixKind::Unsafe, + } +} + +/// Sorted array of builtins that require new keyword. +const BUILTINS_REQUIRING_NEW: &[&str] = &[ + "Array", + "ArrayBuffer", + "BigInt64Array", + "BigUint64Array", + "DataView", + "Date", + "Error", + "FinalizationRegistry", + "Float32Array", + "Float64Array", + "Function", + "Int16Array", + "Int32Array", + "Int8Array", + "Map", + "Object", + "Promise", + "Proxy", + "RegExp", + "Set", + "SharedArrayBuffer", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakRef", + "WeakSet", +]; + +impl Rule for UseConsistentNewBuiltin { + type Query = Semantic; + type State = String; + type Signals = Option; + type Options = (); + + fn run(ctx: &RuleContext) -> Self::Signals { + let node = ctx.query(); + let callee = node.callee().ok()?; + + let (reference, name) = global_identifier(&callee.omit_parentheses())?; + let name_text = name.text(); + + if BUILTINS_REQUIRING_NEW.binary_search(&name_text).is_ok() { + return ctx + .model() + .binding(&reference) + .is_none() + .then_some(name_text.to_string()); + } + + None + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let node = ctx.query(); + + Some(RuleDiagnostic::new( + rule_category!(), + node.range(), + markup! { + "Use ""new "{state}"()"" instead of "{state}"()""." + }, + )) + } + + fn action(ctx: &RuleContext, _: &Self::State) -> Option { + let node = ctx.query(); + let new_expression = convert_call_expression_to_new_expression(node)?; + + let mut mutation = ctx.root().begin(); + mutation.replace_node_discard_trivia::( + node.clone().into(), + new_expression.into(), + ); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Add ""new""." }.to_owned(), + mutation, + }) + } +} + +fn convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { + let mut callee = expr.callee().ok()?; + let leading_trivia_pieces = callee.syntax().first_leading_trivia()?.pieces(); + + let new_token = make::token(JsSyntaxKind::NEW_KW) + .with_leading_trivia_pieces(leading_trivia_pieces) + .with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]); + + // Remove leading trivia from the callee. + callee = callee.with_leading_trivia_pieces([])?; + + Some( + make::js_new_expression(new_token.clone(), callee) + .with_arguments(expr.arguments().ok()?) + .build(), + ) +} + +#[test] +fn test_order() { + for items in BUILTINS_REQUIRING_NEW.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } +} diff --git a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs b/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs deleted file mode 100644 index 27ebdd4b9af9..000000000000 --- a/crates/biome_js_analyze/src/lint/nursery/use_new_for_builtins.rs +++ /dev/null @@ -1,292 +0,0 @@ -use crate::{services::semantic::Semantic, JsRuleAction}; -use biome_analyze::{ - context::RuleContext, declare_rule, ActionCategory, FixKind, Rule, RuleDiagnostic, RuleSource, -}; -use biome_console::markup; -use biome_diagnostics::Applicability; -use biome_js_factory::make::{self, token_decorated_with_space}; -use biome_js_syntax::{ - global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind, -}; -use biome_rowan::{chain_trivia_pieces, declare_node_union, AstNode, BatchMutationExt}; - -declare_rule! { - /// Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. - /// - /// They work the same, but new should be preferred for consistency with other constructors. - /// Enforces the use of new for following builtins: - /// - /// - Object - /// - Array - /// - ArrayBuffer - /// - BigInt64Array - /// - BigUint64Array - /// - DataView - /// - Date - /// - Error - /// - Float32Array - /// - Float64Array - /// - Function - /// - Int8Array - /// - Int16Array - /// - Int32Array - /// - Map - /// - WeakMap - /// - Set - /// - WeakSet - /// - Promise - /// - RegExp - /// - Uint8Array - /// - Uint16Array - /// - Uint32Array - /// - Uint8ClampedArray - /// - SharedArrayBuffer - /// - Proxy - /// - WeakRef - /// - FinalizationRegistry - /// - /// Disallows the use of new for following builtins. - /// - /// - String - /// - Number - /// - Boolean - /// - Symbol - /// - BigInt - /// - /// > These should not use new as that would create object wrappers for the primitive values, which is not what you want. However, without new they can be useful for coercing a value to that type. - /// - /// ## Examples - /// - /// ### Invalid - /// - /// ```js,expect_diagnostic - /// const list = Array(10); - /// const now = Date(); - /// const map = Map([ - /// ['foo', 'bar'] - /// ]); - /// ``` - /// - /// ### Valid - /// - /// ```js - /// const list = new Array(10); - /// const now = new Date(); - /// const map = new Map([ - /// ['foo', 'bar'] - /// ]); - /// ``` - /// - pub UseNewForBuiltins { - version: "next", - name: "useNewForBuiltins", - sources: &[RuleSource::EslintUnicorn("new-for-builtins")], - recommended: false, - fix_kind: FixKind::Unsafe, - } -} - -/// Sorted array of builtins that require new keyword. -const BUILTINS_REQUIRING_NEW: &[&str] = &[ - "Array", - "ArrayBuffer", - "BigInt64Array", - "BigUint64Array", - "DataView", - "Date", - "Error", - "FinalizationRegistry", - "Float32Array", - "Float64Array", - "Function", - "Int16Array", - "Int32Array", - "Int8Array", - "Map", - "Object", - "Promise", - "Proxy", - "RegExp", - "Set", - "SharedArrayBuffer", - "Uint16Array", - "Uint32Array", - "Uint8Array", - "Uint8ClampedArray", - "WeakMap", - "WeakRef", - "WeakSet", -]; - -/// Sorted array of builtins that should not use new keyword. -const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["BigInt", "Boolean", "Number", "String", "Symbol"]; - -enum BuiltinCreationRule { - MustUseNew, - MustNotUseNew, -} - -impl BuiltinCreationRule { - fn forbidden_builtins_list(&self) -> &[&str] { - match self { - BuiltinCreationRule::MustUseNew => BUILTINS_REQUIRING_NEW, - BuiltinCreationRule::MustNotUseNew => BUILTINS_NOT_REQUIRING_NEW, - } - } -} - -pub struct UseNewForBuiltinsState { - name: String, - creation_rule: BuiltinCreationRule, -} - -declare_node_union! { - pub JsNewOrCallExpression = JsNewExpression | JsCallExpression -} - -impl Rule for UseNewForBuiltins { - type Query = Semantic; - type State = UseNewForBuiltinsState; - type Signals = Option; - type Options = (); - - fn run(ctx: &RuleContext) -> Self::Signals { - let node = ctx.query(); - let (callee, creation_rule) = extract_callee_and_rule(node)?; - - let (reference, name) = global_identifier(&callee.omit_parentheses())?; - let name_text = name.text(); - - if creation_rule - .forbidden_builtins_list() - .binary_search(&name_text) - .is_ok() - { - return ctx - .model() - .binding(&reference) - .is_none() - .then_some(UseNewForBuiltinsState { - name: name_text.to_string(), - creation_rule, - }); - } - - None - } - - fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { - let node: &JsNewOrCallExpression = ctx.query(); - - let name = &state.name; - - let (use_this, instead_of) = match state.creation_rule { - BuiltinCreationRule::MustUseNew => ("new ", ""), - BuiltinCreationRule::MustNotUseNew => ("", "new "), - }; - - Some(RuleDiagnostic::new( - rule_category!(), - node.range(), - markup! { - "Use "{use_this}{name}"()"" instead of "{instead_of}{name}"()""." - }, - )) - } - - fn action(ctx: &RuleContext, _: &Self::State) -> Option { - let node = ctx.query(); - - match node { - JsNewOrCallExpression::JsNewExpression(node) => action_remove_new(ctx, node), - JsNewOrCallExpression::JsCallExpression(node) => action_add_new(ctx, node), - } - } -} - -fn extract_callee_and_rule( - node: &JsNewOrCallExpression, -) -> Option<(AnyJsExpression, BuiltinCreationRule)> { - match node { - JsNewOrCallExpression::JsNewExpression(node) => { - let callee = node.callee().ok()?; - - Some((callee, BuiltinCreationRule::MustNotUseNew)) - } - JsNewOrCallExpression::JsCallExpression(node) => { - let callee: AnyJsExpression = node.callee().ok()?; - - Some((callee, BuiltinCreationRule::MustUseNew)) - } - } -} - -fn convert_new_expression_to_call_expression(expr: &JsNewExpression) -> Option { - let new_token = expr.new_token().ok()?; - let mut callee = expr.callee().ok()?; - if new_token.has_leading_comments() || new_token.has_trailing_comments() { - callee = callee.prepend_trivia_pieces(chain_trivia_pieces( - new_token.leading_trivia().pieces(), - new_token.trailing_trivia().pieces(), - ))?; - } - Some(make::js_call_expression(callee, expr.arguments()?).build()) -} - -fn action_remove_new( - ctx: &RuleContext, - node: &JsNewExpression, -) -> Option { - let call_expression = convert_new_expression_to_call_expression(node)?; - let mut mutation: biome_rowan::BatchMutation = ctx.root().begin(); - mutation.replace_node::(node.clone().into(), call_expression.into()); - Some(JsRuleAction { - category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, - message: markup! { "Remove ""new""." }.to_owned(), - mutation, - }) -} - -fn convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { - let new_token = token_decorated_with_space(JsSyntaxKind::NEW_KW); - - let mut callee = expr.callee().ok()?; - if new_token.has_leading_comments() || new_token.has_trailing_comments() { - callee = callee.prepend_trivia_pieces(chain_trivia_pieces( - new_token.leading_trivia().pieces(), - new_token.trailing_trivia().pieces(), - ))?; - } - - Some( - make::js_new_expression(new_token, callee) - .with_arguments(expr.arguments().ok()?) - .build(), - ) -} - -fn action_add_new( - ctx: &RuleContext, - node: &JsCallExpression, -) -> Option { - let new_expression = convert_call_expression_to_new_expression(node)?; - let mut mutation = ctx.root().begin(); - mutation.replace_node::(node.clone().into(), new_expression.into()); - Some(JsRuleAction { - category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, - message: markup! { "Add ""new""." }.to_owned(), - mutation, - }) -} - -#[test] -fn test_order() { - for items in BUILTINS_REQUIRING_NEW.windows(2) { - assert!(items[0] < items[1], "{} < {}", items[0], items[1]); - } - for items in BUILTINS_NOT_REQUIRING_NEW.windows(2) { - assert!(items[0] < items[1], "{} < {}", items[0], items[1]); - } -} diff --git a/crates/biome_js_analyze/src/options.rs b/crates/biome_js_analyze/src/options.rs index 916c5cd9ad02..53de58d055f9 100644 --- a/crates/biome_js_analyze/src/options.rs +++ b/crates/biome_js_analyze/src/options.rs @@ -257,6 +257,7 @@ pub type UseButtonType = pub type UseCollapsedElseIf = ::Options; pub type UseConsistentArrayType = < lint :: style :: use_consistent_array_type :: UseConsistentArrayType as biome_analyze :: Rule > :: Options ; +pub type UseConsistentNewBuiltin = < lint :: nursery :: use_consistent_new_builtin :: UseConsistentNewBuiltin as biome_analyze :: Rule > :: Options ; pub type UseConst = ::Options; pub type UseDefaultParameterLast = < lint :: style :: use_default_parameter_last :: UseDefaultParameterLast as biome_analyze :: Rule > :: Options ; pub type UseDefaultSwitchClauseLast = < lint :: suspicious :: use_default_switch_clause_last :: UseDefaultSwitchClauseLast as biome_analyze :: Rule > :: Options ; @@ -301,8 +302,6 @@ pub type UseNamespaceKeyword = ::Options; pub type UseNamingConvention = ::Options; -pub type UseNewForBuiltins = - ::Options; pub type UseNodeAssertStrict = ::Options; pub type UseNodejsImportProtocol = < lint :: style :: use_nodejs_import_protocol :: UseNodejsImportProtocol as biome_analyze :: Rule > :: Options ; diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js similarity index 64% rename from crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js index 0a50fb5d1868..0b9a0230831e 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js @@ -29,16 +29,5 @@ FinalizationRegistry() window.Object({}) globalThis.Object() function foo() { - return globalThis.Object({ foo: 'bar' }) -} - -new String() -new Number() -new Boolean() -new Symbol() -new BigInt() -new window.String(123) -new globalThis.String() -function foo() { - return new globalThis.String("foo") + return /** Comment */ globalThis.Object({ foo: 'bar' }) } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap new file mode 100644 index 000000000000..b11e31dcce0b --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap @@ -0,0 +1,628 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: invalid.js +--- +# Input +```jsx +Object() +Array() +ArrayBuffer() +BigInt64Array() +BigUint64Array() +DataView() +Date() +Error() +Float32Array() +Float64Array() +Function() +Int8Array() +Int16Array() +Int32Array() +Map() +WeakMap() +Set() +WeakSet() +Promise() +RegExp() +Uint8Array() +Uint16Array() +Uint32Array() +Uint8ClampedArray() +SharedArrayBuffer() +Proxy() +WeakRef() +FinalizationRegistry() +window.Object({}) +globalThis.Object() +function foo() { + return /** Comment */ globalThis.Object({ foo: 'bar' }) +} + +``` + +# Diagnostics +``` +invalid.js:1:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + > 1 │ Object() + │ ^^^^^^^^ + 2 │ Array() + 3 │ ArrayBuffer() + + i Unsafe fix: Add new. + + 1 │ new·Object() + │ ++++ + +``` + +``` +invalid.js:2:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Array() instead of Array(). + + 1 │ Object() + > 2 │ Array() + │ ^^^^^^^ + 3 │ ArrayBuffer() + 4 │ BigInt64Array() + + i Unsafe fix: Add new. + + 2 │ new·Array() + │ ++++ + +``` + +``` +invalid.js:3:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new ArrayBuffer() instead of ArrayBuffer(). + + 1 │ Object() + 2 │ Array() + > 3 │ ArrayBuffer() + │ ^^^^^^^^^^^^^ + 4 │ BigInt64Array() + 5 │ BigUint64Array() + + i Unsafe fix: Add new. + + 3 │ new·ArrayBuffer() + │ ++++ + +``` + +``` +invalid.js:4:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new BigInt64Array() instead of BigInt64Array(). + + 2 │ Array() + 3 │ ArrayBuffer() + > 4 │ BigInt64Array() + │ ^^^^^^^^^^^^^^^ + 5 │ BigUint64Array() + 6 │ DataView() + + i Unsafe fix: Add new. + + 4 │ new·BigInt64Array() + │ ++++ + +``` + +``` +invalid.js:5:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new BigUint64Array() instead of BigUint64Array(). + + 3 │ ArrayBuffer() + 4 │ BigInt64Array() + > 5 │ BigUint64Array() + │ ^^^^^^^^^^^^^^^^ + 6 │ DataView() + 7 │ Date() + + i Unsafe fix: Add new. + + 5 │ new·BigUint64Array() + │ ++++ + +``` + +``` +invalid.js:6:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new DataView() instead of DataView(). + + 4 │ BigInt64Array() + 5 │ BigUint64Array() + > 6 │ DataView() + │ ^^^^^^^^^^ + 7 │ Date() + 8 │ Error() + + i Unsafe fix: Add new. + + 6 │ new·DataView() + │ ++++ + +``` + +``` +invalid.js:7:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Date() instead of Date(). + + 5 │ BigUint64Array() + 6 │ DataView() + > 7 │ Date() + │ ^^^^^^ + 8 │ Error() + 9 │ Float32Array() + + i Unsafe fix: Add new. + + 7 │ new·Date() + │ ++++ + +``` + +``` +invalid.js:8:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Error() instead of Error(). + + 6 │ DataView() + 7 │ Date() + > 8 │ Error() + │ ^^^^^^^ + 9 │ Float32Array() + 10 │ Float64Array() + + i Unsafe fix: Add new. + + 8 │ new·Error() + │ ++++ + +``` + +``` +invalid.js:9:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Float32Array() instead of Float32Array(). + + 7 │ Date() + 8 │ Error() + > 9 │ Float32Array() + │ ^^^^^^^^^^^^^^ + 10 │ Float64Array() + 11 │ Function() + + i Unsafe fix: Add new. + + 9 │ new·Float32Array() + │ ++++ + +``` + +``` +invalid.js:10:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Float64Array() instead of Float64Array(). + + 8 │ Error() + 9 │ Float32Array() + > 10 │ Float64Array() + │ ^^^^^^^^^^^^^^ + 11 │ Function() + 12 │ Int8Array() + + i Unsafe fix: Add new. + + 10 │ new·Float64Array() + │ ++++ + +``` + +``` +invalid.js:11:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Function() instead of Function(). + + 9 │ Float32Array() + 10 │ Float64Array() + > 11 │ Function() + │ ^^^^^^^^^^ + 12 │ Int8Array() + 13 │ Int16Array() + + i Unsafe fix: Add new. + + 11 │ new·Function() + │ ++++ + +``` + +``` +invalid.js:12:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Int8Array() instead of Int8Array(). + + 10 │ Float64Array() + 11 │ Function() + > 12 │ Int8Array() + │ ^^^^^^^^^^^ + 13 │ Int16Array() + 14 │ Int32Array() + + i Unsafe fix: Add new. + + 12 │ new·Int8Array() + │ ++++ + +``` + +``` +invalid.js:13:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Int16Array() instead of Int16Array(). + + 11 │ Function() + 12 │ Int8Array() + > 13 │ Int16Array() + │ ^^^^^^^^^^^^ + 14 │ Int32Array() + 15 │ Map() + + i Unsafe fix: Add new. + + 13 │ new·Int16Array() + │ ++++ + +``` + +``` +invalid.js:14:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Int32Array() instead of Int32Array(). + + 12 │ Int8Array() + 13 │ Int16Array() + > 14 │ Int32Array() + │ ^^^^^^^^^^^^ + 15 │ Map() + 16 │ WeakMap() + + i Unsafe fix: Add new. + + 14 │ new·Int32Array() + │ ++++ + +``` + +``` +invalid.js:15:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Map() instead of Map(). + + 13 │ Int16Array() + 14 │ Int32Array() + > 15 │ Map() + │ ^^^^^ + 16 │ WeakMap() + 17 │ Set() + + i Unsafe fix: Add new. + + 15 │ new·Map() + │ ++++ + +``` + +``` +invalid.js:16:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new WeakMap() instead of WeakMap(). + + 14 │ Int32Array() + 15 │ Map() + > 16 │ WeakMap() + │ ^^^^^^^^^ + 17 │ Set() + 18 │ WeakSet() + + i Unsafe fix: Add new. + + 16 │ new·WeakMap() + │ ++++ + +``` + +``` +invalid.js:17:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Set() instead of Set(). + + 15 │ Map() + 16 │ WeakMap() + > 17 │ Set() + │ ^^^^^ + 18 │ WeakSet() + 19 │ Promise() + + i Unsafe fix: Add new. + + 17 │ new·Set() + │ ++++ + +``` + +``` +invalid.js:18:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new WeakSet() instead of WeakSet(). + + 16 │ WeakMap() + 17 │ Set() + > 18 │ WeakSet() + │ ^^^^^^^^^ + 19 │ Promise() + 20 │ RegExp() + + i Unsafe fix: Add new. + + 18 │ new·WeakSet() + │ ++++ + +``` + +``` +invalid.js:19:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Promise() instead of Promise(). + + 17 │ Set() + 18 │ WeakSet() + > 19 │ Promise() + │ ^^^^^^^^^ + 20 │ RegExp() + 21 │ Uint8Array() + + i Unsafe fix: Add new. + + 19 │ new·Promise() + │ ++++ + +``` + +``` +invalid.js:20:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new RegExp() instead of RegExp(). + + 18 │ WeakSet() + 19 │ Promise() + > 20 │ RegExp() + │ ^^^^^^^^ + 21 │ Uint8Array() + 22 │ Uint16Array() + + i Unsafe fix: Add new. + + 20 │ new·RegExp() + │ ++++ + +``` + +``` +invalid.js:21:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint8Array() instead of Uint8Array(). + + 19 │ Promise() + 20 │ RegExp() + > 21 │ Uint8Array() + │ ^^^^^^^^^^^^ + 22 │ Uint16Array() + 23 │ Uint32Array() + + i Unsafe fix: Add new. + + 21 │ new·Uint8Array() + │ ++++ + +``` + +``` +invalid.js:22:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint16Array() instead of Uint16Array(). + + 20 │ RegExp() + 21 │ Uint8Array() + > 22 │ Uint16Array() + │ ^^^^^^^^^^^^^ + 23 │ Uint32Array() + 24 │ Uint8ClampedArray() + + i Unsafe fix: Add new. + + 22 │ new·Uint16Array() + │ ++++ + +``` + +``` +invalid.js:23:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint32Array() instead of Uint32Array(). + + 21 │ Uint8Array() + 22 │ Uint16Array() + > 23 │ Uint32Array() + │ ^^^^^^^^^^^^^ + 24 │ Uint8ClampedArray() + 25 │ SharedArrayBuffer() + + i Unsafe fix: Add new. + + 23 │ new·Uint32Array() + │ ++++ + +``` + +``` +invalid.js:24:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Uint8ClampedArray() instead of Uint8ClampedArray(). + + 22 │ Uint16Array() + 23 │ Uint32Array() + > 24 │ Uint8ClampedArray() + │ ^^^^^^^^^^^^^^^^^^^ + 25 │ SharedArrayBuffer() + 26 │ Proxy() + + i Unsafe fix: Add new. + + 24 │ new·Uint8ClampedArray() + │ ++++ + +``` + +``` +invalid.js:25:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new SharedArrayBuffer() instead of SharedArrayBuffer(). + + 23 │ Uint32Array() + 24 │ Uint8ClampedArray() + > 25 │ SharedArrayBuffer() + │ ^^^^^^^^^^^^^^^^^^^ + 26 │ Proxy() + 27 │ WeakRef() + + i Unsafe fix: Add new. + + 25 │ new·SharedArrayBuffer() + │ ++++ + +``` + +``` +invalid.js:26:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Proxy() instead of Proxy(). + + 24 │ Uint8ClampedArray() + 25 │ SharedArrayBuffer() + > 26 │ Proxy() + │ ^^^^^^^ + 27 │ WeakRef() + 28 │ FinalizationRegistry() + + i Unsafe fix: Add new. + + 26 │ new·Proxy() + │ ++++ + +``` + +``` +invalid.js:27:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new WeakRef() instead of WeakRef(). + + 25 │ SharedArrayBuffer() + 26 │ Proxy() + > 27 │ WeakRef() + │ ^^^^^^^^^ + 28 │ FinalizationRegistry() + 29 │ window.Object({}) + + i Unsafe fix: Add new. + + 27 │ new·WeakRef() + │ ++++ + +``` + +``` +invalid.js:28:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new FinalizationRegistry() instead of FinalizationRegistry(). + + 26 │ Proxy() + 27 │ WeakRef() + > 28 │ FinalizationRegistry() + │ ^^^^^^^^^^^^^^^^^^^^^^ + 29 │ window.Object({}) + 30 │ globalThis.Object() + + i Unsafe fix: Add new. + + 28 │ new·FinalizationRegistry() + │ ++++ + +``` + +``` +invalid.js:29:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + 27 │ WeakRef() + 28 │ FinalizationRegistry() + > 29 │ window.Object({}) + │ ^^^^^^^^^^^^^^^^^ + 30 │ globalThis.Object() + 31 │ function foo() { + + i Unsafe fix: Add new. + + 29 │ new·window.Object({}) + │ ++++ + +``` + +``` +invalid.js:30:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + 28 │ FinalizationRegistry() + 29 │ window.Object({}) + > 30 │ globalThis.Object() + │ ^^^^^^^^^^^^^^^^^^^ + 31 │ function foo() { + 32 │ return /** Comment */ globalThis.Object({ foo: 'bar' }) + + i Unsafe fix: Add new. + + 30 │ new·globalThis.Object() + │ ++++ + +``` + +``` +invalid.js:32:27 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use new Object() instead of Object(). + + 30 │ globalThis.Object() + 31 │ function foo() { + > 32 │ return /** Comment */ globalThis.Object({ foo: 'bar' }) + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 33 │ } + 34 │ + + i Unsafe fix: Add new. + + 32 │ ····return·/**·Comment·*/·new·globalThis.Object({·foo:·'bar'·}) + │ ++++ + +``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js similarity index 68% rename from crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js index de0cd727eb2d..82ab3a049d0e 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js @@ -31,22 +31,3 @@ new globalThis.Object() function foo() { return new globalThis.Object() } - -String() -Number() -Boolean() -Symbol() -BigInt() -window.String() -globalThis.String(123) -function foo() { - return globalThis.String() -} - -function varCheck() { - { - var String = class {} - } - // This should not reported - return new String() -} diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap similarity index 71% rename from crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap rename to crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap index 78104912c58a..254720319826 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap @@ -38,23 +38,4 @@ function foo() { return new globalThis.Object() } -String() -Number() -Boolean() -Symbol() -BigInt() -window.String() -globalThis.String(123) -function foo() { - return globalThis.String() -} - -function varCheck() { - { - var String = class {} - } - // This should not reported - return new String() -} - ``` diff --git a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap deleted file mode 100644 index 9acacb6e6dff..000000000000 --- a/crates/biome_js_analyze/tests/specs/nursery/useNewForBuiltins/invalid.js.snap +++ /dev/null @@ -1,905 +0,0 @@ ---- -source: crates/biome_js_analyze/tests/spec_tests.rs -expression: invalid.js ---- -# Input -```jsx -Object() -Array() -ArrayBuffer() -BigInt64Array() -BigUint64Array() -DataView() -Date() -Error() -Float32Array() -Float64Array() -Function() -Int8Array() -Int16Array() -Int32Array() -Map() -WeakMap() -Set() -WeakSet() -Promise() -RegExp() -Uint8Array() -Uint16Array() -Uint32Array() -Uint8ClampedArray() -SharedArrayBuffer() -Proxy() -WeakRef() -FinalizationRegistry() -window.Object({}) -globalThis.Object() -function foo() { - return globalThis.Object({ foo: 'bar' }) -} - -new String() -new Number() -new Boolean() -new Symbol() -new BigInt() -new window.String(123) -new globalThis.String() -function foo() { - return new globalThis.String("foo") -} - -``` - -# Diagnostics -``` -invalid.js:1:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Object() instead of Object(). - - > 1 │ Object() - │ ^^^^^^^^ - 2 │ Array() - 3 │ ArrayBuffer() - - i Unsafe fix: Add new. - - 1 │ new·Object() - │ ++++ - -``` - -``` -invalid.js:2:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Array() instead of Array(). - - 1 │ Object() - > 2 │ Array() - │ ^^^^^^^ - 3 │ ArrayBuffer() - 4 │ BigInt64Array() - - i Unsafe fix: Add new. - - 1 1 │ Object() - 2 │ + new· - 2 3 │ Array() - 3 4 │ ArrayBuffer() - - -``` - -``` -invalid.js:3:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new ArrayBuffer() instead of ArrayBuffer(). - - 1 │ Object() - 2 │ Array() - > 3 │ ArrayBuffer() - │ ^^^^^^^^^^^^^ - 4 │ BigInt64Array() - 5 │ BigUint64Array() - - i Unsafe fix: Add new. - - 1 1 │ Object() - 2 2 │ Array() - 3 │ + new· - 3 4 │ ArrayBuffer() - 4 5 │ BigInt64Array() - - -``` - -``` -invalid.js:4:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new BigInt64Array() instead of BigInt64Array(). - - 2 │ Array() - 3 │ ArrayBuffer() - > 4 │ BigInt64Array() - │ ^^^^^^^^^^^^^^^ - 5 │ BigUint64Array() - 6 │ DataView() - - i Unsafe fix: Add new. - - 2 2 │ Array() - 3 3 │ ArrayBuffer() - 4 │ + new· - 4 5 │ BigInt64Array() - 5 6 │ BigUint64Array() - - -``` - -``` -invalid.js:5:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new BigUint64Array() instead of BigUint64Array(). - - 3 │ ArrayBuffer() - 4 │ BigInt64Array() - > 5 │ BigUint64Array() - │ ^^^^^^^^^^^^^^^^ - 6 │ DataView() - 7 │ Date() - - i Unsafe fix: Add new. - - 3 3 │ ArrayBuffer() - 4 4 │ BigInt64Array() - 5 │ + new· - 5 6 │ BigUint64Array() - 6 7 │ DataView() - - -``` - -``` -invalid.js:6:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new DataView() instead of DataView(). - - 4 │ BigInt64Array() - 5 │ BigUint64Array() - > 6 │ DataView() - │ ^^^^^^^^^^ - 7 │ Date() - 8 │ Error() - - i Unsafe fix: Add new. - - 4 4 │ BigInt64Array() - 5 5 │ BigUint64Array() - 6 │ + new· - 6 7 │ DataView() - 7 8 │ Date() - - -``` - -``` -invalid.js:7:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Date() instead of Date(). - - 5 │ BigUint64Array() - 6 │ DataView() - > 7 │ Date() - │ ^^^^^^ - 8 │ Error() - 9 │ Float32Array() - - i Unsafe fix: Add new. - - 5 5 │ BigUint64Array() - 6 6 │ DataView() - 7 │ + new· - 7 8 │ Date() - 8 9 │ Error() - - -``` - -``` -invalid.js:8:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Error() instead of Error(). - - 6 │ DataView() - 7 │ Date() - > 8 │ Error() - │ ^^^^^^^ - 9 │ Float32Array() - 10 │ Float64Array() - - i Unsafe fix: Add new. - - 6 6 │ DataView() - 7 7 │ Date() - 8 │ + new· - 8 9 │ Error() - 9 10 │ Float32Array() - - -``` - -``` -invalid.js:9:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Float32Array() instead of Float32Array(). - - 7 │ Date() - 8 │ Error() - > 9 │ Float32Array() - │ ^^^^^^^^^^^^^^ - 10 │ Float64Array() - 11 │ Function() - - i Unsafe fix: Add new. - - 7 7 │ Date() - 8 8 │ Error() - 9 │ + new· - 9 10 │ Float32Array() - 10 11 │ Float64Array() - - -``` - -``` -invalid.js:10:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Float64Array() instead of Float64Array(). - - 8 │ Error() - 9 │ Float32Array() - > 10 │ Float64Array() - │ ^^^^^^^^^^^^^^ - 11 │ Function() - 12 │ Int8Array() - - i Unsafe fix: Add new. - - 8 8 │ Error() - 9 9 │ Float32Array() - 10 │ + new· - 10 11 │ Float64Array() - 11 12 │ Function() - - -``` - -``` -invalid.js:11:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Function() instead of Function(). - - 9 │ Float32Array() - 10 │ Float64Array() - > 11 │ Function() - │ ^^^^^^^^^^ - 12 │ Int8Array() - 13 │ Int16Array() - - i Unsafe fix: Add new. - - 9 9 │ Float32Array() - 10 10 │ Float64Array() - 11 │ + new· - 11 12 │ Function() - 12 13 │ Int8Array() - - -``` - -``` -invalid.js:12:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Int8Array() instead of Int8Array(). - - 10 │ Float64Array() - 11 │ Function() - > 12 │ Int8Array() - │ ^^^^^^^^^^^ - 13 │ Int16Array() - 14 │ Int32Array() - - i Unsafe fix: Add new. - - 10 10 │ Float64Array() - 11 11 │ Function() - 12 │ + new· - 12 13 │ Int8Array() - 13 14 │ Int16Array() - - -``` - -``` -invalid.js:13:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Int16Array() instead of Int16Array(). - - 11 │ Function() - 12 │ Int8Array() - > 13 │ Int16Array() - │ ^^^^^^^^^^^^ - 14 │ Int32Array() - 15 │ Map() - - i Unsafe fix: Add new. - - 11 11 │ Function() - 12 12 │ Int8Array() - 13 │ + new· - 13 14 │ Int16Array() - 14 15 │ Int32Array() - - -``` - -``` -invalid.js:14:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Int32Array() instead of Int32Array(). - - 12 │ Int8Array() - 13 │ Int16Array() - > 14 │ Int32Array() - │ ^^^^^^^^^^^^ - 15 │ Map() - 16 │ WeakMap() - - i Unsafe fix: Add new. - - 12 12 │ Int8Array() - 13 13 │ Int16Array() - 14 │ + new· - 14 15 │ Int32Array() - 15 16 │ Map() - - -``` - -``` -invalid.js:15:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Map() instead of Map(). - - 13 │ Int16Array() - 14 │ Int32Array() - > 15 │ Map() - │ ^^^^^ - 16 │ WeakMap() - 17 │ Set() - - i Unsafe fix: Add new. - - 13 13 │ Int16Array() - 14 14 │ Int32Array() - 15 │ + new· - 15 16 │ Map() - 16 17 │ WeakMap() - - -``` - -``` -invalid.js:16:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new WeakMap() instead of WeakMap(). - - 14 │ Int32Array() - 15 │ Map() - > 16 │ WeakMap() - │ ^^^^^^^^^ - 17 │ Set() - 18 │ WeakSet() - - i Unsafe fix: Add new. - - 14 14 │ Int32Array() - 15 15 │ Map() - 16 │ + new· - 16 17 │ WeakMap() - 17 18 │ Set() - - -``` - -``` -invalid.js:17:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Set() instead of Set(). - - 15 │ Map() - 16 │ WeakMap() - > 17 │ Set() - │ ^^^^^ - 18 │ WeakSet() - 19 │ Promise() - - i Unsafe fix: Add new. - - 15 15 │ Map() - 16 16 │ WeakMap() - 17 │ + new· - 17 18 │ Set() - 18 19 │ WeakSet() - - -``` - -``` -invalid.js:18:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new WeakSet() instead of WeakSet(). - - 16 │ WeakMap() - 17 │ Set() - > 18 │ WeakSet() - │ ^^^^^^^^^ - 19 │ Promise() - 20 │ RegExp() - - i Unsafe fix: Add new. - - 16 16 │ WeakMap() - 17 17 │ Set() - 18 │ + new· - 18 19 │ WeakSet() - 19 20 │ Promise() - - -``` - -``` -invalid.js:19:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Promise() instead of Promise(). - - 17 │ Set() - 18 │ WeakSet() - > 19 │ Promise() - │ ^^^^^^^^^ - 20 │ RegExp() - 21 │ Uint8Array() - - i Unsafe fix: Add new. - - 17 17 │ Set() - 18 18 │ WeakSet() - 19 │ + new· - 19 20 │ Promise() - 20 21 │ RegExp() - - -``` - -``` -invalid.js:20:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new RegExp() instead of RegExp(). - - 18 │ WeakSet() - 19 │ Promise() - > 20 │ RegExp() - │ ^^^^^^^^ - 21 │ Uint8Array() - 22 │ Uint16Array() - - i Unsafe fix: Add new. - - 18 18 │ WeakSet() - 19 19 │ Promise() - 20 │ + new· - 20 21 │ RegExp() - 21 22 │ Uint8Array() - - -``` - -``` -invalid.js:21:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Uint8Array() instead of Uint8Array(). - - 19 │ Promise() - 20 │ RegExp() - > 21 │ Uint8Array() - │ ^^^^^^^^^^^^ - 22 │ Uint16Array() - 23 │ Uint32Array() - - i Unsafe fix: Add new. - - 19 19 │ Promise() - 20 20 │ RegExp() - 21 │ + new· - 21 22 │ Uint8Array() - 22 23 │ Uint16Array() - - -``` - -``` -invalid.js:22:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Uint16Array() instead of Uint16Array(). - - 20 │ RegExp() - 21 │ Uint8Array() - > 22 │ Uint16Array() - │ ^^^^^^^^^^^^^ - 23 │ Uint32Array() - 24 │ Uint8ClampedArray() - - i Unsafe fix: Add new. - - 20 20 │ RegExp() - 21 21 │ Uint8Array() - 22 │ + new· - 22 23 │ Uint16Array() - 23 24 │ Uint32Array() - - -``` - -``` -invalid.js:23:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Uint32Array() instead of Uint32Array(). - - 21 │ Uint8Array() - 22 │ Uint16Array() - > 23 │ Uint32Array() - │ ^^^^^^^^^^^^^ - 24 │ Uint8ClampedArray() - 25 │ SharedArrayBuffer() - - i Unsafe fix: Add new. - - 21 21 │ Uint8Array() - 22 22 │ Uint16Array() - 23 │ + new· - 23 24 │ Uint32Array() - 24 25 │ Uint8ClampedArray() - - -``` - -``` -invalid.js:24:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Uint8ClampedArray() instead of Uint8ClampedArray(). - - 22 │ Uint16Array() - 23 │ Uint32Array() - > 24 │ Uint8ClampedArray() - │ ^^^^^^^^^^^^^^^^^^^ - 25 │ SharedArrayBuffer() - 26 │ Proxy() - - i Unsafe fix: Add new. - - 22 22 │ Uint16Array() - 23 23 │ Uint32Array() - 24 │ + new· - 24 25 │ Uint8ClampedArray() - 25 26 │ SharedArrayBuffer() - - -``` - -``` -invalid.js:25:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new SharedArrayBuffer() instead of SharedArrayBuffer(). - - 23 │ Uint32Array() - 24 │ Uint8ClampedArray() - > 25 │ SharedArrayBuffer() - │ ^^^^^^^^^^^^^^^^^^^ - 26 │ Proxy() - 27 │ WeakRef() - - i Unsafe fix: Add new. - - 23 23 │ Uint32Array() - 24 24 │ Uint8ClampedArray() - 25 │ + new· - 25 26 │ SharedArrayBuffer() - 26 27 │ Proxy() - - -``` - -``` -invalid.js:26:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Proxy() instead of Proxy(). - - 24 │ Uint8ClampedArray() - 25 │ SharedArrayBuffer() - > 26 │ Proxy() - │ ^^^^^^^ - 27 │ WeakRef() - 28 │ FinalizationRegistry() - - i Unsafe fix: Add new. - - 24 24 │ Uint8ClampedArray() - 25 25 │ SharedArrayBuffer() - 26 │ + new· - 26 27 │ Proxy() - 27 28 │ WeakRef() - - -``` - -``` -invalid.js:27:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new WeakRef() instead of WeakRef(). - - 25 │ SharedArrayBuffer() - 26 │ Proxy() - > 27 │ WeakRef() - │ ^^^^^^^^^ - 28 │ FinalizationRegistry() - 29 │ window.Object({}) - - i Unsafe fix: Add new. - - 25 25 │ SharedArrayBuffer() - 26 26 │ Proxy() - 27 │ + new· - 27 28 │ WeakRef() - 28 29 │ FinalizationRegistry() - - -``` - -``` -invalid.js:28:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new FinalizationRegistry() instead of FinalizationRegistry(). - - 26 │ Proxy() - 27 │ WeakRef() - > 28 │ FinalizationRegistry() - │ ^^^^^^^^^^^^^^^^^^^^^^ - 29 │ window.Object({}) - 30 │ globalThis.Object() - - i Unsafe fix: Add new. - - 26 26 │ Proxy() - 27 27 │ WeakRef() - 28 │ + new· - 28 29 │ FinalizationRegistry() - 29 30 │ window.Object({}) - - -``` - -``` -invalid.js:29:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Object() instead of Object(). - - 27 │ WeakRef() - 28 │ FinalizationRegistry() - > 29 │ window.Object({}) - │ ^^^^^^^^^^^^^^^^^ - 30 │ globalThis.Object() - 31 │ function foo() { - - i Unsafe fix: Add new. - - 27 27 │ WeakRef() - 28 28 │ FinalizationRegistry() - 29 │ + new· - 29 30 │ window.Object({}) - 30 31 │ globalThis.Object() - - -``` - -``` -invalid.js:30:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Object() instead of Object(). - - 28 │ FinalizationRegistry() - 29 │ window.Object({}) - > 30 │ globalThis.Object() - │ ^^^^^^^^^^^^^^^^^^^ - 31 │ function foo() { - 32 │ return globalThis.Object({ foo: 'bar' }) - - i Unsafe fix: Add new. - - 28 28 │ FinalizationRegistry() - 29 29 │ window.Object({}) - 30 │ + new· - 30 31 │ globalThis.Object() - 31 32 │ function foo() { - - -``` - -``` -invalid.js:32:12 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use new Object() instead of Object(). - - 30 │ globalThis.Object() - 31 │ function foo() { - > 32 │ return globalThis.Object({ foo: 'bar' }) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 33 │ } - 34 │ - - i Unsafe fix: Add new. - - 32 │ ····return·new·globalThis.Object({·foo:·'bar'·}) - │ ++++ - -``` - -``` -invalid.js:35:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use String() instead of new String(). - - 33 │ } - 34 │ - > 35 │ new String() - │ ^^^^^^^^^^^^ - 36 │ new Number() - 37 │ new Boolean() - - i Unsafe fix: Remove new. - - 35 │ new·String() - │ ---- - -``` - -``` -invalid.js:36:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use Number() instead of new Number(). - - 35 │ new String() - > 36 │ new Number() - │ ^^^^^^^^^^^^ - 37 │ new Boolean() - 38 │ new Symbol() - - i Unsafe fix: Remove new. - - 36 │ new·Number() - │ ---- - -``` - -``` -invalid.js:37:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use Boolean() instead of new Boolean(). - - 35 │ new String() - 36 │ new Number() - > 37 │ new Boolean() - │ ^^^^^^^^^^^^^ - 38 │ new Symbol() - 39 │ new BigInt() - - i Unsafe fix: Remove new. - - 37 │ new·Boolean() - │ ---- - -``` - -``` -invalid.js:38:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use Symbol() instead of new Symbol(). - - 36 │ new Number() - 37 │ new Boolean() - > 38 │ new Symbol() - │ ^^^^^^^^^^^^ - 39 │ new BigInt() - 40 │ new window.String(123) - - i Unsafe fix: Remove new. - - 38 │ new·Symbol() - │ ---- - -``` - -``` -invalid.js:39:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use BigInt() instead of new BigInt(). - - 37 │ new Boolean() - 38 │ new Symbol() - > 39 │ new BigInt() - │ ^^^^^^^^^^^^ - 40 │ new window.String(123) - 41 │ new globalThis.String() - - i Unsafe fix: Remove new. - - 39 │ new·BigInt() - │ ---- - -``` - -``` -invalid.js:40:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use String() instead of new String(). - - 38 │ new Symbol() - 39 │ new BigInt() - > 40 │ new window.String(123) - │ ^^^^^^^^^^^^^^^^^^^^^^ - 41 │ new globalThis.String() - 42 │ function foo() { - - i Unsafe fix: Remove new. - - 40 │ new·window.String(123) - │ ---- - -``` - -``` -invalid.js:41:1 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use String() instead of new String(). - - 39 │ new BigInt() - 40 │ new window.String(123) - > 41 │ new globalThis.String() - │ ^^^^^^^^^^^^^^^^^^^^^^^ - 42 │ function foo() { - 43 │ return new globalThis.String("foo") - - i Unsafe fix: Remove new. - - 41 │ new·globalThis.String() - │ ---- - -``` - -``` -invalid.js:43:12 lint/nursery/useNewForBuiltins FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - ! Use String() instead of new String(). - - 41 │ new globalThis.String() - 42 │ function foo() { - > 43 │ return new globalThis.String("foo") - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 44 │ } - 45 │ - - i Unsafe fix: Remove new. - - 43 │ ····return·new·globalThis.String("foo") - │ ---- - -``` diff --git a/packages/@biomejs/backend-jsonrpc/src/workspace.ts b/packages/@biomejs/backend-jsonrpc/src/workspace.ts index c7dc6e0b8620..66da5fbbbf6f 100644 --- a/packages/@biomejs/backend-jsonrpc/src/workspace.ts +++ b/packages/@biomejs/backend-jsonrpc/src/workspace.ts @@ -977,13 +977,13 @@ export interface Nursery { */ recommended?: boolean; /** - * Disallows package private imports. + * Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. */ - useImportRestrictions?: RuleConfiguration_for_Null; + useConsistentNewBuiltin?: RuleConfiguration_for_Null; /** - * Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. + * Disallows package private imports. */ - useNewForBuiltins?: RuleConfiguration_for_Null; + useImportRestrictions?: RuleConfiguration_for_Null; /** * Enforce the sorting of CSS utility classes. */ @@ -1966,8 +1966,8 @@ export type Category = | "lint/nursery/noTypeOnlyImportAttributes" | "lint/nursery/noUndeclaredDependencies" | "lint/nursery/useBiomeSuppressionComment" + | "lint/nursery/useConsistentNewBuiltin" | "lint/nursery/useImportRestrictions" - | "lint/nursery/useNewForBuiltins" | "lint/nursery/useSortedClasses" | "lint/performance/noAccumulatingSpread" | "lint/performance/noBarrelFile" diff --git a/packages/@biomejs/biome/configuration_schema.json b/packages/@biomejs/biome/configuration_schema.json index a14801cff07e..4fd236a64079 100644 --- a/packages/@biomejs/biome/configuration_schema.json +++ b/packages/@biomejs/biome/configuration_schema.json @@ -1549,15 +1549,15 @@ "description": "It enables the recommended rules for this group", "type": ["boolean", "null"] }, - "useImportRestrictions": { - "description": "Disallows package private imports.", + "useConsistentNewBuiltin": { + "description": "Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt.", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } ] }, - "useNewForBuiltins": { - "description": "Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt.", + "useImportRestrictions": { + "description": "Disallows package private imports.", "anyOf": [ { "$ref": "#/definitions/RuleConfiguration" }, { "type": "null" } From 6212f10d8a83e4c3e348d88b99a3e9aae6918d81 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Mon, 22 Apr 2024 02:33:57 +0300 Subject: [PATCH 07/11] update fix message --- .../nursery/use_consistent_new_builtin.rs | 2 +- .../useConsistentNewBuiltin/invalid.js.snap | 62 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs index 782f7d434b05..56d1a4d99c1c 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs @@ -164,7 +164,7 @@ impl Rule for UseConsistentNewBuiltin { Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, - message: markup! { "Add ""new""." }.to_owned(), + message: markup! { "Add ""new"" keyword." }.to_owned(), mutation, }) } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap index b11e31dcce0b..a3becc171798 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap @@ -51,7 +51,7 @@ invalid.js:1:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 2 │ Array() 3 │ ArrayBuffer() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 1 │ new·Object() │ ++++ @@ -69,7 +69,7 @@ invalid.js:2:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 3 │ ArrayBuffer() 4 │ BigInt64Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 2 │ new·Array() │ ++++ @@ -88,7 +88,7 @@ invalid.js:3:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 4 │ BigInt64Array() 5 │ BigUint64Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 3 │ new·ArrayBuffer() │ ++++ @@ -107,7 +107,7 @@ invalid.js:4:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 5 │ BigUint64Array() 6 │ DataView() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 4 │ new·BigInt64Array() │ ++++ @@ -126,7 +126,7 @@ invalid.js:5:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 6 │ DataView() 7 │ Date() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 5 │ new·BigUint64Array() │ ++++ @@ -145,7 +145,7 @@ invalid.js:6:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 7 │ Date() 8 │ Error() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 6 │ new·DataView() │ ++++ @@ -164,7 +164,7 @@ invalid.js:7:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 8 │ Error() 9 │ Float32Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 7 │ new·Date() │ ++++ @@ -183,7 +183,7 @@ invalid.js:8:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 9 │ Float32Array() 10 │ Float64Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 8 │ new·Error() │ ++++ @@ -202,7 +202,7 @@ invalid.js:9:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━ 10 │ Float64Array() 11 │ Function() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 9 │ new·Float32Array() │ ++++ @@ -221,7 +221,7 @@ invalid.js:10:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 11 │ Function() 12 │ Int8Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 10 │ new·Float64Array() │ ++++ @@ -240,7 +240,7 @@ invalid.js:11:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 12 │ Int8Array() 13 │ Int16Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 11 │ new·Function() │ ++++ @@ -259,7 +259,7 @@ invalid.js:12:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 13 │ Int16Array() 14 │ Int32Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 12 │ new·Int8Array() │ ++++ @@ -278,7 +278,7 @@ invalid.js:13:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 14 │ Int32Array() 15 │ Map() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 13 │ new·Int16Array() │ ++++ @@ -297,7 +297,7 @@ invalid.js:14:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 15 │ Map() 16 │ WeakMap() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 14 │ new·Int32Array() │ ++++ @@ -316,7 +316,7 @@ invalid.js:15:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 16 │ WeakMap() 17 │ Set() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 15 │ new·Map() │ ++++ @@ -335,7 +335,7 @@ invalid.js:16:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 17 │ Set() 18 │ WeakSet() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 16 │ new·WeakMap() │ ++++ @@ -354,7 +354,7 @@ invalid.js:17:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 18 │ WeakSet() 19 │ Promise() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 17 │ new·Set() │ ++++ @@ -373,7 +373,7 @@ invalid.js:18:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 19 │ Promise() 20 │ RegExp() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 18 │ new·WeakSet() │ ++++ @@ -392,7 +392,7 @@ invalid.js:19:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 20 │ RegExp() 21 │ Uint8Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 19 │ new·Promise() │ ++++ @@ -411,7 +411,7 @@ invalid.js:20:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 21 │ Uint8Array() 22 │ Uint16Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 20 │ new·RegExp() │ ++++ @@ -430,7 +430,7 @@ invalid.js:21:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 22 │ Uint16Array() 23 │ Uint32Array() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 21 │ new·Uint8Array() │ ++++ @@ -449,7 +449,7 @@ invalid.js:22:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 23 │ Uint32Array() 24 │ Uint8ClampedArray() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 22 │ new·Uint16Array() │ ++++ @@ -468,7 +468,7 @@ invalid.js:23:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 24 │ Uint8ClampedArray() 25 │ SharedArrayBuffer() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 23 │ new·Uint32Array() │ ++++ @@ -487,7 +487,7 @@ invalid.js:24:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 25 │ SharedArrayBuffer() 26 │ Proxy() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 24 │ new·Uint8ClampedArray() │ ++++ @@ -506,7 +506,7 @@ invalid.js:25:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 26 │ Proxy() 27 │ WeakRef() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 25 │ new·SharedArrayBuffer() │ ++++ @@ -525,7 +525,7 @@ invalid.js:26:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 27 │ WeakRef() 28 │ FinalizationRegistry() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 26 │ new·Proxy() │ ++++ @@ -544,7 +544,7 @@ invalid.js:27:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 28 │ FinalizationRegistry() 29 │ window.Object({}) - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 27 │ new·WeakRef() │ ++++ @@ -563,7 +563,7 @@ invalid.js:28:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 29 │ window.Object({}) 30 │ globalThis.Object() - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 28 │ new·FinalizationRegistry() │ ++++ @@ -582,7 +582,7 @@ invalid.js:29:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 30 │ globalThis.Object() 31 │ function foo() { - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 29 │ new·window.Object({}) │ ++++ @@ -601,7 +601,7 @@ invalid.js:30:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 31 │ function foo() { 32 │ return /** Comment */ globalThis.Object({ foo: 'bar' }) - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 30 │ new·globalThis.Object() │ ++++ @@ -620,7 +620,7 @@ invalid.js:32:27 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ 33 │ } 34 │ - i Unsafe fix: Add new. + i Unsafe fix: Add new keyword. 32 │ ····return·/**·Comment·*/·new·globalThis.Object({·foo:·'bar'·}) │ ++++ From ec759ee4119e1a2b5023eb55abc4d6eced633927 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Mon, 22 Apr 2024 03:04:53 +0300 Subject: [PATCH 08/11] Bring back new removal --- .../nursery/use_consistent_new_builtin.rs | 135 +++++++++++++++--- .../useConsistentNewBuiltin/invalid.js | 9 ++ .../nursery/useConsistentNewBuiltin/valid.js | 17 +++ .../useConsistentNewBuiltin/valid.js.snap | 16 +++ 4 files changed, 157 insertions(+), 20 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs index 56d1a4d99c1c..4ef4ac866c11 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs @@ -8,7 +8,9 @@ use biome_js_factory::make; use biome_js_syntax::{ global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind, }; -use biome_rowan::{AstNode, BatchMutationExt, TriviaPieceKind}; +use biome_rowan::{ + chain_trivia_pieces, declare_node_union, AstNode, BatchMutationExt, TriviaPieceKind, +}; declare_rule! { /// Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. @@ -45,14 +47,19 @@ declare_rule! { /// - WeakRef /// - FinalizationRegistry /// - /// Following builtins are handled by [noInvalidBuiltin](https://biomejs.dev/linter/rules/no-invalid-new-builtin/): + /// Disallows the use of new for following builtins: /// /// - String /// - Number /// - Boolean + /// + /// These builtins are handled by [noInvalidBuiltin](https://biomejs.dev/linter/rules/no-invalid-new-builtin/) rule: + /// /// - Symbol /// - BigInt /// + /// > These should not use new as that would create object wrappers for the primitive values, which is not what you want. However, without new they can be useful for coercing a value to that type. + /// /// ## Examples /// /// ### Invalid @@ -116,25 +123,54 @@ const BUILTINS_REQUIRING_NEW: &[&str] = &[ "WeakSet", ]; +/// Sorted array of builtins that should not use new keyword. +const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["Boolean", "Number", "String"]; + +enum BuiltinCreationRule { + MustUseNew, + MustNotUseNew, +} + +impl BuiltinCreationRule { + fn forbidden_builtins_list(&self) -> &[&str] { + match self { + BuiltinCreationRule::MustUseNew => BUILTINS_REQUIRING_NEW, + BuiltinCreationRule::MustNotUseNew => BUILTINS_NOT_REQUIRING_NEW, + } + } +} + +pub struct UseNewForBuiltinsState { + name: String, + creation_rule: BuiltinCreationRule, +} + +declare_node_union! { + pub JsNewOrCallExpression = JsNewExpression | JsCallExpression +} + impl Rule for UseConsistentNewBuiltin { - type Query = Semantic; - type State = String; + type Query = Semantic; + type State = UseNewForBuiltinsState; type Signals = Option; type Options = (); fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); - let callee = node.callee().ok()?; + let (callee, creation_rule) = extract_callee_and_rule(node)?; let (reference, name) = global_identifier(&callee.omit_parentheses())?; let name_text = name.text(); - if BUILTINS_REQUIRING_NEW.binary_search(&name_text).is_ok() { + if creation_rule.forbidden_builtins_list().binary_search(&name_text).is_ok() { return ctx .model() .binding(&reference) .is_none() - .then_some(name_text.to_string()); + .then_some(UseNewForBuiltinsState { + name: name_text.to_string(), + creation_rule, + }); } None @@ -143,35 +179,91 @@ impl Rule for UseConsistentNewBuiltin { fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { let node = ctx.query(); + let name = &state.name; + + let (use_this, instead_of) = match state.creation_rule { + BuiltinCreationRule::MustUseNew => ("new ", ""), + BuiltinCreationRule::MustNotUseNew => ("", "new "), + }; + Some(RuleDiagnostic::new( rule_category!(), node.range(), markup! { - "Use ""new "{state}"()"" instead of "{state}"()""." + "Use "{use_this}{name}"()"" instead of "{instead_of}{name}"()""." }, )) } fn action(ctx: &RuleContext, _: &Self::State) -> Option { let node = ctx.query(); - let new_expression = convert_call_expression_to_new_expression(node)?; let mut mutation = ctx.root().begin(); - mutation.replace_node_discard_trivia::( - node.clone().into(), - new_expression.into(), - ); - Some(JsRuleAction { - category: ActionCategory::QuickFix, - applicability: Applicability::MaybeIncorrect, - message: markup! { "Add ""new"" keyword." }.to_owned(), - mutation, - }) + + match node { + JsNewOrCallExpression::JsNewExpression(node) => { + let call_expression = convert_new_expression_to_call_expression(node)?; + + mutation.replace_node_discard_trivia::( + node.clone().into(), + call_expression.into(), + ); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Remove ""new""." }.to_owned(), + mutation, + }) + } + JsNewOrCallExpression::JsCallExpression(node) => { + let new_expression = convert_call_expression_to_new_expression(node)?; + + mutation.replace_node_discard_trivia::( + node.clone().into(), + new_expression.into(), + ); + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Add ""new"" keyword." }.to_owned(), + mutation, + }) + } + } } } -fn convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { +fn extract_callee_and_rule( + node: &JsNewOrCallExpression, +) -> Option<(AnyJsExpression, BuiltinCreationRule)> { + match node { + JsNewOrCallExpression::JsNewExpression(node) => { + let callee = node.callee().ok()?; + + Some((callee, BuiltinCreationRule::MustNotUseNew)) + } + JsNewOrCallExpression::JsCallExpression(node) => { + let callee: AnyJsExpression = node.callee().ok()?; + + Some((callee, BuiltinCreationRule::MustUseNew)) + } + } +} + +fn convert_new_expression_to_call_expression(expr: &JsNewExpression) -> Option { + let new_token = expr.new_token().ok()?; let mut callee = expr.callee().ok()?; + if new_token.has_leading_comments() || new_token.has_trailing_comments() { + callee = callee.prepend_trivia_pieces(chain_trivia_pieces( + new_token.leading_trivia().pieces(), + new_token.trailing_trivia().pieces(), + ))?; + } + Some(make::js_call_expression(callee, expr.arguments()?).build()) +} + +fn convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option { + let mut callee: AnyJsExpression = expr.callee().ok()?; let leading_trivia_pieces = callee.syntax().first_leading_trivia()?.pieces(); let new_token = make::token(JsSyntaxKind::NEW_KW) @@ -193,4 +285,7 @@ fn test_order() { for items in BUILTINS_REQUIRING_NEW.windows(2) { assert!(items[0] < items[1], "{} < {}", items[0], items[1]); } + for items in BUILTINS_NOT_REQUIRING_NEW.windows(2) { + assert!(items[0] < items[1], "{} < {}", items[0], items[1]); + } } diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js index 0b9a0230831e..cbd5a55199b7 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js @@ -31,3 +31,12 @@ globalThis.Object() function foo() { return /** Comment */ globalThis.Object({ foo: 'bar' }) } + +new String() +new Number() +new Boolean() +new window.String(123) +new globalThis.String() +function foo() { + return new globalThis.String("foo") +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js index 82ab3a049d0e..ee0db4caa817 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js @@ -31,3 +31,20 @@ new globalThis.Object() function foo() { return new globalThis.Object() } + +String() +Number() +Boolean() +window.String() +globalThis.String(123) +function foo() { + return globalThis.String() +} + +function varCheck() { + { + var String = class {} + } + // This should not be reported + return new String() +} \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap index 254720319826..c705032a3fc5 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/valid.js.snap @@ -38,4 +38,20 @@ function foo() { return new globalThis.Object() } +String() +Number() +Boolean() +window.String() +globalThis.String(123) +function foo() { + return globalThis.String() +} + +function varCheck() { + { + var String = class {} + } + // This should not be reported + return new String() +} ``` From b4cb496fd94ee376b5abd8fe593cd1b3f9764221 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:23:57 +0300 Subject: [PATCH 09/11] fix: cleanup --- .../nursery/use_consistent_new_builtin.rs | 41 +++--- .../useConsistentNewBuiltin/invalid.js | 4 +- .../useConsistentNewBuiltin/invalid.js.snap | 134 +++++++++++++++++- 3 files changed, 148 insertions(+), 31 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs index 4ef4ac866c11..eac0e03146f8 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs @@ -140,7 +140,7 @@ impl BuiltinCreationRule { } } -pub struct UseNewForBuiltinsState { +pub struct UseConsistentNewBuiltinState { name: String, creation_rule: BuiltinCreationRule, } @@ -151,26 +151,28 @@ declare_node_union! { impl Rule for UseConsistentNewBuiltin { type Query = Semantic; - type State = UseNewForBuiltinsState; + type State = UseConsistentNewBuiltinState; type Signals = Option; type Options = (); fn run(ctx: &RuleContext) -> Self::Signals { let node = ctx.query(); let (callee, creation_rule) = extract_callee_and_rule(node)?; - let (reference, name) = global_identifier(&callee.omit_parentheses())?; + let name_text = name.text(); - if creation_rule.forbidden_builtins_list().binary_search(&name_text).is_ok() { - return ctx - .model() - .binding(&reference) - .is_none() - .then_some(UseNewForBuiltinsState { + if creation_rule + .forbidden_builtins_list() + .binary_search(&name_text) + .is_ok() + { + return ctx.model().binding(&reference).is_none().then_some( + UseConsistentNewBuiltinState { name: name_text.to_string(), creation_rule, - }); + }, + ); } None @@ -204,24 +206,20 @@ impl Rule for UseConsistentNewBuiltin { JsNewOrCallExpression::JsNewExpression(node) => { let call_expression = convert_new_expression_to_call_expression(node)?; - mutation.replace_node_discard_trivia::( - node.clone().into(), - call_expression.into(), - ); + mutation + .replace_node::(node.clone().into(), call_expression.into()); Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, - message: markup! { "Remove ""new""." }.to_owned(), + message: markup! { "Remove ""new"" keyword." }.to_owned(), mutation, }) } JsNewOrCallExpression::JsCallExpression(node) => { let new_expression = convert_call_expression_to_new_expression(node)?; - mutation.replace_node_discard_trivia::( - node.clone().into(), - new_expression.into(), - ); + mutation + .replace_node::(node.clone().into(), new_expression.into()); Some(JsRuleAction { category: ActionCategory::QuickFix, applicability: Applicability::MaybeIncorrect, @@ -263,18 +261,17 @@ fn convert_new_expression_to_call_expression(expr: &JsNewExpression) -> Option Option { - let mut callee: AnyJsExpression = expr.callee().ok()?; + let mut callee = expr.callee().ok()?; let leading_trivia_pieces = callee.syntax().first_leading_trivia()?.pieces(); let new_token = make::token(JsSyntaxKind::NEW_KW) .with_leading_trivia_pieces(leading_trivia_pieces) .with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]); - // Remove leading trivia from the callee. callee = callee.with_leading_trivia_pieces([])?; Some( - make::js_new_expression(new_token.clone(), callee) + make::js_new_expression(new_token, callee) .with_arguments(expr.arguments().ok()?) .build(), ) diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js index cbd5a55199b7..4e2ba875040a 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js @@ -29,7 +29,7 @@ FinalizationRegistry() window.Object({}) globalThis.Object() function foo() { - return /** Comment */ globalThis.Object({ foo: 'bar' }) + return /** Start */ globalThis.Object({ foo: 'bar' }) /** End */ } new String() @@ -38,5 +38,5 @@ new Boolean() new window.String(123) new globalThis.String() function foo() { - return new globalThis.String("foo") + return /** Start */ new globalThis.String("foo") /** End */ } \ No newline at end of file diff --git a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap index a3becc171798..f4dffe3ced4b 100644 --- a/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap +++ b/crates/biome_js_analyze/tests/specs/nursery/useConsistentNewBuiltin/invalid.js.snap @@ -35,9 +35,17 @@ FinalizationRegistry() window.Object({}) globalThis.Object() function foo() { - return /** Comment */ globalThis.Object({ foo: 'bar' }) + return /** Start */ globalThis.Object({ foo: 'bar' }) /** End */ } +new String() +new Number() +new Boolean() +new window.String(123) +new globalThis.String() +function foo() { + return /** Start */ new globalThis.String("foo") /** End */ +} ``` # Diagnostics @@ -599,7 +607,7 @@ invalid.js:30:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ > 30 │ globalThis.Object() │ ^^^^^^^^^^^^^^^^^^^ 31 │ function foo() { - 32 │ return /** Comment */ globalThis.Object({ foo: 'bar' }) + 32 │ return /** Start */ globalThis.Object({ foo: 'bar' }) /** End */ i Unsafe fix: Add new keyword. @@ -609,20 +617,132 @@ invalid.js:30:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━ ``` ``` -invalid.js:32:27 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +invalid.js:32:25 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Use new Object() instead of Object(). 30 │ globalThis.Object() 31 │ function foo() { - > 32 │ return /** Comment */ globalThis.Object({ foo: 'bar' }) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + > 32 │ return /** Start */ globalThis.Object({ foo: 'bar' }) /** End */ + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 33 │ } 34 │ i Unsafe fix: Add new keyword. - 32 │ ····return·/**·Comment·*/·new·globalThis.Object({·foo:·'bar'·}) - │ ++++ + 32 │ ····return·/**·Start·*/·new·globalThis.Object({·foo:·'bar'·})·/**·End·*/ + │ ++++ + +``` + +``` +invalid.js:35:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 33 │ } + 34 │ + > 35 │ new String() + │ ^^^^^^^^^^^^ + 36 │ new Number() + 37 │ new Boolean() + + i Unsafe fix: Remove new keyword. + + 35 │ new·String() + │ ---- + +``` + +``` +invalid.js:36:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use Number() instead of new Number(). + + 35 │ new String() + > 36 │ new Number() + │ ^^^^^^^^^^^^ + 37 │ new Boolean() + 38 │ new window.String(123) + + i Unsafe fix: Remove new keyword. + + 36 │ new·Number() + │ ---- + +``` + +``` +invalid.js:37:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use Boolean() instead of new Boolean(). + + 35 │ new String() + 36 │ new Number() + > 37 │ new Boolean() + │ ^^^^^^^^^^^^^ + 38 │ new window.String(123) + 39 │ new globalThis.String() + + i Unsafe fix: Remove new keyword. + + 37 │ new·Boolean() + │ ---- + +``` + +``` +invalid.js:38:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 36 │ new Number() + 37 │ new Boolean() + > 38 │ new window.String(123) + │ ^^^^^^^^^^^^^^^^^^^^^^ + 39 │ new globalThis.String() + 40 │ function foo() { + + i Unsafe fix: Remove new keyword. + + 38 │ new·window.String(123) + │ ---- + +``` + +``` +invalid.js:39:1 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 37 │ new Boolean() + 38 │ new window.String(123) + > 39 │ new globalThis.String() + │ ^^^^^^^^^^^^^^^^^^^^^^^ + 40 │ function foo() { + 41 │ return /** Start */ new globalThis.String("foo") /** End */ + + i Unsafe fix: Remove new keyword. + + 39 │ new·globalThis.String() + │ ---- + +``` + +``` +invalid.js:41:25 lint/nursery/useConsistentNewBuiltin FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! Use String() instead of new String(). + + 39 │ new globalThis.String() + 40 │ function foo() { + > 41 │ return /** Start */ new globalThis.String("foo") /** End */ + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 42 │ } + + i Unsafe fix: Remove new keyword. + + 41 │ ····return·/**·Start·*/·new·globalThis.String("foo")·/**·End·*/ + │ ---- ``` From dc1f6900d2faac04e119b32c4ed6bc6f485ad5fe Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Mon, 22 Apr 2024 19:44:21 +0300 Subject: [PATCH 10/11] fix: cleanup --- .../migrate/eslint_any_rule_to_biome.rs | 10 ++ .../src/lint/complexity/use_regex_literals.rs | 12 +- .../nursery/use_consistent_new_builtin.rs | 141 +++++++++--------- crates/biome_js_syntax/src/expr_ext.rs | 4 + 4 files changed, 91 insertions(+), 76 deletions(-) diff --git a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs index 54ee88e5188d..bb08d8fe0cf4 100644 --- a/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs +++ b/crates/biome_cli/src/execute/migrate/eslint_any_rule_to_biome.rs @@ -856,6 +856,16 @@ pub(crate) fn migrate_eslint_any_rule( let rule = group.no_new_symbol.get_or_insert(Default::default()); rule.set_level(rule_severity.into()); } + "no-new-wrappers" => { + if !options.include_nursery { + return false; + } + let group = rules.nursery.get_or_insert_with(Default::default); + let rule = group + .use_consistent_new_builtin + .get_or_insert(Default::default()); + rule.set_level(rule_severity.into()); + } "no-nonoctal-decimal-escape" => { let group = rules.correctness.get_or_insert_with(Default::default); let rule = group diff --git a/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs b/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs index f3e4434f804e..1bffc5c8bf8f 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_regex_literals.rs @@ -7,12 +7,10 @@ use biome_js_factory::make::js_regex_literal_expression; use biome_js_semantic::SemanticModel; use biome_js_syntax::{ global_identifier, static_value::StaticValue, AnyJsCallArgument, AnyJsExpression, - AnyJsLiteralExpression, JsCallArguments, JsCallExpression, JsComputedMemberExpression, - JsNewExpression, JsSyntaxKind, JsSyntaxToken, -}; -use biome_rowan::{ - declare_node_union, AstNode, AstSeparatedList, BatchMutationExt, SyntaxError, TokenText, + AnyJsLiteralExpression, JsCallArguments, JsComputedMemberExpression, JsNewOrCallExpression, + JsSyntaxKind, JsSyntaxToken, }; +use biome_rowan::{AstNode, AstSeparatedList, BatchMutationExt, SyntaxError, TokenText}; use crate::{services::semantic::Semantic, JsRuleAction}; @@ -54,10 +52,6 @@ declare_rule! { } } -declare_node_union! { - pub JsNewOrCallExpression = JsNewExpression | JsCallExpression -} - pub struct UseRegexLiteralsState { pattern: String, flags: Option, diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs index eac0e03146f8..90061ca38e1b 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs @@ -6,16 +6,15 @@ use biome_console::markup; use biome_diagnostics::Applicability; use biome_js_factory::make; use biome_js_syntax::{ - global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsSyntaxKind, -}; -use biome_rowan::{ - chain_trivia_pieces, declare_node_union, AstNode, BatchMutationExt, TriviaPieceKind, + global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsNewOrCallExpression, + JsSyntaxKind, }; +use biome_rowan::{chain_trivia_pieces, AstNode, BatchMutationExt, TriviaPieceKind}; declare_rule! { - /// Enforce the use of new for all builtins, except String, Number, Boolean, Symbol and BigInt. + /// Enforce the use of new for all builtins, except `String`, `Number`, `Boolean`, `Symbol` and `BigInt`. /// - /// They work the same, but new should be preferred for consistency with other constructors. + /// `new Builtin()` and `Builtin()` work the same, but new should be preferred for consistency with other constructors. /// Enforces the use of new for following builtins: /// /// - Object @@ -65,8 +64,14 @@ declare_rule! { /// ### Invalid /// /// ```js,expect_diagnostic - /// const list = Array(10); + /// const text = new String(10); + /// ``` + /// + /// ```js,expect_diagnostic /// const now = Date(); + /// ``` + /// + /// ```js,expect_diagnostic /// const map = Map([ /// ['foo', 'bar'] /// ]); @@ -75,8 +80,14 @@ declare_rule! { /// ### Valid /// /// ```js - /// const list = new Array(10); + /// const text = String(10); + /// ``` + /// + /// ```js /// const now = new Date(); + /// ``` + /// + /// ```js /// const map = new Map([ /// ['foo', 'bar'] /// ]); @@ -85,70 +96,12 @@ declare_rule! { pub UseConsistentNewBuiltin { version: "next", name: "useConsistentNewBuiltin", - sources: &[RuleSource::EslintUnicorn("new-for-builtins")], + sources: &[RuleSource::EslintUnicorn("new-for-builtins"), RuleSource::Eslint("no-new-wrappers")], recommended: false, fix_kind: FixKind::Unsafe, } } -/// Sorted array of builtins that require new keyword. -const BUILTINS_REQUIRING_NEW: &[&str] = &[ - "Array", - "ArrayBuffer", - "BigInt64Array", - "BigUint64Array", - "DataView", - "Date", - "Error", - "FinalizationRegistry", - "Float32Array", - "Float64Array", - "Function", - "Int16Array", - "Int32Array", - "Int8Array", - "Map", - "Object", - "Promise", - "Proxy", - "RegExp", - "Set", - "SharedArrayBuffer", - "Uint16Array", - "Uint32Array", - "Uint8Array", - "Uint8ClampedArray", - "WeakMap", - "WeakRef", - "WeakSet", -]; - -/// Sorted array of builtins that should not use new keyword. -const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["Boolean", "Number", "String"]; - -enum BuiltinCreationRule { - MustUseNew, - MustNotUseNew, -} - -impl BuiltinCreationRule { - fn forbidden_builtins_list(&self) -> &[&str] { - match self { - BuiltinCreationRule::MustUseNew => BUILTINS_REQUIRING_NEW, - BuiltinCreationRule::MustNotUseNew => BUILTINS_NOT_REQUIRING_NEW, - } - } -} - -pub struct UseConsistentNewBuiltinState { - name: String, - creation_rule: BuiltinCreationRule, -} - -declare_node_union! { - pub JsNewOrCallExpression = JsNewExpression | JsCallExpression -} - impl Rule for UseConsistentNewBuiltin { type Query = Semantic; type State = UseConsistentNewBuiltinState; @@ -231,6 +184,60 @@ impl Rule for UseConsistentNewBuiltin { } } +/// Sorted array of builtins that require new keyword. +const BUILTINS_REQUIRING_NEW: &[&str] = &[ + "Array", + "ArrayBuffer", + "BigInt64Array", + "BigUint64Array", + "DataView", + "Date", + "Error", + "FinalizationRegistry", + "Float32Array", + "Float64Array", + "Function", + "Int16Array", + "Int32Array", + "Int8Array", + "Map", + "Object", + "Promise", + "Proxy", + "RegExp", + "Set", + "SharedArrayBuffer", + "Uint16Array", + "Uint32Array", + "Uint8Array", + "Uint8ClampedArray", + "WeakMap", + "WeakRef", + "WeakSet", +]; + +/// Sorted array of builtins that should not use new keyword. +const BUILTINS_NOT_REQUIRING_NEW: &[&str] = &["Boolean", "Number", "String"]; + +enum BuiltinCreationRule { + MustUseNew, + MustNotUseNew, +} + +impl BuiltinCreationRule { + fn forbidden_builtins_list(&self) -> &[&str] { + match self { + BuiltinCreationRule::MustUseNew => BUILTINS_REQUIRING_NEW, + BuiltinCreationRule::MustNotUseNew => BUILTINS_NOT_REQUIRING_NEW, + } + } +} + +pub struct UseConsistentNewBuiltinState { + name: String, + creation_rule: BuiltinCreationRule, +} + fn extract_callee_and_rule( node: &JsNewOrCallExpression, ) -> Option<(AnyJsExpression, BuiltinCreationRule)> { diff --git a/crates/biome_js_syntax/src/expr_ext.rs b/crates/biome_js_syntax/src/expr_ext.rs index c10d60fdb1dd..7715e4461486 100644 --- a/crates/biome_js_syntax/src/expr_ext.rs +++ b/crates/biome_js_syntax/src/expr_ext.rs @@ -23,6 +23,10 @@ const GLOBAL_THIS: &str = "globalThis"; const UNDEFINED: &str = "undefined"; const WINDOW: &str = "window"; +declare_node_union! { + pub JsNewOrCallExpression = JsNewExpression | JsCallExpression +} + impl JsReferenceIdentifier { /// Returns `true` if this identifier refers to the `undefined` symbol. /// From 768183a8ce1e81be5f1dd5a6e3667447130f96c8 Mon Sep 17 00:00:00 2001 From: Justinas Delinda <8914032+minht11@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:00:44 +0300 Subject: [PATCH 11/11] chore: use syntax macro --- .../src/lint/nursery/use_consistent_new_builtin.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs index 90061ca38e1b..cc9579ebca3c 100644 --- a/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs +++ b/crates/biome_js_analyze/src/lint/nursery/use_consistent_new_builtin.rs @@ -6,8 +6,7 @@ use biome_console::markup; use biome_diagnostics::Applicability; use biome_js_factory::make; use biome_js_syntax::{ - global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsNewOrCallExpression, - JsSyntaxKind, + global_identifier, AnyJsExpression, JsCallExpression, JsNewExpression, JsNewOrCallExpression, T, }; use biome_rowan::{chain_trivia_pieces, AstNode, BatchMutationExt, TriviaPieceKind}; @@ -271,7 +270,7 @@ fn convert_call_expression_to_new_expression(expr: &JsCallExpression) -> Option< let mut callee = expr.callee().ok()?; let leading_trivia_pieces = callee.syntax().first_leading_trivia()?.pieces(); - let new_token = make::token(JsSyntaxKind::NEW_KW) + let new_token = make::token(T![new]) .with_leading_trivia_pieces(leading_trivia_pieces) .with_trailing_trivia([(TriviaPieceKind::Whitespace, " ")]);