mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 22:22:34 +03:00
feat(es/lints): Add no-prototype-builtins
rule (#8684)
This commit is contained in:
parent
8c024895f9
commit
a5dbb17612
@ -0,0 +1,9 @@
|
||||
{
|
||||
"jsc": {
|
||||
"lints": {
|
||||
"noPrototypeBuiltins": [
|
||||
"error"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
foo.hasOwnProperty('bar');
|
||||
|
||||
foo.isPrototypeOf('bar');
|
||||
|
||||
foo.propertyIsEnumerable('bar');
|
||||
|
||||
foo.bar.hasOwnProperty('bar');
|
||||
|
||||
foo.bar.baz.isPrototypeOf('bar');
|
||||
|
||||
bar?.foo?.hasOwnProperty('bar');
|
||||
|
||||
foo?.bar.hasOwnProperty('baz');
|
||||
|
||||
foo.hasOwnProperty?.('bar');
|
||||
|
||||
foo?.hasOwnProperty('bar').baz;
|
||||
|
||||
foo.hasOwnProperty('bar')?.baz;
|
||||
|
||||
(a,b).hasOwnProperty('bar');
|
||||
|
||||
(foo?.hasOwnProperty)('bar');
|
||||
|
||||
(((foo?.hasOwnProperty)))('dlya-tex-kto-dumaet-cho-on-samiy-umniy');
|
||||
|
||||
(foo?.anotherProp, foo?.hasOwnProperty)('bar');
|
||||
|
||||
(foo.hasOwnProperty('ok'), foo?.hasOwnProperty)('bar');
|
||||
|
||||
foo['hasOwnProperty']('bar');
|
||||
|
||||
foo[`isPrototypeOf`]('bar').baz;
|
||||
|
||||
// valid
|
||||
|
||||
Object.prototype.hasOwnProperty.call(foo, 'bar');
|
||||
Object.prototype.isPrototypeOf.call(foo, 'bar');
|
||||
Object.prototype.propertyIsEnumerable.call(foo, 'bar');
|
||||
Object.prototype.hasOwnProperty.apply(foo, ['bar']);
|
||||
Object.prototype.isPrototypeOf.apply(foo, ['bar']);
|
||||
Object.prototype.propertyIsEnumerable.apply(foo, ['bar']);
|
||||
foo.hasOwnProperty;
|
||||
foo.hasOwnProperty.bar();
|
||||
foo(hasOwnProperty);
|
||||
hasOwnProperty(foo, 'bar');
|
||||
isPrototypeOf(foo, 'bar');
|
||||
propertyIsEnumerable(foo, 'bar');
|
||||
({}.hasOwnProperty.call(foo, 'bar'));
|
||||
({}.isPrototypeOf.call(foo, 'bar'));
|
||||
({}.propertyIsEnumerable.call(foo, 'bar'));
|
||||
({}.hasOwnProperty.apply(foo, ['bar']));
|
||||
({}.isPrototypeOf.apply(foo, ['bar']));
|
||||
({}.propertyIsEnumerable.apply(foo, ['bar']));
|
||||
foo[hasOwnProperty]('bar');
|
||||
foo['HasOwnProperty']('bar');
|
@ -0,0 +1,194 @@
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[1:1]
|
||||
1 | foo.hasOwnProperty('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
2 |
|
||||
3 | foo.isPrototypeOf('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'isPrototypeOf' from target object
|
||||
,-[1:1]
|
||||
1 | foo.hasOwnProperty('bar');
|
||||
2 |
|
||||
3 | foo.isPrototypeOf('bar');
|
||||
: ^^^^^^^^^^^^^
|
||||
4 |
|
||||
5 | foo.propertyIsEnumerable('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'propertyIsEnumerable' from target object
|
||||
,-[2:1]
|
||||
2 |
|
||||
3 | foo.isPrototypeOf('bar');
|
||||
4 |
|
||||
5 | foo.propertyIsEnumerable('bar');
|
||||
: ^^^^^^^^^^^^^^^^^^^^
|
||||
6 |
|
||||
7 | foo.bar.hasOwnProperty('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[4:1]
|
||||
4 |
|
||||
5 | foo.propertyIsEnumerable('bar');
|
||||
6 |
|
||||
7 | foo.bar.hasOwnProperty('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
8 |
|
||||
9 | foo.bar.baz.isPrototypeOf('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'isPrototypeOf' from target object
|
||||
,-[6:1]
|
||||
6 |
|
||||
7 | foo.bar.hasOwnProperty('bar');
|
||||
8 |
|
||||
9 | foo.bar.baz.isPrototypeOf('bar');
|
||||
: ^^^^^^^^^^^^^
|
||||
10 |
|
||||
11 | bar?.foo?.hasOwnProperty('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[8:1]
|
||||
8 |
|
||||
9 | foo.bar.baz.isPrototypeOf('bar');
|
||||
10 |
|
||||
11 | bar?.foo?.hasOwnProperty('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
12 |
|
||||
13 | foo?.bar.hasOwnProperty('baz');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[10:1]
|
||||
10 |
|
||||
11 | bar?.foo?.hasOwnProperty('bar');
|
||||
12 |
|
||||
13 | foo?.bar.hasOwnProperty('baz');
|
||||
: ^^^^^^^^^^^^^^
|
||||
14 |
|
||||
15 | foo.hasOwnProperty?.('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[12:1]
|
||||
12 |
|
||||
13 | foo?.bar.hasOwnProperty('baz');
|
||||
14 |
|
||||
15 | foo.hasOwnProperty?.('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
16 |
|
||||
17 | foo?.hasOwnProperty('bar').baz;
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[14:1]
|
||||
14 |
|
||||
15 | foo.hasOwnProperty?.('bar');
|
||||
16 |
|
||||
17 | foo?.hasOwnProperty('bar').baz;
|
||||
: ^^^^^^^^^^^^^^
|
||||
18 |
|
||||
19 | foo.hasOwnProperty('bar')?.baz;
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[16:1]
|
||||
16 |
|
||||
17 | foo?.hasOwnProperty('bar').baz;
|
||||
18 |
|
||||
19 | foo.hasOwnProperty('bar')?.baz;
|
||||
: ^^^^^^^^^^^^^^
|
||||
20 |
|
||||
21 | (a,b).hasOwnProperty('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[18:1]
|
||||
18 |
|
||||
19 | foo.hasOwnProperty('bar')?.baz;
|
||||
20 |
|
||||
21 | (a,b).hasOwnProperty('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
22 |
|
||||
23 | (foo?.hasOwnProperty)('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[20:1]
|
||||
20 |
|
||||
21 | (a,b).hasOwnProperty('bar');
|
||||
22 |
|
||||
23 | (foo?.hasOwnProperty)('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
24 |
|
||||
25 | (((foo?.hasOwnProperty)))('dlya-tex-kto-dumaet-cho-on-samiy-umniy');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[22:1]
|
||||
22 |
|
||||
23 | (foo?.hasOwnProperty)('bar');
|
||||
24 |
|
||||
25 | (((foo?.hasOwnProperty)))('dlya-tex-kto-dumaet-cho-on-samiy-umniy');
|
||||
: ^^^^^^^^^^^^^^
|
||||
26 |
|
||||
27 | (foo?.anotherProp, foo?.hasOwnProperty)('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[24:1]
|
||||
24 |
|
||||
25 | (((foo?.hasOwnProperty)))('dlya-tex-kto-dumaet-cho-on-samiy-umniy');
|
||||
26 |
|
||||
27 | (foo?.anotherProp, foo?.hasOwnProperty)('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
28 |
|
||||
29 | (foo.hasOwnProperty('ok'), foo?.hasOwnProperty)('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[26:1]
|
||||
26 |
|
||||
27 | (foo?.anotherProp, foo?.hasOwnProperty)('bar');
|
||||
28 |
|
||||
29 | (foo.hasOwnProperty('ok'), foo?.hasOwnProperty)('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
30 |
|
||||
31 | foo['hasOwnProperty']('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[26:1]
|
||||
26 |
|
||||
27 | (foo?.anotherProp, foo?.hasOwnProperty)('bar');
|
||||
28 |
|
||||
29 | (foo.hasOwnProperty('ok'), foo?.hasOwnProperty)('bar');
|
||||
: ^^^^^^^^^^^^^^
|
||||
30 |
|
||||
31 | foo['hasOwnProperty']('bar');
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'hasOwnProperty' from target object
|
||||
,-[28:1]
|
||||
28 |
|
||||
29 | (foo.hasOwnProperty('ok'), foo?.hasOwnProperty)('bar');
|
||||
30 |
|
||||
31 | foo['hasOwnProperty']('bar');
|
||||
: ^^^^^^^^^^^^^^^^
|
||||
32 |
|
||||
33 | foo[`isPrototypeOf`]('bar').baz;
|
||||
`----
|
||||
|
||||
x Do not access Object.prototype method 'isPrototypeOf' from target object
|
||||
,-[30:1]
|
||||
30 |
|
||||
31 | foo['hasOwnProperty']('bar');
|
||||
32 |
|
||||
33 | foo[`isPrototypeOf`]('bar').baz;
|
||||
: ^^^^^^^^^^^^^^^
|
||||
34 |
|
||||
35 | // valid
|
||||
`----
|
@ -217,6 +217,8 @@ pub struct LintConfig {
|
||||
pub no_cond_assign: RuleConfig<()>,
|
||||
|
||||
#[cfg(feature = "non_critical_lints")]
|
||||
#[serde(default, alias = "noPrototypeBuiltins")]
|
||||
pub no_prototype_builtins: RuleConfig<()>,
|
||||
#[serde(default, alias = "noNewObject")]
|
||||
pub no_new_object: RuleConfig<()>,
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ pub(crate) mod non_critical_lints {
|
||||
pub mod no_new_symbol;
|
||||
pub mod no_obj_calls;
|
||||
pub mod no_param_reassign;
|
||||
pub mod no_prototype_builtins;
|
||||
pub mod no_restricted_syntax;
|
||||
pub mod no_sparse_arrays;
|
||||
pub mod no_throw_literal;
|
||||
@ -196,6 +197,10 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
|
||||
|
||||
rules.extend(no_cond_assign::no_cond_assign(&lint_config.no_cond_assign));
|
||||
|
||||
rules.extend(no_prototype_builtins::no_prototype_builtins(
|
||||
&lint_config.no_prototype_builtins,
|
||||
));
|
||||
|
||||
rules.extend(no_new_object::no_new_object(
|
||||
unresolved_ctxt,
|
||||
&lint_config.no_new_object,
|
||||
|
153
crates/swc_ecma_lints/src/rules/no_prototype_builtins.rs
Normal file
153
crates/swc_ecma_lints/src/rules/no_prototype_builtins.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use swc_atoms::Atom;
|
||||
use swc_common::{errors::HANDLER, Span};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::ExprExt;
|
||||
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
|
||||
|
||||
use crate::{
|
||||
config::{LintRuleReaction, RuleConfig},
|
||||
rule::{visitor_rule, Rule},
|
||||
};
|
||||
|
||||
const METHODS: [&str; 3] = ["hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"];
|
||||
|
||||
pub fn no_prototype_builtins(config: &RuleConfig<()>) -> Option<Box<dyn Rule>> {
|
||||
let rule_reaction = config.get_rule_reaction();
|
||||
|
||||
match rule_reaction {
|
||||
LintRuleReaction::Off => None,
|
||||
_ => Some(visitor_rule(NoPrototypeBuiltins::new(rule_reaction))),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct CallInfo {
|
||||
chain: Vec<Atom>,
|
||||
method_span: Option<Span>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct NoPrototypeBuiltins {
|
||||
expected_reaction: LintRuleReaction,
|
||||
call_info: CallInfo,
|
||||
}
|
||||
|
||||
impl NoPrototypeBuiltins {
|
||||
fn new(expected_reaction: LintRuleReaction) -> Self {
|
||||
Self {
|
||||
expected_reaction,
|
||||
call_info: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_error(&self, span: Span, method: &str) {
|
||||
let message = format!(
|
||||
"Do not access Object.prototype method '{}' from target object",
|
||||
method
|
||||
);
|
||||
|
||||
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 extend_chain(&mut self, span: Span, atom: Atom) {
|
||||
if self.call_info.method_span.is_none() {
|
||||
self.call_info.method_span = Some(span);
|
||||
}
|
||||
|
||||
self.call_info.chain.push(atom);
|
||||
}
|
||||
|
||||
fn extract_path(&mut self, expr: &Expr) {
|
||||
match expr {
|
||||
Expr::Member(member) => {
|
||||
match &member.prop {
|
||||
MemberProp::Ident(ident) => {
|
||||
self.extend_chain(ident.span, ident.sym.clone());
|
||||
}
|
||||
MemberProp::Computed(computed_prop_name) => {
|
||||
match computed_prop_name.expr.as_ref() {
|
||||
Expr::Lit(_) | Expr::Tpl(_) | Expr::Paren(_) | Expr::Seq(_) => {
|
||||
self.extract_path(&computed_prop_name.expr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
self.extract_path(member.obj.as_ref());
|
||||
}
|
||||
Expr::OptChain(OptChainExpr { base, .. }) => {
|
||||
if let Some(member_expr) = base.as_member() {
|
||||
if let Some(ident) = member_expr.prop.as_ident() {
|
||||
self.extend_chain(ident.span, ident.sym.clone());
|
||||
}
|
||||
|
||||
self.extract_path(member_expr.obj.as_ref());
|
||||
}
|
||||
}
|
||||
Expr::Paren(ParenExpr { expr, .. }) => {
|
||||
self.extract_path(expr.as_ref());
|
||||
}
|
||||
Expr::Seq(SeqExpr { exprs, .. }) => {
|
||||
self.extract_path(exprs.last().unwrap().as_ref());
|
||||
}
|
||||
Expr::Lit(Lit::Str(lit_str)) => {
|
||||
self.extend_chain(lit_str.span, lit_str.value.clone());
|
||||
}
|
||||
Expr::Tpl(tpl) => {
|
||||
if tpl.exprs.is_empty() && tpl.quasis.len() == 1 {
|
||||
self.extend_chain(tpl.span, tpl.quasis[0].raw.clone());
|
||||
}
|
||||
}
|
||||
Expr::Ident(ident) => {
|
||||
self.extend_chain(ident.span, ident.sym.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn check(&mut self, expr: &Expr) {
|
||||
let prev_call_info = std::mem::take(&mut self.call_info);
|
||||
|
||||
self.extract_path(expr);
|
||||
|
||||
if self.call_info.chain.len() > 1 {
|
||||
let method_name = self.call_info.chain[0].as_str();
|
||||
|
||||
if METHODS.contains(&method_name) {
|
||||
self.emit_error(self.call_info.method_span.unwrap(), method_name);
|
||||
}
|
||||
}
|
||||
|
||||
self.call_info = prev_call_info;
|
||||
}
|
||||
}
|
||||
|
||||
impl Visit for NoPrototypeBuiltins {
|
||||
noop_visit_type!();
|
||||
|
||||
fn visit_opt_chain_base(&mut self, opt_chain_base: &OptChainBase) {
|
||||
if let OptChainBase::Call(opt_call) = opt_chain_base {
|
||||
self.check(opt_call.callee.as_expr());
|
||||
}
|
||||
|
||||
opt_chain_base.visit_children_with(self);
|
||||
}
|
||||
|
||||
fn visit_call_expr(&mut self, call_expr: &CallExpr) {
|
||||
if let Some(expr) = call_expr.callee.as_expr() {
|
||||
self.check(expr.as_ref());
|
||||
}
|
||||
|
||||
call_expr.visit_children_with(self);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user