Skip to content

Commit 284b5cc

Browse files
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 <[email protected]> Reviewed-by: Paul Berry <[email protected]>
1 parent ada8188 commit 284b5cc

10 files changed

+152
-39
lines changed

pkg/analyzer/lib/error/error.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ const List<ErrorCode> errorCodeValues = const [
129129
CompileTimeErrorCode.FIELD_INITIALIZER_REDIRECTING_CONSTRUCTOR,
130130
CompileTimeErrorCode.FINAL_INITIALIZED_MULTIPLE_TIMES,
131131
CompileTimeErrorCode.GENERIC_FUNCTION_TYPED_PARAM_UNSUPPORTED,
132+
CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
132133
CompileTimeErrorCode.GETTER_AND_METHOD_WITH_SAME_NAME,
133134
CompileTimeErrorCode.IMPLEMENTS_DEFERRED_CLASS,
134135
CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS,

pkg/analyzer/lib/src/error/codes.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,16 @@ class CompileTimeErrorCode extends ErrorCode {
23992399
'TYPE_ARGUMENT_NOT_MATCHING_BOUNDS', "'{0}' doesn't extend '{1}'.",
24002400
correction: "Try using a type that is or is a subclass of '{1}'.");
24012401

2402+
/**
2403+
* It is a compile-time error if a generic function type is used as a bound
2404+
* for a formal type parameter of a class or a function.
2405+
*/
2406+
static const CompileTimeErrorCode GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND =
2407+
const CompileTimeErrorCode('GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND',
2408+
'Generic function types may not be used as type parameter bounds',
2409+
correction: 'Try making the free variable in the function type part'
2410+
' of the larger declaration signature');
2411+
24022412
/**
24032413
* 15.3.1 Typedef: Any self reference, either directly, or recursively via
24042414
* another typedef, is a compile time error.

pkg/analyzer/lib/src/generated/error_verifier.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,7 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
12391239
_checkForTypeParameterSupertypeOfItsBound(node);
12401240
_checkForTypeAnnotationDeferredClass(node.bound);
12411241
_checkForImplicitDynamicType(node.bound);
1242+
_checkForGenericFunctionType(node.bound);
12421243
if (_options.strongMode) node.bound?.accept(_uninstantiatedBoundChecker);
12431244
return super.visitTypeParameter(node);
12441245
}
@@ -4183,6 +4184,19 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
41834184
}
41844185
}
41854186

4187+
void _checkForGenericFunctionType(TypeAnnotation node) {
4188+
if (node == null) {
4189+
return;
4190+
}
4191+
DartType type = node.type;
4192+
if (type is FunctionType && type.typeFormals.isNotEmpty) {
4193+
_errorReporter.reportErrorForNode(
4194+
CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND,
4195+
node,
4196+
[type]);
4197+
}
4198+
}
4199+
41864200
void _checkForImplicitDynamicTypedLiteral(TypedLiteral node) {
41874201
if (_options.implicitDynamic || node.typeArguments != null) {
41884202
return;
@@ -5905,7 +5919,9 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
59055919
* Verify that the type arguments in the given [typeName] are all within
59065920
* their bounds.
59075921
*
5908-
* See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS].
5922+
* See [StaticTypeWarningCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS],
5923+
* [CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS],
5924+
* [CompileTimeErrorCode.GENERIC_FUNCTION_CANNOT_BE_BOUND].
59095925
*/
59105926
void _checkForTypeArgumentNotMatchingBounds(TypeName typeName) {
59115927
if (typeName.typeArguments == null) {

pkg/analyzer/lib/src/generated/resolver.dart

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9043,7 +9043,10 @@ class TypeParameterBoundsResolver {
90439043
TypeNameResolver typeNameResolver = null;
90449044

90459045
TypeParameterBoundsResolver(
9046-
this.typeSystem, this.library, this.source, this.errorListener);
9046+
this.typeSystem, this.library, this.source, this.errorListener)
9047+
: libraryScope = new LibraryScope(library),
9048+
typeNameResolver = new TypeNameResolver(typeSystem,
9049+
typeSystem.typeProvider, library, source, errorListener);
90479050

90489051
/**
90499052
* Resolve bounds of type parameters of classes, class and function type
@@ -9070,27 +9073,40 @@ class TypeParameterBoundsResolver {
90709073
typeNameResolver.resolveTypeName(type);
90719074
// TODO(scheglov) report error when don't apply type bounds for type bounds
90729075
} else if (type is GenericFunctionType) {
9073-
void resolveTypeParameter(TypeParameter t) {
9074-
_resolveTypeName(t.bound);
9075-
}
9076+
// While GenericFunctionTypes with free types are not allowed as bounds,
9077+
// those free types *should* ideally be recognized as type parameter types
9078+
// rather than classnames. Create a scope to accomplish that.
9079+
Scope previousScope = typeNameResolver.nameScope;
90769080

9077-
void resolveParameter(FormalParameter p) {
9078-
if (p is SimpleFormalParameter) {
9079-
_resolveTypeName(p.type);
9080-
} else if (p is DefaultFormalParameter) {
9081-
resolveParameter(p.parameter);
9082-
} else if (p is FieldFormalParameter) {
9083-
_resolveTypeName(p.type);
9084-
} else if (p is FunctionTypedFormalParameter) {
9085-
_resolveTypeName(p.returnType);
9086-
p.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
9087-
p.parameters?.parameters?.forEach(resolveParameter);
9081+
try {
9082+
Scope typeParametersScope = new TypeParameterScope(
9083+
typeNameResolver.nameScope, type.type.element);
9084+
typeNameResolver.nameScope = typeParametersScope;
9085+
9086+
void resolveTypeParameter(TypeParameter t) {
9087+
_resolveTypeName(t.bound);
9088+
}
9089+
9090+
void resolveParameter(FormalParameter p) {
9091+
if (p is SimpleFormalParameter) {
9092+
_resolveTypeName(p.type);
9093+
} else if (p is DefaultFormalParameter) {
9094+
resolveParameter(p.parameter);
9095+
} else if (p is FieldFormalParameter) {
9096+
_resolveTypeName(p.type);
9097+
} else if (p is FunctionTypedFormalParameter) {
9098+
_resolveTypeName(p.returnType);
9099+
p.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
9100+
p.parameters?.parameters?.forEach(resolveParameter);
9101+
}
90889102
}
9089-
}
90909103

9091-
_resolveTypeName(type.returnType);
9092-
type.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
9093-
type.parameters?.parameters?.forEach(resolveParameter);
9104+
_resolveTypeName(type.returnType);
9105+
type.typeParameters?.typeParameters?.forEach(resolveTypeParameter);
9106+
type.parameters?.parameters?.forEach(resolveParameter);
9107+
} finally {
9108+
typeNameResolver.nameScope = previousScope;
9109+
}
90949110
}
90959111
}
90969112

@@ -9111,10 +9127,10 @@ class TypeParameterBoundsResolver {
91119127
bound.type = typeParameterElement.bound;
91129128
}
91139129
} else {
9114-
libraryScope ??= new LibraryScope(library);
91159130
typeParametersScope ??= createTypeParametersScope();
9116-
typeNameResolver ??= new TypeNameResolver(typeSystem,
9117-
typeSystem.typeProvider, library, source, errorListener);
9131+
// _resolveTypeParameters is the entry point into each declaration
9132+
// with a separate scope. We can safely, and should, clobber the
9133+
// old scope here.
91189134
typeNameResolver.nameScope = typeParametersScope;
91199135
_resolveTypeName(bound);
91209136
typeParameterElement.bound = bound.type;

pkg/analyzer/test/generated/compile_time_error_code_kernel_test.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,41 @@ class CompileTimeErrorCodeTest_Kernel extends CompileTimeErrorCodeTest_Driver {
30983098
// Test passes, even though if fails in the superclass
30993099
await super.test_yieldInNonGenerator_async();
31003100
}
3101+
3102+
@override
3103+
@failingTest
3104+
@potentialAnalyzerProblem
3105+
test_genericFunctionTypeAsBound_class() async {
3106+
await super.test_genericFunctionTypeAsBound_class();
3107+
}
3108+
3109+
@override
3110+
@failingTest
3111+
@potentialAnalyzerProblem
3112+
test_genericFunctionTypeAsBound_genericFunction() async {
3113+
await super.test_genericFunctionTypeAsBound_genericFunction();
3114+
}
3115+
3116+
@override
3117+
@failingTest
3118+
@potentialAnalyzerProblem
3119+
test_genericFunctionTypeAsBound_genericFunctionTypedef() async {
3120+
await super.test_genericFunctionTypeAsBound_genericFunctionTypedef();
3121+
}
3122+
3123+
@override
3124+
@failingTest
3125+
@potentialAnalyzerProblem
3126+
test_genericFunctionTypeAsBound_parameterOfFunction() async {
3127+
await super.test_genericFunctionTypeAsBound_parameterOfFunction();
3128+
}
3129+
3130+
@override
3131+
@failingTest
3132+
@potentialAnalyzerProblem
3133+
test_genericFunctionTypeAsBound_typedef() async {
3134+
await super.test_genericFunctionTypeAsBound_typedef();
3135+
}
31013136
}
31023137

31033138
/// Tests marked with this annotation fail because of a Fasta problem.

pkg/analyzer/test/generated/compile_time_error_code_test.dart

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2749,6 +2749,55 @@ var b2 = const bool.fromEnvironment('x', defaultValue: 1);''');
27492749
verify([source]);
27502750
}
27512751

2752+
test_genericFunctionTypeAsBound_class() async {
2753+
Source source = addSource(r'''
2754+
class C<T extends S Function<S>(S)> {
2755+
}''');
2756+
await computeAnalysisResult(source);
2757+
assertErrors(
2758+
source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
2759+
verify([source]);
2760+
}
2761+
2762+
test_genericFunctionTypeAsBound_genericFunction() async {
2763+
Source source = addSource(r'''
2764+
T Function<T extends S Function<S>(S)>(T) fun;
2765+
''');
2766+
await computeAnalysisResult(source);
2767+
assertErrors(
2768+
source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
2769+
verify([source]);
2770+
}
2771+
2772+
test_genericFunctionTypeAsBound_genericFunctionTypedef() async {
2773+
Source source = addSource(r'''
2774+
typedef foo = T Function<T extends S Function<S>(S)>(T t);
2775+
''');
2776+
await computeAnalysisResult(source);
2777+
assertErrors(
2778+
source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
2779+
verify([source]);
2780+
}
2781+
2782+
test_genericFunctionTypeAsBound_parameterOfFunction() async {
2783+
Source source = addSource(r'''
2784+
class C<T extends void Function(S Function<S>(S))> {
2785+
}''');
2786+
await computeAnalysisResult(source);
2787+
assertNoErrors(source);
2788+
verify([source]);
2789+
}
2790+
2791+
test_genericFunctionTypeAsBound_typedef() async {
2792+
Source source = addSource(r'''
2793+
typedef T foo<T extends S Function<S>(S)>(T t);
2794+
''');
2795+
await computeAnalysisResult(source);
2796+
assertErrors(
2797+
source, [CompileTimeErrorCode.GENERIC_FUNCTION_TYPE_CANNOT_BE_BOUND]);
2798+
verify([source]);
2799+
}
2800+
27522801
test_genericFunctionTypedParameter() async {
27532802
// Once dartbug.com/28515 is fixed, this syntax should no longer generate an
27542803
// error.

pkg/analyzer/test/generated/non_error_resolver_kernel_test.dart

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -442,13 +442,6 @@ class NonErrorResolverTest_Kernel extends NonErrorResolverTest_Driver {
442442
return super.test_staticAccessToInstanceMember_annotation();
443443
}
444444

445-
@override
446-
@failingTest
447-
@potentialAnalyzerProblem
448-
test_typeArgument_boundToFunctionType() async {
449-
return super.test_typeArgument_boundToFunctionType();
450-
}
451-
452445
@override
453446
@failingTest
454447
test_undefinedGetter_static_conditionalAccess() {

pkg/analyzer/test/generated/non_error_resolver_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5386,7 +5386,7 @@ class B {
53865386
}
53875387

53885388
test_typeArgument_boundToFunctionType() async {
5389-
Source source = addSource("class A<T extends void Function<T>(T)>{}");
5389+
Source source = addSource("class A<T extends void Function(T)>{}");
53905390
await computeAnalysisResult(source);
53915391
assertNoErrors(source);
53925392
verify([source]);

pkg/analyzer/test/generated/strong_mode_kernel_test.dart

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -651,13 +651,6 @@ class StrongModeStaticTypeAnalyzer2Test_Kernel
651651
return super.test_notInstantiatedBound_error_typedef_direct();
652652
}
653653

654-
@override
655-
@failingTest
656-
test_notInstantiatedBound_ok_class_function() {
657-
// Failed to resolve 1 nodes
658-
return super.test_notInstantiatedBound_ok_class_function();
659-
}
660-
661654
@override
662655
@failingTest
663656
test_objectMethodOnFunctions_Typedef() {

pkg/analyzer/test/generated/strong_mode_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4107,7 +4107,7 @@ class C<T extends A<B, B>> {}
41074107

41084108
test_notInstantiatedBound_ok_class_function() async {
41094109
String code = r'''
4110-
class A<T extends void Function<Z>()> {}
4110+
class A<T extends void Function()> {}
41114111
class B<T extends A> {}
41124112
''';
41134113
await resolveTestUnit(code);

0 commit comments

Comments
 (0)