Partial fix of #33343. Reject generic function types as bounds.
Does not handle the other half of #33343, rejecting generic function
types as type arguments.
This revealed a secondary minor issue which was easy enough to fix. Type
arguments were not resolved within bounds:
class C<T extends S Function<S>(S)> {}
this would report 'undefined class S' for the return and parameter types
`S`. I almost split this into a separate CL, but, these two CLs are tied
together inherently by the tests case. Easy to solve at once.
Bug: 33343
Change-Id: Ib34a04d90be08d8d6c6f21a9d485a452017585ba
Reviewed-on: https://dart-review.googlesource.com/61103
Commit-Queue: Mike Fairhurst <mfairhurst@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index a47d7a0..316443a 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -129,6 +129,7 @@
CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR,
CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES,
CompileTimeErrorCode.GENERIC_FUNCTION_TYPED_PARAM_UNSUPPORTED,
+ CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME,
CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS,
CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS,
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index 4be6d72..e071c48 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -2400,6 +2400,16 @@
correction: "Try using a type that is or is a subclass of '{1}'.");
/**
+ * It is a compile-time error if a generic function type is used as a bound
+ * for a formal type parameter of a class or a function.
+ */
+ static const CompileTimeErrorCode GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND =
+ const CompileTimeErrorCode('GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND',
+ 'Generic function types may not be used as type parameter bounds',
+ correction: 'Try making the free variable in the function type part'
+ ' of the larger declaration signature');
+
+ /**
* 15.3.1 Typedef: Any self reference, either directly, or recursively via
* another typedef, is a compile time error.
*/
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 204542e..7cf79c0 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1239,6 +1239,7 @@
_checkForTypeParameterSupertypeOfItsBound(node);
_checkForTypeAnnotationDeferredClass(node.bound);
_checkForImplicitDynamicType(node.bound);
+ _checkForGenericFunctionType(node.bound);
if (_options.strongMode) node.bound?.accept(_uninstantiatedBoundChecker);
return super.visitTypeParameter(node);
}
@@ -4183,6 +4184,19 @@
}
}
+ void _checkForGenericFunctionType(TypeAnnotation node) {
+ if (node == null) {
+ return;
+ }
+ DartType type = node.type;
+ if (type is FunctionType && type.typeFormals.isNotEmpty) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
+ node,
+ [type]);
+ }
+ }
+
void _checkForImplicitDynamicTypedLiteral(TypedLiteral node) {
if (_options.implicitDynamic || node.typeArguments != null) {
return;
@@ -5905,7 +5919,9 @@
* Verify that the type arguments in the given [typeName] are all within
* their bounds.
*
- * See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS].
+ * See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS],
+ * [CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS],
+ * [CompileTimeErrorCode.GENERIC_FUNCTION_CANNOT_BE_BOUND].
*/
void _checkForTypeArgumentNotMatchingBounds(TypeName typeName) {
if (typeName.typeArguments == null) {
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 663b76e..685704b 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -9043,7 +9043,10 @@
TypeNameResolver typeNameResolver = null;
TypeParameterBoundsResolver(
- this.typeSystem, this.library, this.source, this.errorListener);
+ this.typeSystem, this.library, this.source, this.errorListener)
+ : libraryScope = new LibraryScope(library),
+ typeNameResolver = new TypeNameResolver(typeSystem,
+ typeSystem.typeProvider, library, source, errorListener);
/**
* Resolve bounds of type parameters of classes, class and function type
@@ -9070,27 +9073,40 @@
typeNameResolver.resolveTypeName(type);
// TODO(scheglov) report error when don't apply type bounds for type bounds
} else if (type is GenericFunctionType) {
- void resolveTypeParameter(TypeParameter t) {
- _resolveTypeName(t.bound);
- }
+ // While GenericFunctionTypes with free types are not allowed as bounds,
+ // those free types *should* ideally be recognized as type parameter types
+ // rather than classnames. Create a scope to accomplish that.
+ Scope previousScope = typeNameResolver.nameScope;
- void resolveParameter(FormalParameter p) {
- if (p is SimpleFormalParameter) {
- _resolveTypeName(p.type);
- } else if (p is DefaultFormalParameter) {
- resolveParameter(p.parameter);
- } else if (p is FieldFormalParameter) {
- _resolveTypeName(p.type);
- } else if (p is FunctionTypedFormalParameter) {
- _resolveTypeName(p.returnType);
- p.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
- p.parameters?.parameters?.forEach(resolveParameter);
+ try {
+ Scope typeParametersScope = new TypeParameterScope(
+ typeNameResolver.nameScope, type.type.element);
+ typeNameResolver.nameScope = typeParametersScope;
+
+ void resolveTypeParameter(TypeParameter t) {
+ _resolveTypeName(t.bound);
}
- }
- _resolveTypeName(type.returnType);
- type.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
- type.parameters?.parameters?.forEach(resolveParameter);
+ void resolveParameter(FormalParameter p) {
+ if (p is SimpleFormalParameter) {
+ _resolveTypeName(p.type);
+ } else if (p is DefaultFormalParameter) {
+ resolveParameter(p.parameter);
+ } else if (p is FieldFormalParameter) {
+ _resolveTypeName(p.type);
+ } else if (p is FunctionTypedFormalParameter) {
+ _resolveTypeName(p.returnType);
+ p.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
+ p.parameters?.parameters?.forEach(resolveParameter);
+ }
+ }
+
+ _resolveTypeName(type.returnType);
+ type.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
+ type.parameters?.parameters?.forEach(resolveParameter);
+ } finally {
+ typeNameResolver.nameScope = previousScope;
+ }
}
}
@@ -9111,10 +9127,10 @@
bound.type = typeParameterElement.bound;
}
} else {
- libraryScope ??= new LibraryScope(library);
typeParametersScope ??= createTypeParametersScope();
- typeNameResolver ??= new TypeNameResolver(typeSystem,
- typeSystem.typeProvider, library, source, errorListener);
+ // _resolveTypeParameters is the entry point into each declaration
+ // with a separate scope. We can safely, and should, clobber the
+ // old scope here.
typeNameResolver.nameScope = typeParametersScope;
_resolveTypeName(bound);
typeParameterElement.bound = bound.type;
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_kernel_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_kernel_test.dart
index 67aa952..8232bc7 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_kernel_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_kernel_test.dart
@@ -3098,6 +3098,41 @@
// Test passes, even though if fails in the superclass
await super.test_yieldInNonGenerator_async();
}
+
+ @override
+ @failingTest
+ @potentialAnalyzerProblem
+ test_genericFunctionTypeAsBound_class() async {
+ await super.test_genericFunctionTypeAsBound_class();
+ }
+
+ @override
+ @failingTest
+ @potentialAnalyzerProblem
+ test_genericFunctionTypeAsBound_genericFunction() async {
+ await super.test_genericFunctionTypeAsBound_genericFunction();
+ }
+
+ @override
+ @failingTest
+ @potentialAnalyzerProblem
+ test_genericFunctionTypeAsBound_genericFunctionTypedef() async {
+ await super.test_genericFunctionTypeAsBound_genericFunctionTypedef();
+ }
+
+ @override
+ @failingTest
+ @potentialAnalyzerProblem
+ test_genericFunctionTypeAsBound_parameterOfFunction() async {
+ await super.test_genericFunctionTypeAsBound_parameterOfFunction();
+ }
+
+ @override
+ @failingTest
+ @potentialAnalyzerProblem
+ test_genericFunctionTypeAsBound_typedef() async {
+ await super.test_genericFunctionTypeAsBound_typedef();
+ }
}
/// Tests marked with this annotation fail because of a Fasta problem.
diff --git a/pkg/analyzer/test/generated/compile_time_error_code_test.dart b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
index 6923bc6..8b91102 100644
--- a/pkg/analyzer/test/generated/compile_time_error_code_test.dart
+++ b/pkg/analyzer/test/generated/compile_time_error_code_test.dart
@@ -2749,6 +2749,55 @@
verify([source]);
}
+ test_genericFunctionTypeAsBound_class() async {
+ Source source = addSource(r'''
+class C<T extends S Function<S>(S)> {
+}''');
+ await computeAnalysisResult(source);
+ assertErrors(
+ source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
+ verify([source]);
+ }
+
+ test_genericFunctionTypeAsBound_genericFunction() async {
+ Source source = addSource(r'''
+T Function<T extends S Function<S>(S)>(T) fun;
+''');
+ await computeAnalysisResult(source);
+ assertErrors(
+ source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
+ verify([source]);
+ }
+
+ test_genericFunctionTypeAsBound_genericFunctionTypedef() async {
+ Source source = addSource(r'''
+typedef foo = T Function<T extends S Function<S>(S)>(T t);
+''');
+ await computeAnalysisResult(source);
+ assertErrors(
+ source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
+ verify([source]);
+ }
+
+ test_genericFunctionTypeAsBound_parameterOfFunction() async {
+ Source source = addSource(r'''
+class C<T extends void Function(S Function<S>(S))> {
+}''');
+ await computeAnalysisResult(source);
+ assertNoErrors(source);
+ verify([source]);
+ }
+
+ test_genericFunctionTypeAsBound_typedef() async {
+ Source source = addSource(r'''
+typedef T foo<T extends S Function<S>(S)>(T t);
+''');
+ await computeAnalysisResult(source);
+ assertErrors(
+ source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
+ verify([source]);
+ }
+
test_genericFunctionTypedParameter() async {
// Once dartbug.com/28515 is fixed, this syntax should no longer generate an
// error.
diff --git a/pkg/analyzer/test/generated/non_error_resolver_kernel_test.dart b/pkg/analyzer/test/generated/non_error_resolver_kernel_test.dart
index 7b3f14b..b080ee6 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_kernel_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_kernel_test.dart
@@ -444,13 +444,6 @@
@override
@failingTest
- @potentialAnalyzerProblem
- test_typeArgument_boundToFunctionType() async {
- return super.test_typeArgument_boundToFunctionType();
- }
-
- @override
- @failingTest
test_undefinedGetter_static_conditionalAccess() {
// Bad state: No data for A at 36
return super.test_undefinedGetter_static_conditionalAccess();
diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart
index 1084149..6a12526 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart
@@ -5386,7 +5386,7 @@
}
test_typeArgument_boundToFunctionType() async {
- Source source = addSource("class A<T extends void Function<T>(T)>{}");
+ Source source = addSource("class A<T extends void Function(T)>{}");
await computeAnalysisResult(source);
assertNoErrors(source);
verify([source]);
diff --git a/pkg/analyzer/test/generated/strong_mode_kernel_test.dart b/pkg/analyzer/test/generated/strong_mode_kernel_test.dart
index 9157b6d..0e94294 100644
--- a/pkg/analyzer/test/generated/strong_mode_kernel_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_kernel_test.dart
@@ -653,13 +653,6 @@
@override
@failingTest
- test_notInstantiatedBound_ok_class_function() {
- // Failed to resolve 1 nodes
- return super.test_notInstantiatedBound_ok_class_function();
- }
-
- @override
- @failingTest
test_objectMethodOnFunctions_Typedef() {
// UnimplementedError: TODO(paulberry): resynthesize generic typedef
return super.test_objectMethodOnFunctions_Typedef();
diff --git a/pkg/analyzer/test/generated/strong_mode_test.dart b/pkg/analyzer/test/generated/strong_mode_test.dart
index 1414b83..a84ddc6 100644
--- a/pkg/analyzer/test/generated/strong_mode_test.dart
+++ b/pkg/analyzer/test/generated/strong_mode_test.dart
@@ -4107,7 +4107,7 @@
test_notInstantiatedBound_ok_class_function() async {
String code = r'''
-class A<T extends void Function<Z>()> {}
+class A<T extends void Function()> {}
class B<T extends A> {}
''';
await resolveTestUnit(code);