@@ -21,6 +21,10 @@ enum PreferPrimordialsMessage {
21
21
GlobalIntrinsic ,
22
22
#[ display( fmt = "Don't use the unsafe intrinsic" ) ]
23
23
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 ,
24
28
#[ display( fmt = "Don't use iterator protocol directly" ) ]
25
29
Iterator ,
26
30
#[ display( fmt = "Don't use RegExp literal directly" ) ]
@@ -39,6 +43,8 @@ enum PreferPrimordialsHint {
39
43
fmt = "Instead use the safe wrapper from the `primordials` object"
40
44
) ]
41
45
UnsafeIntrinsic ,
46
+ #[ display( fmt = "Add `__proto__: null` to this object literal" ) ]
47
+ NullPrototypeObjectLiteral ,
42
48
#[ display( fmt = "Wrap a SafeIterator from the `primordials` object" ) ]
43
49
SafeIterator ,
44
50
#[ display( fmt = "Wrap `SafeRegExp` from the `primordials` object" ) ]
@@ -333,6 +339,28 @@ const GETTER_TARGETS: &[&str] = &[
333
339
// "length",
334
340
] ;
335
341
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
+
336
364
struct PreferPrimordialsHandler ;
337
365
338
366
impl Handler for PreferPrimordialsHandler {
@@ -405,6 +433,51 @@ impl Handler for PreferPrimordialsHandler {
405
433
PreferPrimordialsHint :: UnsafeIntrinsic ,
406
434
) ;
407
435
}
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
+ }
408
481
}
409
482
410
483
fn member_expr (
@@ -490,6 +563,37 @@ impl Handler for PreferPrimordialsHandler {
490
563
}
491
564
}
492
565
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
+
493
597
fn expr_or_spread (
494
598
& mut self ,
495
599
expr_or_spread : & ast_view:: ExprOrSpread ,
@@ -688,7 +792,22 @@ const a = {
688
792
"# ,
689
793
r#"
690
794
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 } }) {}
692
811
"# ,
693
812
r#"
694
813
const { NumberParseInt } = primordials;
@@ -933,6 +1052,67 @@ ObjectDefineProperty(o, Symbol.toStringTag, { value: "o" });
933
1052
message: PreferPrimordialsMessage :: GlobalIntrinsic ,
934
1053
hint: PreferPrimordialsHint :: GlobalIntrinsic ,
935
1054
} ,
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
+ }
936
1116
] ,
937
1117
r#"
938
1118
const { Number } = primordials;
0 commit comments