Revise is/as parsing for nullable types
Change-Id: I93cd4be70882ee318ea0f08848a23b4acdf35309
Reviewed-on: https://dart-review.googlesource.com/c/88302
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Dan Rubel <danrubel@google.com>
diff --git a/pkg/front_end/lib/src/fasta/parser/parser.dart b/pkg/front_end/lib/src/fasta/parser/parser.dart
index eb45e33..1938d8c 100644
--- a/pkg/front_end/lib/src/fasta/parser/parser.dart
+++ b/pkg/front_end/lib/src/fasta/parser/parser.dart
@@ -100,6 +100,7 @@
isLetter,
isLetterOrDigit,
isOneOf,
+ isOneOfOrEof,
isWhitespace,
optional;
@@ -4881,15 +4882,26 @@
if (optional('!', token.next)) {
not = token = token.next;
}
- TypeInfo typeInfo = computeType(token, true);
- if (typeInfo.isConditionalExpressionStart(token, this)) {
- typeInfo = typeInfo.asNonNullable;
- }
+ TypeInfo typeInfo = computeTypeAfterIsOrAs(token);
token = typeInfo.ensureTypeNotVoid(token, this);
listener.handleIsOperator(operator, not);
return skipChainedAsIsOperators(token);
}
+ TypeInfo computeTypeAfterIsOrAs(Token token) {
+ TypeInfo typeInfo = computeType(token, true);
+ if (typeInfo.isNullable) {
+ Token next = typeInfo.skipType(token).next;
+ if (!isOneOfOrEof(next, const [')', '?', ';', 'is', 'as'])) {
+ // TODO(danrubel): investigate other situations
+ // where `?` should be considered part of the type info
+ // rather than the start of a conditional expression.
+ typeInfo = typeInfo.asNonNullable;
+ }
+ }
+ return typeInfo;
+ }
+
/// ```
/// typeCast:
/// 'as' type
@@ -4898,10 +4910,7 @@
Token parseAsOperatorRest(Token token) {
Token operator = token = token.next;
assert(optional('as', operator));
- TypeInfo typeInfo = computeType(token, true);
- if (typeInfo.isConditionalExpressionStart(token, this)) {
- typeInfo = typeInfo.asNonNullable;
- }
+ TypeInfo typeInfo = computeTypeAfterIsOrAs(token);
token = typeInfo.ensureTypeNotVoid(token, this);
listener.handleAsOperator(operator);
return skipChainedAsIsOperators(token);
@@ -4920,10 +4929,7 @@
if (optional('!', next.next)) {
next = next.next;
}
- TypeInfo typeInfo = computeType(next, true);
- if (typeInfo.isConditionalExpressionStart(next, this)) {
- typeInfo = typeInfo.asNonNullable;
- }
+ TypeInfo typeInfo = computeTypeAfterIsOrAs(next);
token = typeInfo.skipType(next);
next = token.next;
value = next.stringValue;
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 c42442a..b6e38f5 100644
--- a/pkg/front_end/lib/src/fasta/parser/type_info.dart
+++ b/pkg/front_end/lib/src/fasta/parser/type_info.dart
@@ -27,6 +27,9 @@
/// or expressions, while `A<T>` only looks like a type reference.
bool get couldBeExpression;
+ /// Return true if the receiver has a trailing `?`.
+ bool get isNullable;
+
/// Call this function when the token after [token] must be a type (not void).
/// This function will call the appropriate event methods on the [Parser]'s
/// listener to handle the type, inserting a synthetic type reference if
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 f5042e0..c500e04 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
@@ -99,6 +99,9 @@
bool get couldBeExpression => false;
@override
+ bool get isNullable => false;
+
+ @override
Token ensureTypeNotVoid(Token token, Parser parser) {
parser.reportRecoverableErrorWithToken(
token.next, fasta.templateExpectedType);
@@ -140,6 +143,9 @@
bool get couldBeExpression => true;
@override
+ bool get isNullable => false;
+
+ @override
Token ensureTypeNotVoid(Token token, Parser parser) =>
parseType(token, parser);
@@ -189,6 +195,9 @@
TypeInfo get asNonNullable => simpleTypeWith1Argument;
@override
+ bool get isNullable => true;
+
+ @override
bool isConditionalExpressionStart(Token token, Parser parser) =>
isConditionalThenExpression(skipType(token), parser);
@@ -221,6 +230,9 @@
bool get couldBeExpression => false;
@override
+ bool get isNullable => false;
+
+ @override
Token ensureTypeNotVoid(Token token, Parser parser) =>
parseType(token, parser);
@@ -265,6 +277,9 @@
TypeInfo get asNonNullable => simpleType;
@override
+ bool get isNullable => true;
+
+ @override
bool isConditionalExpressionStart(Token token, Parser parser) =>
isConditionalThenExpression(skipType(token), parser);
@@ -293,6 +308,9 @@
bool get couldBeExpression => true;
@override
+ bool get isNullable => false;
+
+ @override
Token ensureTypeNotVoid(Token token, Parser parser) =>
parseType(token, parser);
@@ -338,6 +356,9 @@
bool get couldBeExpression => false;
@override
+ bool get isNullable => false;
+
+ @override
Token ensureTypeNotVoid(Token token, Parser parser) {
// Report an error, then parse `void` as if it were a type name.
parser.reportRecoverableError(token.next, fasta.messageInvalidVoid);
@@ -423,6 +444,9 @@
bool get couldBeExpression => false;
@override
+ bool get isNullable => false;
+
+ @override
Token ensureTypeNotVoid(Token token, Parser parser) =>
parseType(token, parser);