diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs index 5110e1bef..133c466f4 100644 --- a/crates/oxc_linter/src/rules.rs +++ b/crates/oxc_linter/src/rules.rs @@ -103,6 +103,7 @@ mod eslint { pub mod no_with; pub mod radix; pub mod require_yield; + pub mod symbol_description; pub mod unicode_bom; pub mod use_isnan; pub mod valid_typeof; @@ -463,6 +464,7 @@ oxc_macros::declare_all_lint_rules! { eslint::no_with, eslint::radix, eslint::require_yield, + eslint::symbol_description, eslint::unicode_bom, eslint::use_isnan, eslint::valid_typeof, diff --git a/crates/oxc_linter/src/rules/eslint/symbol_description.rs b/crates/oxc_linter/src/rules/eslint/symbol_description.rs new file mode 100644 index 000000000..ba9bce0a6 --- /dev/null +++ b/crates/oxc_linter/src/rules/eslint/symbol_description.rs @@ -0,0 +1,68 @@ +use oxc_ast::AstKind; +use oxc_diagnostics::OxcDiagnostic; +use oxc_macros::declare_oxc_lint; +use oxc_span::Span; + +use crate::{context::LintContext, rule::Rule, AstNode}; + +#[derive(Debug, Default, Clone)] +pub struct SymbolDescription; + +fn symbol_description_diagnostic(span1: Span) -> OxcDiagnostic { + OxcDiagnostic::warn("eslint(symbol-description): Expected Symbol to have a description.") + .with_labels([span1.into()]) +} + +declare_oxc_lint!( + /// ### What it does + /// + /// Require symbol descriptions. + /// + /// ### Why is this bad? + /// + /// The Symbol function may have an optional description. + /// + /// ### Example + /// ```javascript + /// var foo = Symbol(); + /// ``` + SymbolDescription, + pedantic, +); + +impl Rule for SymbolDescription { + fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { + let AstKind::CallExpression(call_expr) = node.kind() else { + return; + }; + + let Some(ident) = call_expr.callee.get_identifier_reference() else { + return; + }; + + if ident.name == "Symbol" + && call_expr.arguments.len() == 0 + && ctx.semantic().is_reference_to_global_variable(ident) + { + ctx.diagnostic(symbol_description_diagnostic(call_expr.span)); + } + } +} + +#[test] +fn test() { + use crate::tester::Tester; + + let pass = vec![ + r#"Symbol("Foo");"#, + r#"var foo = "foo"; Symbol(foo);"#, + "var Symbol = function () {}; Symbol();", + "Symbol(); var Symbol = function () {};", + "function bar() { var Symbol = function () {}; Symbol(); }", + "function bar(Symbol) { Symbol(); }", + ]; + + let fail = vec!["Symbol();", "Symbol(); Symbol = function () {};"]; + + Tester::new(SymbolDescription::NAME, pass, fail).test_and_snapshot(); +} diff --git a/crates/oxc_linter/src/snapshots/symbol_description.snap b/crates/oxc_linter/src/snapshots/symbol_description.snap new file mode 100644 index 000000000..b027b011f --- /dev/null +++ b/crates/oxc_linter/src/snapshots/symbol_description.snap @@ -0,0 +1,15 @@ +--- +source: crates/oxc_linter/src/tester.rs +expression: symbol_description +--- + ⚠ eslint(symbol-description): Expected Symbol to have a description. + ╭─[symbol_description.tsx:1:1] + 1 │ Symbol(); + · ──────── + ╰──── + + ⚠ eslint(symbol-description): Expected Symbol to have a description. + ╭─[symbol_description.tsx:1:1] + 1 │ Symbol(); Symbol = function () {}; + · ──────── + ╰────