| Index: pkg/front_end/lib/src/fasta/parser/parser.dart
|
| diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
|
| index 4529c426be861e14c1cee3ac4a46906801b0486f..5158f046ec9efa7fc51149ea1d5a9b671d96d591 100644
|
| --- a/pkg/front_end/lib/src/fasta/parser/parser.dart
|
| +++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
|
| @@ -63,15 +63,28 @@ bool optional(String value, Token token) {
|
| return identical(value, token.stringValue);
|
| }
|
|
|
| +// TODO(ahe): Convert this to an enum.
|
| class FormalParameterType {
|
| final String type;
|
| - const FormalParameterType(this.type);
|
| +
|
| + final TypeContinuation typeContinuation;
|
| +
|
| + const FormalParameterType(this.type, this.typeContinuation);
|
| +
|
| bool get isRequired => this == REQUIRED;
|
| +
|
| bool get isPositional => this == POSITIONAL;
|
| +
|
| bool get isNamed => this == NAMED;
|
| - static final REQUIRED = const FormalParameterType('required');
|
| - static final POSITIONAL = const FormalParameterType('positional');
|
| - static final NAMED = const FormalParameterType('named');
|
| +
|
| + static final REQUIRED = const FormalParameterType(
|
| + 'required', TypeContinuation.NormalFormalParameter);
|
| +
|
| + static final POSITIONAL = const FormalParameterType(
|
| + 'positional', TypeContinuation.OptionalPositionalFormalParameter);
|
| +
|
| + static final NAMED =
|
| + const FormalParameterType('named', TypeContinuation.NamedFormalParameter);
|
| }
|
|
|
| enum MemberKind {
|
| @@ -141,6 +154,9 @@ enum TypeContinuation {
|
| /// Otherwise, do nothing.
|
| Optional,
|
|
|
| + /// Same as [Optional], but we have seen `var`.
|
| + OptionalAfterVar,
|
| +
|
| /// Indicates that the keyword `typedef` has just been seen, and the parser
|
| /// should parse the following as a type unless it is followed by `=`.
|
| Typedef,
|
| @@ -160,6 +176,27 @@ enum TypeContinuation {
|
| /// Indicates that the parser has just parsed `for '('` and is looking to
|
| /// parse a variable declaration or expression.
|
| VariablesDeclarationOrExpression,
|
| +
|
| + /// Indicates that an optional type followed by a normal formal parameter is
|
| + /// expected.
|
| + NormalFormalParameter,
|
| +
|
| + /// Indicates that an optional type followed by an optional positional formal
|
| + /// parameter is expected.
|
| + OptionalPositionalFormalParameter,
|
| +
|
| + /// Indicates that an optional type followed by a named formal parameter is
|
| + /// expected.
|
| + NamedFormalParameter,
|
| +
|
| + /// Same as [NormalFormalParameter], but we have seen `var`.
|
| + NormalFormalParameterAfterVar,
|
| +
|
| + /// Same as [OptionalPositionalFormalParameter], but we have seen `var`.
|
| + OptionalPositionalFormalParameterAfterVar,
|
| +
|
| + /// Same as [NamedFormalParameter], but we have seen `var`.
|
| + NamedFormalParameterAfterVar,
|
| }
|
|
|
| /// An event generating parser of Dart programs. This parser expects all tokens
|
| @@ -553,7 +590,9 @@ class Parser {
|
| return token;
|
| }
|
|
|
| - Token parseMetadataStar(Token token, {bool forParameter: false}) {
|
| + Token parseMetadataStar(Token token,
|
| + // TODO(ahe): Remove [forParameter].
|
| + {bool forParameter: false}) {
|
| token = listener.injectGenericCommentTypeAssign(token);
|
| listener.beginMetadataStar(token);
|
| int count = 0;
|
| @@ -690,87 +729,7 @@ class Parser {
|
| Token token, FormalParameterType parameterKind, MemberKind memberKind) {
|
| token = parseMetadataStar(token, forParameter: true);
|
| listener.beginFormalParameter(token, memberKind);
|
| -
|
| - bool inFunctionType = memberKind == MemberKind.GeneralizedFunctionType;
|
| token = parseModifiers(token, memberKind, parameterKind: parameterKind);
|
| - bool isNamedParameter = parameterKind == FormalParameterType.NAMED;
|
| -
|
| - Token thisKeyword = null;
|
| - Token nameToken;
|
| - if (inFunctionType) {
|
| - if (isNamedParameter || token.isIdentifier) {
|
| - nameToken = token;
|
| - token = parseIdentifier(
|
| - token, IdentifierContext.formalParameterDeclaration);
|
| - } else {
|
| - listener.handleNoName(token);
|
| - }
|
| - } else {
|
| - if (optional('this', token)) {
|
| - thisKeyword = token;
|
| - token = expect('.', token.next);
|
| - nameToken = token;
|
| - token = parseIdentifier(token, IdentifierContext.fieldInitializer);
|
| - } else {
|
| - nameToken = token;
|
| - token = parseIdentifier(
|
| - token, IdentifierContext.formalParameterDeclaration);
|
| - }
|
| - }
|
| - if (isNamedParameter && nameToken.lexeme.startsWith("_")) {
|
| - reportRecoverableErrorCode(nameToken, fasta.codePrivateNamedParameter);
|
| - }
|
| -
|
| - token = listener.injectGenericCommentTypeList(token);
|
| - if (optional('(', token)) {
|
| - Token inlineFunctionTypeStart = token;
|
| - listener.beginFunctionTypedFormalParameter(token);
|
| - listener.handleNoTypeVariables(token);
|
| - token = parseFormalParameters(token, MemberKind.FunctionTypedParameter);
|
| - listener.endFunctionTypedFormalParameter(thisKeyword, parameterKind);
|
| - // Generalized function types don't allow inline function types.
|
| - // The following isn't allowed:
|
| - // int Function(int bar(String x)).
|
| - if (memberKind == MemberKind.GeneralizedFunctionType) {
|
| - reportRecoverableErrorCode(
|
| - inlineFunctionTypeStart, fasta.codeInvalidInlineFunctionType);
|
| - }
|
| - } else if (optional('<', token)) {
|
| - Token inlineFunctionTypeStart = token;
|
| - listener.beginFunctionTypedFormalParameter(token);
|
| - token = parseTypeVariablesOpt(token);
|
| - token = parseFormalParameters(token, MemberKind.FunctionTypedParameter);
|
| - listener.endFunctionTypedFormalParameter(thisKeyword, parameterKind);
|
| - // Generalized function types don't allow inline function types.
|
| - // The following isn't allowed:
|
| - // int Function(int bar(String x)).
|
| - if (memberKind == MemberKind.GeneralizedFunctionType) {
|
| - reportRecoverableErrorCode(
|
| - inlineFunctionTypeStart, fasta.codeInvalidInlineFunctionType);
|
| - }
|
| - }
|
| - String value = token.stringValue;
|
| - if ((identical('=', value)) || (identical(':', value))) {
|
| - Token equal = token;
|
| - token = parseExpression(token.next);
|
| - listener.handleValuedFormalParameter(equal, token);
|
| - if (parameterKind.isRequired) {
|
| - reportRecoverableErrorCode(
|
| - equal, fasta.codeRequiredParameterWithDefault);
|
| - } else if (parameterKind.isPositional && identical(':', value)) {
|
| - reportRecoverableErrorCode(
|
| - equal, fasta.codePositionalParameterWithEquals);
|
| - } else if (inFunctionType ||
|
| - memberKind == MemberKind.FunctionTypeAlias ||
|
| - memberKind == MemberKind.FunctionTypedParameter) {
|
| - reportRecoverableErrorCode(
|
| - equal.next, fasta.codeFunctionTypeDefaultValue);
|
| - }
|
| - } else {
|
| - listener.handleFormalParameterWithoutValue(token);
|
| - }
|
| - listener.endFormalParameter(
|
| - thisKeyword, nameToken, parameterKind, memberKind);
|
| return token;
|
| }
|
|
|
| @@ -1127,11 +1086,15 @@ class Parser {
|
| /// after the type. Otherwise, it returns null.
|
| Token parseType(Token token,
|
| [TypeContinuation continuation = TypeContinuation.Required,
|
| - IdentifierContext continuationContext]) {
|
| + IdentifierContext continuationContext,
|
| + MemberKind memberKind]) {
|
| /// Returns the close brace, bracket, or parenthesis of [left]. For '<', it
|
| /// may return null.
|
| Token getClose(BeginToken left) => left.endToken;
|
|
|
| + /// True if we've seen the `var` keyword.
|
| + bool hasVar = false;
|
| +
|
| /// Where the type begins.
|
| Token begin;
|
|
|
| @@ -1284,6 +1247,10 @@ class Parser {
|
| listener.endFunctionType(functionToken, token);
|
| }
|
|
|
| + if (hasVar) {
|
| + reportRecoverableErrorCode(begin, fasta.codeTypeAfterVar);
|
| + }
|
| +
|
| return token;
|
| }
|
|
|
| @@ -1301,6 +1268,7 @@ class Parser {
|
| optional('sync', token);
|
| }
|
|
|
| + FormalParameterType parameterKind;
|
| switch (continuation) {
|
| case TypeContinuation.Required:
|
| return commitType();
|
| @@ -1322,6 +1290,10 @@ class Parser {
|
| listener.handleNoType(begin);
|
| return begin;
|
|
|
| + case TypeContinuation.OptionalAfterVar:
|
| + hasVar = true;
|
| + continue optional;
|
| +
|
| case TypeContinuation.Typedef:
|
| if (optional('=', token)) {
|
| return null; // This isn't a type, it's a new-style typedef.
|
| @@ -1432,6 +1404,145 @@ class Parser {
|
| return parseVariablesDeclarationNoSemicolon(begin);
|
| }
|
| return parseExpression(begin);
|
| +
|
| + case TypeContinuation.NormalFormalParameter:
|
| + case TypeContinuation.NormalFormalParameterAfterVar:
|
| + parameterKind = FormalParameterType.REQUIRED;
|
| + hasVar = continuation == TypeContinuation.NormalFormalParameterAfterVar;
|
| + continue handleParameters;
|
| +
|
| + case TypeContinuation.OptionalPositionalFormalParameter:
|
| + case TypeContinuation.OptionalPositionalFormalParameterAfterVar:
|
| + parameterKind = FormalParameterType.POSITIONAL;
|
| + hasVar = continuation ==
|
| + TypeContinuation.OptionalPositionalFormalParameterAfterVar;
|
| + continue handleParameters;
|
| +
|
| + case TypeContinuation.NamedFormalParameterAfterVar:
|
| + hasVar = true;
|
| + continue handleParameters;
|
| +
|
| + handleParameters:
|
| + case TypeContinuation.NamedFormalParameter:
|
| + parameterKind ??= FormalParameterType.NAMED;
|
| + bool inFunctionType = memberKind == MemberKind.GeneralizedFunctionType;
|
| + bool isNamedParameter = parameterKind == FormalParameterType.NAMED;
|
| +
|
| + bool untyped = false;
|
| + if (!looksLikeType || optional("this", begin)) {
|
| + untyped = true;
|
| + token = begin;
|
| + }
|
| +
|
| + Token thisKeyword;
|
| + Token nameToken = token;
|
| + IdentifierContext nameContext =
|
| + IdentifierContext.formalParameterDeclaration;
|
| + token = token.next;
|
| + if (inFunctionType) {
|
| + if (isNamedParameter || nameToken.isIdentifier) {
|
| + nameContext = IdentifierContext.formalParameterDeclaration;
|
| + } else {
|
| + // No name required in a function type.
|
| + nameContext = null;
|
| + token = nameToken;
|
| + }
|
| + } else if (optional('this', nameToken)) {
|
| + thisKeyword = nameToken;
|
| + token = expect('.', token);
|
| + nameToken = token;
|
| + nameContext = IdentifierContext.fieldInitializer;
|
| + token = token.next;
|
| + } else if (!nameToken.isIdentifier) {
|
| + untyped = true;
|
| + nameToken = begin;
|
| + token = nameToken.next;
|
| + }
|
| + if (isNamedParameter && nameToken.lexeme.startsWith("_")) {
|
| + // TODO(ahe): Move this to after commiting the type.
|
| + reportRecoverableErrorCode(
|
| + nameToken, fasta.codePrivateNamedParameter);
|
| + }
|
| +
|
| + token = listener.injectGenericCommentTypeList(token);
|
| +
|
| + Token inlineFunctionTypeStart;
|
| + if (optional("<", token)) {
|
| + Token closer = getClose(token);
|
| + if (closer != null) {
|
| + if (optional("(", closer.next)) {
|
| + inlineFunctionTypeStart = token;
|
| + token = token.next;
|
| + }
|
| + }
|
| + } else if (optional("(", token)) {
|
| + inlineFunctionTypeStart = token;
|
| + token = getClose(token).next;
|
| + }
|
| +
|
| + if (inlineFunctionTypeStart != null) {
|
| + token = parseTypeVariablesOpt(inlineFunctionTypeStart);
|
| + listener.beginFunctionTypedFormalParameter(inlineFunctionTypeStart);
|
| + if (!untyped) {
|
| + if (voidToken != null) {
|
| + listener.handleVoidKeyword(voidToken);
|
| + } else {
|
| + Token saved = token;
|
| + commitType();
|
| + token = saved;
|
| + }
|
| + } else {
|
| + listener.handleNoType(begin);
|
| + }
|
| + token =
|
| + parseFormalParameters(token, MemberKind.FunctionTypedParameter);
|
| + listener.endFunctionTypedFormalParameter();
|
| +
|
| + // Generalized function types don't allow inline function types.
|
| + // The following isn't allowed:
|
| + // int Function(int bar(String x)).
|
| + if (memberKind == MemberKind.GeneralizedFunctionType) {
|
| + reportRecoverableErrorCode(
|
| + inlineFunctionTypeStart, fasta.codeInvalidInlineFunctionType);
|
| + }
|
| + } else if (untyped) {
|
| + listener.handleNoType(begin);
|
| + } else {
|
| + Token saved = token;
|
| + commitType();
|
| + token = saved;
|
| + }
|
| +
|
| + if (nameContext != null) {
|
| + parseIdentifier(nameToken, nameContext);
|
| + } else {
|
| + listener.handleNoName(nameToken);
|
| + }
|
| +
|
| + String value = token.stringValue;
|
| + if ((identical('=', value)) || (identical(':', value))) {
|
| + Token equal = token;
|
| + token = parseExpression(token.next);
|
| + listener.handleValuedFormalParameter(equal, token);
|
| + if (parameterKind.isRequired) {
|
| + reportRecoverableErrorCode(
|
| + equal, fasta.codeRequiredParameterWithDefault);
|
| + } else if (parameterKind.isPositional && identical(':', value)) {
|
| + reportRecoverableErrorCode(
|
| + equal, fasta.codePositionalParameterWithEquals);
|
| + } else if (inFunctionType ||
|
| + memberKind == MemberKind.FunctionTypeAlias ||
|
| + memberKind == MemberKind.FunctionTypedParameter) {
|
| + reportRecoverableErrorCode(
|
| + equal.next, fasta.codeFunctionTypeDefaultValue);
|
| + }
|
| + } else {
|
| + listener.handleFormalParameterWithoutValue(token);
|
| + }
|
| + listener.endFormalParameter(
|
| + thisKeyword, nameToken, parameterKind, memberKind);
|
| +
|
| + return token;
|
| }
|
|
|
| throw "Internal error: Unhandled continuation '$continuation'.";
|
| @@ -1564,7 +1675,7 @@ class Parser {
|
| }
|
| Token token = parseModifiers(start,
|
| isTopLevel ? MemberKind.TopLevelField : MemberKind.NonStaticField,
|
| - isVariable: true);
|
| + isVarAllowed: true);
|
|
|
| if (token != name) {
|
| reportRecoverableErrorCodeWithToken(token, fasta.codeExtraneousModifier);
|
| @@ -1919,15 +2030,12 @@ class Parser {
|
| /// When parsing the formal parameters of any function, [parameterKind] is
|
| /// non-null.
|
| Token parseModifiers(Token token, MemberKind memberKind,
|
| - {FormalParameterType parameterKind, bool isVariable: false}) {
|
| - bool returnTypeAllowed =
|
| - !isVariable && memberKind != MemberKind.GeneralizedFunctionType;
|
| - bool typeRequired =
|
| - isVariable || memberKind == MemberKind.GeneralizedFunctionType;
|
| + {FormalParameterType parameterKind, bool isVarAllowed: false}) {
|
| int count = 0;
|
|
|
| int currentOrder = -1;
|
| - bool hasVar = false;
|
| + TypeContinuation typeContinuation = parameterKind?.typeContinuation;
|
| +
|
| while (token.kind == KEYWORD_TOKEN) {
|
| if (token.type.isPseudo) {
|
| // A pseudo keyword is never a modifier.
|
| @@ -1947,24 +2055,42 @@ class Parser {
|
| if (order > currentOrder) {
|
| currentOrder = order;
|
| if (optional("var", token)) {
|
| - if (!isVariable && parameterKind == null) {
|
| + if (!isVarAllowed && parameterKind == null) {
|
| reportRecoverableErrorCodeWithToken(
|
| token, fasta.codeExtraneousModifier);
|
| }
|
| - hasVar = true;
|
| - typeRequired = false;
|
| + switch (typeContinuation ?? TypeContinuation.Required) {
|
| + case TypeContinuation.NormalFormalParameter:
|
| + typeContinuation =
|
| + TypeContinuation.NormalFormalParameterAfterVar;
|
| + break;
|
| +
|
| + case TypeContinuation.OptionalPositionalFormalParameter:
|
| + typeContinuation =
|
| + TypeContinuation.OptionalPositionalFormalParameterAfterVar;
|
| + break;
|
| +
|
| + case TypeContinuation.NamedFormalParameter:
|
| + typeContinuation =
|
| + TypeContinuation.NamedFormalParameterAfterVar;
|
| + break;
|
| +
|
| + default:
|
| + typeContinuation = TypeContinuation.OptionalAfterVar;
|
| + break;
|
| + }
|
| } else if (optional("final", token)) {
|
| - if (!isVariable && parameterKind == null) {
|
| + if (!isVarAllowed && parameterKind == null) {
|
| reportRecoverableErrorCodeWithToken(
|
| token, fasta.codeExtraneousModifier);
|
| }
|
| - typeRequired = false;
|
| + typeContinuation ??= TypeContinuation.Optional;
|
| } else if (optional("const", token)) {
|
| - if (!isVariable) {
|
| + if (!isVarAllowed) {
|
| reportRecoverableErrorCodeWithToken(
|
| token, fasta.codeExtraneousModifier);
|
| }
|
| - typeRequired = false;
|
| + typeContinuation ??= TypeContinuation.Optional;
|
| } else if (optional("static", token)) {
|
| if (parameterKind != null) {
|
| reportRecoverableErrorCodeWithToken(
|
| @@ -2021,18 +2147,12 @@ class Parser {
|
| }
|
| listener.handleModifiers(count);
|
|
|
| - Token beforeType = token;
|
| - token = parseType(
|
| - token,
|
| - returnTypeAllowed || !typeRequired
|
| - ? TypeContinuation.Optional
|
| - : TypeContinuation.Required);
|
| - if (typeRequired && beforeType == token) {
|
| - reportRecoverableErrorCode(token, fasta.codeTypeRequired);
|
| - }
|
| - if (hasVar && beforeType != token) {
|
| - reportRecoverableErrorCode(beforeType, fasta.codeTypeAfterVar);
|
| - }
|
| + typeContinuation ??=
|
| + (isVarAllowed || memberKind == MemberKind.GeneralizedFunctionType)
|
| + ? TypeContinuation.Required
|
| + : TypeContinuation.Optional;
|
| +
|
| + token = parseType(token, typeContinuation, null, memberKind);
|
| return token;
|
| }
|
|
|
| @@ -3499,7 +3619,7 @@ class Parser {
|
| listener.replaceTokenWithGenericCommentTypeAssign(token, token.next);
|
| }
|
|
|
| - token = parseModifiers(token, MemberKind.Local, isVariable: true);
|
| + token = parseModifiers(token, MemberKind.Local, isVarAllowed: true);
|
| return parseVariablesDeclarationMaybeSemicolonRest(token, endWithSemicolon);
|
| }
|
|
|
|
|