mirror of
https://github.com/swc-project/swc.git
synced 2024-11-27 13:38:33 +03:00
feat(es/lints): Implement symbol-description
rule (#4161)
This commit is contained in:
parent
6debb423d5
commit
10851ece98
@ -0,0 +1,12 @@
|
||||
{
|
||||
"jsc": {
|
||||
"lints": {
|
||||
"symbol-description": [
|
||||
"error",
|
||||
{
|
||||
"enforceStringDescription": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
Symbol()
|
||||
Symbol({})
|
||||
Symbol('name')
|
@ -0,0 +1,6 @@
|
||||
|
||||
x Expected Symbol to have a description
|
||||
,----
|
||||
1 | Symbol()
|
||||
: ^^^^^^^^
|
||||
`----
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"jsc": {
|
||||
"lints": {
|
||||
"symbol-description": [
|
||||
"error"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
Symbol()
|
||||
Symbol({})
|
||||
foo(() => Symbol())
|
||||
Symbol('name')
|
@ -0,0 +1,18 @@
|
||||
|
||||
x Expected Symbol to have a description
|
||||
,----
|
||||
1 | Symbol()
|
||||
: ^^^^^^^^
|
||||
`----
|
||||
|
||||
x Symbol description should be a string
|
||||
,----
|
||||
2 | Symbol({})
|
||||
: ^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Expected Symbol to have a description
|
||||
,----
|
||||
3 | foo(() => Symbol())
|
||||
: ^^^^^^^^
|
||||
`----
|
@ -9,7 +9,8 @@ use crate::rules::non_critical_lints::{
|
||||
no_param_reassign::NoParamReassignConfig, no_restricted_syntax::NoRestrictedSyntaxConfig,
|
||||
no_use_before_define::NoUseBeforeDefineConfig,
|
||||
prefer_regex_literals::PreferRegexLiteralsConfig, quotes::QuotesConfig, radix::RadixConfig,
|
||||
use_is_nan::UseIsNanConfig, valid_typeof::ValidTypeofConfig, yoda::YodaConfig,
|
||||
symbol_description::SymbolDescriptionConfig, use_is_nan::UseIsNanConfig,
|
||||
valid_typeof::ValidTypeofConfig, yoda::YodaConfig,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
@ -155,4 +156,8 @@ pub struct LintConfig {
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "noParamReassign")]
|
||||
pub no_param_reassign: RuleConfig<NoParamReassignConfig>,
|
||||
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "symbolDescription")]
|
||||
pub symbol_description: RuleConfig<SymbolDescriptionConfig>,
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ pub(crate) mod non_critical_lints {
|
||||
pub mod prefer_regex_literals;
|
||||
pub mod quotes;
|
||||
pub mod radix;
|
||||
pub mod symbol_description;
|
||||
pub mod use_is_nan;
|
||||
pub mod valid_typeof;
|
||||
pub mod yoda;
|
||||
@ -145,6 +146,12 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
|
||||
rules.extend(no_param_reassign::no_param_reassign(
|
||||
&lint_config.no_param_reassign,
|
||||
));
|
||||
|
||||
rules.extend(symbol_description::symbol_description(
|
||||
program,
|
||||
top_level_ctxt,
|
||||
&lint_config.symbol_description,
|
||||
));
|
||||
}
|
||||
|
||||
rules
|
||||
|
120
crates/swc_ecma_lints/src/rules/symbol_description.rs
Normal file
120
crates/swc_ecma_lints/src/rules/symbol_description.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
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::{Visit, VisitWith};
|
||||
|
||||
use crate::{
|
||||
config::{LintRuleReaction, RuleConfig},
|
||||
rule::{visitor_rule, Rule},
|
||||
rules::utils::{extract_arg_val, ArgValue},
|
||||
};
|
||||
|
||||
const SYMBOL_EXPECTED_MESSAGE: &str = "Expected Symbol to have a description";
|
||||
const SYMBOL_STRING_DESCRIPTION_EXPECTED_MESSAGE: &str = "Symbol description should be a string";
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SymbolDescriptionConfig {
|
||||
enforce_string_description: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn symbol_description(
|
||||
program: &Program,
|
||||
top_level_ctxt: SyntaxContext,
|
||||
config: &RuleConfig<SymbolDescriptionConfig>,
|
||||
) -> Option<Box<dyn Rule>> {
|
||||
match config.get_rule_reaction() {
|
||||
LintRuleReaction::Off => None,
|
||||
_ => Some(visitor_rule(SymbolDescription::new(
|
||||
collect_decls_with_ctxt(program, top_level_ctxt),
|
||||
top_level_ctxt,
|
||||
config,
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct SymbolDescription {
|
||||
expected_reaction: LintRuleReaction,
|
||||
top_level_ctxt: SyntaxContext,
|
||||
top_level_declared_vars: AHashSet<Id>,
|
||||
enforce_string_description: bool,
|
||||
}
|
||||
|
||||
impl SymbolDescription {
|
||||
fn new(
|
||||
top_level_declared_vars: AHashSet<Id>,
|
||||
top_level_ctxt: SyntaxContext,
|
||||
config: &RuleConfig<SymbolDescriptionConfig>,
|
||||
) -> Self {
|
||||
let rule_config = config.get_rule_config();
|
||||
|
||||
Self {
|
||||
expected_reaction: config.get_rule_reaction(),
|
||||
top_level_ctxt,
|
||||
top_level_declared_vars,
|
||||
enforce_string_description: rule_config.enforce_string_description.unwrap_or(true),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_symbol_call(&self, ident: &Ident) -> bool {
|
||||
if self.top_level_declared_vars.contains(&ident.to_id()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ident.span.ctxt != self.top_level_ctxt {
|
||||
return false;
|
||||
}
|
||||
|
||||
&*ident.sym == "Symbol"
|
||||
}
|
||||
|
||||
fn check(&self, span: Span, first_arg: Option<&ExprOrSpread>) {
|
||||
if let Some(ExprOrSpread { expr, .. }) = first_arg {
|
||||
if self.enforce_string_description {
|
||||
match extract_arg_val(
|
||||
&self.top_level_ctxt,
|
||||
&self.top_level_declared_vars,
|
||||
expr,
|
||||
true,
|
||||
) {
|
||||
ArgValue::Str(_) => {}
|
||||
_ => {
|
||||
self.emit_report(span, SYMBOL_STRING_DESCRIPTION_EXPECTED_MESSAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self.emit_report(span, SYMBOL_EXPECTED_MESSAGE);
|
||||
}
|
||||
|
||||
fn emit_report(&self, span: Span, message: &str) {
|
||||
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 SymbolDescription {
|
||||
fn visit_call_expr(&mut self, call_expr: &CallExpr) {
|
||||
if let Callee::Expr(expr) = &call_expr.callee {
|
||||
if let Expr::Ident(ident) = expr.as_ref() {
|
||||
if self.is_symbol_call(ident) {
|
||||
self.check(call_expr.span, call_expr.args.first());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_expr.visit_children_with(self);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user