blob: 904871b57ef566a009554b489ac5cf425527e0b9 [file] [log] [blame]
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import '../messages/codes.dart' as codes;
import '../scanner/token.dart' show Keyword, Token;
import 'formal_parameter_kind.dart';
import 'member_kind.dart' show MemberKind;
import 'parser_impl.dart' show Parser;
import 'util.dart' show optional;
bool isModifier(Token token) {
if (!token.isModifier) {
return false;
} else if (token.type.isBuiltIn) {
// A built-in keyword can only be a modifier as long as it is
// followed by another keyword or an identifier. Otherwise, it is the
// identifier.
//
// For example, `external` is a modifier in this declaration:
// external Foo foo();
// but is the identifier in this declaration
// external() => true;
// and in
// for (final external in list) { }
Token next = token.next;
Keyword keyword = next.keyword;
if (keyword == null && !next.isIdentifier || keyword == Keyword.IN) {
return false;
}
}
return true;
}
/// This class is used to parse modifiers in most locations where modifiers
/// can occur, but does not call handleModifier or handleModifiers.
class ModifierRecoveryContext {
final Parser parser;
Token abstractToken;
Token constToken;
Token covariantToken;
Token externalToken;
Token finalToken;
Token lateToken;
Token requiredToken;
Token staticToken;
Token varToken;
// Set `true` when parsing modifiers after the `factory` token.
bool afterFactory = false;
// TODO(danrubel): Replace [ModifierRecoveryContext] and [ModifierContext]
// with this class.
ModifierRecoveryContext(this.parser);
set staticOrCovariant(Token staticOrCovariant) {
if (staticOrCovariant == null) {
covariantToken = null;
staticToken = null;
} else if (optional('covariant', staticOrCovariant)) {
covariantToken = staticOrCovariant;
staticToken = null;
} else if (optional('static', staticOrCovariant)) {
covariantToken = null;
staticToken = staticOrCovariant;
} else {
throw "Internal error: "
"Unexpected staticOrCovariant '$staticOrCovariant'.";
}
}
Token get varFinalOrConst => varToken ?? finalToken ?? constToken;
set varFinalOrConst(Token varFinalOrConst) {
if (varFinalOrConst == null) {
varToken = null;
finalToken = null;
constToken = null;
} else if (optional('var', varFinalOrConst)) {
varToken = varFinalOrConst;
finalToken = null;
constToken = null;
} else if (optional('final', varFinalOrConst)) {
varToken = null;
finalToken = varFinalOrConst;
constToken = null;
} else if (optional('const', varFinalOrConst)) {
varToken = null;
finalToken = null;
constToken = varFinalOrConst;
} else {
throw "Internal error: Unexpected varFinalOrConst '$varFinalOrConst'.";
}
}
/// Parse modifiers for class methods and fields.
Token parseClassMemberModifiers(Token token) {
token = parseModifiers(token);
reportExtraneousModifier(requiredToken);
return token;
}
/// Parse modifiers for formal parameters.
Token parseFormalParameterModifiers(
Token token, FormalParameterKind parameterKind, MemberKind memberKind) {
token = parseModifiers(token);
if (parameterKind != FormalParameterKind.optionalNamed) {
reportExtraneousModifier(requiredToken);
}
if (memberKind == MemberKind.StaticMethod ||
memberKind == MemberKind.TopLevelMethod) {
reportExtraneousModifier(this.covariantToken);
} else if (memberKind == MemberKind.ExtensionNonStaticMethod ||
memberKind == MemberKind.ExtensionStaticMethod) {
reportExtraneousModifierInExtension(this.covariantToken);
}
if (constToken != null) {
reportExtraneousModifier(constToken);
} else if (memberKind == MemberKind.GeneralizedFunctionType) {
if (varFinalOrConst != null) {
parser.reportRecoverableError(
varFinalOrConst, codes.messageFunctionTypedParameterVar);
}
}
reportExtraneousModifier(abstractToken);
reportExtraneousModifier(externalToken);
reportExtraneousModifier(lateToken);
reportExtraneousModifier(staticToken);
return token;
}
/// Parse modifiers after the `factory` token.
Token parseModifiersAfterFactory(Token token) {
afterFactory = true;
token = parseModifiers(token);
if (abstractToken != null) {
parser.reportRecoverableError(
abstractToken, codes.messageAbstractClassMember);
}
reportExtraneousModifier(lateToken);
reportExtraneousModifier(requiredToken);
return token;
}
/// Parse modifiers for top level functions and fields.
Token parseTopLevelModifiers(Token token) {
token = parseModifiers(token);
reportExtraneousModifier(abstractToken);
reportExtraneousModifier(covariantToken);
reportExtraneousModifier(requiredToken);
reportExtraneousModifier(staticToken);
return token;
}
/// Parse modifiers for variable declarations.
Token parseVariableDeclarationModifiers(Token token) {
token = parseModifiers(token);
reportExtraneousModifier(abstractToken);
reportExtraneousModifier(covariantToken);
reportExtraneousModifier(externalToken);
reportExtraneousModifier(requiredToken);
reportExtraneousModifier(staticToken);
return token;
}
/// Parse modifiers during recovery when modifiers are out of order
/// or invalid. Typically clients call methods like
/// [parseClassMemberModifiers] which in turn calls this method,
/// rather than calling this method directly.
///
/// The various modifier token parameters represent tokens of modifiers
/// that have already been parsed prior to recovery. The [staticOrCovariant]
/// parameter is for convenience if caller has a token that may be either
/// `static` or `covariant`. The first non-null parameter of
/// [staticOrCovariant], [staticToken], or [covariantToken] will be used,
/// in that order, and the others ignored.
Token parseModifiers(Token token) {
// Process invalid and out-of-order modifiers
Token next = token.next;
while (true) {
final String value = next.stringValue;
if (isModifier(next)) {
if (identical('abstract', value)) {
token = parseAbstract(token);
} else if (identical('const', value)) {
token = parseConst(token);
} else if (identical('covariant', value)) {
token = parseCovariant(token);
} else if (identical('external', value)) {
token = parseExternal(token);
} else if (identical('final', value)) {
token = parseFinal(token);
} else if (identical('late', value)) {
token = parseLate(token);
} else if (identical('required', value)) {
token = parseRequired(token);
} else if (identical('static', value)) {
token = parseStatic(token);
} else if (identical('var', value)) {
token = parseVar(token);
} else {
throw 'Internal Error: Unhandled modifier: $value';
}
} else if (afterFactory && identical('factory', value)) {
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
token = next;
} else {
break;
}
next = token.next;
}
return token;
}
Token parseAbstract(Token token) {
Token next = token.next;
assert(optional('abstract', next));
if (abstractToken == null) {
abstractToken = next;
return next;
}
// Recovery
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
return next;
}
Token parseConst(Token token) {
Token next = token.next;
assert(optional('const', next));
if (varFinalOrConst == null && covariantToken == null) {
constToken = next;
if (afterFactory) {
reportModifierOutOfOrder(next, 'factory');
} else if (lateToken != null) {
reportConflictingModifiers(next, lateToken);
}
return next;
}
// Recovery
if (constToken != null) {
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
} else if (covariantToken != null) {
reportConflictingModifiers(next, covariantToken);
} else if (finalToken != null) {
parser.reportRecoverableError(next, codes.messageConstAndFinal);
} else if (varToken != null) {
reportConflictingModifiers(next, varToken);
} else {
throw 'Internal Error: Unexpected varFinalOrConst: $varFinalOrConst';
}
return next;
}
Token parseCovariant(Token token) {
Token next = token.next;
assert(optional('covariant', next));
if (constToken == null &&
covariantToken == null &&
staticToken == null &&
!afterFactory) {
covariantToken = next;
if (varToken != null) {
reportModifierOutOfOrder(next, varToken.lexeme);
} else if (finalToken != null) {
reportModifierOutOfOrder(next, finalToken.lexeme);
} else if (lateToken != null) {
reportModifierOutOfOrder(next, lateToken.lexeme);
}
return next;
}
// Recovery
if (covariantToken != null) {
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
} else if (afterFactory) {
reportExtraneousModifier(next);
} else if (constToken != null) {
reportConflictingModifiers(next, constToken);
} else if (staticToken != null) {
parser.reportRecoverableError(next, codes.messageCovariantAndStatic);
} else {
throw 'Internal Error: Unhandled recovery: $next';
}
return next;
}
Token parseExternal(Token token) {
Token next = token.next;
assert(optional('external', next));
if (externalToken == null) {
externalToken = next;
if (afterFactory) {
reportModifierOutOfOrder(next, 'factory');
} else if (constToken != null) {
reportModifierOutOfOrder(next, constToken.lexeme);
} else if (staticToken != null) {
reportModifierOutOfOrder(next, staticToken.lexeme);
} else if (lateToken != null) {
reportModifierOutOfOrder(next, lateToken.lexeme);
}
return next;
}
// Recovery
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
return next;
}
Token parseFinal(Token token) {
Token next = token.next;
assert(optional('final', next));
if (varFinalOrConst == null && !afterFactory) {
finalToken = next;
return next;
}
// Recovery
if (finalToken != null) {
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
} else if (afterFactory) {
reportExtraneousModifier(next);
} else if (constToken != null) {
parser.reportRecoverableError(next, codes.messageConstAndFinal);
} else if (varToken != null) {
parser.reportRecoverableError(next, codes.messageFinalAndVar);
} else if (lateToken != null) {
reportModifierOutOfOrder(next, lateToken.lexeme);
} else {
throw 'Internal Error: Unexpected varFinalOrConst: $varFinalOrConst';
}
return next;
}
Token parseLate(Token token) {
Token next = token.next;
assert(optional('late', next));
if (lateToken == null) {
lateToken = next;
if (constToken != null) {
reportConflictingModifiers(next, constToken);
} else if (varToken != null) {
reportModifierOutOfOrder(next, varToken.lexeme);
} else if (finalToken != null) {
reportModifierOutOfOrder(next, finalToken.lexeme);
}
return next;
}
// Recovery
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
return next;
}
Token parseRequired(Token token) {
Token next = token.next;
assert(optional('required', next));
if (requiredToken == null) {
requiredToken = next;
if (constToken != null) {
reportModifierOutOfOrder(requiredToken, constToken.lexeme);
} else if (covariantToken != null) {
reportModifierOutOfOrder(requiredToken, covariantToken.lexeme);
} else if (finalToken != null) {
reportModifierOutOfOrder(requiredToken, finalToken.lexeme);
} else if (varToken != null) {
reportModifierOutOfOrder(requiredToken, varToken.lexeme);
}
return next;
}
// Recovery
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
return next;
}
Token parseStatic(Token token) {
Token next = token.next;
assert(optional('static', next));
if (covariantToken == null && staticToken == null && !afterFactory) {
staticToken = next;
if (constToken != null) {
reportModifierOutOfOrder(next, constToken.lexeme);
} else if (finalToken != null) {
reportModifierOutOfOrder(next, finalToken.lexeme);
} else if (varToken != null) {
reportModifierOutOfOrder(next, varToken.lexeme);
} else if (lateToken != null) {
reportModifierOutOfOrder(next, lateToken.lexeme);
}
return next;
}
// Recovery
if (covariantToken != null) {
parser.reportRecoverableError(next, codes.messageCovariantAndStatic);
} else if (staticToken != null) {
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
} else if (afterFactory) {
reportExtraneousModifier(next);
} else {
throw 'Internal Error: Unhandled recovery: $next';
}
return next;
}
Token parseVar(Token token) {
Token next = token.next;
assert(optional('var', next));
if (varFinalOrConst == null && !afterFactory) {
varToken = next;
return next;
}
// Recovery
if (varToken != null) {
parser.reportRecoverableErrorWithToken(
next, codes.templateDuplicatedModifier);
} else if (afterFactory) {
reportExtraneousModifier(next);
} else if (constToken != null) {
reportConflictingModifiers(next, constToken);
} else if (finalToken != null) {
parser.reportRecoverableError(next, codes.messageFinalAndVar);
} else {
throw 'Internal Error: Unexpected varFinalOrConst: $varFinalOrConst';
}
return next;
}
void reportConflictingModifiers(Token modifier, Token earlierModifier) {
parser.reportRecoverableError(
modifier,
codes.templateConflictingModifiers
.withArguments(modifier.lexeme, earlierModifier.lexeme));
}
void reportExtraneousModifier(Token modifier) {
if (modifier != null) {
parser.reportRecoverableErrorWithToken(
modifier, codes.templateExtraneousModifier);
}
}
void reportExtraneousModifierInExtension(Token modifier) {
if (modifier != null) {
parser.reportRecoverableErrorWithToken(
modifier, codes.templateExtraneousModifierInExtension);
}
}
void reportModifierOutOfOrder(Token modifier, String beforeModifier) {
parser.reportRecoverableError(
modifier,
codes.templateModifierOutOfOrder
.withArguments(modifier.lexeme, beforeModifier));
}
}