@@ -100,13 +100,6 @@ const isNumberMethodCall = node =>
100100 && isStaticProperties ( node . callee , 'Number' , numberMethods ) ;
101101const isGlobalParseToNumberFunctionCall = node => isFunctionCall ( node , 'parseInt' ) || isFunctionCall ( node , 'parseFloat' ) ;
102102
103- // `+x`, `-x`
104- const numberUnaryOperators = new Set ( [ '-' , '+' , '~' ] ) ;
105- const isNumberUnaryExpression = node =>
106- node . type === 'UnaryExpression'
107- && node . prefix
108- && numberUnaryOperators . has ( node . operator ) ;
109-
110103const isStaticNumber = ( node , scope ) => {
111104 const staticResult = getStaticValue ( node , scope ) ;
112105 return staticResult !== null && typeof staticResult . value === 'number' ;
@@ -120,7 +113,8 @@ const isLengthProperty = node =>
120113 && node . property . type === 'Identifier'
121114 && node . property . name === 'length' ;
122115
123- const mathOperators = new Set ( [ '-' , '*' , '/' , '%' , '**' , '<<' , '>>' , '>>>' , '|' , '^' , '&' ] ) ;
116+ // `+` and `>>>` operators are handled separately
117+ const mathOperators = new Set ( [ '-' , '*' , '/' , '%' , '**' , '<<' , '>>' , '|' , '^' , '&' ] ) ;
124118function isNumber ( node , scope ) {
125119 if (
126120 isNumberLiteral ( node )
@@ -130,37 +124,97 @@ function isNumber(node, scope) {
130124 || isNumberProperty ( node )
131125 || isNumberMethodCall ( node )
132126 || isGlobalParseToNumberFunctionCall ( node )
133- || isNumberUnaryExpression ( node )
134127 || isLengthProperty ( node )
135128 ) {
136129 return true ;
137130 }
138131
139132 switch ( node . type ) {
140- case 'BinaryExpression' :
141133 case 'AssignmentExpression' : {
134+ const { operator} = node ;
135+ if ( operator === '=' && isNumber ( node . right , scope ) ) {
136+ return true ;
137+ }
138+
139+ // Fall through
140+ }
141+
142+ case 'BinaryExpression' : {
142143 let { operator} = node ;
143144
144145 if ( node . type === 'AssignmentExpression' ) {
145146 operator = operator . slice ( 0 , - 1 ) ;
146147 }
147148
149+ if ( operator === '+' && isNumber ( node . left , scope ) && isNumber ( node . right , scope ) ) {
150+ return true ;
151+ }
152+
153+ // `>>>` (zero-fill right shift) can't use on `BigInt`
154+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#operators
155+ if ( operator === '>>>' ) {
156+ return true ;
157+ }
158+
159+ // `a * b` can be `BigInt`, we need make sure at least one side is number
160+ if ( mathOperators . has ( operator ) && ( isNumber ( node . left , scope ) || isNumber ( node . right , scope ) ) ) {
161+ return true ;
162+ }
163+
164+ break ;
165+ }
166+
167+ case 'UnaryExpression' : {
168+ const { operator} = node ;
169+
170+ // `+` can't use on `BigInt`
171+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#operators
148172 if ( operator === '+' ) {
149- return isNumber ( node . left , scope ) && isNumber ( node . right , scope ) ;
173+ return true ;
150174 }
151175
152- // `a + b` can be `BigInt`, we need make sure at least one side is number
153- if ( mathOperators . has ( operator ) ) {
154- return isNumber ( node . left , scope ) || isNumber ( node . right , scope ) ;
176+ if ( ( operator === '-' || operator === '~' ) && isNumber ( node . argument , scope ) ) {
177+ return true ;
155178 }
156179
157180 break ;
158181 }
159182
160- case 'ConditionalExpression' :
161- return isNumber ( node . consequent , scope ) && isNumber ( node . alternate , scope ) ;
162- case 'SequenceExpression' :
163- return isNumber ( node . expressions [ node . expressions . length - 1 ] , scope ) ;
183+ case 'UpdateExpression' :
184+ if ( isNumber ( node . argument , scope ) ) {
185+ return true ;
186+ }
187+
188+ break ;
189+ case 'ConditionalExpression' : {
190+ const isConsequentNumber = isNumber ( node . consequent , scope ) ;
191+ const isAlternateNumber = isNumber ( node . alternate , scope ) ;
192+
193+ if ( isConsequentNumber && isAlternateNumber ) {
194+ return true ;
195+ }
196+
197+ const testStaticValueResult = getStaticValue ( node . test , scope ) ;
198+ if (
199+ testStaticValueResult !== null
200+ && (
201+ ( testStaticValueResult . value && isConsequentNumber )
202+ || ( ! testStaticValueResult . value && isAlternateNumber )
203+ )
204+ ) {
205+ return true ;
206+ }
207+
208+ break ;
209+ }
210+
211+ case 'SequenceExpression' : {
212+ if ( isNumber ( node . expressions [ node . expressions . length - 1 ] , scope ) ) {
213+ return true ;
214+ }
215+
216+ break ;
217+ }
164218 // No default
165219 }
166220
0 commit comments