@@ -44,23 +44,14 @@ class FunctionType extends Type {
4444 }
4545}
4646
47- class NamedType extends Type {
47+ class NamedType {
4848 final String name;
49- final Type innerType;
50-
51- NamedType (this .name, this .innerType) : super ._();
49+ final Type type;
5250
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);
5952
6053 @override
61- String _toString ({required bool allowSuffixes}) {
62- return '$innerType $name ' ;
63- }
54+ String toString () => '$type $name ' ;
6455}
6556
6657/// Representation of a "simple" type suitable for unit testing of code in the
@@ -94,6 +85,16 @@ class NonFunctionType extends Type {
9485 }
9586}
9687
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+
9798/// Representation of a promoted type parameter type suitable for unit testing
9899/// of code in the `_fe_analyzer_shared` package. A promoted type parameter is
99100/// often written using the syntax `a&b` , where `a` is the type parameter and
@@ -169,10 +170,10 @@ class RecordType extends Type {
169170
170171 List <NamedType >? newNamed;
171172 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 );
173174 if (newType != null ) {
174175 newNamed ?? = named.toList ();
175- newNamed[i] = newType;
176+ newNamed[i] = NamedType (named[i].name, newType) ;
176177 }
177178 }
178179
@@ -308,7 +309,7 @@ class UnknownType extends Type {
308309
309310class _TypeParser {
310311 static final _typeTokenizationRegexp =
311- RegExp (_identifierPattern + r'|\(|\)|<|>|,|\?|\*|&' );
312+ RegExp (_identifierPattern + r'|\(|\)|<|>|,|\?|\*|&|{|} ' );
312313
313314 static const _identifierPattern = '[_a-zA-Z][_a-zA-Z0-9]*' ;
314315
@@ -329,7 +330,61 @@ class _TypeParser {
329330 }
330331
331332 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 []);
333388 }
334389
335390 Type ? _parseSuffix (Type type) {
@@ -373,6 +428,13 @@ class _TypeParser {
373428 // unsuffixedType := identifier typeArgs?
374429 // | `?`
375430 // | `(` type `)`
431+ // | `(` recordTypeFields `,` recordTypeNamedFields `)`
432+ // | `(` recordTypeFields `,`? `)`
433+ // | `(` recordTypeNamedFields? `)`
434+ // recordTypeFields := type (`,` type)*
435+ // recordTypeNamedFields := `{` recordTypeNamedField
436+ // (`,` recordTypeNamedField)* `,`? `}`
437+ // recordTypeNamedField := type identifier
376438 // typeArgs := `<` type (`,` type)* `>`
377439 // nullability := (`?` | `*`)?
378440 // suffix := `Function` `(` type (`,` type)* `)`
@@ -396,9 +458,16 @@ class _TypeParser {
396458 }
397459 if (_currentToken == '(' ) {
398460 _next ();
461+ if (_currentToken == ')' || _currentToken == '{' ) {
462+ return _parseRecordTypeRest ([]);
463+ }
399464 var type = _parseType ();
465+ if (_currentToken == ',' ) {
466+ _next ();
467+ return _parseRecordTypeRest ([type]);
468+ }
400469 if (_currentToken != ')' ) {
401- _parseFailure ('Expected `)`' );
470+ _parseFailure ('Expected `)` or `,` ' );
402471 }
403472 _next ();
404473 return type;
@@ -431,7 +500,7 @@ class _TypeParser {
431500 var parser = _TypeParser ._(typeStr, _tokenizeTypeStr (typeStr));
432501 var result = parser._parseType ();
433502 if (parser._currentToken != '<END>' ) {
434- fail ('Extra tokens after parsing type `$typeStr `: '
503+ throw ParseError ('Extra tokens after parsing type `$typeStr `: '
435504 '${parser ._tokens .sublist (parser ._i , parser ._tokens .length - 1 )}' );
436505 }
437506 return result;
@@ -443,14 +512,16 @@ class _TypeParser {
443512 for (var match in _typeTokenizationRegexp.allMatches (typeStr)) {
444513 var extraChars = typeStr.substring (lastMatchEnd, match.start).trim ();
445514 if (extraChars.isNotEmpty) {
446- fail ('Unrecognized character(s) in type `$typeStr `: $extraChars ' );
515+ throw ParseError (
516+ 'Unrecognized character(s) in type `$typeStr `: $extraChars ' );
447517 }
448518 result.add (typeStr.substring (match.start, match.end));
449519 lastMatchEnd = match.end;
450520 }
451521 var extraChars = typeStr.substring (lastMatchEnd).trim ();
452522 if (extraChars.isNotEmpty) {
453- fail ('Unrecognized character(s) in type `$typeStr `: $extraChars ' );
523+ throw ParseError (
524+ 'Unrecognized character(s) in type `$typeStr `: $extraChars ' );
454525 }
455526 result.add ('<END>' );
456527 return result;
0 commit comments