Updates to pase tuples in LSP spec
Change-Id: Ia1ce7579e954a8000b3460445859a2d221cf1a8c
Reviewed-on: https://dart-review.googlesource.com/c/87441
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Danny Tuppeny <dantup@google.com>
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index f38bab3..2d59611 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -4935,6 +4935,95 @@
String toString() => jsonEncoder.convert(toJson());
}
+class LocationLink implements ToJsonable {
+ LocationLink(this.originSelectionRange, this.targetUri, this.targetRange,
+ this.targetSelectionRange) {
+ if (targetUri == null) {
+ throw 'targetUri is required but was not provided';
+ }
+ if (targetRange == null) {
+ throw 'targetRange is required but was not provided';
+ }
+ }
+ static LocationLink fromJson(Map<String, dynamic> json) {
+ final originSelectionRange = json['originSelectionRange'] != null
+ ? Range.fromJson(json['originSelectionRange'])
+ : null;
+ final targetUri = json['targetUri'];
+ final targetRange = json['targetRange'] != null
+ ? Range.fromJson(json['targetRange'])
+ : null;
+ final targetSelectionRange = json['targetSelectionRange'] != null
+ ? Range.fromJson(json['targetSelectionRange'])
+ : null;
+ return new LocationLink(
+ originSelectionRange, targetUri, targetRange, targetSelectionRange);
+ }
+
+ /// Span of the origin of this link.
+ ///
+ /// Used as the underlined span for mouse interaction. Defaults to the word
+ /// range at the mouse position.
+ final Range originSelectionRange;
+
+ /// The full target range of this link.
+ final Range targetRange;
+
+ /// The span of this link.
+ final Range targetSelectionRange;
+
+ /// The target resource identifier of this link.
+ final String targetUri;
+
+ Map<String, dynamic> toJson() {
+ Map<String, dynamic> __result = {};
+ if (originSelectionRange != null) {
+ __result['originSelectionRange'] = originSelectionRange;
+ }
+ __result['targetUri'] =
+ targetUri ?? (throw 'targetUri is required but was not set');
+ __result['targetRange'] =
+ targetRange ?? (throw 'targetRange is required but was not set');
+ if (targetSelectionRange != null) {
+ __result['targetSelectionRange'] = targetSelectionRange;
+ }
+ return __result;
+ }
+
+ static bool canParse(Object obj) {
+ return obj is Map<String, dynamic> &&
+ obj.containsKey('targetUri') &&
+ obj['targetUri'] is String &&
+ obj.containsKey('targetRange') &&
+ Range.canParse(obj['targetRange']);
+ }
+
+ @override
+ bool operator ==(other) {
+ if (other is LocationLink) {
+ return originSelectionRange == other.originSelectionRange &&
+ targetUri == other.targetUri &&
+ targetRange == other.targetRange &&
+ targetSelectionRange == other.targetSelectionRange &&
+ true;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode {
+ int hash = 0;
+ hash = JenkinsSmiHash.combine(hash, originSelectionRange.hashCode);
+ hash = JenkinsSmiHash.combine(hash, targetUri.hashCode);
+ hash = JenkinsSmiHash.combine(hash, targetRange.hashCode);
+ hash = JenkinsSmiHash.combine(hash, targetSelectionRange.hashCode);
+ return JenkinsSmiHash.finish(hash);
+ }
+
+ @override
+ String toString() => jsonEncoder.convert(toJson());
+}
+
class LogMessageParams implements ToJsonable {
LogMessageParams(this.type, this.message) {
if (type == null) {
@@ -5276,6 +5365,7 @@
case r'completionItem/resolve':
case r'textDocument/hover':
case r'textDocument/signatureHelp':
+ case r'textDocument/declaration':
case r'textDocument/definition':
case r'textDocument/typeDefinition':
case r'textDocument/implementation':
@@ -5397,6 +5487,10 @@
static const textDocument_signatureHelp =
const Method._(r'textDocument/signatureHelp');
+ /// Constant for the 'textDocument/declaration' method.
+ static const textDocument_declaration =
+ const Method._(r'textDocument/declaration');
+
/// Constant for the 'textDocument/definition' method.
static const textDocument_definition =
const Method._(r'textDocument/definition');
@@ -5571,7 +5665,12 @@
/// but can be omitted.
final Either2<String, MarkupContent> documentation;
- /// The label of this parameter. Will be shown in the UI.
+ /// The label of this parameter information.
+ ///
+ /// Either a string or an inclusive start and exclusive end offsets within its
+ /// containing signature label. (see SignatureInformation.label). *Note*: A
+ /// label of type string must be a substring of its containing signature
+ /// label.
final String label;
Map<String, dynamic> toJson() {
@@ -7866,6 +7965,7 @@
this.formatting,
this.rangeFormatting,
this.onTypeFormatting,
+ this.declaration,
this.definition,
this.typeDefinition,
this.implementation,
@@ -7913,6 +8013,10 @@
? TextDocumentClientCapabilitiesOnTypeFormatting.fromJson(
json['onTypeFormatting'])
: null;
+ final declaration = json['declaration'] != null
+ ? TextDocumentClientCapabilitiesDeclaration.fromJson(
+ json['declaration'])
+ : null;
final definition = json['definition'] != null
? TextDocumentClientCapabilitiesDefinition.fromJson(json['definition'])
: null;
@@ -7960,6 +8064,7 @@
formatting,
rangeFormatting,
onTypeFormatting,
+ declaration,
definition,
typeDefinition,
implementation,
@@ -7987,6 +8092,9 @@
/// Capabilities specific to the `textDocument/completion`
final TextDocumentClientCapabilitiesCompletion completion;
+ /// Capabilities specific to the `textDocument/declaration`
+ final TextDocumentClientCapabilitiesDeclaration declaration;
+
/// Capabilities specific to the `textDocument/definition`
final TextDocumentClientCapabilitiesDefinition definition;
@@ -8071,6 +8179,9 @@
if (onTypeFormatting != null) {
__result['onTypeFormatting'] = onTypeFormatting;
}
+ if (declaration != null) {
+ __result['declaration'] = declaration;
+ }
if (definition != null) {
__result['definition'] = definition;
}
@@ -8121,6 +8232,7 @@
formatting == other.formatting &&
rangeFormatting == other.rangeFormatting &&
onTypeFormatting == other.onTypeFormatting &&
+ declaration == other.declaration &&
definition == other.definition &&
typeDefinition == other.typeDefinition &&
implementation == other.implementation &&
@@ -8149,6 +8261,7 @@
hash = JenkinsSmiHash.combine(hash, formatting.hashCode);
hash = JenkinsSmiHash.combine(hash, rangeFormatting.hashCode);
hash = JenkinsSmiHash.combine(hash, onTypeFormatting.hashCode);
+ hash = JenkinsSmiHash.combine(hash, declaration.hashCode);
hash = JenkinsSmiHash.combine(hash, definition.hashCode);
hash = JenkinsSmiHash.combine(hash, typeDefinition.hashCode);
hash = JenkinsSmiHash.combine(hash, implementation.hashCode);
@@ -8652,22 +8765,88 @@
String toString() => jsonEncoder.convert(toJson());
}
-class TextDocumentClientCapabilitiesDefinition implements ToJsonable {
- TextDocumentClientCapabilitiesDefinition(this.dynamicRegistration);
- static TextDocumentClientCapabilitiesDefinition fromJson(
+class TextDocumentClientCapabilitiesDeclaration implements ToJsonable {
+ TextDocumentClientCapabilitiesDeclaration(
+ this.dynamicRegistration, this.linkSupport);
+ static TextDocumentClientCapabilitiesDeclaration fromJson(
Map<String, dynamic> json) {
final dynamicRegistration = json['dynamicRegistration'];
- return new TextDocumentClientCapabilitiesDefinition(dynamicRegistration);
+ final linkSupport = json['linkSupport'];
+ return new TextDocumentClientCapabilitiesDeclaration(
+ dynamicRegistration, linkSupport);
}
- /// Whether definition supports dynamic registration.
+ /// Whether declaration supports dynamic registration. If this is set to
+ /// `true` the client supports the new `(TextDocumentRegistrationOptions &
+ /// StaticRegistrationOptions)` return value for the corresponding server
+ /// capability as well.
final bool dynamicRegistration;
+ /// The client supports additional metadata in the form of declaration links.
+ final bool linkSupport;
+
Map<String, dynamic> toJson() {
Map<String, dynamic> __result = {};
if (dynamicRegistration != null) {
__result['dynamicRegistration'] = dynamicRegistration;
}
+ if (linkSupport != null) {
+ __result['linkSupport'] = linkSupport;
+ }
+ return __result;
+ }
+
+ static bool canParse(Object obj) {
+ return obj is Map<String, dynamic>;
+ }
+
+ @override
+ bool operator ==(other) {
+ if (other is TextDocumentClientCapabilitiesDeclaration) {
+ return dynamicRegistration == other.dynamicRegistration &&
+ linkSupport == other.linkSupport &&
+ true;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode {
+ int hash = 0;
+ hash = JenkinsSmiHash.combine(hash, dynamicRegistration.hashCode);
+ hash = JenkinsSmiHash.combine(hash, linkSupport.hashCode);
+ return JenkinsSmiHash.finish(hash);
+ }
+
+ @override
+ String toString() => jsonEncoder.convert(toJson());
+}
+
+class TextDocumentClientCapabilitiesDefinition implements ToJsonable {
+ TextDocumentClientCapabilitiesDefinition(
+ this.dynamicRegistration, this.linkSupport);
+ static TextDocumentClientCapabilitiesDefinition fromJson(
+ Map<String, dynamic> json) {
+ final dynamicRegistration = json['dynamicRegistration'];
+ final linkSupport = json['linkSupport'];
+ return new TextDocumentClientCapabilitiesDefinition(
+ dynamicRegistration, linkSupport);
+ }
+
+ /// Whether definition supports dynamic registration.
+ final bool dynamicRegistration;
+
+ /// The client supports additional metadata in the form of definition links.
+ final bool linkSupport;
+
+ Map<String, dynamic> toJson() {
+ Map<String, dynamic> __result = {};
+ if (dynamicRegistration != null) {
+ __result['dynamicRegistration'] = dynamicRegistration;
+ }
+ if (linkSupport != null) {
+ __result['linkSupport'] = linkSupport;
+ }
return __result;
}
@@ -8678,7 +8857,9 @@
@override
bool operator ==(other) {
if (other is TextDocumentClientCapabilitiesDefinition) {
- return dynamicRegistration == other.dynamicRegistration && true;
+ return dynamicRegistration == other.dynamicRegistration &&
+ linkSupport == other.linkSupport &&
+ true;
}
return false;
}
@@ -8687,6 +8868,7 @@
int get hashCode {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, dynamicRegistration.hashCode);
+ hash = JenkinsSmiHash.combine(hash, linkSupport.hashCode);
return JenkinsSmiHash.finish(hash);
}
@@ -9021,12 +9203,14 @@
}
class TextDocumentClientCapabilitiesImplementation implements ToJsonable {
- TextDocumentClientCapabilitiesImplementation(this.dynamicRegistration);
+ TextDocumentClientCapabilitiesImplementation(
+ this.dynamicRegistration, this.linkSupport);
static TextDocumentClientCapabilitiesImplementation fromJson(
Map<String, dynamic> json) {
final dynamicRegistration = json['dynamicRegistration'];
+ final linkSupport = json['linkSupport'];
return new TextDocumentClientCapabilitiesImplementation(
- dynamicRegistration);
+ dynamicRegistration, linkSupport);
}
/// Whether implementation supports dynamic registration. If this is set to
@@ -9035,11 +9219,17 @@
/// capability as well.
final bool dynamicRegistration;
+ /// The client supports additional metadata in the form of definition links.
+ final bool linkSupport;
+
Map<String, dynamic> toJson() {
Map<String, dynamic> __result = {};
if (dynamicRegistration != null) {
__result['dynamicRegistration'] = dynamicRegistration;
}
+ if (linkSupport != null) {
+ __result['linkSupport'] = linkSupport;
+ }
return __result;
}
@@ -9050,7 +9240,9 @@
@override
bool operator ==(other) {
if (other is TextDocumentClientCapabilitiesImplementation) {
- return dynamicRegistration == other.dynamicRegistration && true;
+ return dynamicRegistration == other.dynamicRegistration &&
+ linkSupport == other.linkSupport &&
+ true;
}
return false;
}
@@ -9059,6 +9251,7 @@
int get hashCode {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, dynamicRegistration.hashCode);
+ hash = JenkinsSmiHash.combine(hash, linkSupport.hashCode);
return JenkinsSmiHash.finish(hash);
}
@@ -9109,6 +9302,50 @@
String toString() => jsonEncoder.convert(toJson());
}
+class TextDocumentClientCapabilitiesParameterInformation implements ToJsonable {
+ TextDocumentClientCapabilitiesParameterInformation(this.labelOffsetSupport);
+ static TextDocumentClientCapabilitiesParameterInformation fromJson(
+ Map<String, dynamic> json) {
+ final labelOffsetSupport = json['labelOffsetSupport'];
+ return new TextDocumentClientCapabilitiesParameterInformation(
+ labelOffsetSupport);
+ }
+
+ /// The client supports processing label offsets instead of a simple label
+ /// string.
+ final bool labelOffsetSupport;
+
+ Map<String, dynamic> toJson() {
+ Map<String, dynamic> __result = {};
+ if (labelOffsetSupport != null) {
+ __result['labelOffsetSupport'] = labelOffsetSupport;
+ }
+ return __result;
+ }
+
+ static bool canParse(Object obj) {
+ return obj is Map<String, dynamic>;
+ }
+
+ @override
+ bool operator ==(other) {
+ if (other is TextDocumentClientCapabilitiesParameterInformation) {
+ return labelOffsetSupport == other.labelOffsetSupport && true;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode {
+ int hash = 0;
+ hash = JenkinsSmiHash.combine(hash, labelOffsetSupport.hashCode);
+ return JenkinsSmiHash.finish(hash);
+ }
+
+ @override
+ String toString() => jsonEncoder.convert(toJson());
+}
+
class TextDocumentClientCapabilitiesPublishDiagnostics implements ToJsonable {
TextDocumentClientCapabilitiesPublishDiagnostics(this.relatedInformation);
static TextDocumentClientCapabilitiesPublishDiagnostics fromJson(
@@ -9351,26 +9588,37 @@
}
class TextDocumentClientCapabilitiesSignatureInformation implements ToJsonable {
- TextDocumentClientCapabilitiesSignatureInformation(this.documentationFormat);
+ TextDocumentClientCapabilitiesSignatureInformation(
+ this.documentationFormat, this.parameterInformation);
static TextDocumentClientCapabilitiesSignatureInformation fromJson(
Map<String, dynamic> json) {
final documentationFormat = json['documentationFormat']
?.map((item) => item != null ? MarkupKind.fromJson(item) : null)
?.cast<MarkupKind>()
?.toList();
+ final parameterInformation = json['parameterInformation'] != null
+ ? TextDocumentClientCapabilitiesParameterInformation.fromJson(
+ json['parameterInformation'])
+ : null;
return new TextDocumentClientCapabilitiesSignatureInformation(
- documentationFormat);
+ documentationFormat, parameterInformation);
}
/// The client supports the follow content formats for the documentation
/// property. The order describes the preferred format of the client.
final List<MarkupKind> documentationFormat;
+ /// Client capabilities specific to parameter information.
+ final TextDocumentClientCapabilitiesParameterInformation parameterInformation;
+
Map<String, dynamic> toJson() {
Map<String, dynamic> __result = {};
if (documentationFormat != null) {
__result['documentationFormat'] = documentationFormat;
}
+ if (parameterInformation != null) {
+ __result['parameterInformation'] = parameterInformation;
+ }
return __result;
}
@@ -9383,6 +9631,7 @@
if (other is TextDocumentClientCapabilitiesSignatureInformation) {
return listEqual(documentationFormat, other.documentationFormat,
(MarkupKind a, MarkupKind b) => a == b) &&
+ parameterInformation == other.parameterInformation &&
true;
}
return false;
@@ -9392,6 +9641,7 @@
int get hashCode {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, documentationFormat.hashCode);
+ hash = JenkinsSmiHash.combine(hash, parameterInformation.hashCode);
return JenkinsSmiHash.finish(hash);
}
@@ -9526,12 +9776,14 @@
}
class TextDocumentClientCapabilitiesTypeDefinition implements ToJsonable {
- TextDocumentClientCapabilitiesTypeDefinition(this.dynamicRegistration);
+ TextDocumentClientCapabilitiesTypeDefinition(
+ this.dynamicRegistration, this.linkSupport);
static TextDocumentClientCapabilitiesTypeDefinition fromJson(
Map<String, dynamic> json) {
final dynamicRegistration = json['dynamicRegistration'];
+ final linkSupport = json['linkSupport'];
return new TextDocumentClientCapabilitiesTypeDefinition(
- dynamicRegistration);
+ dynamicRegistration, linkSupport);
}
/// Whether typeDefinition supports dynamic registration. If this is set to
@@ -9540,11 +9792,17 @@
/// capability as well.
final bool dynamicRegistration;
+ /// The client supports additional metadata in the form of definition links.
+ final bool linkSupport;
+
Map<String, dynamic> toJson() {
Map<String, dynamic> __result = {};
if (dynamicRegistration != null) {
__result['dynamicRegistration'] = dynamicRegistration;
}
+ if (linkSupport != null) {
+ __result['linkSupport'] = linkSupport;
+ }
return __result;
}
@@ -9555,7 +9813,9 @@
@override
bool operator ==(other) {
if (other is TextDocumentClientCapabilitiesTypeDefinition) {
- return dynamicRegistration == other.dynamicRegistration && true;
+ return dynamicRegistration == other.dynamicRegistration &&
+ linkSupport == other.linkSupport &&
+ true;
}
return false;
}
@@ -9564,6 +9824,7 @@
int get hashCode {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, dynamicRegistration.hashCode);
+ hash = JenkinsSmiHash.combine(hash, linkSupport.hashCode);
return JenkinsSmiHash.finish(hash);
}
diff --git a/pkg/analysis_server/test/lsp/server_abstract.dart b/pkg/analysis_server/test/lsp/server_abstract.dart
index 5c7c905..45e2ad9 100644
--- a/pkg/analysis_server/test/lsp/server_abstract.dart
+++ b/pkg/analysis_server/test/lsp/server_abstract.dart
@@ -520,6 +520,7 @@
null,
null,
null,
+ null,
null);
final emptyWorkspaceClientCapabilities = new WorkspaceClientCapabilities(
diff --git a/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart b/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart
index d03b5fb..103392d 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/typescript_test.dart
@@ -201,12 +201,12 @@
* Blank lines should remain in-tact, as should:
* - Indented
* - Things
- *
+ *
* Some docs have:
* - List items that are not indented
- *
+ *
* Sometimes after a blank line we'll have a note.
- *
+ *
* *Note* that something.
*/
export interface A {
@@ -283,5 +283,26 @@
expect(delete.commentText,
equals('Supports deleting existing files and folders.'));
});
+
+ test('parses a tuple in an array', () {
+ final String input = '''
+interface SomeInformation {
+ label: string | [number, number];
+}
+ ''';
+ final List<AstNode> output = parseFile(input);
+ expect(output, hasLength(1));
+ expect(output[0], const TypeMatcher<Interface>());
+ final Interface interface = output[0];
+ expect(interface.members, hasLength(1));
+ final Field field = interface.members.first;
+ expect(field, const TypeMatcher<Field>());
+ expect(field.name, equals('label'));
+ expect(field.type, const TypeMatcher<UnionType>());
+ UnionType union = field.type;
+ expect(union.types, hasLength(2));
+ expect(union.types[0], isSimpleType('string'));
+ expect(union.types[1], isArrayOf(isSimpleType('number')));
+ });
});
}
diff --git a/pkg/analysis_server/tool/lsp_spec/typescript.dart b/pkg/analysis_server/tool/lsp_spec/typescript.dart
index dd6e854..6f70d2b 100644
--- a/pkg/analysis_server/tool/lsp_spec/typescript.dart
+++ b/pkg/analysis_server/tool/lsp_spec/typescript.dart
@@ -70,6 +70,9 @@
"SymbolInformation": {
"kind": "SymbolKind",
},
+ "ParameterInformation": {
+ "label": "String",
+ }
};
final interface = _improvedTypeMappings[interfaceName];
diff --git a/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart b/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart
index 5a6a844..30083e3 100644
--- a/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart
+++ b/pkg/analysis_server/tool/lsp_spec/typescript_parser.dart
@@ -379,13 +379,16 @@
_consume(TokenType.LEFT_BRACE, 'Expected {');
final members = <Member>[];
while (!_check(TokenType.RIGHT_BRACE)) {
- members.add(_member(name.lexeme));
+ final member = _member(name.lexeme);
+ // TODO(dantup): Remove this temp workaround once spec is fixed/clarified.
+ // https://github.com/Microsoft/language-server-protocol/issues/643
+ if (members.any((m) => m.name == member.name)) {
+ print('Skipping duplicate member ${member.name} in ${name.lexeme}');
+ continue;
+ }
+ members.add(member);
}
- // TODO(dantup): Temporary hack until we handle indexers. Remove nulls, which
- // are (currently) returned by _field() for indexers.
- members.removeWhere((m) => m == null);
-
_consume(TokenType.RIGHT_BRACE, 'Expected }');
return new Interface(leadingComment, name, typeArgs, baseTypes, members);
@@ -478,10 +481,6 @@
members.add(_member(containerName));
}
- // TODO(dantup): Temporary hack until we handle indexers. Remove nulls, which
- // are (currently) returned by _field() for indexers.
- members.removeWhere((m) => m == null);
-
_consume(TokenType.RIGHT_BRACE, 'Expected }');
// Some of the inline interfaces have trailing commas (and some do not!)
_match([TokenType.COMMA]);
@@ -512,6 +511,21 @@
// export const Invoked: 1 = 1;
// the best we can do is use their base type (number).
type = Type.identifier('number');
+ } else if (_match([TokenType.LEFT_BRACKET])) {
+ // Tuples will just be converted to List/Array.
+ final tupleElementTypes = <TypeBase>[];
+ while (!_check(TokenType.RIGHT_BRACKET)) {
+ tupleElementTypes.add(_type(containerName, fieldName));
+ // Remove commas in between.
+ _match([TokenType.COMMA]);
+ }
+ _consume(TokenType.RIGHT_BRACKET, 'Expected ]');
+
+ final uniqueTypes = _getUniqueTypes(tupleElementTypes);
+ var tupleType = uniqueTypes.length == 1
+ ? uniqueTypes.single
+ : new UnionType(uniqueTypes);
+ type = new ArrayType(tupleType);
} else {
var typeName = _consume(TokenType.IDENTIFIER, 'Expected identifier');
final typeArgs = <Type>[];
@@ -553,12 +567,7 @@
}
}
- // Remove any duplicate types (for ex. if we map multiple types into dynamic)
- // we don't want to end up with `dynamic | dynamic`. Key on dartType to
- // ensure we different types that will map down to the same type.
- final uniqueTypes = new Map.fromEntries(
- types.map((t) => new MapEntry(t.dartTypeWithTypeArgs, t)),
- ).values.toList();
+ final uniqueTypes = _getUniqueTypes(types);
var type = uniqueTypes.length == 1
? uniqueTypes.single
@@ -574,6 +583,16 @@
return type;
}
+ /// Remove any duplicate types (for ex. if we map multiple types into dynamic)
+ /// we don't want to end up with `dynamic | dynamic`. Key on dartType to
+ /// ensure we different types that will map down to the same type.
+ List<TypeBase> _getUniqueTypes(List<TypeBase> types) {
+ final uniqueTypes = new Map.fromEntries(
+ types.map((t) => new MapEntry(t.dartTypeWithTypeArgs, t)),
+ ).values.toList();
+ return uniqueTypes;
+ }
+
TypeAlias _typeAlias(Comment leadingComment) {
final name = _consume(TokenType.IDENTIFIER, 'Expected identifier');
_consume(TokenType.EQUAL, 'Expected =');