@@ -36,7 +36,7 @@ bool enumClassAllowsAnyValue(String name) {
3636
3737String generateDartForTypes (List <AstNode > types) {
3838 final buffer = IndentableStringBuffer ();
39- _getSorted (types).forEach ((t) => _writeType (buffer, t));
39+ _getSortedUnique (types).forEach ((t) => _writeType (buffer, t));
4040 final formattedCode = _formatCode (buffer.toString ());
4141 return formattedCode.trim () + '\n ' ; // Ensure a single trailing newline.
4242}
@@ -95,9 +95,27 @@ List<Field> _getAllFields(Interface interface) {
9595 .toList ();
9696}
9797
98- /// Returns a copy of the list sorted by name.
99- List <AstNode > _getSorted (List <AstNode > items) {
100- final sortedList = items.toList ();
98+ /// Returns a copy of the list sorted by name with duplicates (by name+type) removed.
99+ List <AstNode > _getSortedUnique (List <AstNode > items) {
100+ final uniqueByName = < String , AstNode > {};
101+ items.forEach ((item) {
102+ // It's fine to have the same name used for different types (eg. namespace +
103+ // type alias) but some types are just duplicated entirely in the spec in
104+ // different positions which should not be emitted twice.
105+ final nameTypeKey = '${item .name }|${item .runtimeType }' ;
106+ if (uniqueByName.containsKey (nameTypeKey)) {
107+ // At the time of writing, there were two duplicated types:
108+ // - TextDocumentSyncKind (same defintion in both places)
109+ // - TextDocumentSyncOptions (first definition is just a subset)
110+ // If this list grows, consider handling this better - or try to have the
111+ // spec updated to be unambigious.
112+ print ('WARN: More than one definition for $nameTypeKey .' );
113+ }
114+
115+ // Keep the last one as in some cases the first definition is less specific.
116+ uniqueByName[nameTypeKey] = item;
117+ });
118+ final sortedList = uniqueByName.values.toList ();
101119 sortedList.sort ((item1, item2) => item1.name.compareTo (item2.name));
102120 return sortedList;
103121}
@@ -199,7 +217,7 @@ void _writeCanParseMethod(IndentableStringBuffer buffer, Interface interface) {
199217 }
200218 buffer.write ('!(' );
201219 _writeTypeCheckCondition (
202- buffer, "obj['${field .name }']" , field.type, 'reporter' );
220+ buffer, interface , "obj['${field .name }']" , field.type, 'reporter' );
203221 buffer
204222 ..write (')) {' )
205223 ..indent ()
@@ -302,7 +320,7 @@ void _writeEnumClass(IndentableStringBuffer buffer, Namespace namespace) {
302320 ..indent ();
303321 if (allowsAnyValue) {
304322 buffer.writeIndentedln ('return ' );
305- _writeTypeCheckCondition (buffer, 'obj' , typeOfValues, 'reporter' );
323+ _writeTypeCheckCondition (buffer, null , 'obj' , typeOfValues, 'reporter' );
306324 buffer.writeln (';' );
307325 } else {
308326 buffer
@@ -445,7 +463,8 @@ void _writeFromJsonCodeForUnion(
445463
446464 // Dynamic matches all type checks, so only emit it if required.
447465 if (! isDynamic) {
448- _writeTypeCheckCondition (buffer, valueCode, type, 'nullLspJsonReporter' );
466+ _writeTypeCheckCondition (
467+ buffer, null , valueCode, type, 'nullLspJsonReporter' );
449468 buffer.write (' ? ' );
450469 }
451470
@@ -609,7 +628,7 @@ void _writeMember(IndentableStringBuffer buffer, Member member) {
609628}
610629
611630void _writeMembers (IndentableStringBuffer buffer, List <Member > members) {
612- _getSorted (members).forEach ((m) => _writeMember (buffer, m));
631+ _getSortedUnique (members).forEach ((m) => _writeMember (buffer, m));
613632}
614633
615634void _writeToJsonFieldsForResponseMessage (
@@ -685,8 +704,8 @@ void _writeType(IndentableStringBuffer buffer, AstNode type) {
685704 }
686705}
687706
688- void _writeTypeCheckCondition (IndentableStringBuffer buffer, String valueCode,
689- TypeBase type, String reporter) {
707+ void _writeTypeCheckCondition (IndentableStringBuffer buffer,
708+ Interface interface , String valueCode, TypeBase type, String reporter) {
690709 type = resolveTypeAlias (type);
691710
692711 final dartType = type.dartType;
@@ -703,17 +722,20 @@ void _writeTypeCheckCondition(IndentableStringBuffer buffer, String valueCode,
703722 // TODO(dantup): If we're happy to assume we never have two lists in a union
704723 // we could skip this bit.
705724 buffer.write (' && ($valueCode .every((item) => ' );
706- _writeTypeCheckCondition (buffer, 'item' , type.elementType, reporter);
725+ _writeTypeCheckCondition (
726+ buffer, interface , 'item' , type.elementType, reporter);
707727 buffer.write ('))' );
708728 }
709729 buffer.write (')' );
710730 } else if (type is MapType ) {
711731 buffer.write ('($valueCode is Map' );
712732 if (fullDartType != 'dynamic' ) {
713733 buffer..write (' && (' )..write ('$valueCode .keys.every((item) => ' );
714- _writeTypeCheckCondition (buffer, 'item' , type.indexType, reporter);
734+ _writeTypeCheckCondition (
735+ buffer, interface , 'item' , type.indexType, reporter);
715736 buffer..write ('&& $valueCode .values.every((item) => ' );
716- _writeTypeCheckCondition (buffer, 'item' , type.valueType, reporter);
737+ _writeTypeCheckCondition (
738+ buffer, interface , 'item' , type.valueType, reporter);
717739 buffer.write (')))' );
718740 }
719741 buffer.write (')' );
@@ -724,9 +746,18 @@ void _writeTypeCheckCondition(IndentableStringBuffer buffer, String valueCode,
724746 if (i != 0 ) {
725747 buffer.write (' || ' );
726748 }
727- _writeTypeCheckCondition (buffer, valueCode, type.types[i], reporter);
749+ _writeTypeCheckCondition (
750+ buffer, interface , valueCode, type.types[i], reporter);
728751 }
729752 buffer.write (')' );
753+ } else if (interface != null &&
754+ interface .typeArgs != null &&
755+ interface .typeArgs.any ((typeArg) => typeArg.lexeme == fullDartType)) {
756+ final comment = '/* $fullDartType .canParse($valueCode ) */' ;
757+ print (
758+ 'WARN: Unable to write a type check for $valueCode with generic type $fullDartType . '
759+ 'Please review the generated code annotated with $comment ' );
760+ buffer.write ('true $comment ' );
730761 } else {
731762 throw 'Unable to type check $valueCode against $fullDartType ' ;
732763 }
0 commit comments