11'use strict' ;
22
3+ const CallExpression = ( fnName ) => `CallExpression[callee.name=${ fnName } ]` ;
4+
35function checkProperties ( context , node ) {
46 if (
57 node . type === 'CallExpression' &&
@@ -64,8 +66,10 @@ function checkPropertyDescriptor(context, node) {
6466}
6567
6668function createUnsafeStringMethodReport ( context , name , lookedUpProperty ) {
69+ const lastDotPosition = '$String.prototype.' . length ;
70+ const unsafePrimordialName = `StringPrototype${ name . charAt ( lastDotPosition ) . toUpperCase ( ) } ${ name . slice ( lastDotPosition + 1 , - 1 ) } ` ;
6771 return {
68- [ ` ${ CallExpression } [expression.callee.name= ${ JSON . stringify ( name ) } ]` ] ( node ) {
72+ [ CallExpression ( unsafePrimordialName ) ] ( node ) {
6973 context . report ( {
7074 node,
7175 message : `${ name } looks up the ${ lookedUpProperty } property on the first argument` ,
@@ -74,31 +78,46 @@ function createUnsafeStringMethodReport(context, name, lookedUpProperty) {
7478 } ;
7579}
7680
77- const CallExpression = 'ExpressionStatement[expression.type="CallExpression"]' ;
81+ function createUnsafeStringMethodOnRegexReport ( context , name , lookedUpProperty ) {
82+ const dotPosition = 'Symbol.' . length ;
83+ const safePrimordialName = `RegExpPrototypeSymbol${ lookedUpProperty . charAt ( dotPosition ) . toUpperCase ( ) } ${ lookedUpProperty . slice ( dotPosition + 1 ) } ` ;
84+ const lastDotPosition = '$String.prototype.' . length ;
85+ const unsafePrimordialName = `StringPrototype${ name . charAt ( lastDotPosition ) . toUpperCase ( ) } ${ name . slice ( lastDotPosition + 1 , - 1 ) } ` ;
86+ return {
87+ [ [
88+ `${ CallExpression ( unsafePrimordialName ) } [arguments.1.type=Literal][arguments.1.regex]` ,
89+ `${ CallExpression ( unsafePrimordialName ) } [arguments.1.type=NewExpression][arguments.1.callee.name=RegExp]` ,
90+ ] . join ( ',' ) ] ( node ) {
91+ context . report ( {
92+ node,
93+ message : `${ name } looks up the ${ lookedUpProperty } property of the passed regex, use ${ safePrimordialName } directly` ,
94+ } ) ;
95+ }
96+ } ;
97+ }
98+
7899module . exports = {
79100 meta : { hasSuggestions : true } ,
80101 create ( context ) {
81102 return {
82- [ `${ CallExpression } [expression.callee.name=${ / ^ ( O b j e c t | R e f l e c t ) D e f i n e P r o p e r t ( i e s | y ) $ / } ]` ] (
83- node
84- ) {
85- switch ( node . expression . callee . name ) {
103+ [ CallExpression ( / ^ ( O b j e c t | R e f l e c t ) D e f i n e P r o p e r t ( i e s | y ) $ / ) ] ( node ) {
104+ switch ( node . callee . name ) {
86105 case 'ObjectDefineProperties' :
87- checkProperties ( context , node . expression . arguments [ 1 ] ) ;
106+ checkProperties ( context , node . arguments [ 1 ] ) ;
88107 break ;
89108 case 'ReflectDefineProperty' :
90109 case 'ObjectDefineProperty' :
91- checkPropertyDescriptor ( context , node . expression . arguments [ 2 ] ) ;
110+ checkPropertyDescriptor ( context , node . arguments [ 2 ] ) ;
92111 break ;
93112 default :
94113 throw new Error ( 'Unreachable' ) ;
95114 }
96115 } ,
97116
98- [ `${ CallExpression } [expression.callee.name="ObjectCreate"][expression. arguments.length=2]` ] ( node ) {
99- checkProperties ( context , node . expression . arguments [ 1 ] ) ;
117+ [ `${ CallExpression ( 'ObjectCreate' ) } [ arguments.length=2]` ] ( node ) {
118+ checkProperties ( context , node . arguments [ 1 ] ) ;
100119 } ,
101- [ ` ${ CallExpression } [expression.callee.name=" RegExpPrototypeTest"]` ] ( node ) {
120+ [ CallExpression ( ' RegExpPrototypeTest' ) ] ( node ) {
102121 context . report ( {
103122 node,
104123 message : '%RegExp.prototype.test% looks up the "exec" property of `this` value' ,
@@ -116,18 +135,18 @@ module.exports = {
116135 } ] ,
117136 } ) ;
118137 } ,
119- [ ` ${ CallExpression } [expression.callee.name= ${ / ^ R e g E x p P r o t o t y p e S y m b o l ( M a t c h | M a t c h A l l | S e a r c h ) $ / } ]` ] ( node ) {
138+ [ CallExpression ( / ^ R e g E x p P r o t o t y p e S y m b o l ( M a t c h | M a t c h A l l | S e a r c h ) $ / ) ] ( node ) {
120139 context . report ( {
121140 node,
122- message : node . expression . callee . name + ' looks up the "exec" property of `this` value' ,
141+ message : node . callee . name + ' looks up the "exec" property of `this` value' ,
123142 } ) ;
124143 } ,
125- ...createUnsafeStringMethodReport ( context , 'StringPrototypeMatch ' , 'Symbol.match' ) ,
126- ...createUnsafeStringMethodReport ( context , 'StringPrototypeMatchAll ' , 'Symbol.matchAll' ) ,
127- ...createUnsafeStringMethodReport ( context , 'StringPrototypeReplace ' , 'Symbol.replace' ) ,
128- ...createUnsafeStringMethodReport ( context , 'StringPrototypeReplaceAll ' , 'Symbol.replace' ) ,
129- ...createUnsafeStringMethodReport ( context , 'StringPrototypeSearch ' , 'Symbol.search' ) ,
130- ...createUnsafeStringMethodReport ( context , 'StringPrototypeSplit ' , 'Symbol.split' ) ,
144+ ...createUnsafeStringMethodReport ( context , '%String.prototype.match% ' , 'Symbol.match' ) ,
145+ ...createUnsafeStringMethodReport ( context , '%String.prototype.matchAll% ' , 'Symbol.matchAll' ) ,
146+ ...createUnsafeStringMethodOnRegexReport ( context , '%String.prototype.replace% ' , 'Symbol.replace' ) ,
147+ ...createUnsafeStringMethodOnRegexReport ( context , '%String.prototype.replaceAll% ' , 'Symbol.replace' ) ,
148+ ...createUnsafeStringMethodReport ( context , '%String.prototype.search% ' , 'Symbol.search' ) ,
149+ ...createUnsafeStringMethodOnRegexReport ( context , '%String.prototype.split% ' , 'Symbol.split' ) ,
131150
132151 'NewExpression[callee.name="Proxy"][arguments.1.type="ObjectExpression"]' ( node ) {
133152 for ( const { key, value } of node . arguments [ 1 ] . properties ) {
@@ -146,15 +165,15 @@ module.exports = {
146165 } ) ;
147166 } ,
148167
149- [ ` ${ CallExpression } [expression.callee.name= PromisePrototypeCatch]` ] ( node ) {
168+ [ CallExpression ( ' PromisePrototypeCatch' ) ] ( node ) {
150169 context . report ( {
151170 node,
152171 message : '%Promise.prototype.catch% look up the `then` property of ' +
153172 'the `this` argument, use PromisePrototypeThen instead' ,
154173 } ) ;
155174 } ,
156175
157- [ ` ${ CallExpression } [expression.callee.name= PromisePrototypeFinally]` ] ( node ) {
176+ [ CallExpression ( ' PromisePrototypeFinally' ) ] ( node ) {
158177 context . report ( {
159178 node,
160179 message : '%Promise.prototype.finally% look up the `then` property of ' +
@@ -163,10 +182,10 @@ module.exports = {
163182 } ) ;
164183 } ,
165184
166- [ ` ${ CallExpression } [expression.callee.name= ${ / ^ P r o m i s e ( A l l ( S e t t l e d ) ? | A n y | R a c e ) / } ]` ] ( node ) {
185+ [ CallExpression ( / ^ P r o m i s e ( A l l ( S e t t l e d ) ? | A n y | R a c e ) / ) ] ( node ) {
167186 context . report ( {
168187 node,
169- message : `Use Safe${ node . expression . callee . name } instead of ${ node . expression . callee . name } ` ,
188+ message : `Use Safe${ node . callee . name } instead of ${ node . callee . name } ` ,
170189 } ) ;
171190 } ,
172191 } ;
0 commit comments