@@ -44,23 +44,14 @@ class FunctionType extends Type {
44
44
}
45
45
}
46
46
47
- class NamedType extends Type {
47
+ class NamedType {
48
48
final String name;
49
- final Type innerType;
50
-
51
- NamedType (this .name, this .innerType) : super ._();
49
+ final Type type;
52
50
53
- @override
54
- NamedType ? recursivelyDemote ({required bool covariant }) {
55
- Type ? newInnerType = innerType.recursivelyDemote (covariant : covariant );
56
- if (newInnerType == null ) return null ;
57
- return NamedType (name, newInnerType);
58
- }
51
+ NamedType (this .name, this .type);
59
52
60
53
@override
61
- String _toString ({required bool allowSuffixes}) {
62
- return '$innerType $name ' ;
63
- }
54
+ String toString () => '$type $name ' ;
64
55
}
65
56
66
57
/// Representation of a "simple" type suitable for unit testing of code in the
@@ -94,6 +85,16 @@ class NonFunctionType extends Type {
94
85
}
95
86
}
96
87
88
+ /// Exception thrown if a type fails to parse properly.
89
+ class ParseError extends Error {
90
+ final String message;
91
+
92
+ ParseError (this .message);
93
+
94
+ @override
95
+ String toString () => message;
96
+ }
97
+
97
98
/// Representation of a promoted type parameter type suitable for unit testing
98
99
/// of code in the `_fe_analyzer_shared` package. A promoted type parameter is
99
100
/// often written using the syntax `a&b` , where `a` is the type parameter and
@@ -169,10 +170,10 @@ class RecordType extends Type {
169
170
170
171
List <NamedType >? newNamed;
171
172
for (var i = 0 ; i < named.length; i++ ) {
172
- var newType = named[i].recursivelyDemote (covariant : covariant );
173
+ var newType = named[i].type. recursivelyDemote (covariant : covariant );
173
174
if (newType != null ) {
174
175
newNamed ?? = named.toList ();
175
- newNamed[i] = newType;
176
+ newNamed[i] = NamedType (named[i].name, newType) ;
176
177
}
177
178
}
178
179
@@ -308,7 +309,7 @@ class UnknownType extends Type {
308
309
309
310
class _TypeParser {
310
311
static final _typeTokenizationRegexp =
311
- RegExp (_identifierPattern + r'|\(|\)|<|>|,|\?|\*|&' );
312
+ RegExp (_identifierPattern + r'|\(|\)|<|>|,|\?|\*|&|{|} ' );
312
313
313
314
static const _identifierPattern = '[_a-zA-Z][_a-zA-Z0-9]*' ;
314
315
@@ -329,7 +330,61 @@ class _TypeParser {
329
330
}
330
331
331
332
Never _parseFailure (String message) {
332
- fail ('Error parsing type `$_typeStr ` at token $_currentToken : $message ' );
333
+ throw ParseError (
334
+ 'Error parsing type `$_typeStr ` at token $_currentToken : $message ' );
335
+ }
336
+
337
+ List <NamedType > _parseRecordTypeNamedFields () {
338
+ assert (_currentToken == '{' );
339
+ _next ();
340
+ var namedTypes = < NamedType > [];
341
+ while (_currentToken != '}' ) {
342
+ var type = _parseType ();
343
+ var name = _currentToken;
344
+ if (_identifierRegexp.matchAsPrefix (name) == null ) {
345
+ _parseFailure ('Expected an identifier' );
346
+ }
347
+ namedTypes.add (NamedType (name, type));
348
+ _next ();
349
+ if (_currentToken == ',' ) {
350
+ _next ();
351
+ continue ;
352
+ }
353
+ if (_currentToken == '}' ) {
354
+ break ;
355
+ }
356
+ _parseFailure ('Expected `}` or `,`' );
357
+ }
358
+ if (namedTypes.isEmpty) {
359
+ _parseFailure ('Must have at least one named type between {}' );
360
+ }
361
+ _next ();
362
+ return namedTypes;
363
+ }
364
+
365
+ Type _parseRecordTypeRest (List <Type > positionalTypes) {
366
+ List <NamedType >? namedTypes;
367
+ while (_currentToken != ')' ) {
368
+ if (_currentToken == '{' ) {
369
+ namedTypes = _parseRecordTypeNamedFields ();
370
+ if (_currentToken != ')' ) {
371
+ _parseFailure ('Expected `)`' );
372
+ }
373
+ break ;
374
+ }
375
+ positionalTypes.add (_parseType ());
376
+ if (_currentToken == ',' ) {
377
+ _next ();
378
+ continue ;
379
+ }
380
+ if (_currentToken == ')' ) {
381
+ break ;
382
+ }
383
+ _parseFailure ('Expected `)` or `,`' );
384
+ }
385
+ _next ();
386
+ return RecordType (
387
+ positional: positionalTypes, named: namedTypes ?? const []);
333
388
}
334
389
335
390
Type ? _parseSuffix (Type type) {
@@ -373,6 +428,13 @@ class _TypeParser {
373
428
// unsuffixedType := identifier typeArgs?
374
429
// | `?`
375
430
// | `(` type `)`
431
+ // | `(` recordTypeFields `,` recordTypeNamedFields `)`
432
+ // | `(` recordTypeFields `,`? `)`
433
+ // | `(` recordTypeNamedFields? `)`
434
+ // recordTypeFields := type (`,` type)*
435
+ // recordTypeNamedFields := `{` recordTypeNamedField
436
+ // (`,` recordTypeNamedField)* `,`? `}`
437
+ // recordTypeNamedField := type identifier
376
438
// typeArgs := `<` type (`,` type)* `>`
377
439
// nullability := (`?` | `*`)?
378
440
// suffix := `Function` `(` type (`,` type)* `)`
@@ -396,9 +458,16 @@ class _TypeParser {
396
458
}
397
459
if (_currentToken == '(' ) {
398
460
_next ();
461
+ if (_currentToken == ')' || _currentToken == '{' ) {
462
+ return _parseRecordTypeRest ([]);
463
+ }
399
464
var type = _parseType ();
465
+ if (_currentToken == ',' ) {
466
+ _next ();
467
+ return _parseRecordTypeRest ([type]);
468
+ }
400
469
if (_currentToken != ')' ) {
401
- _parseFailure ('Expected `)`' );
470
+ _parseFailure ('Expected `)` or `,` ' );
402
471
}
403
472
_next ();
404
473
return type;
@@ -431,7 +500,7 @@ class _TypeParser {
431
500
var parser = _TypeParser ._(typeStr, _tokenizeTypeStr (typeStr));
432
501
var result = parser._parseType ();
433
502
if (parser._currentToken != '<END>' ) {
434
- fail ('Extra tokens after parsing type `$typeStr `: '
503
+ throw ParseError ('Extra tokens after parsing type `$typeStr `: '
435
504
'${parser ._tokens .sublist (parser ._i , parser ._tokens .length - 1 )}' );
436
505
}
437
506
return result;
@@ -443,14 +512,16 @@ class _TypeParser {
443
512
for (var match in _typeTokenizationRegexp.allMatches (typeStr)) {
444
513
var extraChars = typeStr.substring (lastMatchEnd, match.start).trim ();
445
514
if (extraChars.isNotEmpty) {
446
- fail ('Unrecognized character(s) in type `$typeStr `: $extraChars ' );
515
+ throw ParseError (
516
+ 'Unrecognized character(s) in type `$typeStr `: $extraChars ' );
447
517
}
448
518
result.add (typeStr.substring (match.start, match.end));
449
519
lastMatchEnd = match.end;
450
520
}
451
521
var extraChars = typeStr.substring (lastMatchEnd).trim ();
452
522
if (extraChars.isNotEmpty) {
453
- fail ('Unrecognized character(s) in type `$typeStr `: $extraChars ' );
523
+ throw ParseError (
524
+ 'Unrecognized character(s) in type `$typeStr `: $extraChars ' );
454
525
}
455
526
result.add ('<END>' );
456
527
return result;
0 commit comments