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 {