forked from Fediversity/fediversity.eu
1106 lines
26 KiB
JavaScript
1106 lines
26 KiB
JavaScript
/* eslint max-len: 0 */
|
|
|
|
import {
|
|
eat,
|
|
lookaheadType,
|
|
lookaheadTypeAndKeyword,
|
|
match,
|
|
next,
|
|
popTypeContext,
|
|
pushTypeContext,
|
|
|
|
} from "../tokenizer/index";
|
|
import {ContextualKeyword} from "../tokenizer/keywords";
|
|
import {TokenType, TokenType as tt} from "../tokenizer/types";
|
|
import {input, state} from "../traverser/base";
|
|
import {
|
|
baseParseMaybeAssign,
|
|
baseParseSubscript,
|
|
baseParseSubscripts,
|
|
parseArrow,
|
|
parseArrowExpression,
|
|
parseCallExpressionArguments,
|
|
parseExprAtom,
|
|
parseExpression,
|
|
parseFunctionBody,
|
|
parseIdentifier,
|
|
parseLiteral,
|
|
|
|
} from "../traverser/expression";
|
|
import {
|
|
baseParseExportStar,
|
|
parseExport,
|
|
parseExportFrom,
|
|
parseExportSpecifiers,
|
|
parseFunctionParams,
|
|
parseImport,
|
|
parseStatement,
|
|
} from "../traverser/statement";
|
|
import {
|
|
canInsertSemicolon,
|
|
eatContextual,
|
|
expect,
|
|
expectContextual,
|
|
isContextual,
|
|
isLookaheadContextual,
|
|
semicolon,
|
|
unexpected,
|
|
} from "../traverser/util";
|
|
|
|
function isMaybeDefaultImport(lookahead) {
|
|
return (
|
|
(lookahead.type === tt.name || !!(lookahead.type & TokenType.IS_KEYWORD)) &&
|
|
lookahead.contextualKeyword !== ContextualKeyword._from
|
|
);
|
|
}
|
|
|
|
function flowParseTypeInitialiser(tok) {
|
|
const oldIsType = pushTypeContext(0);
|
|
expect(tok || tt.colon);
|
|
flowParseType();
|
|
popTypeContext(oldIsType);
|
|
}
|
|
|
|
function flowParsePredicate() {
|
|
expect(tt.modulo);
|
|
expectContextual(ContextualKeyword._checks);
|
|
if (eat(tt.parenL)) {
|
|
parseExpression();
|
|
expect(tt.parenR);
|
|
}
|
|
}
|
|
|
|
function flowParseTypeAndPredicateInitialiser() {
|
|
const oldIsType = pushTypeContext(0);
|
|
expect(tt.colon);
|
|
if (match(tt.modulo)) {
|
|
flowParsePredicate();
|
|
} else {
|
|
flowParseType();
|
|
if (match(tt.modulo)) {
|
|
flowParsePredicate();
|
|
}
|
|
}
|
|
popTypeContext(oldIsType);
|
|
}
|
|
|
|
function flowParseDeclareClass() {
|
|
next();
|
|
flowParseInterfaceish(/* isClass */ true);
|
|
}
|
|
|
|
function flowParseDeclareFunction() {
|
|
next();
|
|
parseIdentifier();
|
|
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterDeclaration();
|
|
}
|
|
|
|
expect(tt.parenL);
|
|
flowParseFunctionTypeParams();
|
|
expect(tt.parenR);
|
|
|
|
flowParseTypeAndPredicateInitialiser();
|
|
|
|
semicolon();
|
|
}
|
|
|
|
function flowParseDeclare() {
|
|
if (match(tt._class)) {
|
|
flowParseDeclareClass();
|
|
} else if (match(tt._function)) {
|
|
flowParseDeclareFunction();
|
|
} else if (match(tt._var)) {
|
|
flowParseDeclareVariable();
|
|
} else if (eatContextual(ContextualKeyword._module)) {
|
|
if (eat(tt.dot)) {
|
|
flowParseDeclareModuleExports();
|
|
} else {
|
|
flowParseDeclareModule();
|
|
}
|
|
} else if (isContextual(ContextualKeyword._type)) {
|
|
flowParseDeclareTypeAlias();
|
|
} else if (isContextual(ContextualKeyword._opaque)) {
|
|
flowParseDeclareOpaqueType();
|
|
} else if (isContextual(ContextualKeyword._interface)) {
|
|
flowParseDeclareInterface();
|
|
} else if (match(tt._export)) {
|
|
flowParseDeclareExportDeclaration();
|
|
} else {
|
|
unexpected();
|
|
}
|
|
}
|
|
|
|
function flowParseDeclareVariable() {
|
|
next();
|
|
flowParseTypeAnnotatableIdentifier();
|
|
semicolon();
|
|
}
|
|
|
|
function flowParseDeclareModule() {
|
|
if (match(tt.string)) {
|
|
parseExprAtom();
|
|
} else {
|
|
parseIdentifier();
|
|
}
|
|
|
|
expect(tt.braceL);
|
|
while (!match(tt.braceR) && !state.error) {
|
|
if (match(tt._import)) {
|
|
next();
|
|
parseImport();
|
|
} else {
|
|
unexpected();
|
|
}
|
|
}
|
|
expect(tt.braceR);
|
|
}
|
|
|
|
function flowParseDeclareExportDeclaration() {
|
|
expect(tt._export);
|
|
|
|
if (eat(tt._default)) {
|
|
if (match(tt._function) || match(tt._class)) {
|
|
// declare export default class ...
|
|
// declare export default function ...
|
|
flowParseDeclare();
|
|
} else {
|
|
// declare export default [type];
|
|
flowParseType();
|
|
semicolon();
|
|
}
|
|
} else if (
|
|
match(tt._var) || // declare export var ...
|
|
match(tt._function) || // declare export function ...
|
|
match(tt._class) || // declare export class ...
|
|
isContextual(ContextualKeyword._opaque) // declare export opaque ..
|
|
) {
|
|
flowParseDeclare();
|
|
} else if (
|
|
match(tt.star) || // declare export * from ''
|
|
match(tt.braceL) || // declare export {} ...
|
|
isContextual(ContextualKeyword._interface) || // declare export interface ...
|
|
isContextual(ContextualKeyword._type) || // declare export type ...
|
|
isContextual(ContextualKeyword._opaque) // declare export opaque type ...
|
|
) {
|
|
parseExport();
|
|
} else {
|
|
unexpected();
|
|
}
|
|
}
|
|
|
|
function flowParseDeclareModuleExports() {
|
|
expectContextual(ContextualKeyword._exports);
|
|
flowParseTypeAnnotation();
|
|
semicolon();
|
|
}
|
|
|
|
function flowParseDeclareTypeAlias() {
|
|
next();
|
|
flowParseTypeAlias();
|
|
}
|
|
|
|
function flowParseDeclareOpaqueType() {
|
|
next();
|
|
flowParseOpaqueType(true);
|
|
}
|
|
|
|
function flowParseDeclareInterface() {
|
|
next();
|
|
flowParseInterfaceish();
|
|
}
|
|
|
|
// Interfaces
|
|
|
|
function flowParseInterfaceish(isClass = false) {
|
|
flowParseRestrictedIdentifier();
|
|
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterDeclaration();
|
|
}
|
|
|
|
if (eat(tt._extends)) {
|
|
do {
|
|
flowParseInterfaceExtends();
|
|
} while (!isClass && eat(tt.comma));
|
|
}
|
|
|
|
if (isContextual(ContextualKeyword._mixins)) {
|
|
next();
|
|
do {
|
|
flowParseInterfaceExtends();
|
|
} while (eat(tt.comma));
|
|
}
|
|
|
|
if (isContextual(ContextualKeyword._implements)) {
|
|
next();
|
|
do {
|
|
flowParseInterfaceExtends();
|
|
} while (eat(tt.comma));
|
|
}
|
|
|
|
flowParseObjectType(isClass, false, isClass);
|
|
}
|
|
|
|
function flowParseInterfaceExtends() {
|
|
flowParseQualifiedTypeIdentifier(false);
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterInstantiation();
|
|
}
|
|
}
|
|
|
|
function flowParseInterface() {
|
|
flowParseInterfaceish();
|
|
}
|
|
|
|
function flowParseRestrictedIdentifier() {
|
|
parseIdentifier();
|
|
}
|
|
|
|
function flowParseTypeAlias() {
|
|
flowParseRestrictedIdentifier();
|
|
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterDeclaration();
|
|
}
|
|
|
|
flowParseTypeInitialiser(tt.eq);
|
|
semicolon();
|
|
}
|
|
|
|
function flowParseOpaqueType(declare) {
|
|
expectContextual(ContextualKeyword._type);
|
|
flowParseRestrictedIdentifier();
|
|
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterDeclaration();
|
|
}
|
|
|
|
// Parse the supertype
|
|
if (match(tt.colon)) {
|
|
flowParseTypeInitialiser(tt.colon);
|
|
}
|
|
|
|
if (!declare) {
|
|
flowParseTypeInitialiser(tt.eq);
|
|
}
|
|
semicolon();
|
|
}
|
|
|
|
function flowParseTypeParameter() {
|
|
flowParseVariance();
|
|
flowParseTypeAnnotatableIdentifier();
|
|
|
|
if (eat(tt.eq)) {
|
|
flowParseType();
|
|
}
|
|
}
|
|
|
|
export function flowParseTypeParameterDeclaration() {
|
|
const oldIsType = pushTypeContext(0);
|
|
// istanbul ignore else: this condition is already checked at all call sites
|
|
if (match(tt.lessThan) || match(tt.typeParameterStart)) {
|
|
next();
|
|
} else {
|
|
unexpected();
|
|
}
|
|
|
|
do {
|
|
flowParseTypeParameter();
|
|
if (!match(tt.greaterThan)) {
|
|
expect(tt.comma);
|
|
}
|
|
} while (!match(tt.greaterThan) && !state.error);
|
|
expect(tt.greaterThan);
|
|
popTypeContext(oldIsType);
|
|
}
|
|
|
|
function flowParseTypeParameterInstantiation() {
|
|
const oldIsType = pushTypeContext(0);
|
|
expect(tt.lessThan);
|
|
while (!match(tt.greaterThan) && !state.error) {
|
|
flowParseType();
|
|
if (!match(tt.greaterThan)) {
|
|
expect(tt.comma);
|
|
}
|
|
}
|
|
expect(tt.greaterThan);
|
|
popTypeContext(oldIsType);
|
|
}
|
|
|
|
function flowParseInterfaceType() {
|
|
expectContextual(ContextualKeyword._interface);
|
|
if (eat(tt._extends)) {
|
|
do {
|
|
flowParseInterfaceExtends();
|
|
} while (eat(tt.comma));
|
|
}
|
|
flowParseObjectType(false, false, false);
|
|
}
|
|
|
|
function flowParseObjectPropertyKey() {
|
|
if (match(tt.num) || match(tt.string)) {
|
|
parseExprAtom();
|
|
} else {
|
|
parseIdentifier();
|
|
}
|
|
}
|
|
|
|
function flowParseObjectTypeIndexer() {
|
|
// Note: bracketL has already been consumed
|
|
if (lookaheadType() === tt.colon) {
|
|
flowParseObjectPropertyKey();
|
|
flowParseTypeInitialiser();
|
|
} else {
|
|
flowParseType();
|
|
}
|
|
expect(tt.bracketR);
|
|
flowParseTypeInitialiser();
|
|
}
|
|
|
|
function flowParseObjectTypeInternalSlot() {
|
|
// Note: both bracketL have already been consumed
|
|
flowParseObjectPropertyKey();
|
|
expect(tt.bracketR);
|
|
expect(tt.bracketR);
|
|
if (match(tt.lessThan) || match(tt.parenL)) {
|
|
flowParseObjectTypeMethodish();
|
|
} else {
|
|
eat(tt.question);
|
|
flowParseTypeInitialiser();
|
|
}
|
|
}
|
|
|
|
function flowParseObjectTypeMethodish() {
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterDeclaration();
|
|
}
|
|
|
|
expect(tt.parenL);
|
|
while (!match(tt.parenR) && !match(tt.ellipsis) && !state.error) {
|
|
flowParseFunctionTypeParam();
|
|
if (!match(tt.parenR)) {
|
|
expect(tt.comma);
|
|
}
|
|
}
|
|
|
|
if (eat(tt.ellipsis)) {
|
|
flowParseFunctionTypeParam();
|
|
}
|
|
expect(tt.parenR);
|
|
flowParseTypeInitialiser();
|
|
}
|
|
|
|
function flowParseObjectTypeCallProperty() {
|
|
flowParseObjectTypeMethodish();
|
|
}
|
|
|
|
function flowParseObjectType(allowStatic, allowExact, allowProto) {
|
|
let endDelim;
|
|
if (allowExact && match(tt.braceBarL)) {
|
|
expect(tt.braceBarL);
|
|
endDelim = tt.braceBarR;
|
|
} else {
|
|
expect(tt.braceL);
|
|
endDelim = tt.braceR;
|
|
}
|
|
|
|
while (!match(endDelim) && !state.error) {
|
|
if (allowProto && isContextual(ContextualKeyword._proto)) {
|
|
const lookahead = lookaheadType();
|
|
if (lookahead !== tt.colon && lookahead !== tt.question) {
|
|
next();
|
|
allowStatic = false;
|
|
}
|
|
}
|
|
if (allowStatic && isContextual(ContextualKeyword._static)) {
|
|
const lookahead = lookaheadType();
|
|
if (lookahead !== tt.colon && lookahead !== tt.question) {
|
|
next();
|
|
}
|
|
}
|
|
|
|
flowParseVariance();
|
|
|
|
if (eat(tt.bracketL)) {
|
|
if (eat(tt.bracketL)) {
|
|
flowParseObjectTypeInternalSlot();
|
|
} else {
|
|
flowParseObjectTypeIndexer();
|
|
}
|
|
} else if (match(tt.parenL) || match(tt.lessThan)) {
|
|
flowParseObjectTypeCallProperty();
|
|
} else {
|
|
if (isContextual(ContextualKeyword._get) || isContextual(ContextualKeyword._set)) {
|
|
const lookahead = lookaheadType();
|
|
if (lookahead === tt.name || lookahead === tt.string || lookahead === tt.num) {
|
|
next();
|
|
}
|
|
}
|
|
|
|
flowParseObjectTypeProperty();
|
|
}
|
|
|
|
flowObjectTypeSemicolon();
|
|
}
|
|
|
|
expect(endDelim);
|
|
}
|
|
|
|
function flowParseObjectTypeProperty() {
|
|
if (match(tt.ellipsis)) {
|
|
expect(tt.ellipsis);
|
|
if (!eat(tt.comma)) {
|
|
eat(tt.semi);
|
|
}
|
|
// Explicit inexact object syntax.
|
|
if (match(tt.braceR)) {
|
|
return;
|
|
}
|
|
flowParseType();
|
|
} else {
|
|
flowParseObjectPropertyKey();
|
|
if (match(tt.lessThan) || match(tt.parenL)) {
|
|
// This is a method property
|
|
flowParseObjectTypeMethodish();
|
|
} else {
|
|
eat(tt.question);
|
|
flowParseTypeInitialiser();
|
|
}
|
|
}
|
|
}
|
|
|
|
function flowObjectTypeSemicolon() {
|
|
if (!eat(tt.semi) && !eat(tt.comma) && !match(tt.braceR) && !match(tt.braceBarR)) {
|
|
unexpected();
|
|
}
|
|
}
|
|
|
|
function flowParseQualifiedTypeIdentifier(initialIdAlreadyParsed) {
|
|
if (!initialIdAlreadyParsed) {
|
|
parseIdentifier();
|
|
}
|
|
while (eat(tt.dot)) {
|
|
parseIdentifier();
|
|
}
|
|
}
|
|
|
|
function flowParseGenericType() {
|
|
flowParseQualifiedTypeIdentifier(true);
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterInstantiation();
|
|
}
|
|
}
|
|
|
|
function flowParseTypeofType() {
|
|
expect(tt._typeof);
|
|
flowParsePrimaryType();
|
|
}
|
|
|
|
function flowParseTupleType() {
|
|
expect(tt.bracketL);
|
|
// We allow trailing commas
|
|
while (state.pos < input.length && !match(tt.bracketR)) {
|
|
flowParseType();
|
|
if (match(tt.bracketR)) {
|
|
break;
|
|
}
|
|
expect(tt.comma);
|
|
}
|
|
expect(tt.bracketR);
|
|
}
|
|
|
|
function flowParseFunctionTypeParam() {
|
|
const lookahead = lookaheadType();
|
|
if (lookahead === tt.colon || lookahead === tt.question) {
|
|
parseIdentifier();
|
|
eat(tt.question);
|
|
flowParseTypeInitialiser();
|
|
} else {
|
|
flowParseType();
|
|
}
|
|
}
|
|
|
|
function flowParseFunctionTypeParams() {
|
|
while (!match(tt.parenR) && !match(tt.ellipsis) && !state.error) {
|
|
flowParseFunctionTypeParam();
|
|
if (!match(tt.parenR)) {
|
|
expect(tt.comma);
|
|
}
|
|
}
|
|
if (eat(tt.ellipsis)) {
|
|
flowParseFunctionTypeParam();
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
function flowParsePrimaryType() {
|
|
let isGroupedType = false;
|
|
const oldNoAnonFunctionType = state.noAnonFunctionType;
|
|
|
|
switch (state.type) {
|
|
case tt.name: {
|
|
if (isContextual(ContextualKeyword._interface)) {
|
|
flowParseInterfaceType();
|
|
return;
|
|
}
|
|
parseIdentifier();
|
|
flowParseGenericType();
|
|
return;
|
|
}
|
|
|
|
case tt.braceL:
|
|
flowParseObjectType(false, false, false);
|
|
return;
|
|
|
|
case tt.braceBarL:
|
|
flowParseObjectType(false, true, false);
|
|
return;
|
|
|
|
case tt.bracketL:
|
|
flowParseTupleType();
|
|
return;
|
|
|
|
case tt.lessThan:
|
|
flowParseTypeParameterDeclaration();
|
|
expect(tt.parenL);
|
|
flowParseFunctionTypeParams();
|
|
expect(tt.parenR);
|
|
expect(tt.arrow);
|
|
flowParseType();
|
|
return;
|
|
|
|
case tt.parenL:
|
|
next();
|
|
|
|
// Check to see if this is actually a grouped type
|
|
if (!match(tt.parenR) && !match(tt.ellipsis)) {
|
|
if (match(tt.name)) {
|
|
const token = lookaheadType();
|
|
isGroupedType = token !== tt.question && token !== tt.colon;
|
|
} else {
|
|
isGroupedType = true;
|
|
}
|
|
}
|
|
|
|
if (isGroupedType) {
|
|
state.noAnonFunctionType = false;
|
|
flowParseType();
|
|
state.noAnonFunctionType = oldNoAnonFunctionType;
|
|
|
|
// A `,` or a `) =>` means this is an anonymous function type
|
|
if (
|
|
state.noAnonFunctionType ||
|
|
!(match(tt.comma) || (match(tt.parenR) && lookaheadType() === tt.arrow))
|
|
) {
|
|
expect(tt.parenR);
|
|
return;
|
|
} else {
|
|
// Eat a comma if there is one
|
|
eat(tt.comma);
|
|
}
|
|
}
|
|
|
|
flowParseFunctionTypeParams();
|
|
|
|
expect(tt.parenR);
|
|
expect(tt.arrow);
|
|
flowParseType();
|
|
return;
|
|
|
|
case tt.minus:
|
|
next();
|
|
parseLiteral();
|
|
return;
|
|
|
|
case tt.string:
|
|
case tt.num:
|
|
case tt._true:
|
|
case tt._false:
|
|
case tt._null:
|
|
case tt._this:
|
|
case tt._void:
|
|
case tt.star:
|
|
next();
|
|
return;
|
|
|
|
default:
|
|
if (state.type === tt._typeof) {
|
|
flowParseTypeofType();
|
|
return;
|
|
} else if (state.type & TokenType.IS_KEYWORD) {
|
|
next();
|
|
state.tokens[state.tokens.length - 1].type = tt.name;
|
|
return;
|
|
}
|
|
}
|
|
|
|
unexpected();
|
|
}
|
|
|
|
function flowParsePostfixType() {
|
|
flowParsePrimaryType();
|
|
while (!canInsertSemicolon() && (match(tt.bracketL) || match(tt.questionDot))) {
|
|
eat(tt.questionDot);
|
|
expect(tt.bracketL);
|
|
if (eat(tt.bracketR)) {
|
|
// Array type
|
|
} else {
|
|
// Indexed access type
|
|
flowParseType();
|
|
expect(tt.bracketR);
|
|
}
|
|
}
|
|
}
|
|
|
|
function flowParsePrefixType() {
|
|
if (eat(tt.question)) {
|
|
flowParsePrefixType();
|
|
} else {
|
|
flowParsePostfixType();
|
|
}
|
|
}
|
|
|
|
function flowParseAnonFunctionWithoutParens() {
|
|
flowParsePrefixType();
|
|
if (!state.noAnonFunctionType && eat(tt.arrow)) {
|
|
flowParseType();
|
|
}
|
|
}
|
|
|
|
function flowParseIntersectionType() {
|
|
eat(tt.bitwiseAND);
|
|
flowParseAnonFunctionWithoutParens();
|
|
while (eat(tt.bitwiseAND)) {
|
|
flowParseAnonFunctionWithoutParens();
|
|
}
|
|
}
|
|
|
|
function flowParseUnionType() {
|
|
eat(tt.bitwiseOR);
|
|
flowParseIntersectionType();
|
|
while (eat(tt.bitwiseOR)) {
|
|
flowParseIntersectionType();
|
|
}
|
|
}
|
|
|
|
function flowParseType() {
|
|
flowParseUnionType();
|
|
}
|
|
|
|
export function flowParseTypeAnnotation() {
|
|
flowParseTypeInitialiser();
|
|
}
|
|
|
|
function flowParseTypeAnnotatableIdentifier() {
|
|
parseIdentifier();
|
|
if (match(tt.colon)) {
|
|
flowParseTypeAnnotation();
|
|
}
|
|
}
|
|
|
|
export function flowParseVariance() {
|
|
if (match(tt.plus) || match(tt.minus)) {
|
|
next();
|
|
state.tokens[state.tokens.length - 1].isType = true;
|
|
}
|
|
}
|
|
|
|
// ==================================
|
|
// Overrides
|
|
// ==================================
|
|
|
|
export function flowParseFunctionBodyAndFinish(funcContextId) {
|
|
// For arrow functions, `parseArrow` handles the return type itself.
|
|
if (match(tt.colon)) {
|
|
flowParseTypeAndPredicateInitialiser();
|
|
}
|
|
|
|
parseFunctionBody(false, funcContextId);
|
|
}
|
|
|
|
export function flowParseSubscript(
|
|
startTokenIndex,
|
|
noCalls,
|
|
stopState,
|
|
) {
|
|
if (match(tt.questionDot) && lookaheadType() === tt.lessThan) {
|
|
if (noCalls) {
|
|
stopState.stop = true;
|
|
return;
|
|
}
|
|
next();
|
|
flowParseTypeParameterInstantiation();
|
|
expect(tt.parenL);
|
|
parseCallExpressionArguments();
|
|
return;
|
|
} else if (!noCalls && match(tt.lessThan)) {
|
|
const snapshot = state.snapshot();
|
|
flowParseTypeParameterInstantiation();
|
|
expect(tt.parenL);
|
|
parseCallExpressionArguments();
|
|
if (state.error) {
|
|
state.restoreFromSnapshot(snapshot);
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
baseParseSubscript(startTokenIndex, noCalls, stopState);
|
|
}
|
|
|
|
export function flowStartParseNewArguments() {
|
|
if (match(tt.lessThan)) {
|
|
const snapshot = state.snapshot();
|
|
flowParseTypeParameterInstantiation();
|
|
if (state.error) {
|
|
state.restoreFromSnapshot(snapshot);
|
|
}
|
|
}
|
|
}
|
|
|
|
// interfaces
|
|
export function flowTryParseStatement() {
|
|
if (match(tt.name) && state.contextualKeyword === ContextualKeyword._interface) {
|
|
const oldIsType = pushTypeContext(0);
|
|
next();
|
|
flowParseInterface();
|
|
popTypeContext(oldIsType);
|
|
return true;
|
|
} else if (isContextual(ContextualKeyword._enum)) {
|
|
flowParseEnumDeclaration();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function flowTryParseExportDefaultExpression() {
|
|
if (isContextual(ContextualKeyword._enum)) {
|
|
flowParseEnumDeclaration();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// declares, interfaces and type aliases
|
|
export function flowParseIdentifierStatement(contextualKeyword) {
|
|
if (contextualKeyword === ContextualKeyword._declare) {
|
|
if (
|
|
match(tt._class) ||
|
|
match(tt.name) ||
|
|
match(tt._function) ||
|
|
match(tt._var) ||
|
|
match(tt._export)
|
|
) {
|
|
const oldIsType = pushTypeContext(1);
|
|
flowParseDeclare();
|
|
popTypeContext(oldIsType);
|
|
}
|
|
} else if (match(tt.name)) {
|
|
if (contextualKeyword === ContextualKeyword._interface) {
|
|
const oldIsType = pushTypeContext(1);
|
|
flowParseInterface();
|
|
popTypeContext(oldIsType);
|
|
} else if (contextualKeyword === ContextualKeyword._type) {
|
|
const oldIsType = pushTypeContext(1);
|
|
flowParseTypeAlias();
|
|
popTypeContext(oldIsType);
|
|
} else if (contextualKeyword === ContextualKeyword._opaque) {
|
|
const oldIsType = pushTypeContext(1);
|
|
flowParseOpaqueType(false);
|
|
popTypeContext(oldIsType);
|
|
}
|
|
}
|
|
semicolon();
|
|
}
|
|
|
|
// export type
|
|
export function flowShouldParseExportDeclaration() {
|
|
return (
|
|
isContextual(ContextualKeyword._type) ||
|
|
isContextual(ContextualKeyword._interface) ||
|
|
isContextual(ContextualKeyword._opaque) ||
|
|
isContextual(ContextualKeyword._enum)
|
|
);
|
|
}
|
|
|
|
export function flowShouldDisallowExportDefaultSpecifier() {
|
|
return (
|
|
match(tt.name) &&
|
|
(state.contextualKeyword === ContextualKeyword._type ||
|
|
state.contextualKeyword === ContextualKeyword._interface ||
|
|
state.contextualKeyword === ContextualKeyword._opaque ||
|
|
state.contextualKeyword === ContextualKeyword._enum)
|
|
);
|
|
}
|
|
|
|
export function flowParseExportDeclaration() {
|
|
if (isContextual(ContextualKeyword._type)) {
|
|
const oldIsType = pushTypeContext(1);
|
|
next();
|
|
|
|
if (match(tt.braceL)) {
|
|
// export type { foo, bar };
|
|
parseExportSpecifiers();
|
|
parseExportFrom();
|
|
} else {
|
|
// export type Foo = Bar;
|
|
flowParseTypeAlias();
|
|
}
|
|
popTypeContext(oldIsType);
|
|
} else if (isContextual(ContextualKeyword._opaque)) {
|
|
const oldIsType = pushTypeContext(1);
|
|
next();
|
|
// export opaque type Foo = Bar;
|
|
flowParseOpaqueType(false);
|
|
popTypeContext(oldIsType);
|
|
} else if (isContextual(ContextualKeyword._interface)) {
|
|
const oldIsType = pushTypeContext(1);
|
|
next();
|
|
flowParseInterface();
|
|
popTypeContext(oldIsType);
|
|
} else {
|
|
parseStatement(true);
|
|
}
|
|
}
|
|
|
|
export function flowShouldParseExportStar() {
|
|
return match(tt.star) || (isContextual(ContextualKeyword._type) && lookaheadType() === tt.star);
|
|
}
|
|
|
|
export function flowParseExportStar() {
|
|
if (eatContextual(ContextualKeyword._type)) {
|
|
const oldIsType = pushTypeContext(2);
|
|
baseParseExportStar();
|
|
popTypeContext(oldIsType);
|
|
} else {
|
|
baseParseExportStar();
|
|
}
|
|
}
|
|
|
|
// parse a the super class type parameters and implements
|
|
export function flowAfterParseClassSuper(hasSuper) {
|
|
if (hasSuper && match(tt.lessThan)) {
|
|
flowParseTypeParameterInstantiation();
|
|
}
|
|
if (isContextual(ContextualKeyword._implements)) {
|
|
const oldIsType = pushTypeContext(0);
|
|
next();
|
|
state.tokens[state.tokens.length - 1].type = tt._implements;
|
|
do {
|
|
flowParseRestrictedIdentifier();
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterInstantiation();
|
|
}
|
|
} while (eat(tt.comma));
|
|
popTypeContext(oldIsType);
|
|
}
|
|
}
|
|
|
|
// parse type parameters for object method shorthand
|
|
export function flowStartParseObjPropValue() {
|
|
// method shorthand
|
|
if (match(tt.lessThan)) {
|
|
flowParseTypeParameterDeclaration();
|
|
if (!match(tt.parenL)) unexpected();
|
|
}
|
|
}
|
|
|
|
export function flowParseAssignableListItemTypes() {
|
|
const oldIsType = pushTypeContext(0);
|
|
eat(tt.question);
|
|
if (match(tt.colon)) {
|
|
flowParseTypeAnnotation();
|
|
}
|
|
popTypeContext(oldIsType);
|
|
}
|
|
|
|
// parse typeof and type imports
|
|
export function flowStartParseImportSpecifiers() {
|
|
if (match(tt._typeof) || isContextual(ContextualKeyword._type)) {
|
|
const lh = lookaheadTypeAndKeyword();
|
|
if (isMaybeDefaultImport(lh) || lh.type === tt.braceL || lh.type === tt.star) {
|
|
next();
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse import-type/typeof shorthand
|
|
export function flowParseImportSpecifier() {
|
|
const isTypeKeyword =
|
|
state.contextualKeyword === ContextualKeyword._type || state.type === tt._typeof;
|
|
if (isTypeKeyword) {
|
|
next();
|
|
} else {
|
|
parseIdentifier();
|
|
}
|
|
|
|
if (isContextual(ContextualKeyword._as) && !isLookaheadContextual(ContextualKeyword._as)) {
|
|
parseIdentifier();
|
|
if (isTypeKeyword && !match(tt.name) && !(state.type & TokenType.IS_KEYWORD)) {
|
|
// `import {type as ,` or `import {type as }`
|
|
} else {
|
|
// `import {type as foo`
|
|
parseIdentifier();
|
|
}
|
|
} else {
|
|
if (isTypeKeyword && (match(tt.name) || !!(state.type & TokenType.IS_KEYWORD))) {
|
|
// `import {type foo`
|
|
parseIdentifier();
|
|
}
|
|
if (eatContextual(ContextualKeyword._as)) {
|
|
parseIdentifier();
|
|
}
|
|
}
|
|
}
|
|
|
|
// parse function type parameters - function foo<T>() {}
|
|
export function flowStartParseFunctionParams() {
|
|
// 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 (match(tt.lessThan)) {
|
|
const oldIsType = pushTypeContext(0);
|
|
flowParseTypeParameterDeclaration();
|
|
popTypeContext(oldIsType);
|
|
}
|
|
}
|
|
|
|
// parse flow type annotations on variable declarator heads - let foo: string = bar
|
|
export function flowAfterParseVarHead() {
|
|
if (match(tt.colon)) {
|
|
flowParseTypeAnnotation();
|
|
}
|
|
}
|
|
|
|
// parse the return type of an async arrow function - let foo = (async (): number => {});
|
|
export function flowStartParseAsyncArrowFromCallExpression() {
|
|
if (match(tt.colon)) {
|
|
const oldNoAnonFunctionType = state.noAnonFunctionType;
|
|
state.noAnonFunctionType = true;
|
|
flowParseTypeAnnotation();
|
|
state.noAnonFunctionType = oldNoAnonFunctionType;
|
|
}
|
|
}
|
|
|
|
// 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
|
|
export function flowParseMaybeAssign(noIn, isWithinParens) {
|
|
if (match(tt.lessThan)) {
|
|
const snapshot = state.snapshot();
|
|
let wasArrow = baseParseMaybeAssign(noIn, isWithinParens);
|
|
if (state.error) {
|
|
state.restoreFromSnapshot(snapshot);
|
|
state.type = tt.typeParameterStart;
|
|
} else {
|
|
return wasArrow;
|
|
}
|
|
|
|
const oldIsType = pushTypeContext(0);
|
|
flowParseTypeParameterDeclaration();
|
|
popTypeContext(oldIsType);
|
|
wasArrow = baseParseMaybeAssign(noIn, isWithinParens);
|
|
if (wasArrow) {
|
|
return true;
|
|
}
|
|
unexpected();
|
|
}
|
|
|
|
return baseParseMaybeAssign(noIn, isWithinParens);
|
|
}
|
|
|
|
// handle return types for arrow functions
|
|
export function flowParseArrow() {
|
|
if (match(tt.colon)) {
|
|
const oldIsType = pushTypeContext(0);
|
|
const snapshot = state.snapshot();
|
|
|
|
const oldNoAnonFunctionType = state.noAnonFunctionType;
|
|
state.noAnonFunctionType = true;
|
|
flowParseTypeAndPredicateInitialiser();
|
|
state.noAnonFunctionType = oldNoAnonFunctionType;
|
|
|
|
if (canInsertSemicolon()) unexpected();
|
|
if (!match(tt.arrow)) unexpected();
|
|
|
|
if (state.error) {
|
|
state.restoreFromSnapshot(snapshot);
|
|
}
|
|
popTypeContext(oldIsType);
|
|
}
|
|
return eat(tt.arrow);
|
|
}
|
|
|
|
export function flowParseSubscripts(startTokenIndex, noCalls = false) {
|
|
if (
|
|
state.tokens[state.tokens.length - 1].contextualKeyword === ContextualKeyword._async &&
|
|
match(tt.lessThan)
|
|
) {
|
|
const snapshot = state.snapshot();
|
|
const wasArrow = parseAsyncArrowWithTypeParameters();
|
|
if (wasArrow && !state.error) {
|
|
return;
|
|
}
|
|
state.restoreFromSnapshot(snapshot);
|
|
}
|
|
|
|
baseParseSubscripts(startTokenIndex, noCalls);
|
|
}
|
|
|
|
// Returns true if there was an arrow function here.
|
|
function parseAsyncArrowWithTypeParameters() {
|
|
state.scopeDepth++;
|
|
const startTokenIndex = state.tokens.length;
|
|
parseFunctionParams();
|
|
if (!parseArrow()) {
|
|
return false;
|
|
}
|
|
parseArrowExpression(startTokenIndex);
|
|
return true;
|
|
}
|
|
|
|
function flowParseEnumDeclaration() {
|
|
expectContextual(ContextualKeyword._enum);
|
|
state.tokens[state.tokens.length - 1].type = tt._enum;
|
|
parseIdentifier();
|
|
flowParseEnumBody();
|
|
}
|
|
|
|
function flowParseEnumBody() {
|
|
if (eatContextual(ContextualKeyword._of)) {
|
|
next();
|
|
}
|
|
expect(tt.braceL);
|
|
flowParseEnumMembers();
|
|
expect(tt.braceR);
|
|
}
|
|
|
|
function flowParseEnumMembers() {
|
|
while (!match(tt.braceR) && !state.error) {
|
|
if (eat(tt.ellipsis)) {
|
|
break;
|
|
}
|
|
flowParseEnumMember();
|
|
if (!match(tt.braceR)) {
|
|
expect(tt.comma);
|
|
}
|
|
}
|
|
}
|
|
|
|
function flowParseEnumMember() {
|
|
parseIdentifier();
|
|
if (eat(tt.eq)) {
|
|
// Flow enum values are always just one token (a string, number, or boolean literal).
|
|
next();
|
|
}
|
|
}
|