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 {