feat(es/lints): Implement no-new-symbol rule (#4076)

This commit is contained in:
Artur 2022-03-18 17:19:02 +03:00 committed by GitHub
parent 174b48d4c3
commit b39e345d8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 124 additions and 0 deletions

View File

@ -0,0 +1,9 @@
{
"jsc": {
"lints": {
"no-new-symbol": [
"error"
]
}
}
}

View File

@ -0,0 +1,9 @@
new Symbol();
new A(new Symbol())
Symbol()
function f1(Symbol) {
new Symbol()
}

View File

@ -0,0 +1,12 @@
x `Symbol` cannot be called as a constructor
,----
2 | new Symbol();
: ^^^^^^^^^^^^
`----
x `Symbol` cannot be called as a constructor
,----
4 | new A(new Symbol())
: ^^^^^^^^^^^^
`----

View File

@ -138,4 +138,8 @@ pub struct LintConfig {
#[cfg(feature = "non_critical_lints")]
#[serde(default)]
pub yoda: RuleConfig<YodaConfig>,
#[cfg(feature = "non_critical_lints")]
#[serde(default, alias = "noNewSymbol")]
pub no_new_symbol: RuleConfig<()>,
}

View File

@ -26,6 +26,7 @@ pub(crate) mod non_critical_lints {
pub mod no_empty_pattern;
pub mod no_loop_func;
pub mod no_new;
pub mod no_new_symbol;
pub mod no_restricted_syntax;
pub mod no_use_before_define;
pub mod prefer_regex_literals;
@ -130,6 +131,12 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
));
rules.extend(yoda::yoda(&lint_config.yoda));
rules.extend(no_new_symbol::no_new_symbol(
program,
top_level_ctxt,
&lint_config.no_new_symbol,
));
}
rules

View File

@ -0,0 +1,83 @@
use swc_common::{collections::AHashSet, errors::HANDLER, Span, SyntaxContext};
use swc_ecma_ast::*;
use swc_ecma_utils::{collect_decls_with_ctxt, ident::IdentLike};
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
use crate::{
config::{LintRuleReaction, RuleConfig},
rule::{visitor_rule, Rule},
};
const MESSAGE: &str = "`Symbol` cannot be called as a constructor";
pub fn no_new_symbol(
program: &Program,
top_level_ctxt: SyntaxContext,
config: &RuleConfig<()>,
) -> Option<Box<dyn Rule>> {
let expected_reaction = config.get_rule_reaction();
match expected_reaction {
LintRuleReaction::Off => None,
_ => Some(visitor_rule(NoNewSymbol::new(
collect_decls_with_ctxt(program, top_level_ctxt),
top_level_ctxt,
expected_reaction,
))),
}
}
#[derive(Debug, Default)]
struct NoNewSymbol {
expected_reaction: LintRuleReaction,
top_level_ctxt: SyntaxContext,
top_level_declared_vars: AHashSet<Id>,
}
impl NoNewSymbol {
fn new(
top_level_declared_vars: AHashSet<Id>,
top_level_ctxt: SyntaxContext,
expected_reaction: LintRuleReaction,
) -> Self {
Self {
expected_reaction,
top_level_ctxt,
top_level_declared_vars,
}
}
fn check(&self, span: Span, ident: &Ident) {
if self.top_level_declared_vars.contains(&ident.to_id()) {
return;
}
if ident.span.ctxt != self.top_level_ctxt {
return;
}
if &*ident.sym == "Symbol" {
HANDLER.with(|handler| match self.expected_reaction {
LintRuleReaction::Error => {
handler.struct_span_err(span, MESSAGE).emit();
}
LintRuleReaction::Warning => {
handler.struct_span_warn(span, MESSAGE).emit();
}
_ => {}
});
}
}
}
impl Visit for NoNewSymbol {
noop_visit_type!();
fn visit_new_expr(&mut self, new_expr: &NewExpr) {
if let Expr::Ident(ident) = new_expr.callee.as_ref() {
self.check(new_expr.span, ident);
}
new_expr.visit_children_with(self);
}
}