Update DartEditBuilderImpl.writeType() to support type aliases.

Change-Id: I560596384e8a8d422a013b6597ca9ab91c2d00df
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/182663
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index 2445867..9914359 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -852,6 +852,10 @@
       );
     }
 
+    if (type is NeverType) {
+      return 'Never';
+    }
+
     if (type is TypeParameterType) {
       var element = type.element;
       if (_isTypeParameterVisible(element)) {
@@ -865,7 +869,7 @@
       return 'void';
     }
 
-    throw StateError('(${type.runtimeType}) $type');
+    throw UnimplementedError('(${type.runtimeType}) $type');
   }
 
   /// Indents given source left or right.
diff --git a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
index 993cf7e..eb831fc 100644
--- a/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
+++ b/pkg/analyzer_plugin/lib/src/utilities/change_builder/change_builder_dart.dart
@@ -24,6 +24,7 @@
 import 'package:analyzer_plugin/utilities/change_builder/change_workspace.dart';
 import 'package:analyzer_plugin/utilities/range_factory.dart';
 import 'package:dart_style/dart_style.dart';
+import 'package:meta/meta.dart';
 
 /// A [ChangeBuilder] used to build changes in Dart files.
 @Deprecated('Use ChangeBuilder')
@@ -1178,22 +1179,19 @@
       }
       return false;
     }
-    // The type `void` does not have an element.
-    if (type is VoidType) {
-      write('void');
+
+    var aliasElement = type.aliasElement;
+    if (aliasElement != null) {
+      _writeTypeElementArguments(
+        element: aliasElement,
+        typeArguments: type.aliasArguments,
+        methodBeingCopied: methodBeingCopied,
+      );
+      _writeTypeNullability(type);
       return true;
     }
 
-    var element = type.element;
-    // Typedef(s) are represented as GenericFunctionTypeElement(s).
-    if (element is GenericFunctionTypeElement &&
-        element.typeParameters.isEmpty &&
-        element.enclosingElement is FunctionTypeAliasElement) {
-      element = element.enclosingElement;
-    }
-
-    // Just a Function, not FunctionTypeAliasElement.
-    if (type is FunctionType && element is! FunctionTypeAliasElement) {
+    if (type is FunctionType) {
       if (_writeType(type.returnType, methodBeingCopied: methodBeingCopied)) {
         write(' ');
       }
@@ -1204,6 +1202,41 @@
       return true;
     }
 
+    if (type is InterfaceType) {
+      _writeTypeElementArguments(
+        element: type.element,
+        typeArguments: type.typeArguments,
+        methodBeingCopied: methodBeingCopied,
+      );
+      _writeTypeNullability(type);
+      return true;
+    }
+
+    if (type is NeverType) {
+      write('Never');
+      _writeTypeNullability(type);
+      return true;
+    }
+
+    if (type is TypeParameterType) {
+      write(type.element.name);
+      _writeTypeNullability(type);
+      return true;
+    }
+
+    if (type is VoidType) {
+      write('void');
+      return true;
+    }
+
+    throw UnimplementedError('(${type.runtimeType}) $type');
+  }
+
+  void _writeTypeElementArguments({
+    @required Element element,
+    @required List<DartType> typeArguments,
+    @required ExecutableElement methodBeingCopied,
+  }) {
     // Ensure that the element is imported.
     _writeLibraryReference(element);
 
@@ -1212,12 +1245,11 @@
     write(name);
 
     // Write type arguments.
-    if (type is ParameterizedType) {
-      var arguments = type.typeArguments;
+    if (typeArguments.isNotEmpty) {
       // Check if has arguments.
       var hasArguments = false;
       var allArgumentsVisible = true;
-      for (var argument in arguments) {
+      for (var argument in typeArguments) {
         hasArguments = hasArguments || !argument.isDynamic;
         allArgumentsVisible = allArgumentsVisible &&
             _getVisibleType(argument, methodBeingCopied: methodBeingCopied) !=
@@ -1226,8 +1258,8 @@
       // Write type arguments only if they are useful.
       if (hasArguments && allArgumentsVisible) {
         write('<');
-        for (var i = 0; i < arguments.length; i++) {
-          var argument = arguments[i];
+        for (var i = 0; i < typeArguments.length; i++) {
+          var argument = typeArguments[i];
           if (i != 0) {
             write(', ');
           }
@@ -1237,12 +1269,12 @@
         write('>');
       }
     }
+  }
 
+  void _writeTypeNullability(DartType type) {
     if (type.nullabilitySuffix == NullabilitySuffix.question) {
       write('?');
     }
-
-    return true;
   }
 }
 
diff --git a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
index 6d37cfe..430be10 100644
--- a/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
+++ b/pkg/analyzer_plugin/test/src/utilities/change_builder/change_builder_dart_test.dart
@@ -106,6 +106,14 @@
     var edit = getEdit(builder);
     expect(edit.replacement, equalsIgnoringWhitespace('required a'));
   }
+
+  Future<void> test_writeType_Never_none() async {
+    await _assertWriteType('Never');
+  }
+
+  Future<void> test_writeType_Never_question() async {
+    await _assertWriteType('Never?');
+  }
 }
 
 class DartEditBuilderImplTest extends AbstractContextTest