Skip to content

Commit f3fc061

Browse files
authored
fix(prefer-primordials): Force to use null [[Prototype]] object (#1307)
1 parent bdf77c5 commit f3fc061

File tree

1 file changed

+181
-1
lines changed

1 file changed

+181
-1
lines changed

src/rules/prefer_primordials.rs

+181-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ enum PreferPrimordialsMessage {
2121
GlobalIntrinsic,
2222
#[display(fmt = "Don't use the unsafe intrinsic")]
2323
UnsafeIntrinsic,
24+
#[display(fmt = "Use null [[prototype]] object in the define property")]
25+
DefineProperty,
26+
#[display(fmt = "Use null [[prototype]] object in the default parameter")]
27+
ObjectAssignInDefaultParameter,
2428
#[display(fmt = "Don't use iterator protocol directly")]
2529
Iterator,
2630
#[display(fmt = "Don't use RegExp literal directly")]
@@ -39,6 +43,8 @@ enum PreferPrimordialsHint {
3943
fmt = "Instead use the safe wrapper from the `primordials` object"
4044
)]
4145
UnsafeIntrinsic,
46+
#[display(fmt = "Add `__proto__: null` to this object literal")]
47+
NullPrototypeObjectLiteral,
4248
#[display(fmt = "Wrap a SafeIterator from the `primordials` object")]
4349
SafeIterator,
4450
#[display(fmt = "Wrap `SafeRegExp` from the `primordials` object")]
@@ -333,6 +339,28 @@ const GETTER_TARGETS: &[&str] = &[
333339
// "length",
334340
];
335341

342+
fn is_null_proto(object_lit: &ast_view::ObjectLit) -> bool {
343+
for prop_or_spread in object_lit.props {
344+
if_chain! {
345+
if let ast_view::PropOrSpread::Prop(prop) = prop_or_spread;
346+
if let ast_view::Prop::KeyValue(key_value_prop) = prop;
347+
if matches!(key_value_prop.value, ast_view::Expr::Lit(ast_view::Lit::Null(..)));
348+
then {
349+
if let ast_view::PropName::Ident(ident) = key_value_prop.key {
350+
if ident.sym().as_ref() == "__proto__" {
351+
return true
352+
}
353+
} else if let ast_view::PropName::Str(str) = key_value_prop.key {
354+
if str.inner.value.as_ref() == "__proto__" {
355+
return true
356+
}
357+
}
358+
}
359+
}
360+
}
361+
false
362+
}
363+
336364
struct PreferPrimordialsHandler;
337365

338366
impl Handler for PreferPrimordialsHandler {
@@ -405,6 +433,51 @@ impl Handler for PreferPrimordialsHandler {
405433
PreferPrimordialsHint::UnsafeIntrinsic,
406434
);
407435
}
436+
437+
match &ident.sym().as_ref() {
438+
&"ObjectDefineProperty" | &"ReflectDefineProperty" => {
439+
if_chain! {
440+
if let ast_view::Node::CallExpr(call_expr) = ident.parent();
441+
if let Some(expr_or_spread) = call_expr.args.get(2);
442+
if let ast_view::Expr::Object(object_lit) = expr_or_spread.expr;
443+
if !is_null_proto(object_lit);
444+
then {
445+
ctx.add_diagnostic_with_hint(
446+
object_lit.range(),
447+
CODE,
448+
PreferPrimordialsMessage::DefineProperty,
449+
PreferPrimordialsHint::NullPrototypeObjectLiteral,
450+
);
451+
}
452+
}
453+
}
454+
&"ObjectDefineProperties" => {
455+
if_chain! {
456+
if let ast_view::Node::CallExpr(call_expr) = ident.parent();
457+
if let Some(expr_or_spread) = call_expr.args.get(1);
458+
if let ast_view::Expr::Object(object_lit) = expr_or_spread.expr;
459+
then {
460+
for prop_or_spread in object_lit.props {
461+
if_chain! {
462+
if let ast_view::PropOrSpread::Prop(prop) = prop_or_spread;
463+
if let ast_view::Prop::KeyValue(key_value_prop) = prop;
464+
if let ast_view::Expr::Object(object_lit) = key_value_prop.value;
465+
if !is_null_proto(object_lit);
466+
then {
467+
ctx.add_diagnostic_with_hint(
468+
object_lit.range(),
469+
CODE,
470+
PreferPrimordialsMessage::DefineProperty,
471+
PreferPrimordialsHint::NullPrototypeObjectLiteral,
472+
);
473+
}
474+
}
475+
}
476+
}
477+
}
478+
}
479+
_ => (),
480+
}
408481
}
409482

