Report errors when non-function type alias expands to a type parameter.

Change-Id: Ibed352c45972f1ff6477566a2c2dcf98839fb44b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/176321
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index f782575..b4ca2b5 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -222,6 +222,7 @@
   CompileTimeErrorCode.INSTANCE_MEMBER_ACCESS_FROM_STATIC,
   CompileTimeErrorCode.INSTANTIATE_ABSTRACT_CLASS,
   CompileTimeErrorCode.INSTANTIATE_ENUM,
+  CompileTimeErrorCode.INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
   CompileTimeErrorCode.INTEGER_LITERAL_IMPRECISE_AS_DOUBLE,
   CompileTimeErrorCode.INTEGER_LITERAL_OUT_OF_RANGE,
   CompileTimeErrorCode.INVALID_ANNOTATION,
@@ -374,6 +375,7 @@
   CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR,
   CompileTimeErrorCode.REDIRECT_TO_NON_CLASS,
   CompileTimeErrorCode.REDIRECT_TO_NON_CONST_CONSTRUCTOR,
+  CompileTimeErrorCode.REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
   CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
   CompileTimeErrorCode.RETHROW_OUTSIDE_CATCH,
   CompileTimeErrorCode.RETURN_IN_GENERATIVE_CONSTRUCTOR,
diff --git a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
index eef2897..37497f3 100644
--- a/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
+++ b/pkg/analyzer/lib/src/dart/resolver/type_name_resolver.dart
@@ -79,7 +79,7 @@
         return;
       }
 
-      if (prefixElement is ClassElement) {
+      if (prefixElement is ClassElement || prefixElement is TypeAliasElement) {
         _rewriteToConstructorName(node, typeIdentifier);
         return;
       }
@@ -206,7 +206,7 @@
           nullabilitySuffix: nullability,
         );
         type = typeSystem.toLegacyType(type);
-        return _verifyTypeAliasForContext(node, type);
+        return _verifyTypeAliasForContext(node, element, type);
       } else if (_isInstanceCreation(node)) {
         _ErrorHelper(errorReporter).reportNewWithNonType(node);
         return dynamicType;
@@ -249,7 +249,7 @@
         typeAliasElement: element,
         nullabilitySuffix: nullability,
       );
-      return _verifyTypeAliasForContext(node, type);
+      return _verifyTypeAliasForContext(node, element, type);
     } else if (_isInstanceCreation(node)) {
       _ErrorHelper(errorReporter).reportNewWithNonType(node);
       return dynamicType;
@@ -367,7 +367,19 @@
     }
   }
 
-  DartType _verifyTypeAliasForContext(TypeName node, DartType type) {
+  DartType _verifyTypeAliasForContext(
+    TypeName node,
+    TypeAliasElement element,
+    DartType type,
+  ) {
+    if (element.aliasedType is TypeParameterType) {
+      var constructorName = node.parent;
+      if (constructorName is ConstructorName) {
+        _ErrorHelper(errorReporter)
+            .reportTypeAliasExpandsToTypeParameter(constructorName, element);
+        return dynamicType;
+      }
+    }
     if (type is! InterfaceType && _isInstanceCreation(node)) {
       _ErrorHelper(errorReporter).reportNewWithNonType(node);
       return dynamicType;
@@ -522,6 +534,28 @@
     );
   }
 
+  void reportTypeAliasExpandsToTypeParameter(
+    ConstructorName constructorName,
+    TypeAliasElement element,
+  ) {
+    var errorNode = _getErrorNode(constructorName.type);
+    var constructorUsage = constructorName.parent;
+    if (constructorUsage is InstanceCreationExpression) {
+      errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
+        errorNode,
+      );
+    } else if (constructorUsage is ConstructorDeclaration &&
+        constructorUsage.redirectedConstructor == constructorName) {
+      errorReporter.reportErrorForNode(
+        CompileTimeErrorCode.REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
+        errorNode,
+      );
+    } else {
+      throw UnimplementedError('${constructorUsage.runtimeType}');
+    }
+  }
+
   /// Returns the simple identifier of the given (maybe prefixed) identifier.
   static Identifier _getErrorNode(TypeName node) {
     Identifier identifier = node.name;
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 22fbfd8..2acc9c4 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -4922,6 +4922,18 @@
       correction: "Try using one of the defined constants.");
 
   /**
+   * It is a compile-time error for an instance creation `C<T1, .. Tk>(...)` or
+   * `C<T1, .. Tk>.name()` (where `k` may be zero, which means that the type
+   * argument list is absent) if `C` denotes a type alias that expands to a
+   * type variable.
+   */
+  static const CompileTimeErrorCode
+      INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER = CompileTimeErrorCode(
+          'INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER',
+          "Type aliases that expand to a type parameter can't be instantiated.",
+          correction: "Try replacing it with a class.");
+
+  /**
    * An integer literal with static type `double` and numeric value `i`
    * evaluates to an instance of the `double` class representing the value `i`.
    * It is a compile-time error if the value `i` cannot be represented
@@ -8999,6 +9011,19 @@
           correction: "Try redirecting to a different constructor.");
 
   /**
+   * It is a compile-time error for a redirecting factory constructor to have
+   * a body which is a type alias that expands to a type variable, or a body
+   * which is a parameterized type of the form `F<T1, .. Tk>`, where `F` is
+   * a type alias that expands to a type variable.
+   */
+  static const CompileTimeErrorCode
+      REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER = CompileTimeErrorCode(
+          'REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER',
+          "Redirecting constructor can't redirect to a type alias "
+              "that expands to a type parameter.",
+          correction: "Try replacing it with a class.");
+
+  /**
    * No parameters.
    */
   // #### Description
