UNPKG

sucrase

Version:

Super-fast alternative to Babel for when you can target modern JS runtimes

1,025 lines (1,024 loc) 35.7 kB
/* eslint max-len: 0 */ import { types as tt } from "../tokenizer/types"; import * as charCodes from "../util/charcodes"; // tslint:disable-next-line no-any function isMaybeDefaultImport(lookahead) { return (lookahead.type === tt.name || !!lookahead.type.keyword) && lookahead.value !== "from"; } export default (superClass) => class extends superClass { flowParseTypeInitialiser(tok) { this.runInTypeContext(0, () => { const oldInType = this.state.inType; this.state.inType = true; this.expect(tok || tt.colon); this.flowParseType(); this.state.inType = oldInType; }); } flowParsePredicate() { this.expect(tt.modulo); this.expectContextual("checks"); if (this.eat(tt.parenL)) { this.parseExpression(); this.expect(tt.parenR); } } flowParseTypeAndPredicateInitialiser() { this.runInTypeContext(0, () => { const oldInType = this.state.inType; this.state.inType = true; this.expect(tt.colon); if (this.match(tt.modulo)) { this.state.inType = oldInType; this.flowParsePredicate(); } else { this.flowParseType(); this.state.inType = oldInType; if (this.match(tt.modulo)) { this.flowParsePredicate(); } } }); } flowParseDeclareClass() { this.next(); this.flowParseInterfaceish(/* isClass */ true); } flowParseDeclareFunction() { this.next(); this.parseIdentifier(); if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } this.expect(tt.parenL); this.flowParseFunctionTypeParams(); this.expect(tt.parenR); this.flowParseTypeAndPredicateInitialiser(); this.semicolon(); } flowParseDeclare() { if (this.match(tt._class)) { this.flowParseDeclareClass(); } else if (this.match(tt._function)) { this.flowParseDeclareFunction(); } else if (this.match(tt._var)) { this.flowParseDeclareVariable(); } else if (this.isContextual("module")) { if (this.lookaheadType() === tt.dot) { this.flowParseDeclareModuleExports(); } else { this.flowParseDeclareModule(); } } else if (this.isContextual("type")) { this.flowParseDeclareTypeAlias(); } else if (this.isContextual("opaque")) { this.flowParseDeclareOpaqueType(); } else if (this.isContextual("interface")) { this.flowParseDeclareInterface(); } else if (this.match(tt._export)) { this.flowParseDeclareExportDeclaration(); } else { throw this.unexpected(); } } flowParseDeclareVariable() { this.next(); this.flowParseTypeAnnotatableIdentifier(); this.semicolon(); } flowParseDeclareModule() { this.next(); if (this.match(tt.string)) { this.parseExprAtom(); } else { this.parseIdentifier(); } this.expect(tt.braceL); while (!this.match(tt.braceR)) { if (this.match(tt._import)) { this.next(); this.parseImport(); } } this.expect(tt.braceR); } flowParseDeclareExportDeclaration() { this.expect(tt._export); if (this.eat(tt._default)) { if (this.match(tt._function) || this.match(tt._class)) { // declare export default class ... // declare export default function ... this.flowParseDeclare(); } else { // declare export default [type]; this.flowParseType(); this.semicolon(); } } else if (this.match(tt._var) || // declare export var ... this.match(tt._function) || // declare export function ... this.match(tt._class) || // declare export class ... this.isContextual("opaque") // declare export opaque .. ) { this.flowParseDeclare(); } else if (this.match(tt.star) || // declare export * from '' this.match(tt.braceL) || // declare export {} ... this.isContextual("interface") || // declare export interface ... this.isContextual("type") || // declare export type ... this.isContextual("opaque") // declare export opaque type ... ) { this.parseExport(); } else { throw this.unexpected(); } } flowParseDeclareModuleExports() { this.expectContextual("module"); this.expect(tt.dot); this.expectContextual("exports"); this.flowParseTypeAnnotation(); this.semicolon(); } flowParseDeclareTypeAlias() { this.next(); this.flowParseTypeAlias(); } flowParseDeclareOpaqueType() { this.next(); this.flowParseOpaqueType(true); } flowParseDeclareInterface() { this.next(); this.flowParseInterfaceish(); } // Interfaces flowParseInterfaceish(isClass) { this.flowParseRestrictedIdentifier(); if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } if (this.eat(tt._extends)) { do { this.flowParseInterfaceExtends(); } while (!isClass && this.eat(tt.comma)); } if (this.isContextual("mixins")) { this.next(); do { this.flowParseInterfaceExtends(); } while (this.eat(tt.comma)); } this.flowParseObjectType(true, false); } flowParseInterfaceExtends() { this.flowParseQualifiedTypeIdentifier(false); if (this.match(tt.lessThan)) { this.flowParseTypeParameterInstantiation(); } } flowParseInterface() { this.flowParseInterfaceish(); } flowParseRestrictedIdentifier() { this.parseIdentifier(); } // Type aliases flowParseTypeAlias() { this.flowParseRestrictedIdentifier(); if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } this.flowParseTypeInitialiser(tt.eq); this.semicolon(); } flowParseOpaqueType(declare) { this.expectContextual("type"); this.flowParseRestrictedIdentifier(); if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } // Parse the supertype if (this.match(tt.colon)) { this.flowParseTypeInitialiser(tt.colon); } if (!declare) { this.flowParseTypeInitialiser(tt.eq); } this.semicolon(); } // Type annotations flowParseTypeParameter() { this.flowParseVariance(); this.flowParseTypeAnnotatableIdentifier(); if (this.eat(tt.eq)) { this.flowParseType(); } } flowParseTypeParameterDeclaration() { this.runInTypeContext(0, () => { const oldInType = this.state.inType; this.state.inType = true; // istanbul ignore else: this condition is already checked at all call sites if (this.match(tt.lessThan) || this.match(tt.typeParameterStart)) { this.next(); } else { this.unexpected(); } do { this.flowParseTypeParameter(); if (!this.match(tt.greaterThan)) { this.expect(tt.comma); } } while (!this.match(tt.greaterThan)); this.expect(tt.greaterThan); this.state.inType = oldInType; }); } flowParseTypeParameterInstantiation() { const oldInType = this.state.inType; this.state.inType = true; this.expect(tt.lessThan); while (!this.match(tt.greaterThan)) { this.flowParseType(); if (!this.match(tt.greaterThan)) { this.expect(tt.comma); } } this.expect(tt.greaterThan); this.state.inType = oldInType; } flowParseObjectPropertyKey() { if (this.match(tt.num) || this.match(tt.string)) { this.parseExprAtom(); } else { this.parseIdentifier(); } } flowParseObjectTypeIndexer() { this.expect(tt.bracketL); if (this.lookaheadType() === tt.colon) { this.flowParseObjectPropertyKey(); this.flowParseTypeInitialiser(); } else { this.flowParseType(); } this.expect(tt.bracketR); this.flowParseTypeInitialiser(); } flowParseObjectTypeMethodish() { if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } this.expect(tt.parenL); while (!this.match(tt.parenR) && !this.match(tt.ellipsis)) { this.flowParseFunctionTypeParam(); if (!this.match(tt.parenR)) { this.expect(tt.comma); } } if (this.eat(tt.ellipsis)) { this.flowParseFunctionTypeParam(); } this.expect(tt.parenR); this.flowParseTypeInitialiser(); } flowParseObjectTypeCallProperty() { this.flowParseObjectTypeMethodish(); } flowParseObjectType(allowStatic, allowExact) { const oldInType = this.state.inType; this.state.inType = true; let endDelim; if (allowExact && this.match(tt.braceBarL)) { this.expect(tt.braceBarL); endDelim = tt.braceBarR; } else { this.expect(tt.braceL); endDelim = tt.braceR; } while (!this.match(endDelim)) { let isStatic = false; if (allowStatic && this.isContextual("static") && this.lookaheadType() !== tt.colon) { this.next(); isStatic = true; } this.flowParseVariance(); if (this.match(tt.bracketL)) { this.flowParseObjectTypeIndexer(); } else if (this.match(tt.parenL) || this.match(tt.lessThan)) { this.flowParseObjectTypeCallProperty(); } else { if (this.isContextual("get") || this.isContextual("set")) { const lookaheadType = this.lookaheadType(); if (lookaheadType === tt.name || lookaheadType === tt.string || lookaheadType === tt.num) { this.next(); } } this.flowParseObjectTypeProperty(); } this.flowObjectTypeSemicolon(); } this.expect(endDelim); this.state.inType = oldInType; } flowParseObjectTypeProperty() { if (this.match(tt.ellipsis)) { this.expect(tt.ellipsis); this.flowParseType(); } else { this.flowParseObjectPropertyKey(); if (this.match(tt.lessThan) || this.match(tt.parenL)) { // This is a method property this.flowParseObjectTypeMethodish(); } else { this.eat(tt.question); this.flowParseTypeInitialiser(); } } } flowObjectTypeSemicolon() { if (!this.eat(tt.semi) && !this.eat(tt.comma) && !this.match(tt.braceR) && !this.match(tt.braceBarR)) { this.unexpected(); } } flowParseQualifiedTypeIdentifier(initialIdAlreadyParsed) { if (!initialIdAlreadyParsed) { this.parseIdentifier(); } while (this.eat(tt.dot)) { this.parseIdentifier(); } } flowParseGenericType() { this.flowParseQualifiedTypeIdentifier(true); if (this.match(tt.lessThan)) { this.flowParseTypeParameterInstantiation(); } } flowParseTypeofType() { this.expect(tt._typeof); this.flowParsePrimaryType(); } flowParseTupleType() { this.expect(tt.bracketL); // We allow trailing commas while (this.state.pos < this.input.length && !this.match(tt.bracketR)) { this.flowParseType(); if (this.match(tt.bracketR)) { break; } this.expect(tt.comma); } this.expect(tt.bracketR); } flowParseFunctionTypeParam() { const lookaheadType = this.lookaheadType(); if (lookaheadType === tt.colon || lookaheadType === tt.question) { this.parseIdentifier(); this.eat(tt.question); this.flowParseTypeInitialiser(); } else { this.flowParseType(); } } flowParseFunctionTypeParams() { while (!this.match(tt.parenR) && !this.match(tt.ellipsis)) { this.flowParseFunctionTypeParam(); if (!this.match(tt.parenR)) { this.expect(tt.comma); } } if (this.eat(tt.ellipsis)) { this.flowParseFunctionTypeParam(); } } flowIdentToTypeAnnotation(name) { switch (name) { case "any": case "void": case "bool": case "boolean": case "mixed": case "empty": case "number": case "string": return; default: this.flowParseGenericType(); } } // The parsing of types roughly parallels the parsing of expressions, and // primary types are kind of like primary expressions...they're the // primitives with which other types are constructed. flowParsePrimaryType() { let isGroupedType = false; const oldNoAnonFunctionType = this.state.noAnonFunctionType; switch (this.state.type) { case tt.name: { this.parseIdentifier(); const name = this.state.tokens[this.state.tokens.length - 1].value; this.flowIdentToTypeAnnotation(name); return; } case tt.braceL: this.flowParseObjectType(false, false); return; case tt.braceBarL: this.flowParseObjectType(false, true); return; case tt.bracketL: this.flowParseTupleType(); return; case tt.lessThan: this.flowParseTypeParameterDeclaration(); this.expect(tt.parenL); this.flowParseFunctionTypeParams(); this.expect(tt.parenR); this.expect(tt.arrow); this.flowParseType(); return; case tt.parenL: this.next(); // Check to see if this is actually a grouped type if (!this.match(tt.parenR) && !this.match(tt.ellipsis)) { if (this.match(tt.name)) { const token = this.lookaheadType(); isGroupedType = token !== tt.question && token !== tt.colon; } else { isGroupedType = true; } } if (isGroupedType) { this.state.noAnonFunctionType = false; this.flowParseType(); this.state.noAnonFunctionType = oldNoAnonFunctionType; // A `,` or a `) =>` means this is an anonymous function type if (this.state.noAnonFunctionType || !(this.match(tt.comma) || (this.match(tt.parenR) && this.lookaheadType() === tt.arrow))) { this.expect(tt.parenR); return; } else { // Eat a comma if there is one this.eat(tt.comma); } } this.flowParseFunctionTypeParams(); this.expect(tt.parenR); this.expect(tt.arrow); this.flowParseType(); return; case tt.plusMin: // Only allow negative signs, not plus signs. if (this.state.value === "-") { this.next(); this.parseLiteral(); return; } throw this.unexpected(); case tt.string: case tt.num: case tt._true: case tt._false: case tt._null: case tt._this: case tt.star: this.next(); return; default: if (this.state.type.keyword === "typeof") { this.flowParseTypeofType(); return; } } throw this.unexpected(); } flowParsePostfixType() { this.flowParsePrimaryType(); while (!this.canInsertSemicolon() && this.match(tt.bracketL)) { this.expect(tt.bracketL); this.expect(tt.bracketR); } } flowParsePrefixType() { if (this.eat(tt.question)) { this.flowParsePrefixType(); } else { this.flowParsePostfixType(); } } flowParseAnonFunctionWithoutParens() { this.flowParsePrefixType(); if (!this.state.noAnonFunctionType && this.eat(tt.arrow)) { this.flowParseType(); } } flowParseIntersectionType() { this.eat(tt.bitwiseAND); this.flowParseAnonFunctionWithoutParens(); while (this.eat(tt.bitwiseAND)) { this.flowParseAnonFunctionWithoutParens(); } } flowParseUnionType() { this.eat(tt.bitwiseOR); this.flowParseIntersectionType(); while (this.eat(tt.bitwiseOR)) { this.flowParseIntersectionType(); } } flowParseType() { const oldInType = this.state.inType; this.state.inType = true; this.flowParseUnionType(); this.state.inType = oldInType; // Ensure that a brace after a function generic type annotation is a // statement, except in arrow functions (noAnonFunctionType) this.state.exprAllowed = this.state.exprAllowed || this.state.noAnonFunctionType; } flowParseTypeAnnotation() { this.flowParseTypeInitialiser(); } flowParseTypeAnnotatableIdentifier() { this.parseIdentifier(); if (this.match(tt.colon)) { this.flowParseTypeAnnotation(); } } flowParseVariance() { if (this.match(tt.plusMin)) { this.next(); } } // ================================== // Overrides // ================================== parseFunctionBodyAndFinish(functionStart, isGenerator, allowExpressionBody, funcContextId) { // For arrow functions, `parseArrow` handles the return type itself. if (!allowExpressionBody && this.match(tt.colon)) { this.flowParseTypeAndPredicateInitialiser(); } super.parseFunctionBodyAndFinish(functionStart, isGenerator, allowExpressionBody, funcContextId); } // interfaces parseStatement(declaration, topLevel) { if (this.match(tt.name) && this.state.value === "interface") { this.runInTypeContext(0, () => { this.next(); this.flowParseInterface(); }); } else { super.parseStatement(declaration, topLevel); } } // declares, interfaces and type aliases parseIdentifierStatement(name) { if (name === "declare") { if (this.match(tt._class) || this.match(tt.name) || this.match(tt._function) || this.match(tt._var) || this.match(tt._export)) { this.runInTypeContext(1, () => { this.flowParseDeclare(); }); } } else if (this.match(tt.name)) { if (name === "interface") { this.runInTypeContext(1, () => { this.flowParseInterface(); }); } else if (name === "type") { this.runInTypeContext(1, () => { this.flowParseTypeAlias(); }); } else if (name === "opaque") { this.runInTypeContext(1, () => { this.flowParseOpaqueType(false); }); } } super.parseIdentifierStatement(name); } // export type shouldParseExportDeclaration() { return (this.isContextual("type") || this.isContextual("interface") || this.isContextual("opaque") || super.shouldParseExportDeclaration()); } isExportDefaultSpecifier() { if (this.match(tt.name) && (this.state.value === "type" || this.state.value === "interface" || this.state.value === "opaque")) { return false; } return super.isExportDefaultSpecifier(); } parseConditional(noIn, startPos) { // only do the expensive clone if there is a question mark if (this.match(tt.question)) { const snapshot = this.state.snapshot(); try { super.parseConditional(noIn, startPos); return; } catch (err) { if (err instanceof SyntaxError) { this.state.restoreFromSnapshot(snapshot); return; } else { // istanbul ignore next: no such error is expected throw err; } } } super.parseConditional(noIn, startPos); } parseParenItem() { super.parseParenItem(); if (this.eat(tt.question)) { this.state.tokens[this.state.tokens.length - 1].isType = true; } if (this.match(tt.colon)) { this.flowParseTypeAnnotation(); } } parseExportDeclaration() { if (this.isContextual("type")) { this.runInTypeContext(1, () => { this.next(); if (this.match(tt.braceL)) { // export type { foo, bar }; this.parseExportSpecifiers(); this.parseExportFrom(); } else { // export type Foo = Bar; this.flowParseTypeAlias(); } }); } else if (this.isContextual("opaque")) { this.runInTypeContext(1, () => { this.next(); // export opaque type Foo = Bar; this.flowParseOpaqueType(false); }); } else if (this.isContextual("interface")) { this.runInTypeContext(1, () => { this.next(); this.flowParseInterface(); }); } else { super.parseExportDeclaration(); } } shouldParseExportStar() { return (super.shouldParseExportStar() || (this.isContextual("type") && this.lookaheadType() === tt.star)); } parseExportStar() { if (this.eatContextual("type")) { this.runInTypeContext(2, () => { super.parseExportStar(); }); } else { super.parseExportStar(); } } parseClassId(isStatement, optionalId = false) { super.parseClassId(isStatement, optionalId); if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } } // don't consider `void` to be a keyword as then it'll use the void token type // and set startExpr isKeyword(name) { if (this.state.inType && name === "void") { return false; } else { return super.isKeyword(name); } } // ensure that inside flow types, we bypass the jsx parser plugin readToken(code) { if (this.state.inType && (code === charCodes.lessThan || code === charCodes.greaterThan)) { this.finishOp(code === charCodes.lessThan ? tt.lessThan : tt.greaterThan, 1); } else { super.readToken(code); } } // parse an item inside a expression list eg. `(NODE, NODE)` where NODE represents // the position where this function is called parseExprListItem(allowEmpty) { super.parseExprListItem(allowEmpty); if (this.match(tt.colon)) { this.flowParseTypeAnnotation(); } } // parse class property type annotations parseClassProperty() { if (this.match(tt.colon)) { this.flowParseTypeAnnotation(); } super.parseClassProperty(); } // determine whether or not we're currently in the position where a class method would appear isClassMethod() { return this.match(tt.lessThan) || super.isClassMethod(); } // determine whether or not we're currently in the position where a class property would appear isClassProperty() { return this.match(tt.colon) || super.isClassProperty(); } // parse type parameters for class methods parseClassMethod(functionStart, isGenerator, isConstructor) { if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); } super.parseClassMethod(functionStart, isGenerator, isConstructor); } // parse a the super class type parameters and implements parseClassSuper() { const hadSuper = super.parseClassSuper(); if (hadSuper && this.match(tt.lessThan)) { this.flowParseTypeParameterInstantiation(); } if (this.isContextual("implements")) { this.state.tokens[this.state.tokens.length - 1].type = tt._implements; this.runInTypeContext(0, () => { this.next(); do { this.flowParseRestrictedIdentifier(); if (this.match(tt.lessThan)) { this.flowParseTypeParameterInstantiation(); } } while (this.eat(tt.comma)); }); } return hadSuper; } parsePropertyName(classContextId) { this.flowParseVariance(); super.parsePropertyName(classContextId); } // parse type parameters for object method shorthand parseObjPropValue(isGenerator, isPattern, isBlockScope, objectContextId) { // method shorthand if (this.match(tt.lessThan)) { this.flowParseTypeParameterDeclaration(); if (!this.match(tt.parenL)) this.unexpected(); } super.parseObjPropValue(isGenerator, isPattern, isBlockScope, objectContextId); } parseAssignableListItemTypes() { this.runInTypeContext(0, () => { this.eat(tt.question); if (this.match(tt.colon)) { this.flowParseTypeAnnotation(); } }); } // parse typeof and type imports parseImportSpecifiers() { let kind = null; if (this.match(tt._typeof)) { kind = "typeof"; } else if (this.isContextual("type")) { kind = "type"; } if (kind) { const lh = this.lookaheadTypeAndValue(); if (isMaybeDefaultImport(lh) || lh.type === tt.braceL || lh.type === tt.star) { this.next(); } } super.parseImportSpecifiers(); } // parse import-type/typeof shorthand parseImportSpecifier() { this.parseIdentifier(); const firstIdentName = this.state.tokens[this.state.tokens.length - 1].value; let specifierTypeKind = null; if (firstIdentName === "type") { this.state.tokens[this.state.tokens.length - 1].type = tt._type; specifierTypeKind = "type"; } else if (firstIdentName === "typeof") { specifierTypeKind = "typeof"; this.state.tokens[this.state.tokens.length - 1].type = tt._typeof; } if (this.isContextual("as") && !this.isLookaheadContextual("as")) { this.parseIdentifier(); if (specifierTypeKind !== null && !this.match(tt.name) && !this.state.type.keyword) { // `import {type as ,` or `import {type as }` } else { // `import {type as foo` this.parseIdentifier(); } } else if (specifierTypeKind !== null && (this.match(tt.name) || this.state.type.keyword)) { // `import {type foo` this.parseIdentifier(); if (this.eatContextual("as")) { this.parseIdentifier(); } } } // parse function type parameters - function foo<T>() {} parseFunctionParams(allowModifiers, contextId) { // Originally this checked if the method is a getter/setter, but if it was, we'd crash soon // anyway, so don't try to propagate that information. if (this.match(tt.lessThan)) { this.runInTypeContext(0, () => { this.flowParseTypeParameterDeclaration(); }); } super.parseFunctionParams(allowModifiers, contextId); } // parse flow type annotations on variable declarator heads - let foo: string = bar parseVarHead(isBlockScope) { super.parseVarHead(isBlockScope); if (this.match(tt.colon)) { this.flowParseTypeAnnotation(); } } // parse the return type of an async arrow function - let foo = (async (): number => {}); parseAsyncArrowFromCallExpression(functionStart, startTokenIndex) { if (this.match(tt.colon)) { const oldNoAnonFunctionType = this.state.noAnonFunctionType; this.state.noAnonFunctionType = true; this.flowParseTypeAnnotation(); this.state.noAnonFunctionType = oldNoAnonFunctionType; } super.parseAsyncArrowFromCallExpression(functionStart, startTokenIndex); } // todo description shouldParseAsyncArrow() { return this.match(tt.colon) || super.shouldParseAsyncArrow(); } // We need to support type parameter declarations for arrow functions. This // is tricky. There are three situations we need to handle // // 1. This is either JSX or an arrow function. We'll try JSX first. If that // fails, we'll try an arrow function. If that fails, we'll throw the JSX // error. // 2. This is an arrow function. We'll parse the type parameter declaration, // parse the rest, make sure the rest is an arrow function, and go from // there // 3. This is neither. Just call the super method parseMaybeAssign(noIn, afterLeftParse) { let jsxError = null; if (tt.jsxTagStart && this.match(tt.jsxTagStart)) { const snapshot = this.state.snapshot(); try { return super.parseMaybeAssign(noIn, afterLeftParse); } catch (err) { if (err instanceof SyntaxError) { this.state.restoreFromSnapshot(snapshot); // Remove `tc.j_expr` and `tc.j_oTag` from context added // by parsing `jsxTagStart` to stop the JSX plugin from // messing with the tokens this.state.context.length -= 2; this.state.type = tt.typeParameterStart; jsxError = err; } else { // istanbul ignore next: no such error is expected throw err; } } } if (jsxError != null || this.match(tt.lessThan)) { let wasArrow = false; try { this.runInTypeContext(0, () => { this.flowParseTypeParameterDeclaration(); }); wasArrow = super.parseMaybeAssign(noIn, afterLeftParse); } catch (err) { throw jsxError || err; } if (wasArrow) { return true; } this.unexpected(); } return super.parseMaybeAssign(noIn, afterLeftParse); } // handle return types for arrow functions parseArrow() { if (this.match(tt.colon)) { this.runInTypeContext(0, () => { const snapshot = this.state.snapshot(); try { const oldNoAnonFunctionType = this.state.noAnonFunctionType; this.state.noAnonFunctionType = true; this.flowParseTypeAndPredicateInitialiser(); this.state.noAnonFunctionType = oldNoAnonFunctionType; if (this.canInsertSemicolon()) this.unexpected(); if (!this.match(tt.arrow)) this.unexpected(); } catch (err) { if (err instanceof SyntaxError) { this.state.restoreFromSnapshot(snapshot); } else { // istanbul ignore next: no such error is expected throw err; } } }); } return super.parseArrow(); } shouldParseArrow() { return this.match(tt.colon) || super.shouldParseArrow(); } parseSubscripts(startPos, noCalls) { if (this.state.tokens[this.state.tokens.length - 1].value === "async" && this.match(tt.lessThan)) { const snapshot = this.state.snapshot(); let error; try { const wasArrow = this.parseAsyncArrowWithTypeParameters(startPos); if (wasArrow) { return; } } catch (e) { error = e; } this.state.restoreFromSnapshot(snapshot); try { super.parseSubscripts(startPos, noCalls); return; } catch (e) { throw error || e; } } super.parseSubscripts(startPos, noCalls); } // Returns true if there was an arrow function here. parseAsyncArrowWithTypeParameters(startPos) { const startTokenIndex = this.state.tokens.length; this.parseFunctionParams(); if (!this.parseArrow()) { return false; } this.parseArrowExpression(startPos, startTokenIndex); return true; } };