410483
fn member_expr(
@@ -490,6 +563,37 @@ impl Handler for PreferPrimordialsHandler {
490563
}
491564
}
492565

566+
fn object_lit(
567+
&mut self,
568+
object_lit: &ast_view::ObjectLit,
569+
ctx: &mut Context,
570+
) {
571+
fn inside_param(orig: ast_view::Node, node: ast_view::Node) -> bool {
572+
if let Some(param) = node.to::<ast_view::Param>() {
573+
return param.range().contains(&orig.range());
574+
}
575+
576+
match node.parent() {
577+
None => false,
578+
Some(parent) => inside_param(orig, parent),
579+
}
580+
}
581+
582+
if_chain! {
583+
if !is_null_proto(object_lit);
584+
if matches!(object_lit.parent(), ast_view::Node::AssignPat(_) | ast_view::Node::AssignPatProp(_));
585+
if inside_param(object_lit.as_node(), object_lit.as_node());
586+
then {
587+
ctx.add_diagnostic_with_hint(
588+
object_lit.range(),
589+
CODE,
590+
PreferPrimordialsMessage::ObjectAssignInDefaultParameter,
591+
PreferPrimordialsHint::NullPrototypeObjectLiteral,
592+
);
593+
}
594+
}
595+
}
596+
493597
fn expr_or_spread(
494598
&mut self,
495599
expr_or_spread: &ast_view::ExprOrSpread,
@@ -688,7 +792,22 @@ const a = {
688792
"#,
689793
r#"
690794
const { ObjectDefineProperty, SymbolToStringTag } = primordials;
691-
ObjectDefineProperty(o, SymbolToStringTag, { value: "o" });
795+
ObjectDefineProperty(o, SymbolToStringTag, { __proto__: null, value: "o" });
796+
"#,
797+
r#"
798+
const { ReflectDefineProperty, SymbolToStringTag } = primordials;
799+
ReflectDefineProperty(o, SymbolToStringTag, { __proto__: null, value: "o" });
800+
"#,
801+
r#"
802+
const { ObjectDefineProperties } = primordials;
803+
ObjectDefineProperties(o, {
804+
foo: { __proto__: null, value: "o" },
805+
bar: { "__proto__": null, value: "o" },
806+
});
807+
"#,
808+
r#"
809+
function foo(o = { __proto__: null }) {}
810+
function bar({ o = { __proto__: null } }) {}
692811
"#,
693812
r#"
694813
const { NumberParseInt } = primordials;
@@ -933,6 +1052,67 @@ ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" });
9331052
message: PreferPrimordialsMessage::GlobalIntrinsic,
9341053
hint: PreferPrimordialsHint::GlobalIntrinsic,
9351054
},
1055+
{
1056+
line: 3,
1057+
col: 44,
1058+
message: PreferPrimordialsMessage::DefineProperty,
1059+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1060+
},
1061+
],
1062+
r#"
1063+
const { ObjectDefineProperty, SymbolToStringTag } = primordials;
1064+
ObjectDefineProperty(o, SymbolToStringTag, { value: "o" });
1065+
"#: [
1066+
{
1067+
line: 3,
1068+
col: 43,
1069+
message: PreferPrimordialsMessage::DefineProperty,
1070+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1071+
},
1072+
],
1073+
r#"
1074+
const { ObjectDefineProperties } = primordials;
1075+
ObjectDefineProperties(o, {
1076+
foo: { value: "o" },
1077+
bar: { __proto__: {}, value: "o" },
1078+
baz: { ["__proto__"]: null, value: "o" },
1079+
});
1080+
"#: [
1081+
{
1082+
line: 4,
1083+
col: 7,
1084+
message: PreferPrimordialsMessage::DefineProperty,
1085+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1086+
},
1087+
{
1088+
line: 5,
1089+
col: 7,
1090+
message: PreferPrimordialsMessage::DefineProperty,
1091+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1092+
},
1093+
{
1094+
line: 6,
1095+
col: 7,
1096+
message: PreferPrimordialsMessage::DefineProperty,
1097+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1098+
},
1099+
],
1100+
r#"
1101+
function foo(o = {}) {}
1102+
function bar({ o = {} }) {}
1103+
"#: [
1104+
{
1105+
line: 2,
1106+
col: 17,
1107+
message: PreferPrimordialsMessage::ObjectAssignInDefaultParameter,
1108+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1109+
},
1110+
{
1111+
line: 3,
1112+
col: 19,
1113+
message: PreferPrimordialsMessage::ObjectAssignInDefaultParameter,
1114+
hint: PreferPrimordialsHint::NullPrototypeObjectLiteral,
1115+
}
9361116
],
9371117
r#"
9381118
const { Number } = primordials;

0 commit comments

Comments
 (0)