Revise fasta type arg/param parsing to ignore "<".endGroup
The scanner associates '<' with '>' and '>>',
but the parser cannot use '<'.endToken/endGroup reliably
and wastes time updating that token's endToken/endGroup field.
This CL updates type info parsing to ignore '<'.endGroup
and step through the token stream (which it has to do anyway),
for improved parsing.
This CL causes a handful of existing tests to fail (marked as @failingTest)
and test failures will be addressed in a subsequent CL.
Change-Id: I788e9a74cd2891f98f44bba15d5669febfec6c64
Reviewed-on: https://dart-review.googlesource.com/70001
Commit-Queue: Dan Rubel <danrubel@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/test/src/computer/closingLabels_computer_test.dart b/pkg/analysis_server/test/src/computer/closingLabels_computer_test.dart
index 2c9e9dd..f63237e 100644
--- a/pkg/analysis_server/test/src/computer/closingLabels_computer_test.dart
+++ b/pkg/analysis_server/test/src/computer/closingLabels_computer_test.dart
@@ -412,8 +412,4 @@
class ClosingLabelsComputerTest_UseCFE extends ClosingLabelsComputerTest {
@override
bool get useCFE => true;
-
- @failingTest
- @override
- test_knownBadCode1() => super.test_knownBadCode1();
}
diff --git a/pkg/analyzer/test/generated/parser_fasta_test.dart b/pkg/analyzer/test/generated/parser_fasta_test.dart
index 92a6f6a..24cd719 100644
--- a/pkg/analyzer/test/generated/parser_fasta_test.dart
+++ b/pkg/analyzer/test/generated/parser_fasta_test.dart
@@ -1018,7 +1018,21 @@
@reflectiveTest
class SimpleParserTest_Fasta extends FastaParserTestCase
- with SimpleParserTestMixin {}
+ with SimpleParserTestMixin {
+ @override
+ @failingTest
+ void test_parseTypeParameterList_parameterizedWithTrailingEquals() {
+ // TODO(danrubel): Update TypeInfo to handle '>>='
+ super.test_parseTypeParameterList_parameterizedWithTrailingEquals();
+ }
+
+ @override
+ @failingTest
+ void test_parseTypeParameterList_parameterizedWithTrailingEquals2() {
+ // TODO(danrubel): Update TypeInfo to handle '>>='
+ super.test_parseTypeParameterList_parameterizedWithTrailingEquals2();
+ }
+}
/**
* Tests of the fasta parser based on [StatementParserTestMixin].
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index e7298b6..9f24489 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -2674,7 +2674,6 @@
expectedError(
CompileTimeErrorCode.TYPE_PARAMETER_ON_CONSTRUCTOR, 11, 2),
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 13, 1),
- expectedError(ParserErrorCode.EXPECTED_TOKEN, 13, 1),
expectedError(ParserErrorCode.MISSING_METHOD_PARAMETERS, 10, 1),
expectedError(ParserErrorCode.MISSING_FUNCTION_BODY, 13, 1),
]
@@ -2694,7 +2693,6 @@
expectedError(
CompileTimeErrorCode.TYPE_PARAMETER_ON_CONSTRUCTOR, 11, 6),
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 17, 1),
- expectedError(ParserErrorCode.EXPECTED_TOKEN, 17, 1),
expectedError(ParserErrorCode.MISSING_METHOD_PARAMETERS, 10, 1),
expectedError(ParserErrorCode.MISSING_FUNCTION_BODY, 17, 1)
]
@@ -2714,7 +2712,6 @@
expectedError(
CompileTimeErrorCode.TYPE_PARAMETER_ON_CONSTRUCTOR, 11, 13),
expectedError(ParserErrorCode.MISSING_IDENTIFIER, 24, 1),
- expectedError(ParserErrorCode.EXPECTED_TOKEN, 24, 1),
expectedError(ParserErrorCode.MISSING_METHOD_PARAMETERS, 10, 1),
expectedError(ParserErrorCode.MISSING_FUNCTION_BODY, 24, 1)
]
@@ -6751,7 +6748,7 @@
void test_parseConstExpression_mapLiteral_typed_missingGt() {
Expression expression = parseExpression('const <A, B {}',
- errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 12, 1)]);
+ errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 10, 1)]);
expect(expression, isNotNull);
var literal = expression as MapLiteral;
expect(literal.leftBracket, isNotNull);
@@ -7043,20 +7040,17 @@
void test_parseFunctionExpression_functionInPlaceOfTypeName() {
Expression expression = parseExpression('<test(' ', (){});>[0, 1, 2]',
codes: usingFastaParser
- ? [
- ParserErrorCode.EXPECTED_TOKEN,
- ParserErrorCode.UNEXPECTED_TOKEN,
- ParserErrorCode.MISSING_IDENTIFIER,
- ParserErrorCode.MISSING_IDENTIFIER,
- ParserErrorCode.EXPECTED_TOKEN,
- ParserErrorCode.MISSING_FUNCTION_BODY,
- ]
+ ? [ParserErrorCode.UNEXPECTED_TOKEN]
: [
ParserErrorCode.EXPECTED_TOKEN,
ParserErrorCode.MISSING_IDENTIFIER,
ParserErrorCode.EXPECTED_LIST_OR_MAP_LITERAL,
]);
expect(expression, isNotNull);
+ if (usingFastaParser) {
+ ListLiteral literal = expression;
+ expect(literal.typeArguments.arguments, hasLength(1));
+ }
}
void test_parseFunctionExpression_typeParameterComments() {
@@ -11281,7 +11275,7 @@
result[new Symbol(name)] = value;
});
return result;
-}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 36, 1)]);
+}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 12, 24)]);
}
}
@@ -11610,7 +11604,7 @@
CompilationUnit unit = parseCompilationUnit(r'''
class C {
final List<int f;
-}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 27, 1)]);
+}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 23, 3)]);
// one class
List<CompilationUnitMember> declarations = unit.declarations;
expect(declarations, hasLength(1));
@@ -11637,7 +11631,7 @@
void test_incompleteTypeParameters() {
CompilationUnit unit = parseCompilationUnit(r'''
class C<K {
-}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 10, 1)]);
+}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 8, 1)]);
// one class
List<CompilationUnitMember> declarations = unit.declarations;
expect(declarations, hasLength(1));
@@ -11654,7 +11648,7 @@
void test_incompleteTypeParameters2() {
CompilationUnit unit = parseCompilationUnit(r'''
class C<K extends L<T> {
-}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 23, 1)]);
+}''', errors: [expectedError(ParserErrorCode.EXPECTED_TOKEN, 21, 1)]);
// one class
List<CompilationUnitMember> declarations = unit.declarations;
expect(declarations, hasLength(1));
@@ -11671,10 +11665,13 @@
void test_incompleteTypeParameters3() {
CompilationUnit unit = parseCompilationUnit(r'''
class C<K extends L<T {
-}''', errors: [
- expectedError(ParserErrorCode.EXPECTED_TOKEN, 22, 1),
- expectedError(ParserErrorCode.EXPECTED_TOKEN, 22, 1)
- ]);
+}''',
+ errors: usingFastaParser
+ ? [expectedError(ParserErrorCode.EXPECTED_TOKEN, 20, 1)]
+ : [
+ expectedError(ParserErrorCode.EXPECTED_TOKEN, 20, 1),
+ expectedError(ParserErrorCode.EXPECTED_TOKEN, 22, 1)
+ ]);
// one class
List<CompilationUnitMember> declarations = unit.declarations;
expect(declarations, hasLength(1));
@@ -11693,6 +11690,29 @@
codes: [ParserErrorCode.MISSING_STAR_AFTER_SYNC]);
}
+ void test_invalidMapLiteral() {
+ parseCompilationUnit("class C { var f = Map<A, B> {}; }",
+ codes: usingFastaParser
+ ? [
+ // TODO(danrubel): Improve error message to indicate
+ // that "Map" should be removed.
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.MISSING_KEYWORD_OPERATOR,
+ ParserErrorCode.MISSING_METHOD_PARAMETERS,
+ ParserErrorCode.EXPECTED_CLASS_MEMBER,
+ ]
+ : [
+ ParserErrorCode.EXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_CLASS_MEMBER,
+ ParserErrorCode.EXPECTED_CLASS_MEMBER,
+ ParserErrorCode.UNEXPECTED_TOKEN,
+ ParserErrorCode.UNEXPECTED_TOKEN,
+ ParserErrorCode.UNEXPECTED_TOKEN,
+ ParserErrorCode.UNEXPECTED_TOKEN,
+ ParserErrorCode.EXPECTED_EXECUTABLE,
+ ]);
+ }
+
void test_invalidTypeParameters() {
CompilationUnit unit = parseCompilationUnit(r'''
class C {
diff --git a/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart b/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart
index edc6d29..74849c3 100644
--- a/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart
+++ b/pkg/analyzer/test/src/fasta/recovery/paired_tokens_test.dart
@@ -62,6 +62,7 @@
''');
}
+ @failingTest
void test_typeParameters_gtGtEq() {
testRecovery('''
f<T extends List<int>>=() => null;
diff --git a/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart b/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart
index 0c8d710..cc01a67 100644
--- a/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart
+++ b/pkg/front_end/lib/src/fasta/parser/token_stream_rewriter.dart
@@ -14,9 +14,6 @@
Token,
TokenType;
-import 'util.dart'
- show optional, splitGtEq, splitGtGt, splitGtGtEq, syntheticGt;
-
/// Provides the capability of inserting tokens into a token stream. This
/// implementation does this by rewriting the previous token to point to the
/// inserted token.
@@ -95,7 +92,7 @@
return previousToken;
}
- /// Move [endGroup] (a synthetic `)`, `]`, `}`, or `>` token) and associated
+ /// Move [endGroup] (a synthetic `)`, `]`, or `}` token) and associated
/// error token after [token] in the token stream and return [endGroup].
Token moveSynthetic(Token token, Token endGroup) {
assert(endGroup.beforeSynthetic != null);
@@ -134,43 +131,6 @@
return replacementToken;
}
- /// Split a `>>` token into two separate `>` tokens, updates the token stream,
- /// and returns the first `>`. If [start].endGroup is `>>` then sets
- /// [start].endGroup to the second `>` but does not set the inner group's
- /// endGroup, otherwise sets [start].endGroup to the first `>`.
- Token splitEndGroup(BeginToken start, [Token end]) {
- end ??= start.endGroup;
- assert(end != null);
-
- Token gt;
- if (optional('>>', end)) {
- gt = splitGtGt(end);
- } else if (optional('>=', end)) {
- gt = splitGtEq(end);
- } else if (optional('>>=', end)) {
- gt = splitGtGtEq(end);
- } else {
- gt = syntheticGt(end);
- }
-
- Token token = start;
- Token next = token.next;
- while (!identical(next, end)) {
- token = next;
- next = token.next;
- }
- token.setNext(gt);
-
- if (start.endGroup != null) {
- assert(optional('>>', start.endGroup));
- start.endGroup = gt.next;
- } else {
- // Recovery
- start.endGroup = gt;
- }
- return gt;
- }
-
/// Given the [firstToken] in a chain of tokens to be inserted, return the
/// last token in the chain.
///
diff --git a/pkg/front_end/lib/src/fasta/parser/type_info.dart b/pkg/front_end/lib/src/fasta/parser/type_info.dart
index b463e33..317cc51 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info.dart
@@ -125,17 +125,13 @@
///
/// If [inDeclaration] is `true`, then this will more aggressively recover
/// given unbalanced `<` `>` and invalid parameters or arguments.
-///
-/// If this method is called by [computeTypeParamOrArg] and the outer group ends
-/// with `>>`, then then [innerEndGroup] is set to either `>>` if the token
-/// has not been split or the first `>` if the `>>` token has been split.
TypeInfo computeType(final Token token, bool required,
- [bool inDeclaration = false, Token innerEndGroup]) {
+ [bool inDeclaration = false]) {
Token next = token.next;
if (!isValidTypeReference(next)) {
if (next.type.isBuiltIn) {
TypeParamOrArgInfo typeParamOrArg =
- computeTypeParamOrArg(next, inDeclaration, innerEndGroup);
+ computeTypeParamOrArg(next, inDeclaration);
if (typeParamOrArg != noTypeParamOrArg) {
// Recovery: built-in `<` ... `>`
if (required || looksLikeName(typeParamOrArg.skip(next).next)) {
@@ -156,7 +152,7 @@
} else if (required && optional('.', next)) {
// Recovery: looks like prefixed type missing the prefix
return new ComplexTypeInfo(
- token, computeTypeParamOrArg(next, inDeclaration, innerEndGroup))
+ token, computeTypeParamOrArg(next, inDeclaration))
.computePrefixedType(required);
}
return noType;
@@ -182,7 +178,7 @@
// We've seen an identifier.
TypeParamOrArgInfo typeParamOrArg =
- computeTypeParamOrArg(next, inDeclaration, innerEndGroup);
+ computeTypeParamOrArg(next, inDeclaration);
if (typeParamOrArg != noTypeParamOrArg) {
if (typeParamOrArg.isSimpleTypeArgument) {
// We've seen identifier `<` identifier `>`
@@ -212,8 +208,7 @@
next = next.next;
if (isValidTypeReference(next)) {
// We've seen identifier `.` identifier
- typeParamOrArg =
- computeTypeParamOrArg(next, inDeclaration, innerEndGroup);
+ typeParamOrArg = computeTypeParamOrArg(next, inDeclaration);
next = next.next;
if (typeParamOrArg == noTypeParamOrArg &&
!isGeneralizedFunctionType(next)) {
@@ -231,8 +226,7 @@
}
// identifier `.` non-identifier
if (required) {
- typeParamOrArg =
- computeTypeParamOrArg(token.next.next, inDeclaration, innerEndGroup);
+ typeParamOrArg = computeTypeParamOrArg(token.next.next, inDeclaration);
return new ComplexTypeInfo(token, typeParamOrArg)
.computePrefixedType(required);
}
@@ -259,42 +253,30 @@
///
/// If [inDeclaration] is `true`, then this will more aggressively recover
/// given unbalanced `<` `>` and invalid parameters or arguments.
-///
-/// If this method is called by [computeType] and the outer group ends
-/// with `>>`, then then [innerEndGroup] is set to either `>>` if the token
-/// has not been split or the first `>` if the `>>` token has been split.
TypeParamOrArgInfo computeTypeParamOrArg(Token token,
- [bool inDeclaration = false, Token innerEndGroup]) {
+ [bool inDeclaration = false]) {
Token beginGroup = token.next;
if (!optional('<', beginGroup)) {
return noTypeParamOrArg;
}
- Token endGroup = beginGroup.endGroup ?? innerEndGroup;
- if (endGroup == null) {
- if (!inDeclaration) {
- return noTypeParamOrArg;
- }
- // Recovery:
- // Since the leading `<` cannot be part of an expression, fall through and
- // try to more aggressively recover given an unbalanced '<'.
- }
// identifier `<` `void` `>` and `<` `dynamic` `>`
// are handled by ComplexTypeInfo.
- Token identifier = beginGroup.next;
- if ((identifier.kind == IDENTIFIER_TOKEN || identifier.type.isPseudo)) {
- if (optional('>', identifier.next)) {
+ Token next = beginGroup.next;
+ if ((next.kind == IDENTIFIER_TOKEN || next.type.isPseudo)) {
+ if (optional('>', next.next)) {
return simpleTypeArgument1;
- } else if (optional('>>', identifier.next)) {
+ } else if (optional('>>', next.next)) {
return simpleTypeArgument1GtGt;
- } else if (optional('>=', identifier.next)) {
+ } else if (optional('>=', next.next)) {
return simpleTypeArgument1GtEq;
}
+ } else if (optional('(', next)) {
+ return noTypeParamOrArg;
}
// TODO(danrubel): Consider adding additional const for common situations.
- return new ComplexTypeParamOrArgInfo(token, inDeclaration)
- .compute(innerEndGroup);
+ return new ComplexTypeParamOrArgInfo(token, inDeclaration).compute();
}
/// Called by the parser to obtain information about a possible group of type
diff --git a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
index a327d8f..2324a81 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info_impl.dart
@@ -5,7 +5,7 @@
library fasta.parser.type_info_impl;
import '../../scanner/token.dart'
- show BeginToken, SyntheticToken, Token, TokenType;
+ show SimpleToken, SyntheticToken, Token, TokenType;
import '../fasta_codes.dart' as fasta;
@@ -23,7 +23,8 @@
import 'type_info.dart';
-import 'util.dart' show isOneOf, optional, skipMetadata, splitGtEq, splitGtGt;
+import 'util.dart'
+ show optional, skipMetadata, splitGtEq, splitGtGt, syntheticGt;
/// [SimpleType] is a specialized [TypeInfo] returned by [computeType]
/// when there is a single identifier as the type reference.
@@ -275,7 +276,8 @@
if (inDeclaration && token.kind == IDENTIFIER_TOKEN) {
Token next = token.next;
if (next.kind == IDENTIFIER_TOKEN ||
- isOneOf(next, const [',', '>', '>>'])) {
+ optional(',', next) ||
+ isCloser(next)) {
return true;
}
}
@@ -330,8 +332,10 @@
token, IdentifierContext.prefixedTypeReference);
}
+ final typeVariableEndGroups = <Token>[];
for (Link<Token> t = typeVariableStarters; t.isNotEmpty; t = t.tail) {
- computeTypeParamOrArg(t.head, true).parseVariables(t.head, parser);
+ typeVariableEndGroups.add(
+ computeTypeParamOrArg(t.head, true).parseVariables(t.head, parser));
parser.listener.beginFunctionType(start);
}
@@ -364,14 +368,17 @@
}
}
+ int endGroupIndex = typeVariableEndGroups.length - 1;
for (Link<Token> t = typeVariableStarters; t.isNotEmpty; t = t.tail) {
token = token.next;
assert(optional('Function', token));
Token functionToken = token;
if (optional("<", token.next)) {
// Skip type parameters, they were parsed above.
- token = token.next.endGroup;
+ token = typeVariableEndGroups[endGroupIndex];
+ assert(optional('>', token));
}
+ --endGroupIndex;
token = parser.parseFormalParametersRequiredOpt(
token, MemberKind.GeneralizedFunctionType);
parser.listener.endFunctionType(functionToken);
@@ -545,9 +552,9 @@
@override
Token parseArguments(Token token, Parser parser) {
- BeginToken beginGroup = token.next;
+ Token beginGroup = token.next;
assert(optional('<', beginGroup));
- Token endGroup = updateEndGroup(beginGroup, beginGroup.next);
+ Token endGroup = parseEndGroup(beginGroup, beginGroup.next);
Listener listener = parser.listener;
listener.beginTypeArguments(beginGroup);
simpleType.parseType(beginGroup, parser);
@@ -557,10 +564,10 @@
@override
Token parseVariables(Token token, Parser parser) {
- BeginToken beginGroup = token.next;
+ Token beginGroup = token.next;
assert(optional('<', beginGroup));
token = beginGroup.next;
- Token endGroup = updateEndGroup(beginGroup, token);
+ Token endGroup = parseEndGroup(beginGroup, token);
Listener listener = parser.listener;
listener.beginTypeVariables(beginGroup);
listener.beginMetadataStar(token);
@@ -589,10 +596,9 @@
return token;
}
- Token updateEndGroup(BeginToken beginGroup, Token token) {
+ Token parseEndGroup(Token beginGroup, Token token) {
token = token.next;
assert(optional('>', token));
- beginGroup.endGroup = token;
return token;
}
}
@@ -609,12 +615,12 @@
return splitGtEq(token);
}
- Token updateEndGroup(BeginToken beginGroup, Token beforeEndGroup) {
+ Token parseEndGroup(Token beginGroup, Token beforeEndGroup) {
Token endGroup = beforeEndGroup.next;
if (!optional('>', endGroup)) {
endGroup = splitGtEq(endGroup);
+ endGroup.next.setNext(endGroup.next.next);
}
- beginGroup.endGroup = endGroup;
beforeEndGroup.setNext(endGroup);
return endGroup;
}
@@ -627,16 +633,17 @@
TypeInfo get typeInfo => simpleTypeWith1ArgumentGtGt;
Token skipEndGroup(Token token) {
- assert(optional('>>', token.next));
- return token;
+ token = token.next;
+ assert(optional('>>', token));
+ return splitGtGt(token);
}
- Token updateEndGroup(BeginToken beginGroup, Token beforeEndGroup) {
+ Token parseEndGroup(Token beginGroup, Token beforeEndGroup) {
Token endGroup = beforeEndGroup.next;
if (!optional('>', endGroup)) {
endGroup = splitGtGt(endGroup);
+ endGroup.next.setNext(endGroup.next.next);
}
- beginGroup.endGroup = endGroup;
beforeEndGroup.setNext(endGroup);
return endGroup;
}
@@ -644,16 +651,15 @@
class ComplexTypeParamOrArgInfo extends TypeParamOrArgInfo {
/// The first token in the type var.
- final BeginToken start;
+ final Token start;
/// If [inDeclaration] is `true`, then this will more aggressively recover
/// given unbalanced `<` `>` and invalid parameters or arguments.
final bool inDeclaration;
- /// The last token in the group (typically `>`).
- /// If a `>>` has not yet been split, then this field will be
- /// `>>` for the outer group and the token before `>>` for the inner group.
- Token end;
+ /// The token before the end group token (e.g. `>`, `>>`, `>=`, or `>>=`)
+ /// or after which a synthetic end group token should be inserted.
+ Token beforeEnd;
ComplexTypeParamOrArgInfo(Token token, this.inDeclaration)
: assert(optional('<', token.next)),
@@ -663,29 +669,25 @@
/// Parse the tokens and return the receiver or [noTypeParamOrArg] if there
/// are no type parameters or arguments. This does not modify the token
/// stream.
- ///
- /// If this group is enclosed and the outer group ends with `>>`, then
- /// [endGroup] is set to either `>>` if the token has not been split
- /// or the first `>` if the `>>` token has been split.
- TypeParamOrArgInfo compute(Token endGroup) {
- Token innerEndGroup;
- if (start.endGroup != null && optional('>>', start.endGroup)) {
- innerEndGroup = start.endGroup;
- }
-
+ TypeParamOrArgInfo compute() {
Token token;
Token next = start;
while (true) {
- TypeInfo typeInfo = computeType(next, true, inDeclaration, innerEndGroup);
+ TypeInfo typeInfo = computeType(next, true, inDeclaration);
if (typeInfo == noType) {
while (typeInfo == noType && optional('@', next.next)) {
next = skipMetadata(next);
- typeInfo = computeType(next, true, inDeclaration, innerEndGroup);
+ typeInfo = computeType(next, true, inDeclaration);
}
- if (typeInfo == noType && !optional(',', next.next)) {
- token = next;
- next = token.next;
- break;
+ if (typeInfo == noType) {
+ if (next == start && !inDeclaration && !isCloser(next.next)) {
+ return noTypeParamOrArg;
+ }
+ if (!optional(',', next.next)) {
+ token = next;
+ next = token.next;
+ break;
+ }
}
assert(typeInfo != noType || optional(',', next.next));
// Fall through to process type (if any) and consume `,`
@@ -693,33 +695,41 @@
token = typeInfo.skipType(next);
next = token.next;
if (optional('extends', next) || optional('super', next)) {
- token = computeType(next, true, inDeclaration, innerEndGroup)
- .skipType(next);
+ token = computeType(next, true, inDeclaration).skipType(next);
next = token.next;
}
if (!optional(',', next)) {
+ if (isCloser(next)) {
+ beforeEnd = token;
+ return this;
+ }
+ if (!inDeclaration) {
+ return noTypeParamOrArg;
+ }
+
+ // Recovery
if (!looksLikeTypeParamOrArg(inDeclaration, next)) {
break;
}
- // Recovery: Missing comma. Continue looping
+ // Looks like missing comma. Continue looping.
+ next = token;
}
}
- if (next == start.endGroup) {
- end = next;
- } else if (next == endGroup) {
- assert(start.endGroup == null);
- assert(optional('>', endGroup) || optional('>>', endGroup));
- // If `>>`, then the end or last consumed token is the token before `>>`.
- end = optional('>>', next) ? token : next;
- } else if (inDeclaration) {
- end = start.endGroup;
- if (end == null) {
- // Recovery: Unbalanced `<`
- end = token;
+ // Recovery
+ beforeEnd = token;
+ if (!isCloser(next)) {
+ if (optional('(', next)) {
+ token = next.endGroup;
+ next = token.next;
}
- } else {
- return noTypeParamOrArg;
+ if (!isCloser(next)) {
+ token = next;
+ next = token.next;
+ }
+ if (isCloser(next)) {
+ beforeEnd = token;
+ }
}
return this;
}
@@ -728,18 +738,17 @@
Token parseArguments(Token token, Parser parser) {
assert(identical(token.next, start));
Token next = start;
- Token innerEndGroup = processBeginGroup(start, parser);
parser.listener.beginTypeArguments(start);
int count = 0;
while (true) {
- TypeInfo typeInfo = computeType(next, true, inDeclaration, innerEndGroup);
+ TypeInfo typeInfo = computeType(next, true, inDeclaration);
if (typeInfo == noType) {
// Recovery
while (typeInfo == noType && optional('@', next.next)) {
parser.reportRecoverableErrorWithToken(
next.next, fasta.templateUnexpectedToken);
next = skipMetadata(next);
- typeInfo = computeType(next, true, inDeclaration, innerEndGroup);
+ typeInfo = computeType(next, true, inDeclaration);
}
// Fall through to process type (if any) and consume `,`
}
@@ -747,22 +756,23 @@
next = token.next;
++count;
if (!optional(',', next)) {
- if (!looksLikeTypeParamOrArg(inDeclaration, next)) {
+ if (parseCloser(token)) {
+ beforeEnd = token;
break;
}
- // Recovery: missing comma
- parser.reportRecoverableError(
- next, fasta.templateExpectedButGot.withArguments(','));
- next = parser.rewriter
- .insertTokenAfter(
- token, new SyntheticToken(TokenType.COMMA, next.charOffset))
- .next;
+ // Recovery
+ if (!looksLikeTypeParamOrArg(inDeclaration, next)) {
+ parseUnexpectedEnd(token, parser);
+ break;
+ }
+ // Missing comma. Report error, insert comma, and continue looping.
+ next = parseMissingComma(token, parser);
}
}
- end = processEndGroup(token, start, parser);
- parser.listener.endTypeArguments(count, start, end);
- return end;
+ Token endGroup = beforeEnd.next;
+ parser.listener.endTypeArguments(count, start, endGroup);
+ return endGroup;
}
@override
@@ -771,7 +781,6 @@
Token next = start;
Listener listener = parser.listener;
listener.beginTypeVariables(start);
- Token innerEndGroup = processBeginGroup(start, parser);
int count = 0;
Link<Token> typeStarts = const Link<Token>();
@@ -779,15 +788,18 @@
while (true) {
token = parser.parseMetadataStar(next);
- token = parser.ensureIdentifier(
+ next = parser.ensureIdentifier(
token, IdentifierContext.typeVariableDeclaration);
+ if (beforeEnd == token) {
+ beforeEnd = next;
+ }
+ token = next;
listener.beginTypeVariable(token);
typeStarts = typeStarts.prepend(token);
next = token.next;
if (optional('extends', next) || optional('super', next)) {
- TypeInfo typeInfo =
- computeType(next, true, inDeclaration, innerEndGroup);
+ TypeInfo typeInfo = computeType(next, true, inDeclaration);
token = typeInfo.skipType(next);
next = token.next;
superTypeInfos = superTypeInfos.prepend(typeInfo);
@@ -797,17 +809,16 @@
++count;
if (!optional(',', next)) {
- if (!looksLikeTypeParamOrArg(inDeclaration, next)) {
+ if (isCloser(token)) {
break;
}
- // Recovery: missing comma
- parser.reportRecoverableError(
- next, fasta.templateExpectedButGot.withArguments(','));
- next = parser.rewriter
- .insertTokenAfter(
- token, new SyntheticToken(TokenType.COMMA, next.charOffset))
- .next;
+ // Recovery
+ if (!looksLikeTypeParamOrArg(inDeclaration, next)) {
+ break;
+ }
+ // Missing comma. Report error, insert comma, and continue looping.
+ next = parseMissingComma(token, parser);
}
}
@@ -841,53 +852,92 @@
superTypeInfos = superTypeInfos.tail;
}
- end = processEndGroup(token, start, parser);
- listener.endTypeVariables(start, end);
- return end;
+ if (parseCloser(token)) {
+ beforeEnd = token;
+ } else {
+ parseUnexpectedEnd(token, parser);
+ }
+ Token endGroup = beforeEnd.next;
+ listener.endTypeVariables(start, endGroup);
+ return endGroup;
+ }
+
+ Token parseMissingComma(Token token, Parser parser) {
+ Token next = token.next;
+ parser.reportRecoverableError(
+ next, fasta.templateExpectedButGot.withArguments(','));
+ return parser.rewriter.insertToken(
+ token, new SyntheticToken(TokenType.COMMA, next.charOffset));
+ }
+
+ void parseUnexpectedEnd(Token token, Parser parser) {
+ if (beforeEnd.isSynthetic && beforeEnd.charOffset == token.charOffset) {
+ // Ensure that beforeEnd is in the token stream
+ // as a nested type argument or parameter may have inserted
+ // a synthetic closer.
+ beforeEnd = token;
+ }
+ if (parseCloser(beforeEnd)) {
+ parser.reportRecoverableErrorWithToken(
+ token.next, fasta.templateUnexpectedToken);
+ } else {
+ // If token is synthetic, then an error has already been reported.
+ if (!token.isSynthetic) {
+ parser.reportRecoverableError(
+ token, fasta.templateExpectedAfterButGot.withArguments('>'));
+ }
+ Token next = beforeEnd.next;
+ Token endGroup = syntheticGt(next);
+ endGroup.setNext(next);
+ beforeEnd.setNext(endGroup);
+ }
}
@override
- Token skip(Token token) => end;
+ Token skip(Token token) {
+ final next = beforeEnd.next;
+ final value = next.stringValue;
+ if (identical(value, '>')) {
+ return next;
+ } else if (identical(value, '>>')) {
+ return splitGtGt(next);
+ } else if (identical(value, '>=')) {
+ return splitGtEq(next);
+ } else if (identical(value, '>>=')) {
+ // TODO(danrubel): Add support for this
+ }
+ return syntheticGt(next);
+ }
}
-Token processBeginGroup(BeginToken start, Parser parser) {
- if (start.endGroup != null && optional('>>', start.endGroup)) {
- return parser.rewriter.splitEndGroup(start);
- }
- return null;
+/// Return `true` if [token] is one of `>`, `>>`, `>=', or `>>=`.
+bool isCloser(Token token) {
+ final value = token.stringValue;
+ return identical(value, '>') ||
+ identical(value, '>>') ||
+ identical(value, '>=');
+ // TODO(danrubel): Add support for `>>=`.
}
-Token processEndGroup(Token token, BeginToken start, Parser parser) {
- Token next = token.next;
- if (next == start.endGroup) {
- return next;
- } else if (optional('>', next) && !next.isSynthetic) {
- // When `>>` is split, the inner group's endGroup updated here.
- assert(start.endGroup == null);
- start.endGroup = next;
- return next;
+/// If [token] is one of `>`, `>>`, `>=', or `>>=`,
+/// then update the token stream and return `true`.
+bool parseCloser(Token beforeCloser) {
+ Token closer = beforeCloser.next;
+ String value = closer.stringValue;
+ if (identical(value, '>')) {
+ return true;
+ } else if (identical(value, '>>')) {
+ SimpleToken split = splitGtGt(closer);
+ split.next.setNext(closer.next);
+ beforeCloser.setNext(split);
+ return true;
+ } else if (identical(value, '>=')) {
+ Token split = splitGtEq(closer);
+ split.next.setNext(closer.next);
+ beforeCloser.setNext(split);
+ return true;
+ } else if (identical(value, '>>=')) {
+ // TODO(danrubel): Add support for parsing `>>=`
}
-
- // Recovery
- if (start.endGroup != null) {
- // Extraneous tokens between `<` and `>`.
- parser.reportRecoverableErrorWithToken(next, fasta.templateUnexpectedToken);
- return start.endGroup;
- } else if (isOneOf(next, const ['>>', '>=', '>>='])) {
- // Found single unbalanced `<`.
- return parser.rewriter.splitEndGroup(start, next);
- }
- // Ensure that `>` is inserted after any newly inserted synthetic tokens.
- while (next.isSynthetic && !next.isEof) {
- token = next;
- next = token.next;
- }
- // Unbalanced `<` `>`
- parser.reportRecoverableError(
- next, fasta.templateExpectedButGot.withArguments('>'));
- start.endGroup = parser.rewriter
- .insertTokenAfter(
- token, new SyntheticToken(TokenType.GT, next.charOffset))
- .next;
- return start.endGroup;
+ return false;
}
diff --git a/pkg/front_end/lib/src/fasta/parser/util.dart b/pkg/front_end/lib/src/fasta/parser/util.dart
index 527d4b4..f617749 100644
--- a/pkg/front_end/lib/src/fasta/parser/util.dart
+++ b/pkg/front_end/lib/src/fasta/parser/util.dart
@@ -136,42 +136,59 @@
}
/// Split `>=` into two separate tokens.
+/// Call [Token.setNext] to add the token to the stream.
Token splitGtEq(Token token) {
assert(optional('>=', token));
return new SimpleToken(
TokenType.GT, token.charOffset, token.precedingComments)
..setNext(new SimpleToken(TokenType.EQ, token.charOffset + 1)
- ..setNext(token.next));
+ // Set next rather than calling Token.setNext
+ // so that the previous token is not set.
+ ..next = token.next);
}
/// Split `>>` into two separate tokens.
+/// Call [Token.setNext] to add the token to the stream.
SimpleToken splitGtGt(Token token) {
assert(optional('>>', token));
return new SimpleToken(
TokenType.GT, token.charOffset, token.precedingComments)
..setNext(new SimpleToken(TokenType.GT, token.charOffset + 1)
- ..setNext(token.next));
+ // Set next rather than calling Token.setNext
+ // so that the previous token is not set.
+ ..next = token.next);
}
/// Split `>>=` into three separate tokens.
+/// Call [Token.setNext] to add the token to the stream.
Token splitGtGtEq(Token token) {
assert(optional('>>=', token));
return new SimpleToken(
TokenType.GT, token.charOffset, token.precedingComments)
..setNext(new SimpleToken(TokenType.GT, token.charOffset + 1)
..setNext(new SimpleToken(TokenType.EQ, token.charOffset + 2)
- ..setNext(token.next)));
+ // Set next rather than calling Token.setNext
+ // so that the previous token is not set.
+ ..next = token.next));
}
/// Split `>>=` into two separate tokens... `>` followed by `>=`.
+/// Call [Token.setNext] to add the token to the stream.
Token splitGtFromGtGtEq(Token token) {
assert(optional('>>=', token));
return new SimpleToken(
TokenType.GT, token.charOffset, token.precedingComments)
..setNext(new SimpleToken(TokenType.GT_EQ, token.charOffset + 1)
- ..setNext(token.next));
+ // Set next rather than calling Token.setNext
+ // so that the previous token is not set.
+ ..next = token.next);
}
-Token syntheticGt(Token token) {
- return new SyntheticToken(TokenType.GT, token.charOffset)..setNext(token);
+/// Return a synthetic `<` followed by [next].
+/// Call [Token.setNext] to add the token to the stream.
+Token syntheticGt(Token next) {
+ return new SyntheticToken(TokenType.GT, next.charOffset)
+ // Set next rather than calling Token.setNext
+ // so that the previous token is not set.
+ ..next = next;
}
diff --git a/pkg/front_end/test/fasta/parser/type_info_test.dart b/pkg/front_end/test/fasta/parser/type_info_test.dart
index 9164339..1c8618a 100644
--- a/pkg/front_end/test/fasta/parser/type_info_test.dart
+++ b/pkg/front_end/test/fasta/parser/type_info_test.dart
@@ -18,8 +18,11 @@
defineReflectiveTests(SimpleTypeInfoTest);
defineReflectiveTests(SimpleTypeWith1ArgumentTest);
defineReflectiveTests(TypeInfoTest);
- defineReflectiveTests(TypeParamOrArgInfoTest);
defineReflectiveTests(VoidTypeInfoTest);
+
+ defineReflectiveTests(NoTypeParamOrArgTest);
+ defineReflectiveTests(SimpleTypeParamOrArgTest);
+ defineReflectiveTests(TypeParamOrArgInfoTest);
});
}
@@ -32,6 +35,70 @@
expect(noType.skipType(start), start);
}
+ void test_compute() {
+ expectInfo(noType, '');
+ expectInfo(noType, ';');
+ expectInfo(noType, '( foo');
+ expectInfo(noType, '< foo');
+ expectInfo(noType, '= foo');
+ expectInfo(noType, '* foo');
+ expectInfo(noType, 'do foo');
+ expectInfo(noType, 'get foo');
+ expectInfo(noType, 'set foo');
+ expectInfo(noType, 'operator *');
+
+ expectInfo(noType, 'C', required: false);
+ expectInfo(noType, 'C;', required: false);
+ expectInfo(noType, 'C(', required: false);
+ expectInfo(noType, 'C<', required: false);
+ expectInfo(noType, 'C.', required: false);
+ expectInfo(noType, 'C=', required: false);
+ expectInfo(noType, 'C*', required: false);
+ expectInfo(noType, 'C do', required: false);
+
+ expectInfo(noType, 'C.a', required: false);
+ expectInfo(noType, 'C.a;', required: false);
+ expectInfo(noType, 'C.a(', required: false);
+ expectInfo(noType, 'C.a<', required: false);
+ expectInfo(noType, 'C.a=', required: false);
+ expectInfo(noType, 'C.a*', required: false);
+ expectInfo(noType, 'C.a do', required: false);
+
+ expectInfo(noType, 'C<T>', required: false);
+ expectInfo(noType, 'C<T>;', required: false);
+ expectInfo(noType, 'C<T>(', required: false);
+ expectInfo(noType, 'C<T> do', required: false);
+ expectInfo(noType, 'C<void>', required: false);
+
+ expectInfo(noType, 'C<T>= foo', required: false);
+ expectInfo(noType, 'C<T>= get', required: false);
+ expectInfo(noType, 'C<T>= set', required: false);
+ expectInfo(noType, 'C<T>= operator', required: false);
+ expectInfo(noType, 'C<T>= Function', required: false);
+
+ expectInfo(noType, 'C<T>> foo', required: false);
+ expectInfo(noType, 'C<T>> get', required: false);
+ expectInfo(noType, 'C<T>> set', required: false);
+ expectInfo(noType, 'C<T>> operator', required: false);
+ expectInfo(noType, 'C<T>> Function', required: false);
+
+ expectInfo(noType, 'C<T>>= foo', required: false);
+ expectInfo(noType, 'C<T>>= get', required: false);
+ expectInfo(noType, 'C<T>>= set', required: false);
+ expectInfo(noType, 'C<T>>= operator', required: false);
+ expectInfo(noType, 'C<T>>= Function', required: false);
+
+ expectInfo(noType, 'C<S,T>', required: false);
+ expectInfo(noType, 'C<S<T>>', required: false);
+ expectInfo(noType, 'C.a<T>', required: false);
+ expectInfo(noType, 'C<S,T>=', required: false);
+ expectInfo(noType, 'C<S<T>>=', required: false);
+ expectInfo(noType, 'C.a<T>=', required: false);
+
+ expectInfo(noType, 'Function(int x)', required: false);
+ expectInfo(noType, 'Function<T>(int x)', required: false);
+ }
+
void test_ensureTypeNotVoid() {
final Token start = scanString('before ;').tokens;
final TypeInfoListener listener = new TypeInfoListener();
@@ -88,6 +155,29 @@
expect(voidType.couldBeExpression, isFalse);
}
+ void test_compute() {
+ expectInfo(voidType, 'void');
+ expectInfo(voidType, 'void;');
+ expectInfo(voidType, 'void(');
+ expectInfo(voidType, 'void<');
+ expectInfo(voidType, 'void=');
+ expectInfo(voidType, 'void*');
+ expectInfo(voidType, 'void<T>');
+ expectInfo(voidType, 'void do');
+ expectInfo(voidType, 'void foo');
+ expectInfo(voidType, 'void get');
+ expectInfo(voidType, 'void set');
+ expectInfo(voidType, 'void operator');
+ expectInfo(voidType, 'void Function');
+
+ expectInfo(voidType, 'void Function()', required: false);
+ expectInfo(voidType, 'void Function<T>()', required: false);
+ expectInfo(voidType, 'void Function(int)', required: false);
+ expectInfo(voidType, 'void Function<T>(int)', required: false);
+ expectInfo(voidType, 'void Function(int x)', required: false);
+ expectInfo(voidType, 'void Function<T>(int x)', required: false);
+ }
+
void test_ensureTypeNotVoid() {
final Token start = scanString('before void ;').tokens;
final TypeInfoListener listener = new TypeInfoListener();
@@ -135,6 +225,22 @@
@reflectiveTest
class PrefixedTypeInfoTest {
+ void test_compute() {
+ expectInfo(prefixedType, 'C.a', required: true);
+ expectInfo(prefixedType, 'C.a;', required: true);
+ expectInfo(prefixedType, 'C.a(', required: true);
+ expectInfo(prefixedType, 'C.a<', required: true);
+ expectInfo(prefixedType, 'C.a=', required: true);
+ expectInfo(prefixedType, 'C.a*', required: true);
+ expectInfo(prefixedType, 'C.a do', required: true);
+
+ expectInfo(prefixedType, 'C.a foo');
+ expectInfo(prefixedType, 'C.a get');
+ expectInfo(prefixedType, 'C.a set');
+ expectInfo(prefixedType, 'C.a operator');
+ expectInfo(prefixedType, 'C.a Function');
+ }
+
void test_prefixedTypeInfo() {
final Token start = scanString('before C.a ;').tokens;
final Token expectedEnd = start.next.next.next;
@@ -171,6 +277,32 @@
@reflectiveTest
class SimpleTypeInfoTest {
+ void test_compute() {
+ expectInfo(simpleType, 'C', required: true);
+ expectInfo(simpleType, 'C;', required: true);
+ expectInfo(simpleType, 'C(', required: true);
+ expectInfo(simpleType, 'C<', required: true);
+ expectComplexInfo('C.',
+ required: true, expectedErrors: [error(codeExpectedType, 2, 0)]);
+ expectInfo(simpleType, 'C=', required: true);
+ expectInfo(simpleType, 'C*', required: true);
+ expectInfo(simpleType, 'C do', required: true);
+
+ expectInfo(simpleType, 'C foo');
+ expectInfo(simpleType, 'C get');
+ expectInfo(simpleType, 'C set');
+ expectInfo(simpleType, 'C operator');
+ expectInfo(simpleType, 'C this');
+ expectInfo(simpleType, 'C Function');
+
+ expectInfo(simpleType, 'C Function()', required: false);
+ expectInfo(simpleType, 'C Function<T>()', required: false);
+ expectInfo(simpleType, 'C Function(int)', required: false);
+ expectInfo(simpleType, 'C Function<T>(int)', required: false);
+ expectInfo(simpleType, 'C Function(int x)', required: false);
+ expectInfo(simpleType, 'C Function<T>(int x)', required: false);
+ }
+
void test_simpleTypeInfo() {
final Token start = scanString('before C ;').tokens;
final Token expectedEnd = start.next;
@@ -205,6 +337,45 @@
@reflectiveTest
class SimpleTypeWith1ArgumentTest {
+ void test_compute_gt() {
+ expectInfo(simpleTypeWith1Argument, 'C<T>', required: true);
+ expectInfo(simpleTypeWith1Argument, 'C<T>;', required: true);
+ expectInfo(simpleTypeWith1Argument, 'C<T>(', required: true);
+ expectInfo(simpleTypeWith1Argument, 'C<T> do', required: true);
+
+ expectInfo(simpleTypeWith1Argument, 'C<T> foo');
+ expectInfo(simpleTypeWith1Argument, 'C<T> get');
+ expectInfo(simpleTypeWith1Argument, 'C<T> set');
+ expectInfo(simpleTypeWith1Argument, 'C<T> operator');
+ expectInfo(simpleTypeWith1Argument, 'C<T> Function');
+ }
+
+ void test_compute_gt_eq() {
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>=', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>=;', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>=(', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>= do', required: true);
+
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>= foo', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>= get', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>= set', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>= operator', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtEq, 'C<T>= Function', required: true);
+ }
+
+ void test_compute_gt_gt() {
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>>', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>>;', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>>(', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>> do', required: true);
+
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>> foo', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>> get', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>> set', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>> operator', required: true);
+ expectInfo(simpleTypeWith1ArgumentGtGt, 'C<T>> Function', required: true);
+ }
+
void test_gt() {
final Token start = scanString('before C<T> ;').tokens;
final Token expectedEnd = start.next.next.next.next;
@@ -293,11 +464,13 @@
void test_gt_gt() {
final Token start = scanString('before C<T>> ;').tokens;
- final Token t = start.next.next.next;
- final Token semicolon = t.next.next;
+ final Token semicolon = start.next.next.next.next.next;
expect(semicolon.lexeme, ';');
- expect(simpleTypeWith1ArgumentGtGt.skipType(start), t);
+ Token skip = simpleTypeWith1ArgumentGtGt.skipType(start);
+ expect(skip.lexeme, '>');
+ expect(skip.next.lexeme, '>');
+ expect(skip.next.next, semicolon);
expect(simpleTypeWith1ArgumentGtGt.couldBeExpression, isFalse);
TypeInfoListener listener;
@@ -338,17 +511,6 @@
@reflectiveTest
class TypeInfoTest {
void test_computeType_basic() {
- expectInfo(noType, '');
- expectInfo(noType, ';');
- expectInfo(noType, '( foo');
- expectInfo(noType, '< foo');
- expectInfo(noType, '= foo');
- expectInfo(noType, '* foo');
- expectInfo(noType, 'do foo');
- expectInfo(noType, 'get foo');
- expectInfo(noType, 'set foo');
- expectInfo(noType, 'operator *');
-
expectInfo(noType, '.', required: false);
expectComplexInfo('.', required: true, expectedErrors: [
error(codeExpectedType, 0, 1),
@@ -449,9 +611,6 @@
'endFunctionType Function',
]);
- expectInfo(noType, 'Function(int x)', required: false);
- expectInfo(noType, 'Function<T>(int x)', required: false);
-
expectComplexInfo('Function(int x)', required: true);
expectComplexInfo('Function<T>(int x)', required: true);
@@ -464,42 +623,7 @@
expectedAfter: 'm');
}
- void test_computeType_identifier() {
- expectInfo(noType, 'C', required: false);
- expectInfo(noType, 'C;', required: false);
- expectInfo(noType, 'C(', required: false);
- expectInfo(noType, 'C<', required: false);
- expectInfo(noType, 'C.', required: false);
- expectInfo(noType, 'C=', required: false);
- expectInfo(noType, 'C*', required: false);
- expectInfo(noType, 'C do', required: false);
-
- expectInfo(simpleType, 'C', required: true);
- expectInfo(simpleType, 'C;', required: true);
- expectInfo(simpleType, 'C(', required: true);
- expectInfo(simpleType, 'C<', required: true);
- expectComplexInfo('C.',
- required: true, expectedErrors: [error(codeExpectedType, 2, 0)]);
- expectInfo(simpleType, 'C=', required: true);
- expectInfo(simpleType, 'C*', required: true);
- expectInfo(simpleType, 'C do', required: true);
-
- expectInfo(simpleType, 'C foo');
- expectInfo(simpleType, 'C get');
- expectInfo(simpleType, 'C set');
- expectInfo(simpleType, 'C operator');
- expectInfo(simpleType, 'C this');
- expectInfo(simpleType, 'C Function');
- }
-
void test_computeType_identifierComplex() {
- expectInfo(simpleType, 'C Function()', required: false);
- expectInfo(simpleType, 'C Function<T>()', required: false);
- expectInfo(simpleType, 'C Function(int)', required: false);
- expectInfo(simpleType, 'C Function<T>(int)', required: false);
- expectInfo(simpleType, 'C Function(int x)', required: false);
- expectInfo(simpleType, 'C Function<T>(int x)', required: false);
-
expectComplexInfo('C Function()', required: true);
expectComplexInfo('C Function<T>()', required: true);
expectComplexInfo('C Function(int)', required: true);
@@ -525,16 +649,6 @@
}
void test_computeType_identifierTypeArg() {
- expectInfo(noType, 'C<T>', required: false);
- expectInfo(noType, 'C<T>;', required: false);
- expectInfo(noType, 'C<T>(', required: false);
- expectInfo(noType, 'C<T> do', required: false);
- expectInfo(noType, 'C<void>', required: false);
-
- expectInfo(simpleTypeWith1Argument, 'C<T>', required: true);
- expectInfo(simpleTypeWith1Argument, 'C<T>;', required: true);
- expectInfo(simpleTypeWith1Argument, 'C<T>(', required: true);
- expectInfo(simpleTypeWith1Argument, 'C<T> do', required: true);
expectComplexInfo('C<void>', required: true, expectedCalls: [
'handleIdentifier C typeReference',
'beginTypeArguments <',
@@ -542,19 +656,9 @@
'endTypeArguments 1 < >',
'handleType C ',
]);
-
- expectInfo(simpleTypeWith1Argument, 'C<T> foo');
- expectInfo(simpleTypeWith1Argument, 'C<T> get');
- expectInfo(simpleTypeWith1Argument, 'C<T> set');
- expectInfo(simpleTypeWith1Argument, 'C<T> operator');
- expectInfo(simpleTypeWith1Argument, 'C<T> Function');
}
void test_computeType_identifierTypeArgComplex() {
- expectInfo(noType, 'C<S,T>', required: false);
- expectInfo(noType, 'C<S<T>>', required: false);
- expectInfo(noType, 'C.a<T>', required: false);
-
expectComplexInfo('C<S,T>', required: true, expectedCalls: [
'handleIdentifier C typeReference',
'beginTypeArguments <',
@@ -704,30 +808,6 @@
expectNestedInfo(simpleTypeWith1Argument, '<T<S>,>');
}
- void test_computeType_prefixed() {
- expectInfo(noType, 'C.a', required: false);
- expectInfo(noType, 'C.a;', required: false);
- expectInfo(noType, 'C.a(', required: false);
- expectInfo(noType, 'C.a<', required: false);
- expectInfo(noType, 'C.a=', required: false);
- expectInfo(noType, 'C.a*', required: false);
- expectInfo(noType, 'C.a do', required: false);
-
- expectInfo(prefixedType, 'C.a', required: true);
- expectInfo(prefixedType, 'C.a;', required: true);
- expectInfo(prefixedType, 'C.a(', required: true);
- expectInfo(prefixedType, 'C.a<', required: true);
- expectInfo(prefixedType, 'C.a=', required: true);
- expectInfo(prefixedType, 'C.a*', required: true);
- expectInfo(prefixedType, 'C.a do', required: true);
-
- expectInfo(prefixedType, 'C.a foo');
- expectInfo(prefixedType, 'C.a get');
- expectInfo(prefixedType, 'C.a set');
- expectInfo(prefixedType, 'C.a operator');
- expectInfo(prefixedType, 'C.a Function');
- }
-
void test_computeType_prefixedComplex() {
expectComplexInfo('a < b, c > d', expectedAfter: 'd');
expectComplexInfo('a < b, c > d', expectedAfter: 'd');
@@ -852,19 +932,6 @@
}
void test_computeType_void() {
- expectInfo(voidType, 'void');
- expectInfo(voidType, 'void;');
- expectInfo(voidType, 'void(');
- expectInfo(voidType, 'void<');
- expectInfo(voidType, 'void=');
- expectInfo(voidType, 'void*');
- expectInfo(voidType, 'void<T>');
- expectInfo(voidType, 'void do');
- expectInfo(voidType, 'void foo');
- expectInfo(voidType, 'void get');
- expectInfo(voidType, 'void set');
- expectInfo(voidType, 'void operator');
- expectInfo(voidType, 'void Function');
expectComplexInfo('void Function(', // Scanner inserts synthetic ')'.
required: true,
expectedCalls: [
@@ -878,7 +945,6 @@
}
void test_computeType_voidComplex() {
- expectInfo(voidType, 'void Function()', required: false);
expectComplexInfo('void Function()', required: true, expectedCalls: [
'handleNoTypeVariables (',
'beginFunctionType void',
@@ -888,12 +954,6 @@
'endFunctionType Function',
]);
- expectInfo(voidType, 'void Function<T>()', required: false);
- expectInfo(voidType, 'void Function(int)', required: false);
- expectInfo(voidType, 'void Function<T>(int)', required: false);
- expectInfo(voidType, 'void Function(int x)', required: false);
- expectInfo(voidType, 'void Function<T>(int x)', required: false);
-
expectComplexInfo('void Function<T>()', required: true);
expectComplexInfo('void Function(int)', required: true);
expectComplexInfo('void Function<T>(int)', required: true);
@@ -908,65 +968,129 @@
}
@reflectiveTest
-class TypeParamOrArgInfoTest {
- void test_noTypeParamOrArg() {
- final Token start = scanString('before after').tokens;
+class NoTypeParamOrArgTest {
+ void test_basic() {
+ expect(noTypeParamOrArg.isSimpleTypeArgument, isFalse);
+ final Token start = scanString('before after').tokens;
expect(noTypeParamOrArg.skip(start), start);
+ validateTokens(start);
}
- void test_noTypeParamOrArg_parseArguments() {
+ void test_compute() {
+ expectTypeParamOrArg(noTypeParamOrArg, '');
+ expectTypeParamOrArg(noTypeParamOrArg, 'a');
+ expectTypeParamOrArg(noTypeParamOrArg, 'a b');
+ expectTypeParamOrArg(noTypeParamOrArg, '<');
+ expectTypeParamOrArg(noTypeParamOrArg, '< b');
+ expectTypeParamOrArg(noTypeParamOrArg, '< 3 >');
+ expectTypeParamOrArg(noTypeParamOrArg, '< (');
+ expectTypeParamOrArg(noTypeParamOrArg, '< (', inDeclaration: true);
+ }
+
+ void test_parseArguments() {
final Token start = scanString('before after').tokens;
final TypeInfoListener listener = new TypeInfoListener();
expect(noTypeParamOrArg.parseArguments(start, new Parser(listener)), start);
+ validateTokens(start);
expect(listener.calls, ['handleNoTypeArguments after']);
expect(listener.errors, isNull);
}
- void test_noTypeParamOrArg_parseVariables() {
+ void test_parseVariables() {
final Token start = scanString('before after').tokens;
final TypeInfoListener listener = new TypeInfoListener();
expect(noTypeParamOrArg.parseVariables(start, new Parser(listener)), start);
+ validateTokens(start);
expect(listener.calls, ['handleNoTypeVariables after']);
expect(listener.errors, isNull);
}
+}
- void test_simple_skip() {
+@reflectiveTest
+class SimpleTypeParamOrArgTest {
+ void test_basic_gt() {
+ expect(simpleTypeArgument1.isSimpleTypeArgument, isTrue);
+ expect(simpleTypeArgument1.typeInfo, simpleTypeWith1Argument);
+
final Token start = scanString('before <T> after').tokens;
final Token gt = start.next.next.next;
expect(gt.lexeme, '>');
- expect(simpleTypeArgument1.skip(start), gt);
+ Token skip = simpleTypeArgument1.skip(start);
+ validateTokens(start);
+ expect(skip, gt);
}
- void test_simple_skip2() {
- final Token start = scanString('before <S<T>> after').tokens.next.next;
- Token t = start.next.next;
- expect(t.next.lexeme, '>>');
+ void test_basic_gt_eq() {
+ expect(simpleTypeArgument1GtEq.isSimpleTypeArgument, isTrue);
+ expect(simpleTypeArgument1GtEq.typeInfo, simpleTypeWith1ArgumentGtEq);
- expect(simpleTypeArgument1GtGt.skip(start), t);
- }
-
- void test_simple_skip3() {
final Token start = scanString('before <T>= after').tokens;
Token t = start.next.next;
expect(t.next.lexeme, '>=');
Token skip = simpleTypeArgument1GtEq.skip(start);
+ validateTokens(start);
expect(skip.lexeme, '>');
expect(skip.next.lexeme, '=');
expect(skip.next.next, t.next.next);
}
- void test_simple_parseArguments() {
- final Token start = scanString('before <T> after').tokens;
- final Token gt = start.next.next.next;
- expect(gt.lexeme, '>');
+ void test_basic_gt_gt() {
+ expect(simpleTypeArgument1GtGt.isSimpleTypeArgument, isTrue);
+ expect(simpleTypeArgument1GtGt.typeInfo, simpleTypeWith1ArgumentGtGt);
+
+ final Token start = scanString('before <S<T>> after').tokens.next.next;
+ var gtgt = start.next.next.next;
+ expect(gtgt.lexeme, '>>');
+ Token after = gtgt.next;
+ expect(after.lexeme, 'after');
+
+ Token skip = simpleTypeArgument1GtGt.skip(start);
+ validateTokens(start);
+ expect(skip.lexeme, '>');
+ expect(skip.next.lexeme, '>');
+ expect(skip.next.next, after);
+ }
+
+ void test_compute_gt() {
+ expectTypeParamOrArg(simpleTypeArgument1, '<T>');
+ }
+
+ void test_compute_gt_eq() {
+ expectTypeParamOrArg(simpleTypeArgument1GtEq, '<T>=');
+ }
+
+ void test_compute_gt_gt() {
+ String source = '<C<T>>';
+ Token start = scan(source).next.next;
+ expect(start.lexeme, 'C');
+ Token gtgt = start.next.next.next;
+ expect(gtgt.lexeme, '>>');
+
+ expect(computeTypeParamOrArg(start, false), simpleTypeArgument1GtGt);
+ validateTokens(start);
+ }
+
+ void testParseArguments(TypeParamOrArgInfo typeArg, String source,
+ [String next]) {
+ final Token start = scanString('before $source after').tokens;
+ final Token after = start.next.next.next.next;
+ expect(after.lexeme, 'after');
final TypeInfoListener listener = new TypeInfoListener();
- expect(simpleTypeArgument1.parseArguments(start, new Parser(listener)), gt);
+ var token = typeArg.parseArguments(start, new Parser(listener));
+ validateTokens(start);
+ expect(token.lexeme, '>');
+ token = token.next;
+ if (next != null) {
+ expect(token.lexeme, next);
+ token = token.next;
+ }
+ expect(token, after);
expect(listener.calls, [
'beginTypeArguments <',
'handleIdentifier T typeReference',
@@ -977,13 +1101,34 @@
expect(listener.errors, isNull);
}
- void test_simple_parseVariables() {
- final Token start = scanString('before <T> after').tokens;
- final Token gt = start.next.next.next;
- expect(gt.lexeme, '>');
+ void test_parseArguments_gt() {
+ testParseArguments(simpleTypeArgument1, '<T>');
+ }
+
+ void test_parseArguments_gt_eq() {
+ testParseArguments(simpleTypeArgument1GtEq, '<T>=', '=');
+ }
+
+ void test_parseArguments_gt_gt() {
+ testParseArguments(simpleTypeArgument1GtGt, '<T>>', '>');
+ }
+
+ void testParseVariables(TypeParamOrArgInfo typeParam, String source,
+ [String next]) {
+ final Token start = scanString('before $source after').tokens;
+ final Token after = start.next.next.next.next;
+ expect(after.lexeme, 'after');
final TypeInfoListener listener = new TypeInfoListener();
- expect(simpleTypeArgument1.parseVariables(start, new Parser(listener)), gt);
+ Token token = typeParam.parseVariables(start, new Parser(listener));
+ validateTokens(start);
+ expect(token.lexeme, '>');
+ token = token.next;
+ if (next != null) {
+ expect(token.lexeme, next);
+ token = token.next;
+ }
+ expect(token, after);
expect(listener.calls, [
'beginTypeVariables <',
'beginMetadataStar T',
@@ -998,34 +1143,21 @@
expect(listener.errors, isNull);
}
- void test_computeTypeParamOrArg_basic() {
- expectTypeParamOrArg(noTypeParamOrArg, '');
- expectTypeParamOrArg(noTypeParamOrArg, 'a');
- expectTypeParamOrArg(noTypeParamOrArg, 'a b');
- expectTypeParamOrArg(noTypeParamOrArg, '<');
- expectTypeParamOrArg(noTypeParamOrArg, '< b');
- expectTypeParamOrArg(noTypeParamOrArg, '< 3 >');
+ void test_parseVariables_gt() {
+ testParseVariables(simpleTypeArgument1, '<T>');
}
- void test_computeTypeParamOrArg_simple() {
- expectTypeParamOrArg(simpleTypeArgument1, '<T>');
+ void test_parseVariables_gt_eq() {
+ testParseVariables(simpleTypeArgument1GtEq, '<T>=', '=');
}
- void test_computeTypeParamOrArg_simple2() {
- expectTypeParamOrArg(simpleTypeArgument1GtEq, '<T>=', inDeclaration: true);
+ void test_parseVariables_gt_gt() {
+ testParseVariables(simpleTypeArgument1GtGt, '<T>>', '>');
}
+}
- void test_computeTypeParamOrArg_simple_nested() {
- String source = '<C<T>>';
- Token start = scan(source).next.next;
- expect(start.lexeme, 'C');
- Token gtgt = start.next.next.next;
- expect(gtgt.lexeme, '>>');
-
- TypeParamOrArgInfo typeVarInfo = computeTypeParamOrArg(start, false, gtgt);
- expect(typeVarInfo, simpleTypeArgument1GtGt, reason: source);
- }
-
+@reflectiveTest
+class TypeParamOrArgInfoTest {
void test_computeTypeArg_complex() {
expectComplexTypeArg('<S,T>', expectedCalls: [
'beginTypeArguments <',
@@ -1159,10 +1291,24 @@
expectComplexTypeArg('<S T>',
inDeclaration: true, expectedErrors: [error(codeExpectedButGot, 3, 1)]);
expectComplexTypeArg('<S',
- inDeclaration: true, expectedErrors: [error(codeExpectedButGot, 2, 0)]);
+ inDeclaration: true,
+ expectedErrors: [error(codeExpectedAfterButGot, 1, 1)]);
expectComplexTypeArg('<@Foo S', inDeclaration: true, expectedErrors: [
error(codeUnexpectedToken, 1, 1),
- error(codeExpectedButGot, 7, 0)
+ error(codeExpectedAfterButGot, 6, 1)
+ ]);
+ expectComplexTypeArg('<S<T', inDeclaration: true, expectedErrors: [
+ error(codeExpectedAfterButGot, 3, 1)
+ ], expectedCalls: [
+ 'beginTypeArguments <',
+ 'handleIdentifier S typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier T typeReference',
+ 'handleNoTypeArguments ',
+ 'handleType T ',
+ 'endTypeArguments 1 < >',
+ 'handleType S ',
+ 'endTypeArguments 1 < >'
]);
}
@@ -1369,10 +1515,36 @@
error(codeExpectedButGot, 3, 1),
]);
expectComplexTypeParam('<S', inDeclaration: true, expectedErrors: [
- error(codeExpectedButGot, 2, 0),
+ error(codeExpectedAfterButGot, 1, 1),
]);
expectComplexTypeParam('<@Foo S',
- inDeclaration: true, expectedErrors: [error(codeExpectedButGot, 7, 0)]);
+ inDeclaration: true,
+ expectedErrors: [error(codeExpectedAfterButGot, 6, 1)]);
+ expectComplexTypeParam('<@Foo }',
+ inDeclaration: true,
+ expectedAfter: '}',
+ expectedErrors: [error(codeExpectedIdentifier, 6, 1)]);
+ expectComplexTypeParam('<S extends List<T fieldName;',
+ inDeclaration: true,
+ expectedErrors: [error(codeExpectedAfterButGot, 16, 1)],
+ expectedAfter: 'fieldName',
+ expectedCalls: [
+ 'beginTypeVariables <',
+ 'beginMetadataStar S',
+ 'endMetadataStar 0',
+ 'handleIdentifier S typeVariableDeclaration',
+ 'beginTypeVariable S',
+ 'handleTypeVariablesDefined > 1',
+ 'handleIdentifier List typeReference',
+ 'beginTypeArguments <',
+ 'handleIdentifier T typeReference',
+ 'handleNoTypeArguments fieldName',
+ 'handleType T fieldName',
+ 'endTypeArguments 1 < >',
+ 'handleType List fieldName',
+ 'endTypeVariable fieldName 0 extends',
+ 'endTypeVariables < >',
+ ]);
}
void test_computeTypeParam_31846() {
@@ -1447,7 +1619,7 @@
'endMetadataStar 0',
'handleIdentifier T typeVariableDeclaration',
'beginTypeVariable T',
- 'handleTypeVariablesDefined >> 1',
+ 'handleTypeVariablesDefined > 1',
'handleIdentifier List typeReference',
'beginTypeArguments <',
'handleIdentifier List typeReference',
@@ -1494,14 +1666,7 @@
void expectNestedInfo(expectedInfo, String source) {
expect(source.startsWith('<'), isTrue);
Token start = scan(source).next;
- Token innerEndGroup = start;
- while (!innerEndGroup.next.isEof) {
- innerEndGroup = innerEndGroup.next;
- }
- if (!optional('>>', innerEndGroup)) {
- innerEndGroup = null;
- }
- compute(expectedInfo, source, start, true, innerEndGroup: innerEndGroup);
+ compute(expectedInfo, source, start, true);
}
void expectNestedComplexInfo(String source) {
@@ -1509,10 +1674,9 @@
}
TypeInfo compute(expectedInfo, String source, Token start, bool required,
- {bool inDeclaration = false, Token innerEndGroup}) {
+ {bool inDeclaration = false}) {
int expectedGtGtAndNullEndCount = countGtGtAndNullEnd(start);
- TypeInfo typeInfo =
- computeType(start, required, inDeclaration, innerEndGroup);
+ TypeInfo typeInfo = computeType(start, required, inDeclaration);
expect(typeInfo, expectedInfo, reason: source);
expect(countGtGtAndNullEnd(start), expectedGtGtAndNullEndCount,
reason: 'computeType should not modify the token stream');
@@ -1563,6 +1727,7 @@
expect(typeVarInfo.start, start.next, reason: source);
expectEnd(expectedAfter, typeVarInfo.skip(start));
+ validateTokens(start);
expect(countGtGtAndNullEnd(start), expectedGtGtAndNullEndCount,
reason: 'TypeParamOrArgInfo.skipType'
' should not modify the token stream');
@@ -1570,6 +1735,7 @@
TypeInfoListener listener = new TypeInfoListener();
Parser parser = new Parser(listener);
Token actualEnd = typeVarInfo.parseArguments(start, parser);
+ validateTokens(start);
expectEnd(expectedAfter, actualEnd);
if (expectedCalls != null) {
@@ -1593,6 +1759,7 @@
expect(typeVarInfo.start, start.next, reason: source);
expectEnd(expectedAfter, typeVarInfo.skip(start));
+ validateTokens(start);
expect(countGtGtAndNullEnd(start), expectedGtGtAndNullEndCount,
reason: 'TypeParamOrArgInfo.skipType'
' should not modify the token stream');
@@ -1600,6 +1767,7 @@
TypeInfoListener listener = new TypeInfoListener(metadataAllowed: true);
Parser parser = new Parser(listener);
Token actualEnd = typeVarInfo.parseVariables(start, parser);
+ validateTokens(start);
expectEnd(expectedAfter, actualEnd);
if (expectedCalls != null) {
@@ -1621,6 +1789,7 @@
expectedInfo, String source, Token start, bool inDeclaration) {
int expectedGtGtAndNullEndCount = countGtGtAndNullEnd(start);
TypeParamOrArgInfo typeVarInfo = computeTypeParamOrArg(start, inDeclaration);
+ validateTokens(start);
expect(typeVarInfo, expectedInfo, reason: source);
expect(countGtGtAndNullEnd(start), expectedGtGtAndNullEndCount,
reason: 'computeTypeParamOrArg should not modify the token stream');
@@ -1656,6 +1825,24 @@
return count;
}
+void validateTokens(Token token) {
+ int count = 0;
+ if (token.isEof && !token.next.isEof) {
+ token = token.next;
+ }
+ while (!token.isEof) {
+ Token next = token.next;
+ expect(token.charOffset, lessThanOrEqualTo(next.charOffset));
+ expect(next.previous, token);
+ if (next is SyntheticToken) {
+ expect(next.beforeSynthetic, token);
+ }
+ expect(count, lessThanOrEqualTo(10000));
+ token = next;
+ ++count;
+ }
+}
+
class TypeInfoListener implements Listener {
final bool metadataAllowed;
List<String> calls = <String>[];
diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status
index b496893..6d1d6c5 100644
--- a/tests/language_2/language_2_analyzer.status
+++ b/tests/language_2/language_2_analyzer.status
@@ -177,6 +177,7 @@
library_ambiguous_test/00: Crash # Error recovery in method body (ambiguous imported name)
library_test/01: Crash # Issue 33686 - No core library found
list_literal_negative_test: Crash # Error recovery in method body (malformed constructor invocation)
+list_literal_syntax_test/06: Crash
main_not_a_function_test: Crash # Issue 33686 - No core library found
metadata_test: Crash # Poor handling of annotation inside typedef
missing_part_of_tag_test/01: Crash # Issue #34043 - Error recovery in outline (part file missing "part of" declaration - see also #33587)