feat(es/lints): Implement valid-typeof rule (#4095)

This commit is contained in:
Artur 2022-03-20 09:46:41 +03:00 committed by GitHub
parent b31ead5cbe
commit 9ceefa734f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 236 additions and 1 deletions

View File

@ -0,0 +1,9 @@
{
"jsc": {
"lints": {
"valid-typeof": [
"error"
]
}
}
}

View File

@ -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;

View File

@ -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();
: ^^^^^^^^^^^^^^^^^^^^
`----

View File

@ -0,0 +1,12 @@
{
"jsc": {
"lints": {
"valid-typeof": [
"error",
{
"requireStringLiterals": false
}
]
}
}
}

View File

@ -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

View File

@ -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"
: ^^^^^^^^^^^^^^^^^^^^^^^^^
`----

View File

@ -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>,
}

View File

@ -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

View 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);
}
}