diff --git a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
index f802202..f739c14 100644
--- a/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
+++ b/pkg/analyzer/test/src/dart/resolution/type_name_test.dart
@@ -422,6 +422,19 @@
     );
   }
 
+  test_typeAlias_asParameterType_question() async {
+    await assertNoErrorsInCode(r'''
+typedef X<T> = T?;
+void f(X<int> a) {}
+''');
+
+    assertTypeName(
+      findNode.typeName('X<int>'),
+      findElement.typeAlias('X'),
+      'int?',
+    );
+  }
+
   test_typeAlias_asReturnType_interfaceType() async {
     await assertNoErrorsInCode(r'''
 typedef X<T> = Map<int, T>;
diff --git a/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart b/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart
new file mode 100644
index 0000000..0cb16f19
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/instantiate_type_alias_expands_to_type_parameter_test.dart
@@ -0,0 +1,114 @@
+// 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/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(InstantiateTypeAliasExpandsToTypeParameterTest);
+  });
+}
+
+@reflectiveTest
+class InstantiateTypeAliasExpandsToTypeParameterTest
+    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+  CompileTimeErrorCode get _errorCode =>
+      CompileTimeErrorCode.INSTANTIATE_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER;
+
+  test_const_generic_noArguments_unnamed_typeParameter() async {
+    await assertErrorsInCode(r'''
+typedef A<T> = T;
+
+void f() {
+  const A();
+}
+''', [
+      error(_errorCode, 38, 1),
+    ]);
+  }
+
+  test_const_notGeneric_unnamed_class() async {
+    await assertNoErrorsInCode(r'''
+class A {
+  const A();
+}
+
+typedef X = A;
+
+void f() {
+  const X();
+}
+''');
+  }
+
+  test_new_generic_noArguments_unnamed_typeParameter() async {
+    await assertErrorsInCode(r'''
+typedef A<T> = T;
+
+void f() {
+  new A();
+}
+''', [
+      error(_errorCode, 36, 1),
+    ]);
+  }
+
+  test_new_generic_withArgument_named_typeParameter() async {
+    await assertErrorsInCode(r'''
+class A {
+  A.named();
+}
+
+typedef B<T> = T;
+
+void f() {
+  new B<A>.named();
+}
+''', [
+      error(_errorCode, 62, 1),
+    ]);
+  }
+
+  test_new_generic_withArgument_unnamed_typeParameter() async {
+    await assertErrorsInCode(r'''
+class A {}
+
+typedef B<T> = T;
+
+void f() {
+  new B<A>();
+}
+''', [
+      error(_errorCode, 48, 1),
+    ]);
+  }
+
+  test_new_notGeneric_unnamed_class() async {
+    await assertNoErrorsInCode(r'''
+class A {}
+
+typedef X = A;
+
+void f() {
+  new X();
+}
+''');
+  }
+
+  test_new_notGeneric_unnamed_typeParameter2() async {
+    await assertErrorsInCode(r'''
+typedef A<T> = T;
+typedef B<T> = A<T>;
+
+void f() {
+  new B();
+}
+''', [
+      error(_errorCode, 57, 1),
+    ]);
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart b/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart
new file mode 100644
index 0000000..dbe1342
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/redirect_to_type_alias_expands_to_type_parameter_test.dart
@@ -0,0 +1,97 @@
+// 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/src/error/codes.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../dart/resolution/context_collection_resolution.dart';
+
+main() {
+  defineReflectiveSuite(() {
+    defineReflectiveTests(RedirectTypeAliasExpandsToTypeParameterTest);
+  });
+}
+
+@reflectiveTest
+class RedirectTypeAliasExpandsToTypeParameterTest
+    extends PubPackageResolutionTest with WithNonFunctionTypeAliasesMixin {
+  test_generic_typeParameter_withArgument_named() async {
+    await assertErrorsInCode(r'''
+class A implements C {
+  A.named();
+}
+
+typedef B<T> = T;
+
+class C {
+  factory C() = B<A>.named;
+}
+''', [
+      error(
+          CompileTimeErrorCode.REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
+          84,
+          1),
+    ]);
+  }
+
+  test_generic_typeParameter_withArgument_unnamed() async {
+    await assertErrorsInCode(r'''
+class A implements C {}
+
+typedef B<T> = T;
+
+class C {
+  factory C() = B<A>;
+}
+''', [
+      error(
+          CompileTimeErrorCode.REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
+          70,
+          1),
+    ]);
+  }
+
+  test_generic_typeParameter_withoutArgument_unnamed() async {
+    await assertErrorsInCode(r'''
+class A implements C {}
+
+typedef B<T> = T;
+
+class C {
+  factory C() = B;
+}
+''', [
+      error(
+          CompileTimeErrorCode.REDIRECT_TO_TYPE_ALIAS_EXPANDS_TO_TYPE_PARAMETER,
+          70,
+          1),
+    ]);
+  }
+
+  test_notGeneric_class_named() async {
+    await assertNoErrorsInCode(r'''
+class A implements C {
+  A.named();
+}
+
+typedef B = A;
+
+class C {
+  factory C() = B.named;
+}
+''');
+  }
+
+  test_notGeneric_class_unnamed() async {
+    await assertNoErrorsInCode(r'''
+class A implements C {}
+
+typedef B = A;
+
+class C {
+  factory C() = B;
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 05d7fb0..d8c3e4e 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -267,6 +267,8 @@
     as instance_member_access_from_static;
 import 'instantiate_abstract_class_test.dart' as instantiate_abstract_class;
 import 'instantiate_enum_test.dart' as instantiate_enum;
+import 'instantiate_type_alias_expands_to_type_parameter_test.dart'
+    as instantiate_type_alias_expands_to_type_parameter;
 import 'integer_literal_imprecise_as_double_test.dart'
     as integer_literal_imprecise_as_double;
 import 'integer_literal_out_of_range_test.dart' as integer_literal_out_of_range;
@@ -527,6 +529,8 @@
 import 'redirect_to_non_class_test.dart' as redirect_to_non_class;
 import 'redirect_to_non_const_constructor_test.dart'
     as redirect_to_non_const_constructor;
+import 'redirect_to_type_alias_expands_to_type_parameter_test.dart'
+    as redirect_to_type_alias_expands_to_type_parameter;
 import 'referenced_before_declaration_test.dart'
     as referenced_before_declaration;
 import 'rethrow_outside_catch_test.dart' as rethrow_outside_catch;
@@ -833,6 +837,7 @@
     instance_member_access_from_static.main();
     instantiate_abstract_class.main();
     instantiate_enum.main();
+    instantiate_type_alias_expands_to_type_parameter.main();
     integer_literal_imprecise_as_double.main();
     integer_literal_out_of_range.main();
     invalid_annotation.main();
@@ -1005,6 +1010,7 @@
     redirect_to_missing_constructor.main();
     redirect_to_non_class.main();
     redirect_to_non_const_constructor.main();
+    redirect_to_type_alias_expands_to_type_parameter.main();
     referenced_before_declaration.main();
     rethrow_outside_catch.main();
     return_in_generative_constructor.main();
diff --git a/pkg/analyzer/test/src/summary/resynthesize_common.dart b/pkg/analyzer/test/src/summary/resynthesize_common.dart
index 99a5202..a7c9e90 100644
--- a/pkg/analyzer/test/src/summary/resynthesize_common.dart
+++ b/pkg/analyzer/test/src/summary/resynthesize_common.dart
@@ -5119,6 +5119,30 @@
 ''');
   }
 
+  test_constructor_redirected_factory_named_generic_viaTypeAlias() async {
+    featureSet = FeatureSets.nonFunctionTypeAliases;
+    var library = await checkLibrary('''
+typedef A<T, U> = C<T, U>;
+class B<T, U> {
+  factory B() = A<U, T>.named;
+  B._();
+}
+class C<T, U> extends A<U, T> {
+  C.named() : super._();
+}
+''');
+    checkElementText(library, r'''
+typedef A<T, U> = C<T, U>;
+class B<T, U> {
+  factory B() = C<U, T>.named;
+  B._();
+}
+class C<T, U> extends C<U, T> {
+  C.named();
+}
+''');
+  }
+
   test_constructor_redirected_factory_named_imported() async {
     addLibrarySource('/foo.dart', '''
 import 'test.dart';
@@ -5282,6 +5306,30 @@
 ''');
   }
 
+  test_constructor_redirected_factory_unnamed_generic_viaTypeAlias() async {
+    featureSet = FeatureSets.nonFunctionTypeAliases;
+    var library = await checkLibrary('''
+typedef A<T, U> = C<T, U>;
+class B<T, U> {
+  factory B() = A<U, T>;
+  B_();
+}
+class C<T, U> extends B<U, T> {
+  C() : super._();
+}
+''');
+    checkElementText(library, r'''
+typedef A<T, U> = C<T, U>;
+class B<T, U> {
+  factory B() = C<U, T>;
+  dynamic B_();
+}
+class C<T, U> extends B<U, T> {
+  C();
+}
+''');
+  }
+
   test_constructor_redirected_factory_unnamed_imported() async {
     addLibrarySource('/foo.dart', '''
 import 'test.dart';
@@ -5328,6 +5376,31 @@
 ''');
   }
 
+  test_constructor_redirected_factory_unnamed_imported_viaTypeAlias() async {
+    featureSet = FeatureSets.nonFunctionTypeAliases;
+    addLibrarySource('/foo.dart', '''
+import 'test.dart';
+typedef A = B;
+class B extends C {
+  B() : super._();
+}
+''');
+    var library = await checkLibrary('''
+import 'foo.dart';
+class C {
+  factory C() = A;
+  C._();
+}
+''');
+    checkElementText(library, r'''
+import 'foo.dart';
+class C {
+  factory C() = B;
+  C._();
+}
+''');
+  }
+
   test_constructor_redirected_factory_unnamed_prefixed() async {
     addLibrarySource('/foo.dart', '''
 import 'test.dart';
@@ -5374,6 +5447,31 @@
 ''');
   }
 
+  test_constructor_redirected_factory_unnamed_prefixed_viaTypeAlias() async {
+    featureSet = FeatureSets.nonFunctionTypeAliases;
+    addLibrarySource('/foo.dart', '''
+import 'test.dart';
+typedef A = B;
+class B extends C {
+  B() : super._();
+}
+''');
+    var library = await checkLibrary('''
+import 'foo.dart' as foo;
+class C {
+  factory C() = foo.A;
+  C._();
+}
+''');
+    checkElementText(library, r'''
+import 'foo.dart' as foo;
+class C {
+  factory C() = B;
+  C._();
+}
+''');
+  }
+
   test_constructor_redirected_factory_unnamed_unresolved() async {
     var library = await checkLibrary('''
 class C<E> {
@@ -5387,6 +5485,30 @@
 ''');
   }
 
+  test_constructor_redirected_factory_unnamed_viaTypeAlias() async {
+    featureSet = FeatureSets.nonFunctionTypeAliases;
+    var library = await checkLibrary('''
+typedef A = C;
+class B {
+  factory B() = A;
+  B._();
+}
+class C extends B {
+  C() : super._();
+}
+''');
+    checkElementText(library, r'''
+typedef A = C;
+class B {
+  factory B() = C;
+  B._();
+}
+class C extends B {
+  C();
+}
+''');
+  }
+
   test_constructor_redirected_thisInvocation_named() async {
     var library = await checkLibrary('''
 class C {