feat(linter) eslint: no-new-wrappers (#2413)

Part of: #479 

docs: https://eslint.org/docs/latest/rules/no-new-wrappers

I am unsure of what do do with 2 of the eslint tests that use
`languageOptions: { globals: { String: "off" } }` and `/* global
Boolean:off */`.

I've commented them out as of now.

---------

Co-authored-by: Boshen <boshenc@gmail.com>
This commit is contained in:
alakhpc 2024-02-21 10:43:37 +04:00 committed by GitHub
parent 6b3b260dcc
commit 9d69167d96
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 144 additions and 0 deletions

View file

@ -72,6 +72,7 @@ mod eslint {
pub mod no_loss_of_precision;
pub mod no_mixed_operators;
pub mod no_new_symbol;
pub mod no_new_wrappers;
pub mod no_obj_calls;
pub mod no_prototype_builtins;
pub mod no_redeclare;
@ -370,6 +371,7 @@ oxc_macros::declare_all_lint_rules! {
eslint::no_loss_of_precision,
eslint::no_mixed_operators,
eslint::no_new_symbol,
eslint::no_new_wrappers,
eslint::no_obj_calls,
eslint::no_prototype_builtins,
eslint::no_redeclare,

View file

@ -0,0 +1,98 @@
use oxc_ast::{ast::Expression, AstKind};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::Error,
};
use oxc_macros::declare_oxc_lint;
use oxc_span::{Atom, Span};
use crate::{context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Error, Diagnostic)]
#[error(
"eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects"
)]
#[diagnostic(
severity(warning),
help("do not use {0} as a constructor, consider removing the new operator.")
)]
struct NoNewWrappersDiagnostic(Atom, #[label] pub Span);
#[derive(Debug, Default, Clone)]
pub struct NoNewWrappers;
declare_oxc_lint!(
/// ### What it does
///
/// Disallow new operators with the String, Number, and Boolean objects
///
/// ### Why is this bad?
///
/// The first problem is that primitive wrapper objects are, in fact, objects. That means typeof will return "object" instead of "string", "number", or "boolean".
/// The second problem comes with boolean objects. Every object is truthy, that means an instance of Boolean always resolves to true even when its actual value is false.
/// https://eslint.org/docs/latest/rules/no-new-wrappers
///
/// ### Example
/// ```javascript
/// var stringObject = new String('Hello world');
/// ```
NoNewWrappers,
pedantic
);
impl Rule for NoNewWrappers {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::NewExpression(expr) = node.kind() else { return };
let Expression::Identifier(ident) = &expr.callee else { return };
if (ident.name == "String" || ident.name == "Number" || ident.name == "Boolean")
&& ctx.semantic().is_reference_to_global_variable(ident)
{
ctx.diagnostic(NoNewWrappersDiagnostic(ident.name.clone(), expr.span));
}
}
}
#[test]
fn test() {
use crate::tester::Tester;
let pass = vec![
"var a = new Object();",
"var a = String('test'), b = String.fromCharCode(32);",
"function test(Number) { return new Number; }",
r#"
import String from "./string";
const str = new String(42);
"#,
"
if (foo) {
result = new Boolean(bar);
} else {
var Boolean = CustomBoolean;
}
",
// Disabled because the eslint-test uses languageOptions: { globals: { String: "off" } }
// "new String()",
// Disabled as the global option from the eslint-test does not work
// "
// /* global Boolean:off */
// assert(new Boolean);
// ",
];
let fail = vec![
"var a = new String('hello');",
"var a = new Number(10);",
"var a = new Boolean(false);",
"
const a = new String('bar');
{
const String = CustomString;
const b = new String('foo');
}
",
];
Tester::new(NoNewWrappers::NAME, pass, fail).test_and_snapshot();
}

View file

@ -0,0 +1,44 @@
---
source: crates/oxc_linter/src/tester.rs
expression: no_new_wrappers
---
⚠ eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects
╭─[no_new_wrappers.tsx:1:9]
1 │ var a = new String('hello');
· ───────────────────
╰────
help: do not use String as a constructor, consider removing the new operator.
⚠ eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects
╭─[no_new_wrappers.tsx:1:9]
1 │ var a = new Number(10);
· ──────────────
╰────
help: do not use Number as a constructor, consider removing the new operator.
⚠ eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects
╭─[no_new_wrappers.tsx:1:9]
1 │ var a = new Boolean(false);
· ──────────────────
╰────
help: do not use Boolean as a constructor, consider removing the new operator.
⚠ eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects
╭─[no_new_wrappers.tsx:2:23]
1 │
2 │ const a = new String('bar');
· ─────────────────
3 │ {
╰────
help: do not use String as a constructor, consider removing the new operator.
⚠ eslint(no-new-wrappers): Disallow new operators with the String, Number, and Boolean objects
╭─[no_new_wrappers.tsx:5:27]
4 │ const String = CustomString;
5 │ const b = new String('foo');
· ─────────────────
6 │ }
╰────
help: do not use String as a constructor, consider removing the new operator.