@@ -273,29 +273,66 @@ pp.parseSubscripts = function(base, startPos, startLoc, noCalls) {
273
273
let maybeAsyncArrow = this . options . ecmaVersion >= 8 && base . type === "Identifier" && base . name === "async" &&
274
274
this . lastTokEnd === base . end && ! this . canInsertSemicolon ( ) && base . end - base . start === 5 &&
275
275
this . potentialArrowAt === base . start
276
+
277
+ // Wrap the `base` node by a `ChainExpression` node to disconnect the optional chaining
278
+ // if all of the following conditions are true:
279
+ // - The next node is a `(Call|Member)Expression` node as well (check the current token).
280
+ // - The `base` node is parenthesized.
281
+ // - The `base` node is a `(Call|Member)Expression` node.
282
+ // - The `base` node can be short-circuited by optional chaining.
283
+ if (
284
+ this . options . ecmaVersion >= 11 &&
285
+ ( this . type === tt . dot || this . type === tt . optionalChaining || ( ! noCalls && this . type === tt . parenL ) )
286
+ ) {
287
+ if ( base . end !== this . lastTokEnd && this . isOptionalChained ( base ) ) {
288
+ base = this . createChainExpressionNode ( base )
289
+ } else if ( base . type === "ParenthesizedExpression" && this . isOptionalChained ( base . expression ) ) {
290
+ base . expression = this . createChainExpressionNode ( base . expression )
291
+ }
292
+ }
293
+
276
294
while ( true ) {
277
295
let element = this . parseSubscript ( base , startPos , startLoc , noCalls , maybeAsyncArrow )
278
296
if ( element === base || element . type === "ArrowFunctionExpression" ) return element
279
297
base = element
280
298
}
281
299
}
282
300
301
+ pp . createChainExpressionNode = function ( expression ) {
302
+ const { start, end, loc} = expression
303
+ let startLoc , endLoc
304
+ if ( loc ) {
305
+ startLoc = loc . start
306
+ endLoc = loc . end
307
+ }
308
+
309
+ const node = this . startNodeAt ( start , startLoc )
310
+ node . expression = expression
311
+ return this . finishNodeAt ( node , "ChainExpression" , end , endLoc )
312
+ }
313
+
283
314
pp . parseSubscript = function ( base , startPos , startLoc , noCalls , maybeAsyncArrow ) {
315
+ let optional = this . options . ecmaVersion >= 11 && this . eat ( tt . optionalChaining )
316
+ if ( noCalls && optional ) this . raise ( this . lastTokStart , "Optional chaining cannot appear in the callee of new expressions" )
317
+
284
318
let computed = this . eat ( tt . bracketL )
285
- if ( computed || this . eat ( tt . dot ) ) {
319
+ if ( computed || ( optional && this . type !== tt . parenL && this . type !== tt . backQuote ) || this . eat ( tt . dot ) ) {
286
320
let node = this . startNodeAt ( startPos , startLoc )
287
321
node . object = base
288
322
node . property = computed ? this . parseExpression ( ) : this . parseIdent ( this . options . allowReserved !== "never" )
289
323
node . computed = ! ! computed
290
324
if ( computed ) this . expect ( tt . bracketR )
325
+ if ( this . options . ecmaVersion >= 11 ) {
326
+ node . optional = optional
327
+ }
291
328
base = this . finishNode ( node , "MemberExpression" )
292
329
} else if ( ! noCalls && this . eat ( tt . parenL ) ) {
293
330
let refDestructuringErrors = new DestructuringErrors , oldYieldPos = this . yieldPos , oldAwaitPos = this . awaitPos , oldAwaitIdentPos = this . awaitIdentPos
294
331
this . yieldPos = 0
295
332
this . awaitPos = 0
296
333
this . awaitIdentPos = 0
297
334
let exprList = this . parseExprList ( tt . parenR , this . options . ecmaVersion >= 8 , false , refDestructuringErrors )
298
- if ( maybeAsyncArrow && ! this . canInsertSemicolon ( ) && this . eat ( tt . arrow ) ) {
335
+ if ( maybeAsyncArrow && ! optional && ! this . canInsertSemicolon ( ) && this . eat ( tt . arrow ) ) {
299
336
this . checkPatternErrors ( refDestructuringErrors , false )
300
337
this . checkYieldAwaitInDefaultParams ( )
301
338
if ( this . awaitIdentPos > 0 )
@@ -312,8 +349,14 @@ pp.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow)
312
349
let node = this . startNodeAt ( startPos , startLoc )
313
350
node . callee = base
314
351
node . arguments = exprList
352
+ if ( this . options . ecmaVersion >= 11 ) {
353
+ node . optional = optional
354
+ }
315
355
base = this . finishNode ( node , "CallExpression" )
316
356
} else if ( this . type === tt . backQuote ) {
357
+ if ( optional || ( base . end === this . lastTokEnd && this . isOptionalChained ( base ) ) ) {
358
+ this . raise ( this . start , "Optional chaining cannot appear in the tag of tagged template expressions" )
359
+ }
317
360
let node = this . startNodeAt ( startPos , startLoc )
318
361
node . tag = base
319
362
node . quasi = this . parseTemplate ( { isTagged : true } )
@@ -322,6 +365,28 @@ pp.parseSubscript = function(base, startPos, startLoc, noCalls, maybeAsyncArrow)
322
365
return base
323
366
}
324
367
368
+ pp . isOptionalChained = function ( node ) {
369
+ if ( this . options . ecmaVersion >= 11 ) {
370
+ while ( true ) {
371
+ switch ( node . type ) {
372
+ case "CallExpression" :
373
+ if ( node . optional ) return true
374
+ node = node . callee
375
+ break
376
+
377
+ case "MemberExpression" :
378
+ if ( node . optional ) return true
379
+ node = node . object
380
+ break
381
+
382
+ default :
383
+ return false
384
+ }
385
+ }
386
+ }
387
+ return false
388
+ }
389
+
325
390
// Parse an atomic expression — either a single token that is an
326
391
// expression, an expression started by a keyword like `function` or
327
392
// `new`, or an expression wrapped in punctuation like `()`, `[]`,
0 commit comments