Skip to content

Commit c5fd11b

Browse files
danrubelcommit-bot@chromium.org
danrubel
authored andcommitted
Add support for parsing simple nullable types
... as part of adding NNBD as outlined in dart-lang/language#110 This only supports parsing simple nullable types such as int? and List<int>? while subsequent CLs will add support for parsing more complex types. Change-Id: I144858946cb115755af437299899c2631105bf8c Reviewed-on: https://dart-review.googlesource.com/c/87501 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Dan Rubel <[email protected]>
1 parent 6d4d3c6 commit c5fd11b

File tree

4 files changed

+288
-36
lines changed

4 files changed

+288
-36
lines changed

pkg/front_end/lib/src/fasta/parser/parser.dart

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4489,11 +4489,7 @@ class Parser {
44894489
Token name = beforeName.next;
44904490
if (name.isIdentifier) {
44914491
TypeParamOrArgInfo typeParam = computeTypeParamOrArg(name);
4492-
Token next = name;
4493-
if (typeParam != noTypeParamOrArg) {
4494-
next = typeParam.skip(next);
4495-
}
4496-
next = next.next;
4492+
Token next = typeParam.skip(name).next;
44974493
if (optional('(', next)) {
44984494
if (looksLikeFunctionBody(next.endGroup.next)) {
44994495
return parseFunctionLiteral(
@@ -4873,8 +4869,10 @@ class Parser {
48734869
if (optional('!', token.next)) {
48744870
not = token = token.next;
48754871
}
4876-
// Ignore trailing `?` if there is one as it may be part of an expression
4877-
TypeInfo typeInfo = computeType(token, true).asNonNullableType();
4872+
TypeInfo typeInfo = computeType(token, true);
4873+
if (typeInfo.isConditionalExpressionStart(token, this)) {
4874+
typeInfo = typeInfo.asNonNullable;
4875+
}
48784876
token = typeInfo.ensureTypeNotVoid(token, this);
48794877
listener.handleIsOperator(operator, not);
48804878
return skipChainedAsIsOperators(token);
@@ -4888,8 +4886,10 @@ class Parser {
48884886
Token parseAsOperatorRest(Token token) {
48894887
Token operator = token = token.next;
48904888
assert(optional('as', operator));
4891-
// Ignore trailing `?` if there is one as it may be part of an expression
4892-
TypeInfo typeInfo = computeType(token, true).asNonNullableType();
4889+
TypeInfo typeInfo = computeType(token, true);
4890+
if (typeInfo.isConditionalExpressionStart(token, this)) {
4891+
typeInfo = typeInfo.asNonNullable;
4892+
}
48934893
token = typeInfo.ensureTypeNotVoid(token, this);
48944894
listener.handleAsOperator(operator);
48954895
return skipChainedAsIsOperators(token);
@@ -4908,7 +4908,11 @@ class Parser {
49084908
if (optional('!', next.next)) {
49094909
next = next.next;
49104910
}
4911-
token = computeType(next, true).skipType(next);
4911+
TypeInfo typeInfo = computeType(next, true);
4912+
if (typeInfo.isConditionalExpressionStart(next, this)) {
4913+
typeInfo = typeInfo.asNonNullable;
4914+
}
4915+
token = typeInfo.skipType(next);
49124916
next = token.next;
49134917
value = next.stringValue;
49144918
}
@@ -5029,6 +5033,10 @@ class Parser {
50295033
TypeInfo typeInfo,
50305034
bool onlyParseVariableDeclarationStart = false]) {
50315035
typeInfo ??= computeType(beforeType, false);
5036+
if (typeInfo.isConditionalExpressionStart(beforeType, this)) {
5037+
typeInfo = noType;
5038+
}
5039+
50325040
Token token = typeInfo.skipType(beforeType);
50335041
Token next = token.next;
50345042

pkg/front_end/lib/src/fasta/parser/type_info.dart

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ import 'util.dart' show isOneOf, optional;
1919
abstract class TypeInfo {
2020
/// Return type info representing the receiver without the trailing `?`
2121
/// or the receiver if the receiver does not represent a nullable type.
22-
TypeInfo asNonNullableType();
22+
TypeInfo get asNonNullable;
2323

2424
/// Return `true` if the tokens comprising the type represented by the
2525
/// receiver could be interpreted as a valid standalone expression.
26-
/// For example, `A` or `A.b` could be interpreted as a type references
27-
/// or as expressions, while `A<T>` only looks like a type reference.
26+
/// For example, `A` or `A.b` could be interpreted as type references
27+
/// or expressions, while `A<T>` only looks like a type reference.
2828
bool get couldBeExpression;
2929

3030
/// Call this function when the token after [token] must be a type (not void).
@@ -41,6 +41,13 @@ abstract class TypeInfo {
4141
/// in valid code or during recovery.
4242
Token ensureTypeOrVoid(Token token, Parser parser);
4343

44+
/// Return `true` if the tokens comprising the type represented by the
45+
/// receiver are the start of a conditional expression.
46+
/// For example, `A?` or `A.b?` could be the start of a conditional expression
47+
/// and require arbitrary look ahead to determine if it is,
48+
/// while `A<T>?` cannot be the start of a conditional expression.
49+
bool isConditionalExpressionStart(Token token, Parser parser);
50+
4451
/// Call this function to parse an optional type (not void) after [token].
4552
/// This function will call the appropriate event methods on the [Parser]'s
4653
/// listener to handle the type. This may modify the token stream
@@ -199,13 +206,19 @@ TypeInfo computeType(final Token token, bool required,
199206
// We've seen identifier `<` identifier `>`
200207
next = typeParamOrArg.skip(next).next;
201208
if (!isGeneralizedFunctionType(next)) {
202-
if (required || looksLikeName(next)) {
203-
// identifier `<` identifier `>` identifier
204-
return typeParamOrArg.typeInfo;
209+
if (optional('?', next) && typeParamOrArg == simpleTypeArgument1) {
210+
if (required || looksLikeName(next.next)) {
211+
// identifier `<` identifier `>` `?` identifier
212+
return simpleNullableTypeWith1Argument;
213+
}
205214
} else {
206-
// identifier `<` identifier `>` non-identifier
207-
return noType;
215+
if (required || looksLikeName(next)) {
216+
// identifier `<` identifier `>` identifier
217+
return typeParamOrArg.typeInfo;
218+
}
208219
}
220+
// identifier `<` identifier `>` non-identifier
221+
return noType;
209222
}
210223
}
211224
// TODO(danrubel): Consider adding a const for
@@ -255,7 +268,21 @@ TypeInfo computeType(final Token token, bool required,
255268
.computeIdentifierGFT(required);
256269
}
257270

258-
if (required || looksLikeName(next)) {
271+
if (optional('?', next)) {
272+
if (required) {
273+
// identifier `?`
274+
return simpleNullableType;
275+
} else {
276+
next = next.next;
277+
if (isGeneralizedFunctionType(next)) {
278+
// identifier `?` Function `(`
279+
return simpleNullableType;
280+
} else if (looksLikeName(next)) {
281+
// identifier `?` identifier `=`
282+
return simpleNullableType;
283+
}
284+
}
285+
} else if (required || looksLikeName(next)) {
259286
// identifier identifier
260287
return simpleType;
261288
}

pkg/front_end/lib/src/fasta/parser/type_info_impl.dart

Lines changed: 110 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ import 'util.dart'
3838
/// when there is a single identifier as the type reference.
3939
const TypeInfo simpleType = const SimpleType();
4040

41+
/// [SimpleNullableType] is a specialized [TypeInfo] returned by [computeType]
42+
/// when there is a single identifier followed by `?` as the type reference.
43+
const TypeInfo simpleNullableType = const SimpleNullableType();
44+
4145
/// [PrefixedType] is a specialized [TypeInfo] returned by [computeType]
4246
/// when the type reference is of the form: identifier `.` identifier.
4347
const TypeInfo prefixedType = const PrefixedType();
@@ -60,6 +64,12 @@ const TypeInfo simpleTypeWith1ArgumentGtEq =
6064
const TypeInfo simpleTypeWith1ArgumentGtGt =
6165
const SimpleTypeWith1Argument(simpleTypeArgument1GtGt);
6266

67+
/// [SimpleNullableTypeWith1Argument] is a specialized [TypeInfo] returned by
68+
/// [computeType] when the type reference is of the form:
69+
/// identifier `<` identifier `>` `?`.
70+
const TypeInfo simpleNullableTypeWith1Argument =
71+
const SimpleNullableTypeWith1Argument();
72+
6373
/// [SimpleTypeArgument1] is a specialized [TypeParamOrArgInfo] returned by
6474
/// [computeTypeParamOrArg] when the type reference is of the form:
6575
/// `<` identifier `>`.
@@ -82,10 +92,10 @@ class NoType implements TypeInfo {
8292
const NoType();
8393

8494
@override
85-
bool get couldBeExpression => false;
95+
TypeInfo get asNonNullable => this;
8696

8797
@override
88-
TypeInfo asNonNullableType() => this;
98+
bool get couldBeExpression => false;
8999

90100
@override
91101
Token ensureTypeNotVoid(Token token, Parser parser) {
@@ -99,6 +109,9 @@ class NoType implements TypeInfo {
99109
Token ensureTypeOrVoid(Token token, Parser parser) =>
100110
ensureTypeNotVoid(token, parser);
101111

112+
@override
113+
bool isConditionalExpressionStart(Token token, Parser parser) => false;
114+
102115
@override
103116
Token parseTypeNotVoid(Token token, Parser parser) =>
104117
parseType(token, parser);
@@ -120,10 +133,10 @@ class PrefixedType implements TypeInfo {
120133
const PrefixedType();
121134

122135
@override
123-
bool get couldBeExpression => true;
136+
TypeInfo get asNonNullable => this;
124137

125138
@override
126-
TypeInfo asNonNullableType() => this;
139+
bool get couldBeExpression => true;
127140

128141
@override
129142
Token ensureTypeNotVoid(Token token, Parser parser) =>
@@ -133,6 +146,9 @@ class PrefixedType implements TypeInfo {
133146
Token ensureTypeOrVoid(Token token, Parser parser) =>
134147
parseType(token, parser);
135148

149+
@override
150+
bool isConditionalExpressionStart(Token token, Parser parser) => false;
151+
136152
@override
137153
Token parseTypeNotVoid(Token token, Parser parser) =>
138154
parseType(token, parser);
@@ -164,17 +180,44 @@ class PrefixedType implements TypeInfo {
164180
}
165181
}
166182

183+
/// See documentation on the [simpleNullableTypeWith1Argument] const.
184+
class SimpleNullableTypeWith1Argument extends SimpleTypeWith1Argument {
185+
const SimpleNullableTypeWith1Argument() : super(simpleTypeArgument1);
186+
187+
@override
188+
TypeInfo get asNonNullable => simpleTypeWith1Argument;
189+
190+
@override
191+
bool isConditionalExpressionStart(Token token, Parser parser) =>
192+
isConditionalThenExpression(skipType(token), parser);
193+
194+
@override
195+
Token parseTypeRest(Token start, Token token, Parser parser) {
196+
token = token.next;
197+
assert(optional('?', token));
198+
parser.listener.handleType(start, token);
199+
return token;
200+
}
201+
202+
@override
203+
Token skipType(Token token) {
204+
token = super.skipType(token).next;
205+
assert(optional('?', token));
206+
return token;
207+
}
208+
}
209+
167210
/// See documentation on the [simpleTypeWith1Argument] const.
168211
class SimpleTypeWith1Argument implements TypeInfo {
169212
final TypeParamOrArgInfo typeArg;
170213

171214
const SimpleTypeWith1Argument(this.typeArg);
172215

173216
@override
174-
bool get couldBeExpression => false;
217+
TypeInfo get asNonNullable => this;
175218

176219
@override
177-
TypeInfo asNonNullableType() => this;
220+
bool get couldBeExpression => false;
178221

179222
@override
180223
Token ensureTypeNotVoid(Token token, Parser parser) =>
@@ -184,6 +227,9 @@ class SimpleTypeWith1Argument implements TypeInfo {
184227
Token ensureTypeOrVoid(Token token, Parser parser) =>
185228
parseType(token, parser);
186229

230+
@override
231+
bool isConditionalExpressionStart(Token token, Parser parser) => false;
232+
187233
@override
188234
Token parseTypeNotVoid(Token token, Parser parser) =>
189235
parseType(token, parser);
@@ -210,15 +256,40 @@ class SimpleTypeWith1Argument implements TypeInfo {
210256
}
211257
}
212258

259+
/// See documentation on the [simpleNullableType] const.
260+
class SimpleNullableType extends SimpleType {
261+
const SimpleNullableType();
262+
263+
@override
264+
TypeInfo get asNonNullable => simpleType;
265+
266+
@override
267+
bool isConditionalExpressionStart(Token token, Parser parser) =>
268+
isConditionalThenExpression(skipType(token), parser);
269+
270+
@override
271+
Token parseTypeRest(Token start, Parser parser) {
272+
Token token = start.next;
273+
assert(optional('?', token));
274+
parser.listener.handleType(start, token);
275+
return token;
276+
}
277+
278+
@override
279+
Token skipType(Token token) {
280+
return token.next.next;
281+
}
282+
}
283+
213284
/// See documentation on the [simpleType] const.
214285
class SimpleType implements TypeInfo {
215286
const SimpleType();
216287

217288
@override
218-
bool get couldBeExpression => true;
289+
TypeInfo get asNonNullable => this;
219290

220291
@override
221-
TypeInfo asNonNullableType() => this;
292+
bool get couldBeExpression => true;
222293

223294
@override
224295
Token ensureTypeNotVoid(Token token, Parser parser) =>
@@ -228,6 +299,9 @@ class SimpleType implements TypeInfo {
228299
Token ensureTypeOrVoid(Token token, Parser parser) =>
229300
parseType(token, parser);
230301

302+
@override
303+
bool isConditionalExpressionStart(Token token, Parser parser) => false;
304+
231305
@override
232306
Token parseTypeNotVoid(Token token, Parser parser) =>
233307
parseType(token, parser);
@@ -257,10 +331,10 @@ class VoidType implements TypeInfo {
257331
const VoidType();
258332

259333
@override
260-
bool get couldBeExpression => false;
334+
TypeInfo get asNonNullable => this;
261335

262336
@override
263-
TypeInfo asNonNullableType() => this;
337+
bool get couldBeExpression => false;
264338

265339
@override
266340
Token ensureTypeNotVoid(Token token, Parser parser) {
@@ -273,6 +347,9 @@ class VoidType implements TypeInfo {
273347
Token ensureTypeOrVoid(Token token, Parser parser) =>
274348
parseType(token, parser);
275349

350+
@override
351+
bool isConditionalExpressionStart(Token token, Parser parser) => false;
352+
276353
@override
277354
Token parseTypeNotVoid(Token token, Parser parser) =>
278355
ensureTypeNotVoid(token, parser);
@@ -337,13 +414,13 @@ class ComplexTypeInfo implements TypeInfo {
337414
: this.start = beforeStart.next;
338415

339416
@override
340-
bool get couldBeExpression => false;
341-
342-
@override
343-
TypeInfo asNonNullableType() {
417+
TypeInfo get asNonNullable {
344418
return this;
345419
}
346420

421+
@override
422+
bool get couldBeExpression => false;
423+
347424
@override
348425
Token ensureTypeNotVoid(Token token, Parser parser) =>
349426
parseType(token, parser);
@@ -352,6 +429,12 @@ class ComplexTypeInfo implements TypeInfo {
352429
Token ensureTypeOrVoid(Token token, Parser parser) =>
353430
parseType(token, parser);
354431

432+
@override
433+
bool isConditionalExpressionStart(Token token, Parser parser) {
434+
//return isConditionalThenExpression(token.next.next, parser);
435+
return false;
436+
}
437+
355438
@override
356439
Token parseTypeNotVoid(Token token, Parser parser) =>
357440
parseType(token, parser);
@@ -1061,3 +1144,16 @@ Token splitCloser(Token closer) {
10611144
}
10621145
return null;
10631146
}
1147+
1148+
/// Return `true` if the tokens after [token]
1149+
/// represent a valid expression followed by a `:`.
1150+
bool isConditionalThenExpression(Token token, Parser parser) {
1151+
// TODO(danrubel): Consider adding checks for simple situations
1152+
// before resorting to heavy weight lookahead.
1153+
1154+
final originalListener = parser.listener;
1155+
parser.listener = new ForwardingListener();
1156+
token = parser.parseExpression(token);
1157+
parser.listener = originalListener;
1158+
return optional(':', token.next);
1159+
}

0 commit comments

Comments
 (0)