mirror of
https://github.com/swc-project/swc.git
synced 2024-12-28 08:04:43 +03:00
feat(es/lints): Implement valid-typeof
rule (#4095)
This commit is contained in:
parent
b31ead5cbe
commit
9ceefa734f
@ -0,0 +1,9 @@
|
||||
{
|
||||
"jsc": {
|
||||
"lints": {
|
||||
"valid-typeof": [
|
||||
"error"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
typeof foo === "strnig";
|
||||
typeof foo == "undefimed";
|
||||
typeof bar != "nunber";
|
||||
typeof bar !== "fucntion";
|
||||
typeof bar !== foo();
|
||||
typeof foo > "strnig";
|
||||
typeof bar !== "function";
|
||||
typeof x === typeof y;
|
||||
a === b;
|
@ -0,0 +1,30 @@
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
1 | typeof foo === "strnig";
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
2 | typeof foo == "undefimed";
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
3 | typeof bar != "nunber";
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
4 | typeof bar !== "fucntion";
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
5 | typeof bar !== foo();
|
||||
: ^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"jsc": {
|
||||
"lints": {
|
||||
"valid-typeof": [
|
||||
"error",
|
||||
{
|
||||
"requireStringLiterals": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
typeof foo === "strnig"
|
||||
typeof foo == "undefimed"
|
||||
typeof bar != "nunber"
|
||||
typeof bar !== "fucntion"
|
||||
|
||||
typeof bar !== foo()
|
||||
|
||||
typeof bar !== ident
|
||||
|
||||
// Just for test =)
|
||||
typeof foo > "strnig"
|
||||
typeof bar !== "function"
|
||||
typeof x === typeof y
|
@ -0,0 +1,24 @@
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
1 | typeof foo === "strnig"
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
2 | typeof foo == "undefimed"
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
3 | typeof bar != "nunber"
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
||||
|
||||
x Invalid typeof comparison value
|
||||
,----
|
||||
4 | typeof bar !== "fucntion"
|
||||
: ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
`----
|
@ -8,7 +8,7 @@ use crate::rules::non_critical_lints::{
|
||||
no_console::NoConsoleConfig, no_empty_function::NoEmptyFunctionConfig,
|
||||
no_restricted_syntax::NoRestrictedSyntaxConfig, no_use_before_define::NoUseBeforeDefineConfig,
|
||||
prefer_regex_literals::PreferRegexLiteralsConfig, quotes::QuotesConfig, radix::RadixConfig,
|
||||
yoda::YodaConfig,
|
||||
valid_typeof::ValidTypeofConfig, yoda::YodaConfig,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
@ -142,4 +142,8 @@ pub struct LintConfig {
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "noNewSymbol")]
|
||||
pub no_new_symbol: RuleConfig<()>,
|
||||
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "validTypeof")]
|
||||
pub valid_typeof: RuleConfig<ValidTypeofConfig>,
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ pub(crate) mod non_critical_lints {
|
||||
pub mod prefer_regex_literals;
|
||||
pub mod quotes;
|
||||
pub mod radix;
|
||||
pub mod valid_typeof;
|
||||
pub mod yoda;
|
||||
}
|
||||
|
||||
@ -137,6 +138,8 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
|
||||
top_level_ctxt,
|
||||
&lint_config.no_new_symbol,
|
||||
));
|
||||
|
||||
rules.extend(valid_typeof::valid_typeof(&lint_config.valid_typeof));
|
||||
}
|
||||
|
||||
rules
|
||||
|
131
crates/swc_ecma_lints/src/rules/valid_typeof.rs
Normal file
131
crates/swc_ecma_lints/src/rules/valid_typeof.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use swc_common::{errors::HANDLER, Span};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{Visit, VisitWith};
|
||||
|
||||
use crate::{
|
||||
config::{LintRuleReaction, RuleConfig},
|
||||
rule::{visitor_rule, Rule},
|
||||
};
|
||||
|
||||
const MESSAGE: &str = "Invalid typeof comparison value";
|
||||
|
||||
const VALID_TYPES: &[&str] = &[
|
||||
"undefined",
|
||||
"object",
|
||||
"boolean",
|
||||
"number",
|
||||
"string",
|
||||
"function",
|
||||
"symbol",
|
||||
"bigint",
|
||||
];
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ValidTypeofConfig {
|
||||
require_string_literals: Option<bool>,
|
||||
}
|
||||
|
||||
pub fn valid_typeof(config: &RuleConfig<ValidTypeofConfig>) -> Option<Box<dyn Rule>> {
|
||||
match config.get_rule_reaction() {
|
||||
LintRuleReaction::Off => None,
|
||||
_ => Some(visitor_rule(ValidTypeof::new(config))),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ValidTypeof {
|
||||
expected_reaction: LintRuleReaction,
|
||||
require_string_literals: bool,
|
||||
}
|
||||
|
||||
impl ValidTypeof {
|
||||
fn new(config: &RuleConfig<ValidTypeofConfig>) -> Self {
|
||||
let rule_config = config.get_rule_config();
|
||||
|
||||
Self {
|
||||
expected_reaction: config.get_rule_reaction(),
|
||||
require_string_literals: rule_config.require_string_literals.unwrap_or(true),
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_report(&self, span: Span) {
|
||||
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();
|
||||
}
|
||||
_ => {}
|
||||
});
|
||||
}
|
||||
|
||||
fn check(&self, span: Span, str_operand: &str) {
|
||||
if !VALID_TYPES.contains(&str_operand) {
|
||||
self.emit_report(span);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visit for ValidTypeof {
|
||||
fn visit_bin_expr(&mut self, bin_expr: &BinExpr) {
|
||||
if let op!("==") | op!("===") | op!("!=") | op!("!==") = bin_expr.op {
|
||||
match (bin_expr.left.as_ref(), bin_expr.right.as_ref()) {
|
||||
// case typeof x === "type"
|
||||
(
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("typeof"), ..
|
||||
}),
|
||||
Expr::Lit(Lit::Str(Str { value, .. })),
|
||||
) => {
|
||||
self.check(bin_expr.span, &*value);
|
||||
}
|
||||
// case "type" === typeof x
|
||||
(
|
||||
Expr::Lit(Lit::Str(Str { value, .. })),
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("typeof"), ..
|
||||
}),
|
||||
) => {
|
||||
self.check(bin_expr.span, &*value);
|
||||
}
|
||||
// case typeof x === typeof y
|
||||
(
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("typeof"), ..
|
||||
}),
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("typeof"), ..
|
||||
}),
|
||||
) => {}
|
||||
// case typeof x === foo()
|
||||
(
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("typeof"), ..
|
||||
}),
|
||||
_,
|
||||
) => {
|
||||
if self.require_string_literals {
|
||||
self.emit_report(bin_expr.span);
|
||||
}
|
||||
}
|
||||
// case foo() === typeof x
|
||||
(
|
||||
_,
|
||||
Expr::Unary(UnaryExpr {
|
||||
op: op!("typeof"), ..
|
||||
}),
|
||||
) => {
|
||||
if self.require_string_literals {
|
||||
self.emit_report(bin_expr.span);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
bin_expr.visit_children_with(self);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user