Compute variance for type parameters of typedefs.
Bug: https://github.com/dart-lang/sdk/issues/34699
Change-Id: I30f010f89da6abb1b3d27fe1eab11d5eb1c5e190
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164660
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
index dcdf36e..bd2def3 100644
--- a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
+++ b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
@@ -8,6 +8,7 @@
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/element_type_provider.dart';
import 'package:meta/meta.dart';
@@ -171,8 +172,9 @@
void writeTypeParameter(TypeParameterElement element) {
if (element is TypeParameterElementImpl) {
- if (!element.isLegacyCovariant) {
- _write(element.variance.toKeywordString());
+ var variance = element.variance;
+ if (!element.isLegacyCovariant && variance != Variance.unrelated) {
+ _write(variance.toKeywordString());
_write(' ');
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index 63df85d..77c0646 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -7426,10 +7426,7 @@
if (_variance != null) return _variance;
if (linkedNode != null) {
- var varianceKeyword = linkedContext.getTypeParameterVariance(linkedNode);
- if (varianceKeyword != null) {
- _variance = Variance.fromKeywordString(varianceKeyword.lexeme);
- }
+ _variance = linkedContext.getTypeParameterVariance(linkedNode);
}
return _variance ?? Variance.covariant;
diff --git a/pkg/analyzer/lib/src/dart/resolver/variance.dart b/pkg/analyzer/lib/src/dart/resolver/variance.dart
index 34962c9..b248774 100644
--- a/pkg/analyzer/lib/src/dart/resolver/variance.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/variance.dart
@@ -204,4 +204,20 @@
'Missing keyword lexeme representation for variance: $this');
}
}
+
+ @override
+ String toString() {
+ switch (this) {
+ case contravariant:
+ return 'contravariant';
+ case invariant:
+ return 'invariant';
+ case covariant:
+ return 'covariant';
+ case unrelated:
+ return 'unrelated';
+ default:
+ throw UnimplementedError('encoding: $_encoding');
+ }
+ }
}
diff --git a/pkg/analyzer/lib/src/summary/format.dart b/pkg/analyzer/lib/src/summary/format.dart
index 8898a83..cddf91d 100644
--- a/pkg/analyzer/lib/src/summary/format.dart
+++ b/pkg/analyzer/lib/src/summary/format.dart
@@ -6702,6 +6702,12 @@
return _variantField_15 ??= 0;
}
+ @override
+ int get typeParameter_variance {
+ assert(kind == idl.LinkedNodeKind.typeParameter);
+ return _variantField_15 ??= 0;
+ }
+
set assignmentExpression_element(int value) {
assert(kind == idl.LinkedNodeKind.assignmentExpression);
assert(value == null || value >= 0);
@@ -6774,6 +6780,12 @@
_variantField_15 = value;
}
+ set typeParameter_variance(int value) {
+ assert(kind == idl.LinkedNodeKind.typeParameter);
+ assert(value == null || value >= 0);
+ _variantField_15 = value;
+ }
+
@override
idl.UnlinkedTokenType get assignmentExpression_operator {
assert(kind == idl.LinkedNodeKind.assignmentExpression);
@@ -6804,12 +6816,6 @@
return _variantField_28 ??= idl.UnlinkedTokenType.NOTHING;
}
- @override
- idl.UnlinkedTokenType get typeParameter_variance {
- assert(kind == idl.LinkedNodeKind.typeParameter);
- return _variantField_28 ??= idl.UnlinkedTokenType.NOTHING;
- }
-
set assignmentExpression_operator(idl.UnlinkedTokenType value) {
assert(kind == idl.LinkedNodeKind.assignmentExpression);
_variantField_28 = value;
@@ -6835,11 +6841,6 @@
_variantField_28 = value;
}
- set typeParameter_variance(idl.UnlinkedTokenType value) {
- assert(kind == idl.LinkedNodeKind.typeParameter);
- _variantField_28 = value;
- }
-
@override
bool get booleanLiteral_value {
assert(kind == idl.LinkedNodeKind.booleanLiteral);
@@ -8619,13 +8620,13 @@
LinkedNodeBuilder.typeParameter({
List<LinkedNodeBuilder> annotatedNode_metadata,
LinkedNodeBuilder typeParameter_bound,
- idl.UnlinkedTokenType typeParameter_variance,
+ int typeParameter_variance,
int informativeId,
LinkedNodeTypeBuilder typeParameter_defaultType,
}) : _kind = idl.LinkedNodeKind.typeParameter,
_variantField_4 = annotatedNode_metadata,
_variantField_6 = typeParameter_bound,
- _variantField_28 = typeParameter_variance,
+ _variantField_15 = typeParameter_variance,
_variantField_36 = informativeId,
_variantField_23 = typeParameter_defaultType;
@@ -10668,12 +10669,10 @@
}
signature.addBool(this.typeParameter_bound != null);
this.typeParameter_bound?.collectApiSignature(signature);
+ signature.addInt(this.typeParameter_variance ?? 0);
signature.addInt(this.flags ?? 0);
signature.addBool(this.typeParameter_defaultType != null);
this.typeParameter_defaultType?.collectApiSignature(signature);
- signature.addInt(this.typeParameter_variance == null
- ? 0
- : this.typeParameter_variance.index);
signature.addString(this.name ?? '');
} else if (kind == idl.LinkedNodeKind.typeParameterList) {
signature.addInt(this.kind == null ? 0 : this.kind.index);
@@ -12712,6 +12711,14 @@
}
@override
+ int get typeParameter_variance {
+ assert(kind == idl.LinkedNodeKind.typeParameter);
+ _variantField_15 ??=
+ const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 15, 0);
+ return _variantField_15;
+ }
+
+ @override
idl.UnlinkedTokenType get assignmentExpression_operator {
assert(kind == idl.LinkedNodeKind.assignmentExpression);
_variantField_28 ??= const _UnlinkedTokenTypeReader()
@@ -12752,14 +12759,6 @@
}
@override
- idl.UnlinkedTokenType get typeParameter_variance {
- assert(kind == idl.LinkedNodeKind.typeParameter);
- _variantField_28 ??= const _UnlinkedTokenTypeReader()
- .vTableGet(_bc, _bcOffset, 28, idl.UnlinkedTokenType.NOTHING);
- return _variantField_28;
- }
-
- @override
bool get booleanLiteral_value {
assert(kind == idl.LinkedNodeKind.booleanLiteral);
_variantField_27 ??=
@@ -14797,9 +14796,8 @@
if (typeParameter_bound != null) {
_result["typeParameter_bound"] = typeParameter_bound.toJson();
}
- if (typeParameter_variance != idl.UnlinkedTokenType.NOTHING) {
- _result["typeParameter_variance"] =
- typeParameter_variance.toString().split('.')[1];
+ if (typeParameter_variance != 0) {
+ _result["typeParameter_variance"] = typeParameter_variance;
}
if (informativeId != 0) {
_result["informativeId"] = informativeId;
diff --git a/pkg/analyzer/lib/src/summary/idl.dart b/pkg/analyzer/lib/src/summary/idl.dart
index c89630c..4b84de9 100644
--- a/pkg/analyzer/lib/src/summary/idl.dart
+++ b/pkg/analyzer/lib/src/summary/idl.dart
@@ -1557,8 +1557,8 @@
@VariantId(23, variant: LinkedNodeKind.typeParameter)
LinkedNodeType get typeParameter_defaultType;
- @VariantId(28, variant: LinkedNodeKind.typeParameter)
- UnlinkedTokenType get typeParameter_variance;
+ @VariantId(15, variant: LinkedNodeKind.typeParameter)
+ int get typeParameter_variance;
@VariantId(2, variant: LinkedNodeKind.typeParameterList)
List<LinkedNode> get typeParameterList_typeParameters;
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
index 65a5737..4df499f 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_reader.dart
@@ -12,6 +12,7 @@
import 'package:analyzer/src/dart/ast/ast_factory.dart';
import 'package:analyzer/src/dart/element/member.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
import 'package:analyzer/src/generated/testing/token_factory.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
@@ -1598,12 +1599,13 @@
// TODO (kallentu) : Clean up AstFactoryImpl casting once variance is
// added to the interface.
var node = (astFactory as AstFactoryImpl).typeParameter2(
- comment: _readDocumentationComment(data),
- metadata: _readNodeListLazy(data.annotatedNode_metadata),
- name: _declaredIdentifier(data),
- extendsKeyword: _Tokens.EXTENDS,
- bound: _readNodeLazy(data.typeParameter_bound),
- varianceKeyword: _varianceKeyword(data));
+ comment: _readDocumentationComment(data),
+ metadata: _readNodeListLazy(data.annotatedNode_metadata),
+ name: _declaredIdentifier(data),
+ extendsKeyword: _Tokens.EXTENDS,
+ bound: _readNodeLazy(data.typeParameter_bound),
+ );
+ LazyAst.setVariance(node, _decodeVariance(data.typeParameter_variance));
LazyTypeParameter.setData(node, data);
return node;
}
@@ -1959,11 +1961,20 @@
return _unitContext.readType(data);
}
- Token _varianceKeyword(LinkedNode data) {
- if (data.typeParameter_variance != UnlinkedTokenType.NOTHING) {
- return _Tokens.fromType(data.typeParameter_variance);
+ static Variance _decodeVariance(int encoding) {
+ if (encoding == 0) {
+ return null;
+ } else if (encoding == 1) {
+ return Variance.unrelated;
+ } else if (encoding == 2) {
+ return Variance.covariant;
+ } else if (encoding == 3) {
+ return Variance.contravariant;
+ } else if (encoding == 4) {
+ return Variance.invariant;
+ } else {
+ throw UnimplementedError('encoding: $encoding');
}
- return null;
}
static ParameterKind _toParameterKind(LinkedNodeFormalParameterKind kind) {
diff --git a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
index e3a4384..0035dd8 100644
--- a/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_binary_writer.dart
@@ -10,6 +10,7 @@
import 'package:analyzer/src/dart/analysis/experiments.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/member.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary2/ast_binary_flags.dart';
@@ -1377,7 +1378,7 @@
var builder = LinkedNodeBuilder.typeParameter(
typeParameter_bound: node.bound?.accept(this),
typeParameter_defaultType: _writeType(LazyAst.getDefaultType(node)),
- typeParameter_variance: _getVarianceToken(node),
+ typeParameter_variance: _encodeVariance(LazyAst.getVariance(node)),
informativeId: getInformativeId(node),
);
builder.name = node.name.name;
@@ -1508,15 +1509,6 @@
return _ElementComponents(elementIndex, null);
}
- UnlinkedTokenType _getVarianceToken(TypeParameter parameter) {
- // TODO (kallentu) : Clean up TypeParameterImpl casting once variance is
- // added to the interface.
- var parameterImpl = parameter as TypeParameterImpl;
- return parameterImpl.varianceKeyword != null
- ? TokensWriter.astToBinaryTokenType(parameterImpl.varianceKeyword.type)
- : null;
- }
-
int _indexOfElement(Element element) {
return _linkingContext.indexOfElement(element);
}
@@ -1712,6 +1704,22 @@
return _linkingContext.writeType(type);
}
+ static int _encodeVariance(Variance variance) {
+ if (variance == null) {
+ return 0;
+ } else if (variance == Variance.unrelated) {
+ return 1;
+ } else if (variance == Variance.covariant) {
+ return 2;
+ } else if (variance == Variance.contravariant) {
+ return 3;
+ } else if (variance == Variance.invariant) {
+ return 4;
+ } else {
+ throw UnimplementedError('$variance');
+ }
+ }
+
/// Return `true` if the expression might be successfully serialized.
///
/// This does not mean that the expression is constant, it just means that
diff --git a/pkg/analyzer/lib/src/summary2/function_type_builder.dart b/pkg/analyzer/lib/src/summary2/function_type_builder.dart
index 74cf1fd..a4c8987 100644
--- a/pkg/analyzer/lib/src/summary2/function_type_builder.dart
+++ b/pkg/analyzer/lib/src/summary2/function_type_builder.dart
@@ -53,7 +53,7 @@
) {
return FunctionTypeBuilder(
_getTypeParameters(node.typeParameters),
- _getParameters(isNNBD, node.parameters),
+ getParameters(isNNBD, node.parameters),
_getNodeType(node.returnType),
nullabilitySuffix,
node: node,
@@ -138,6 +138,20 @@
);
}
+ /// [isNNBD] indicates whether the containing library is opted into NNBD.
+ static List<ParameterElementImpl> getParameters(
+ bool isNNBD,
+ FormalParameterList node,
+ ) {
+ return node.parameters.asImpl.map((parameter) {
+ return ParameterElementImpl.synthetic(
+ parameter.identifier?.name ?? '',
+ _getParameterType(isNNBD, parameter),
+ parameter.kind,
+ );
+ }).toList();
+ }
+
/// If the [type] is a [TypeBuilder], build it; otherwise return as is.
static DartType _buildType(DartType type) {
if (type is TypeBuilder) {
@@ -156,20 +170,6 @@
}
}
- /// [isNNBD] indicates whether the containing library is opted into NNBD.
- static List<ParameterElementImpl> _getParameters(
- bool isNNBD,
- FormalParameterList node,
- ) {
- return node.parameters.asImpl.map((parameter) {
- return ParameterElementImpl.synthetic(
- parameter.identifier?.name ?? '',
- _getParameterType(isNNBD, parameter),
- parameter.kind,
- );
- }).toList();
- }
-
/// Return the type of the [node] as is, possibly a [TypeBuilder].
///
/// [isNNBD] indicates whether the containing library is opted into NNBD.
@@ -190,7 +190,7 @@
return FunctionTypeBuilder(
_getTypeParameters(node.typeParameters),
- _getParameters(isNNBD, node.parameters),
+ getParameters(isNNBD, node.parameters),
_getNodeType(node.returnType),
nullabilitySuffix,
);
diff --git a/pkg/analyzer/lib/src/summary2/lazy_ast.dart b/pkg/analyzer/lib/src/summary2/lazy_ast.dart
index 43008b7..dea4fa9 100644
--- a/pkg/analyzer/lib/src/summary2/lazy_ast.dart
+++ b/pkg/analyzer/lib/src/summary2/lazy_ast.dart
@@ -6,6 +6,7 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary2/ast_binary_flags.dart';
@@ -16,7 +17,7 @@
/// Accessor for reading AST lazily, or read data that is stored in IDL, but
/// cannot be stored in AST, like inferred types.
class LazyAst {
- static const _defaultTypedKey = 'lazyAst_defaultType';
+ static const _defaultTypeKey = 'lazyAst_defaultType';
static const _genericFunctionTypeIdKey = 'lazyAst_genericFunctionTypeId';
static const _hasOverrideInferenceKey = 'lazyAst_hasOverrideInference';
static const _inheritsCovariantKey = 'lazyAst_isCovariant';
@@ -27,13 +28,14 @@
static const _returnTypeKey = 'lazyAst_returnType';
static const _typeInferenceErrorKey = 'lazyAst_typeInferenceError';
static const _typeKey = 'lazyAst_type';
+ static const _varianceKey = 'lazyAst_variance';
final LinkedNode data;
LazyAst(this.data);
static DartType getDefaultType(TypeParameter node) {
- return node.getProperty(_defaultTypedKey);
+ return node.getProperty(_defaultTypeKey);
}
static int getGenericFunctionTypeId(GenericFunctionType node) {
@@ -60,6 +62,10 @@
return node.getProperty(_typeInferenceErrorKey);
}
+ static Variance getVariance(TypeParameter node) {
+ return node.getProperty(_varianceKey);
+ }
+
static bool hasOperatorEqualParameterTypeFromObject(AstNode node) {
return node.getProperty(_isOperatorEqualParameterTypeFromObjectKey) ??
false;
@@ -74,7 +80,7 @@
}
static void setDefaultType(TypeParameter node, DartType type) {
- node.setProperty(_defaultTypedKey, type);
+ node.setProperty(_defaultTypeKey, type);
}
static void setGenericFunctionTypeId(GenericFunctionType node, int id) {
@@ -113,6 +119,10 @@
AstNode node, TopLevelInferenceError error) {
node.setProperty(_typeInferenceErrorKey, error);
}
+
+ static void setVariance(TypeParameter node, Variance variance) {
+ return node.setProperty(_varianceKey, variance);
+ }
}
class LazyClassDeclaration {
diff --git a/pkg/analyzer/lib/src/summary2/link.dart b/pkg/analyzer/lib/src/summary2/link.dart
index 3f34f0d..281f0dc 100644
--- a/pkg/analyzer/lib/src/summary2/link.dart
+++ b/pkg/analyzer/lib/src/summary2/link.dart
@@ -19,6 +19,7 @@
import 'package:analyzer/src/summary2/top_level_inference.dart';
import 'package:analyzer/src/summary2/type_alias.dart';
import 'package:analyzer/src/summary2/types_builder.dart';
+import 'package:analyzer/src/summary2/variance_builder.dart';
var timerLinkingLinkingBundle = Stopwatch();
var timerLinkingRemoveBundle = Stopwatch();
@@ -227,6 +228,7 @@
for (var library in builders.values) {
library.resolveTypes(nodesToBuildType);
}
+ VarianceBuilder().perform(this);
computeSimplyBounded(bundleContext, builders.values);
TypesBuilder().build(nodesToBuildType);
}
diff --git a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
index 1a6410a..24a749f 100644
--- a/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_unit_context.dart
@@ -4,7 +4,6 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/standard_ast_factory.dart';
-import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
@@ -13,6 +12,7 @@
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/testing/token_factory.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
import 'package:analyzer/src/summary/idl.dart';
@@ -683,10 +683,8 @@
}
}
- Token getTypeParameterVariance(TypeParameter node) {
- // TODO (kallentu) : Clean up TypeParameterImpl casting once variance is
- // added to the interface.
- return (node as TypeParameterImpl).varianceKeyword;
+ Variance getTypeParameterVariance(TypeParameter node) {
+ return LazyAst.getVariance(node);
}
WithClause getWithClause(AstNode node) {
diff --git a/pkg/analyzer/lib/src/summary2/variance_builder.dart b/pkg/analyzer/lib/src/summary2/variance_builder.dart
new file mode 100644
index 0000000..704bd83
--- /dev/null
+++ b/pkg/analyzer/lib/src/summary2/variance_builder.dart
@@ -0,0 +1,245 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/resolver/variance.dart';
+import 'package:analyzer/src/summary2/function_type_builder.dart';
+import 'package:analyzer/src/summary2/lazy_ast.dart';
+import 'package:analyzer/src/summary2/link.dart';
+import 'package:analyzer/src/summary2/named_type_builder.dart';
+import 'package:meta/meta.dart';
+
+class VarianceBuilder {
+ final Set<TypeAlias> _pending = Set.identity();
+ final Set<TypeAlias> _visit = Set.identity();
+
+ void perform(Linker linker) {
+ for (var builder in linker.builders.values) {
+ for (var unitContext in builder.context.units) {
+ for (var node in unitContext.unit.declarations) {
+ if (node is FunctionTypeAlias || node is GenericTypeAlias) {
+ _pending.add(node);
+ }
+ }
+ }
+ }
+
+ for (var builder in linker.builders.values) {
+ for (var unitContext in builder.context.units) {
+ for (var node in unitContext.unit.declarations) {
+ if (node is ClassTypeAlias) {
+ _typeParameters(node.typeParameters);
+ } else if (node is ClassDeclaration) {
+ _typeParameters(node.typeParameters);
+ } else if (node is FunctionTypeAlias) {
+ _functionTypeAlias(node);
+ } else if (node is GenericTypeAlias) {
+ _genericTypeAlias(node);
+ } else if (node is MixinDeclaration) {
+ _typeParameters(node.typeParameters);
+ }
+ }
+ }
+ }
+ }
+
+ Variance _compute(TypeParameterElement variable, DartType type) {
+ if (type is TypeParameterType) {
+ var element = type.element;
+ if (element is TypeParameterElement) {
+ if (element == variable) {
+ return Variance.covariant;
+ } else {
+ return Variance.unrelated;
+ }
+ }
+ } else if (type is NamedTypeBuilder) {
+ var element = type.element;
+ if (element is ClassElement) {
+ var result = Variance.unrelated;
+ if (type.arguments.isNotEmpty) {
+ var parameters = element.typeParameters;
+ for (int i = 0; i < type.arguments.length; ++i) {
+ var parameter = parameters[i] as TypeParameterElementImpl;
+ result = result.meet(
+ parameter.variance.combine(
+ _compute(variable, type.arguments[i]),
+ ),
+ );
+ }
+ }
+ return result;
+ } else if (element is GenericTypeAliasElementImpl) {
+ _functionTypeAliasElement(element);
+
+ var result = Variance.unrelated;
+
+ if (type.arguments.isNotEmpty) {
+ var parameters = element.typeParameters;
+ for (var i = 0; i < type.arguments.length; ++i) {
+ var parameter = parameters[i] as TypeParameterElementImpl;
+ var parameterVariance = parameter.variance;
+ result = result.meet(
+ parameterVariance.combine(
+ _compute(variable, type.arguments[i]),
+ ),
+ );
+ }
+ }
+ return result;
+ }
+ } else if (type is FunctionTypeBuilder) {
+ return _computeFunctionType(
+ variable,
+ returnType: type.returnType,
+ typeFormals: type.typeFormals,
+ parameters: type.parameters,
+ );
+ }
+ return Variance.unrelated;
+ }
+
+ Variance _computeFunctionType(
+ TypeParameterElement variable, {
+ @required DartType returnType,
+ @required List<TypeParameterElement> typeFormals,
+ @required List<ParameterElement> parameters,
+ }) {
+ var result = Variance.unrelated;
+
+ if (result != null) {
+ result = result.meet(
+ _compute(variable, returnType),
+ );
+ }
+
+ // If [variable] is referenced in a bound at all, it makes the
+ // variance of [variable] in the entire type invariant.
+ if (typeFormals != null) {
+ for (var parameter in typeFormals) {
+ var bound = parameter.bound;
+ if (bound != null && _compute(variable, bound) != Variance.unrelated) {
+ result = Variance.invariant;
+ }
+ }
+ }
+
+ for (var parameter in parameters) {
+ result = result.meet(
+ Variance.contravariant.combine(
+ _compute(variable, parameter.type),
+ ),
+ );
+ }
+
+ return result;
+ }
+
+ void _functionTypeAlias(FunctionTypeAlias node) {
+ var parameterList = node.typeParameters;
+ if (parameterList == null) {
+ return;
+ }
+
+ // Recursion detected, recover.
+ if (_visit.contains(node)) {
+ for (var parameter in parameterList.typeParameters) {
+ LazyAst.setVariance(parameter, Variance.covariant);
+ }
+ return;
+ }
+
+ // Not being linked, or already linked.
+ if (!_pending.remove(node)) {
+ return;
+ }
+
+ _visit.add(node);
+ try {
+ for (var parameter in parameterList.typeParameters) {
+ var variance = _computeFunctionType(
+ parameter.declaredElement,
+ returnType: node.returnType?.type,
+ typeFormals: null,
+ parameters: FunctionTypeBuilder.getParameters(
+ false,
+ node.parameters,
+ ),
+ );
+ LazyAst.setVariance(parameter, variance);
+ }
+ } finally {
+ _visit.remove(node);
+ }
+ }
+
+ void _functionTypeAliasElement(GenericTypeAliasElementImpl element) {
+ var node = element.linkedNode;
+ if (node is GenericTypeAlias) {
+ _genericTypeAlias(node);
+ } else if (node is FunctionTypeAlias) {
+ _functionTypeAlias(node);
+ } else {
+ throw UnimplementedError('(${node.runtimeType}) $node');
+ }
+ }
+
+ void _genericTypeAlias(GenericTypeAlias node) {
+ var parameterList = node.typeParameters;
+ if (parameterList == null) {
+ return;
+ }
+
+ // Recursion detected, recover.
+ if (_visit.contains(node)) {
+ for (var parameter in parameterList.typeParameters) {
+ LazyAst.setVariance(parameter, Variance.covariant);
+ }
+ return;
+ }
+
+ // Not being linked, or already linked.
+ if (!_pending.remove(node)) {
+ return;
+ }
+
+ var type = node.functionType?.type;
+
+ // Not a function type, recover.
+ if (type == null) {
+ for (var parameter in parameterList.typeParameters) {
+ LazyAst.setVariance(parameter, Variance.covariant);
+ }
+ }
+
+ _visit.add(node);
+ try {
+ for (var parameter in parameterList.typeParameters) {
+ var variance = _compute(parameter.declaredElement, type);
+ LazyAst.setVariance(parameter, variance);
+ }
+ } finally {
+ _visit.remove(node);
+ }
+ }
+
+ void _typeParameters(TypeParameterList parameterList) {
+ if (parameterList == null) {
+ return;
+ }
+
+ for (var parameter in parameterList.typeParameters) {
+ var parameterImpl = parameter as TypeParameterImpl;
+ var varianceKeyword = parameterImpl.varianceKeyword;
+ if (varianceKeyword != null) {
+ var variance = Variance.fromKeywordString(varianceKeyword.lexeme);
+ LazyAst.setVariance(parameter, variance);
+ }
+ }
+ }
+}
diff --git a/pkg/analyzer/test/src/summary/element_text.dart b/pkg/analyzer/test/src/summary/element_text.dart
index 1158676..88ea0b6 100644
--- a/pkg/analyzer/test/src/summary/element_text.dart
+++ b/pkg/analyzer/test/src/summary/element_text.dart
@@ -55,6 +55,7 @@
bool withSyntheticAccessors = false,
bool withSyntheticFields = false,
bool withTypes = false,
+ bool withTypeParameterVariance = false,
bool annotateNullability = false,
}) {
var writer = _ElementWriter(
@@ -67,6 +68,7 @@
withSyntheticAccessors: withSyntheticAccessors,
withSyntheticFields: withSyntheticFields,
withTypes: withTypes,
+ withTypeParameterVariance: withTypeParameterVariance,
annotateNullability: annotateNullability,
);
writer.writeLibraryElement(library);
@@ -136,6 +138,7 @@
final bool withSyntheticAccessors;
final bool withSyntheticFields;
final bool withTypes;
+ final bool withTypeParameterVariance;
final bool annotateNullability;
final StringBuffer buffer = StringBuffer();
@@ -151,6 +154,7 @@
this.withSyntheticAccessors = false,
this.withSyntheticFields = false,
this.withTypes = false,
+ this.withTypeParameterVariance,
this.annotateNullability = false,
});
@@ -1044,12 +1048,15 @@
void writeTypeParameterElement(TypeParameterElement e) {
writeMetadata(e, '', '\n');
+
// TODO (kallentu) : Clean up TypeParameterElementImpl casting once
// variance is added to the interface.
- if (!(e as TypeParameterElementImpl).isLegacyCovariant) {
- buffer.write(
- (e as TypeParameterElementImpl).variance.toKeywordString() + ' ');
+ if (withTypeParameterVariance) {
+ var impl = e as TypeParameterElementImpl;
+ var variance = impl.variance;
+ buffer.write(variance.toString() + ' ');
}
+
writeName(e);
writeCodeRange(e);
if (e.bound != null && !e.bound.isDartCoreObject) {
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 826a082..210f03a 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -1724,34 +1724,46 @@
test_class_type_parameters_variance_contravariant() async {
var library = await checkLibrary('class C<in T> {}');
- checkElementText(library, r'''
-class C<in T> {
+ checkElementText(
+ library,
+ r'''
+class C<contravariant T> {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_class_type_parameters_variance_covariant() async {
var library = await checkLibrary('class C<out T> {}');
- checkElementText(library, r'''
-class C<out T> {
+ checkElementText(
+ library,
+ r'''
+class C<covariant T> {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_class_type_parameters_variance_invariant() async {
var library = await checkLibrary('class C<inout T> {}');
- checkElementText(library, r'''
-class C<inout T> {
+ checkElementText(
+ library,
+ r'''
+class C<invariant T> {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_class_type_parameters_variance_multiple() async {
var library = await checkLibrary('class C<inout T, in U, out V> {}');
- checkElementText(library, r'''
-class C<inout T, in U, out V> {
+ checkElementText(
+ library,
+ r'''
+class C<invariant T, contravariant U, covariant V> {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_classes() async {
@@ -6606,6 +6618,136 @@
''');
}
+ test_functionTypeAlias_typeParameters_variance_contravariant() async {
+ var library = await checkLibrary(r'''
+typedef void F<T>(T a);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<contravariant T> = void Function(T a);
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_contravariant2() async {
+ var library = await checkLibrary(r'''
+typedef void F1<T>(T a);
+typedef F1<T> F2<T>();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<contravariant T> = void Function(T a);
+typedef F2<contravariant T> = void Function(T) Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_contravariant3() async {
+ var library = await checkLibrary(r'''
+typedef F1<T> F2<T>();
+typedef void F1<T>(T a);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F2<contravariant T> = void Function(T) Function();
+typedef F1<contravariant T> = void Function(T a);
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_covariant() async {
+ var library = await checkLibrary(r'''
+typedef T F<T>();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<covariant T> = T Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_covariant2() async {
+ var library = await checkLibrary(r'''
+typedef List<T> F<T>();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<covariant T> = List<T> Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_covariant3() async {
+ var library = await checkLibrary(r'''
+typedef T F1<T>();
+typedef F1<T> F2<T>();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<covariant T> = T Function();
+typedef F2<covariant T> = T Function() Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_covariant4() async {
+ var library = await checkLibrary(r'''
+typedef void F1<T>(T a);
+typedef void F2<T>(F1<T> a);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<contravariant T> = void Function(T a);
+typedef F2<covariant T> = void Function(void Function(T) a);
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_invariant() async {
+ var library = await checkLibrary(r'''
+typedef T F<T>(T a);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<invariant T> = T Function(T a);
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_invariant2() async {
+ var library = await checkLibrary(r'''
+typedef T F1<T>();
+typedef F1<T> F2<T>(T a);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<covariant T> = T Function();
+typedef F2<invariant T> = T Function() Function(T a);
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_functionTypeAlias_typeParameters_variance_unrelated() async {
+ var library = await checkLibrary(r'''
+typedef void F<T>(int a);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<unrelated T> = void Function(int a);
+''',
+ withTypeParameterVariance: true);
+ }
+
test_futureOr() async {
var library = await checkLibrary('import "dart:async"; FutureOr<int> x;');
checkElementText(library, r'''
@@ -6788,6 +6930,122 @@
''');
}
+ test_genericTypeAlias_typeParameters_variance_contravariant() async {
+ var library = await checkLibrary(r'''
+typedef F<T> = void Function(T);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<contravariant T> = void Function(T );
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_contravariant2() async {
+ var library = await checkLibrary(r'''
+typedef F1<T> = void Function(T);
+typedef F2<T> = F1<T> Function();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<contravariant T> = void Function(T );
+typedef F2<contravariant T> = void Function(T) Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_covariant() async {
+ var library = await checkLibrary(r'''
+typedef F<T> = T Function();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<covariant T> = T Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_covariant2() async {
+ var library = await checkLibrary(r'''
+typedef F<T> = List<T> Function();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<covariant T> = List<T> Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_covariant3() async {
+ var library = await checkLibrary(r'''
+typedef F1<T> = T Function();
+typedef F2<T> = F1<T> Function();
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<covariant T> = T Function();
+typedef F2<covariant T> = T Function() Function();
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_covariant4() async {
+ var library = await checkLibrary(r'''
+typedef F1<T> = void Function(T);
+typedef F2<T> = void Function(F1<T>);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<contravariant T> = void Function(T );
+typedef F2<covariant T> = void Function(void Function(T) );
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_invariant() async {
+ var library = await checkLibrary(r'''
+typedef F<T> = T Function(T);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<invariant T> = T Function(T );
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_invariant2() async {
+ var library = await checkLibrary(r'''
+typedef F1<T> = T Function();
+typedef F2<T> = F1<T> Function(T);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F1<covariant T> = T Function();
+typedef F2<invariant T> = T Function() Function(T );
+''',
+ withTypeParameterVariance: true);
+ }
+
+ test_genericTypeAlias_typeParameters_variance_unrelated() async {
+ var library = await checkLibrary(r'''
+typedef F<T> = void Function(int);
+''');
+ checkElementText(
+ library,
+ r'''
+typedef F<unrelated T> = void Function(int );
+''',
+ withTypeParameterVariance: true);
+ }
+
test_getter_documented() async {
var library = await checkLibrary('''
// Extra comment so doc comment offset != 0
@@ -9528,34 +9786,46 @@
test_mixin_type_parameters_variance_contravariant() async {
var library = await checkLibrary('mixin M<in T> {}');
- checkElementText(library, r'''
-mixin M<in T> on Object {
+ checkElementText(
+ library,
+ r'''
+mixin M<contravariant T> on Object {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_mixin_type_parameters_variance_covariant() async {
var library = await checkLibrary('mixin M<out T> {}');
- checkElementText(library, r'''
-mixin M<out T> on Object {
+ checkElementText(
+ library,
+ r'''
+mixin M<covariant T> on Object {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_mixin_type_parameters_variance_invariant() async {
var library = await checkLibrary('mixin M<inout T> {}');
- checkElementText(library, r'''
-mixin M<inout T> on Object {
+ checkElementText(
+ library,
+ r'''
+mixin M<invariant T> on Object {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_mixin_type_parameters_variance_multiple() async {
var library = await checkLibrary('mixin M<inout T, in U, out V> {}');
- checkElementText(library, r'''
-mixin M<inout T, in U, out V> on Object {
+ checkElementText(
+ library,
+ r'''
+mixin M<invariant T, contravariant U, covariant V> on Object {
}
-''');
+''',
+ withTypeParameterVariance: true);
}
test_nameConflict_exportedAndLocal() async {