mirror of
https://github.com/swc-project/swc.git
synced 2024-12-25 22:56:11 +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<()>,
|
pub no_cond_assign: RuleConfig<()>,
|
||||||
|
|
||||||
#[cfg(feature = "non_critical_lints")]
|
#[cfg(feature = "non_critical_lints")]
|
||||||
|
#[serde(default, alias = "noPrototypeBuiltins")]
|
||||||
|
pub no_prototype_builtins: RuleConfig<()>,
|
||||||
#[serde(default, alias = "noNewObject")]
|
#[serde(default, alias = "noNewObject")]
|
||||||
pub no_new_object: RuleConfig<()>,
|
pub no_new_object: RuleConfig<()>,
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ pub(crate) mod non_critical_lints {
|
|||||||
pub mod no_new_symbol;
|
pub mod no_new_symbol;
|
||||||
pub mod no_obj_calls;
|
pub mod no_obj_calls;
|
||||||
pub mod no_param_reassign;
|
pub mod no_param_reassign;
|
||||||
|
pub mod no_prototype_builtins;
|
||||||
pub mod no_restricted_syntax;
|
pub mod no_restricted_syntax;
|
||||||
pub mod no_sparse_arrays;
|
pub mod no_sparse_arrays;
|
||||||
pub mod no_throw_literal;
|
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_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(
|
rules.extend(no_new_object::no_new_object(
|
||||||
unresolved_ctxt,
|
unresolved_ctxt,
|
||||||
&lint_config.no_new_object,
|
&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