From f5a3bce734ac8b77e94c255899d65997c4677ace Mon Sep 17 00:00:00 2001 From: Paul Berry Date: Tue, 17 Aug 2021 15:17:02 +0000 Subject: [PATCH] Fix scope resolution of metadata on type parameters Previously, when encountering identifiers in metadata on a class's type parameter, the analyzer would resolve them using the type parameter scope, but then fall back on using implicit `this`. The CFE would resolve them using the class body scope. In both cases, the end result was that the annotation could refer to static class members. This change brings the behavior of both the analyzer and the CFE in line with the spec, by preventing the use of implicit `this` in these annotations, and resolving them in the type parameter scope. This is not expected to break any code in practice, because annotations on type parameters are rare, as are annotations referring to static class members, and the overlap between these two should be negligibly small. Fixes https://github.com/dart-lang/language/issues/1790. Bug: https://github.com/dart-lang/language/issues/1790 Change-Id: Ibe5a421e04a53d29074a8b1509e1390658ed72e5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210040 Commit-Queue: Paul Berry Reviewed-by: Johnni Winther Reviewed-by: Konstantin Shcheglov --- CHANGELOG.md | 23 +++ pkg/analyzer/lib/src/generated/resolver.dart | 11 -- .../undefined_identifier_test.dart | 155 +++++++++--------- .../lib/src/fasta/builder/class_builder.dart | 8 +- .../lib/src/fasta/builder/field_builder.dart | 4 +- .../src/fasta/builder/function_builder.dart | 8 +- .../src/fasta/builder/metadata_builder.dart | 8 +- .../fasta/builder/type_variable_builder.dart | 5 +- .../source/source_extension_builder.dart | 6 +- .../fasta/source/source_library_builder.dart | 2 +- .../source/source_type_alias_builder.dart | 24 ++- .../type_parameter_scope_inner_test.dart | 66 ++++++++ .../type_parameter_scope_other_test.dart | 51 ++++++ .../type_parameter_scope_inner_test.dart | 66 ++++++++ .../type_parameter_scope_other_test.dart | 51 ++++++ tests/lib/mirrors/metadata_scope_test.dart | 6 +- tests/lib_2/mirrors/metadata_scope_test.dart | 6 +- 17 files changed, 388 insertions(+), 112 deletions(-) create mode 100644 tests/language/metadata/type_parameter_scope_inner_test.dart create mode 100644 tests/language/metadata/type_parameter_scope_other_test.dart create mode 100644 tests/language_2/metadata/type_parameter_scope_inner_test.dart create mode 100644 tests/language_2/metadata/type_parameter_scope_other_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6512f46df76..944c8345ba31d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +## 2.15.0 + +### Language + +- Annotations on type parameters of classes can no longer refer to class members + without a prefix. For example, this used to be permitted: + + ```dart + class C<@Annotation(foo) T> { + static void foo() {} + } + ``` + + Now, the reference must be qualified with the class name, i.e.: + + ```dart + class C<@Annotation(C.foo) T> { + static void foo() {} + } + ``` + + This brings the implementation behavior in line with the spec. + ## 2.14.0 ### Language diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index 9fad340990dc4..92573bd1903bf 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -2007,17 +2007,6 @@ class ResolverVisitor extends ResolverBase with ErrorDetectionHelpers { @override void visitTypeName(TypeName node) {} - @override - void visitTypeParameter(TypeParameter node) { - var previousThisType = _thisType; - try { - _setupThisType(); - super.visitTypeParameter(node); - } finally { - _thisType = previousThisType; - } - } - @override void visitVariableDeclaration(VariableDeclaration node) { _variableDeclarationResolver.resolve(node as VariableDeclarationImpl); diff --git a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart index c339039bf985c..c484dd0c2d88e 100644 --- a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart +++ b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart @@ -43,61 +43,6 @@ void f(int value) { } mixin UndefinedIdentifierTestCases on PubPackageResolutionTest { - test_annotation_favors_scope_resolution_over_this_resolution_class() async { - // If an annotation on a class type parameter cannot be resolved using the - // normal scope resolution mechanism, it is resolved via implicit `this`. - // Note: this behavior doesn't match the spec so we may change it - see - // https://github.com/dart-lang/language/issues/1790 - await assertNoErrorsInCode(''' -class C<@Annotation.function(foo) @Annotation.type(B) T> { - static void foo() {} - static void B() {} -} -class B {} -class Annotation { - const Annotation.function(void Function() f); - const Annotation.type(Type t); -} -'''); - } - - test_annotation_favors_scope_resolution_over_this_resolution_extension() async { - // If an annotation on an extension type parameter cannot be resolved using - // the normal scope resolution mechanism, it is resolved via implicit - // `this`. Note: this behavior doesn't match the spec so we may change it - - // see https://github.com/dart-lang/language/issues/1790 - await assertNoErrorsInCode(''' -extension E<@Annotation.function(foo) @Annotation.type(B) T> on C {} -class C { - static void foo() {} - static void B() {} -} -class B {} -class Annotation { - const Annotation.function(void Function() f); - const Annotation.type(Type t); -} -'''); - } - - test_annotation_favors_scope_resolution_over_this_resolution_mixin() async { - // If an annotation on a mixin type parameter cannot be resolved using the - // normal scope resolution mechanism, it is resolved via implicit `this`. - // Note: this behavior doesn't match the spec so we may change it - see - // https://github.com/dart-lang/language/issues/1790 - await assertNoErrorsInCode(''' -mixin M<@Annotation.function(foo) @Annotation.type(B) T> { - static void foo() {} - static void B() {} -} -class B {} -class Annotation { - const Annotation.function(void Function() f); - const Annotation.type(Type t); -} -'''); - } - test_annotation_references_static_method_in_class() async { await assertErrorsInCode(''' @Annotation(foo) @@ -114,18 +59,19 @@ class Annotation { } test_annotation_references_static_method_in_class_from_type_parameter() async { - // It is allowed for an annotation of a class type parameter to refer to - // a method in a class (note: this doesn't match the spec but we currently - // test it to make sure we match CFE behavior - see - // https://github.com/dart-lang/language/issues/1790) - await assertNoErrorsInCode(''' + // It not is allowed for an annotation of a class type parameter to refer to + // a method in a class. + await assertErrorsInCode(''' class C<@Annotation(foo) T> { static void foo() {} } class Annotation { const Annotation(dynamic d); } -'''); +''', [ + error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 20, 3), + error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 20, 3), + ]); } test_annotation_references_static_method_in_extension() async { @@ -144,18 +90,19 @@ class Annotation { } test_annotation_references_static_method_in_extension_from_type_parameter() async { - // It is allowed for an annotation of a mixin type parameter to refer to - // a method in a class (note: this doesn't match the spec but we currently - // test it to make sure we match CFE behavior - see - // https://github.com/dart-lang/language/issues/1790) - await assertNoErrorsInCode(''' + // It is not allowed for an annotation of an extension type parameter to + // refer to a method in a class. + await assertErrorsInCode(''' extension E<@Annotation(foo) T> on T { static void foo() {} } class Annotation { const Annotation(dynamic d); } -'''); +''', [ + error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 24, 3), + error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 24, 3), + ]); } test_annotation_references_static_method_in_mixin() async { @@ -174,18 +121,80 @@ class Annotation { } test_annotation_references_static_method_in_mixin_from_type_parameter() async { - // It is allowed for an annotation of a mixin type parameter to refer to - // a method in a class (note: this doesn't match the spec but we currently - // test it to make sure we match CFE behavior - see - // https://github.com/dart-lang/language/issues/1790) - await assertNoErrorsInCode(''' + // It is not allowed for an annotation of a mixin type parameter to refer to + // a method in a class. + await assertErrorsInCode(''' mixin M<@Annotation(foo) T> { static void foo() {} } class Annotation { const Annotation(dynamic d); } -'''); +''', [ + error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 20, 3), + error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 20, 3), + ]); + } + + test_annotation_uses_scope_resolution_class() async { + // If an annotation on a class type parameter cannot be resolved using the + // normal scope resolution mechanism, it is not resolved via implicit + // `this`. + await assertErrorsInCode(''' +class C<@Annotation.function(foo) @Annotation.type(B) T> { + static void foo() {} + static void B() {} +} +class B {} +class Annotation { + const Annotation.function(void Function() f); + const Annotation.type(Type t); +} +''', [ + error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 29, 3), + error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 29, 3), + ]); + } + + test_annotation_uses_scope_resolution_extension() async { + // If an annotation on an extension type parameter cannot be resolved using + // the normal scope resolution mechanism, it is not resolved via implicit + // `this`. + await assertErrorsInCode(''' +extension E<@Annotation.function(foo) @Annotation.type(B) T> on C {} +class C { + static void foo() {} + static void B() {} +} +class B {} +class Annotation { + const Annotation.function(void Function() f); + const Annotation.type(Type t); +} +''', [ + error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 33, 3), + error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 33, 3), + ]); + } + + test_annotation_uses_scope_resolution_mixin() async { + // If an annotation on a mixin type parameter cannot be resolved using the + // normal scope resolution mechanism, it is not resolved via implicit + // `this`. + await assertErrorsInCode(''' +mixin M<@Annotation.function(foo) @Annotation.type(B) T> { + static void foo() {} + static void B() {} +} +class B {} +class Annotation { + const Annotation.function(void Function() f); + const Annotation.type(Type t); +} +''', [ + error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 29, 3), + error(CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT, 29, 3), + ]); } @failingTest diff --git a/pkg/front_end/lib/src/fasta/builder/class_builder.dart b/pkg/front_end/lib/src/fasta/builder/class_builder.dart index 5d66e3c13feb1..eaf482ead2158 100644 --- a/pkg/front_end/lib/src/fasta/builder/class_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/class_builder.dart @@ -354,12 +354,12 @@ abstract class ClassBuilderImpl extends DeclarationBuilderImpl delayedActionPerformers, synthesizedFunctionNodes); } - MetadataBuilder.buildAnnotations( - isPatch ? origin.cls : cls, metadata, library, this, null, fileUri); + MetadataBuilder.buildAnnotations(isPatch ? origin.cls : cls, metadata, + library, this, null, fileUri, library.scope); if (typeVariables != null) { for (int i = 0; i < typeVariables!.length; i++) { - typeVariables![i].buildOutlineExpressions( - library, this, null, coreTypes, delayedActionPerformers); + typeVariables![i].buildOutlineExpressions(library, this, null, + coreTypes, delayedActionPerformers, scope.parent!); } } diff --git a/pkg/front_end/lib/src/fasta/builder/field_builder.dart b/pkg/front_end/lib/src/fasta/builder/field_builder.dart index eceff880dd2d3..e898f399e27ac 100644 --- a/pkg/front_end/lib/src/fasta/builder/field_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/field_builder.dart @@ -400,8 +400,8 @@ class SourceFieldBuilder extends MemberBuilderImpl implements FieldBuilder { _fieldEncoding.completeSignature(coreTypes); for (Annotatable annotatable in _fieldEncoding.annotatables) { - MetadataBuilder.buildAnnotations( - annotatable, metadata, library, classBuilder, this, fileUri); + MetadataBuilder.buildAnnotations(annotatable, metadata, library, + classBuilder, this, fileUri, classBuilder?.scope ?? library.scope); } // For modular compilation we need to include initializers of all const diff --git a/pkg/front_end/lib/src/fasta/builder/function_builder.dart b/pkg/front_end/lib/src/fasta/builder/function_builder.dart index 71dc58a3e1575..1d02cb5d946f4 100644 --- a/pkg/front_end/lib/src/fasta/builder/function_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/function_builder.dart @@ -486,8 +486,9 @@ abstract class FunctionBuilderImpl extends MemberBuilderImpl isClassMember || isExtensionMember ? parent as DeclarationBuilder : null; - MetadataBuilder.buildAnnotations( - member, metadata, library, classOrExtensionBuilder, this, fileUri); + Scope parentScope = classOrExtensionBuilder?.scope ?? library.scope; + MetadataBuilder.buildAnnotations(member, metadata, library, + classOrExtensionBuilder, this, fileUri, parentScope); if (typeVariables != null) { for (int i = 0; i < typeVariables!.length; i++) { typeVariables![i].buildOutlineExpressions( @@ -495,7 +496,8 @@ abstract class FunctionBuilderImpl extends MemberBuilderImpl classOrExtensionBuilder, this, coreTypes, - delayedActionPerformers); + delayedActionPerformers, + computeTypeParameterScope(parentScope)); } } diff --git a/pkg/front_end/lib/src/fasta/builder/metadata_builder.dart b/pkg/front_end/lib/src/fasta/builder/metadata_builder.dart index 21d767b4ccd08..c4497b1bb853d 100644 --- a/pkg/front_end/lib/src/fasta/builder/metadata_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/metadata_builder.dart @@ -30,14 +30,8 @@ class MetadataBuilder { SourceLibraryBuilder library, DeclarationBuilder? classOrExtensionBuilder, MemberBuilder? member, - Uri fileUri) { + Uri fileUri, Scope scope) { if (metadata == null) return; - Scope scope = parent is Library || - parent is Class || - parent is Extension || - classOrExtensionBuilder == null - ? library.scope - : classOrExtensionBuilder.scope; BodyBuilder bodyBuilder = library.loader .createBodyBuilderForOutlineExpression(library, classOrExtensionBuilder, member ?? classOrExtensionBuilder ?? library, scope, fileUri); diff --git a/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart b/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart index 9d91421a3b90e..b6a6ed974cccc 100644 --- a/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart +++ b/pkg/front_end/lib/src/fasta/builder/type_variable_builder.dart @@ -13,6 +13,7 @@ import '../fasta_codes.dart' templateInternalProblemUnfinishedTypeVariable, templateTypeArgumentsOnTypeVariable; +import '../scope.dart'; import '../source/source_library_builder.dart'; import '../util/helpers.dart'; @@ -200,9 +201,9 @@ class TypeVariableBuilder extends TypeDeclarationBuilderImpl { DeclarationBuilder? classOrExtensionBuilder, MemberBuilder? memberBuilder, CoreTypes coreTypes, - List delayedActionPerformers) { + List delayedActionPerformers, Scope scope) { MetadataBuilder.buildAnnotations(parameter, metadata, libraryBuilder, - classOrExtensionBuilder, memberBuilder, fileUri!); + classOrExtensionBuilder, memberBuilder, fileUri!, scope); } @override diff --git a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart index 5ffcb2066486f..8bb753874fb11 100644 --- a/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_extension_builder.dart @@ -278,11 +278,11 @@ class SourceExtensionBuilder extends ExtensionBuilderImpl { List delayedActionPerformers, List synthesizedFunctionNodes) { MetadataBuilder.buildAnnotations(isPatch ? origin.extension : extension, - metadata, library, this, null, fileUri); + metadata, library, this, null, fileUri, library.scope); if (typeParameters != null) { for (int i = 0; i < typeParameters!.length; i++) { - typeParameters![i].buildOutlineExpressions( - library, this, null, coreTypes, delayedActionPerformers); + typeParameters![i].buildOutlineExpressions(library, this, null, + coreTypes, delayedActionPerformers, scope.parent!); } } diff --git a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart index 4ed49c90b2eaa..5c4ebef3f874f 100644 --- a/pkg/front_end/lib/src/fasta/source/source_library_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_library_builder.dart @@ -2648,7 +2648,7 @@ class SourceLibraryBuilder extends LibraryBuilderImpl { @override void buildOutlineExpressions() { MetadataBuilder.buildAnnotations( - library, metadata, this, null, null, fileUri); + library, metadata, this, null, null, fileUri, scope); } /// Builds the core AST structures for [declaration] needed for the outline. diff --git a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart index 5bdcbaab84dd6..a64606ef5394e 100644 --- a/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart +++ b/pkg/front_end/lib/src/fasta/source/source_type_alias_builder.dart @@ -18,7 +18,9 @@ import '../fasta_codes.dart' show noLength, templateCyclicTypedef, templateTypeArgumentMismatch; import '../problems.dart' show unhandled; +import '../scope.dart'; +import '../builder/builder.dart'; import '../builder/class_builder.dart'; import '../builder/fixed_type_builder.dart'; import '../builder/formal_parameter_builder.dart'; @@ -256,11 +258,16 @@ class SourceTypeAliasBuilder extends TypeAliasBuilderImpl { List delayedActionPerformers, List synthesizedFunctionNodes) { MetadataBuilder.buildAnnotations( - typedef, metadata, library, null, null, fileUri); + typedef, metadata, library, null, null, fileUri, library.scope); if (typeVariables != null) { for (int i = 0; i < typeVariables!.length; i++) { typeVariables![i].buildOutlineExpressions( - library, null, null, coreTypes, delayedActionPerformers); + library, + null, + null, + coreTypes, + delayedActionPerformers, + computeTypeParameterScope(library.scope)); } } _tearOffDependencies?.forEach((Procedure tearOff, Member target) { @@ -273,6 +280,19 @@ class SourceTypeAliasBuilder extends TypeAliasBuilderImpl { }); } + Scope computeTypeParameterScope(Scope parent) { + if (typeVariables == null) return parent; + Map local = {}; + for (TypeVariableBuilder variable in typeVariables!) { + local[variable.name] = variable; + } + return new Scope( + local: local, + parent: parent, + debugName: "type parameter", + isModifiable: false); + } + Map? _tearOffDependencies; void buildTypedefTearOffs( diff --git a/tests/language/metadata/type_parameter_scope_inner_test.dart b/tests/language/metadata/type_parameter_scope_inner_test.dart new file mode 100644 index 0000000000000..ecbb4a231b551 --- /dev/null +++ b/tests/language/metadata/type_parameter_scope_inner_test.dart @@ -0,0 +1,66 @@ +// Copyright (c) 2021, 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. + +// Test that metadata on a type parameter cannot refer to declarations in an +// inner scope. See https://github.com/dart-lang/language/issues/1790. + +class Annotation { + const Annotation(dynamic d); +} + +class Class<@Annotation(foo) T> { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + static void foo() {} +} + +void function<@Annotation(foo) T>(dynamic foo) { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + dynamic foo; +} + +extension Extension<@Annotation(foo) T> on Class { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + static void foo() {} + + void extensionMethod<@Annotation(foo) T, @Annotation(bar) U>() {} + // ^^^ + // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT + // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER + // [cfe] Getter not found: 'bar'. +} + +class C { + void method<@Annotation(foo) T, @Annotation(bar) U>(dynamic foo) { + // ^^^ + // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT + // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER + // [cfe] Getter not found: 'foo'. + dynamic foo; + } + + static void bar() {} +} + +mixin Mixin<@Annotation(foo) T> { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + static void foo() {} +} + +typedef Typedef<@Annotation(foo) T> = void Function(); +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. diff --git a/tests/language/metadata/type_parameter_scope_other_test.dart b/tests/language/metadata/type_parameter_scope_other_test.dart new file mode 100644 index 0000000000000..d411e2f40a48d --- /dev/null +++ b/tests/language/metadata/type_parameter_scope_other_test.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2021, 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. + +// Test that identifiers in type parameter metadata can refer to other type +// parameters in the same declaration; they are in scope and take precedence +// over top level declarations, even if this leads to compile errors. + +/// Top level declaration of T; nothing should resolve to this. +void T() {} + +class Annotation { + const Annotation(dynamic d); +} + +class Class {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used in static members. + +void function() {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used as constants. + +extension Extension on Map {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used in static members. + +class C { + void method() {} + // ^ + // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT + // [cfe] Type variables can't be used as constants. +} + +mixin Mixin {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used in static members. + +typedef void Typedef1(T t, U u); +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used as constants. + +typedef Typedef2 = void Function(T t, U u); +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used as constants. diff --git a/tests/language_2/metadata/type_parameter_scope_inner_test.dart b/tests/language_2/metadata/type_parameter_scope_inner_test.dart new file mode 100644 index 0000000000000..ecbb4a231b551 --- /dev/null +++ b/tests/language_2/metadata/type_parameter_scope_inner_test.dart @@ -0,0 +1,66 @@ +// Copyright (c) 2021, 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. + +// Test that metadata on a type parameter cannot refer to declarations in an +// inner scope. See https://github.com/dart-lang/language/issues/1790. + +class Annotation { + const Annotation(dynamic d); +} + +class Class<@Annotation(foo) T> { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + static void foo() {} +} + +void function<@Annotation(foo) T>(dynamic foo) { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + dynamic foo; +} + +extension Extension<@Annotation(foo) T> on Class { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + static void foo() {} + + void extensionMethod<@Annotation(foo) T, @Annotation(bar) U>() {} + // ^^^ + // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT + // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER + // [cfe] Getter not found: 'bar'. +} + +class C { + void method<@Annotation(foo) T, @Annotation(bar) U>(dynamic foo) { + // ^^^ + // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT + // [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER + // [cfe] Getter not found: 'foo'. + dynamic foo; + } + + static void bar() {} +} + +mixin Mixin<@Annotation(foo) T> { +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. + static void foo() {} +} + +typedef Typedef<@Annotation(foo) T> = void Function(); +// ^^^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [analyzer] COMPILE_TIME_ERROR.UNDEFINED_IDENTIFIER +// [cfe] Getter not found: 'foo'. diff --git a/tests/language_2/metadata/type_parameter_scope_other_test.dart b/tests/language_2/metadata/type_parameter_scope_other_test.dart new file mode 100644 index 0000000000000..d411e2f40a48d --- /dev/null +++ b/tests/language_2/metadata/type_parameter_scope_other_test.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2021, 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. + +// Test that identifiers in type parameter metadata can refer to other type +// parameters in the same declaration; they are in scope and take precedence +// over top level declarations, even if this leads to compile errors. + +/// Top level declaration of T; nothing should resolve to this. +void T() {} + +class Annotation { + const Annotation(dynamic d); +} + +class Class {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used in static members. + +void function() {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used as constants. + +extension Extension on Map {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used in static members. + +class C { + void method() {} + // ^ + // [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT + // [cfe] Type variables can't be used as constants. +} + +mixin Mixin {} +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used in static members. + +typedef void Typedef1(T t, U u); +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used as constants. + +typedef Typedef2 = void Function(T t, U u); +// ^ +// [analyzer] COMPILE_TIME_ERROR.CONST_WITH_NON_CONSTANT_ARGUMENT +// [cfe] Type variables can't be used as constants. diff --git a/tests/lib/mirrors/metadata_scope_test.dart b/tests/lib/mirrors/metadata_scope_test.dart index 8848d41d336ac..0596b39aba8f3 100644 --- a/tests/lib/mirrors/metadata_scope_test.dart +++ b/tests/lib/mirrors/metadata_scope_test.dart @@ -15,8 +15,10 @@ class Annotation { // Note there is no compile-time constant 'foo' in scope. In particular, A.foo // is not in scope here. -@Annotation(foo) // //# 01: compile-time error -class A<@Annotation(foo) T> { +@Annotation(foo) //# 01: compile-time error +class A< + @Annotation(foo) //# 02: compile-time error + T> { @Annotation(foo) static foo() {} diff --git a/tests/lib_2/mirrors/metadata_scope_test.dart b/tests/lib_2/mirrors/metadata_scope_test.dart index 0ecaeb1942711..fec9be7293604 100644 --- a/tests/lib_2/mirrors/metadata_scope_test.dart +++ b/tests/lib_2/mirrors/metadata_scope_test.dart @@ -17,8 +17,10 @@ class Annotation { // Note there is no compile-time constant 'foo' in scope. In particular, A.foo // is not in scope here. -@Annotation(foo) // //# 01: compile-time error -class A<@Annotation(foo) T> { +@Annotation(foo) //# 01: compile-time error +class A< + @Annotation(foo) //# 02: compile-time error + T> { @Annotation(foo) static foo() {}