diff --git a/pkg/front_end/lib/src/fragment/constructor/declaration.dart b/pkg/front_end/lib/src/fragment/constructor/declaration.dart index 8c37caca0db..36c0ce90b3f 100644 --- a/pkg/front_end/lib/src/fragment/constructor/declaration.dart +++ b/pkg/front_end/lib/src/fragment/constructor/declaration.dart @@ -31,7 +31,6 @@ import '../../source/check_helper.dart'; import '../../source/name_scheme.dart'; import '../../source/source_class_builder.dart'; import '../../source/source_constructor_builder.dart'; -import '../../source/source_extension_type_declaration_builder.dart'; import '../../source/source_function_builder.dart'; import '../../source/source_library_builder.dart'; import '../../source/source_loader.dart'; @@ -1337,9 +1336,7 @@ class PrimaryConstructorDeclaration f, constructorBuilder: constructorBuilder, libraryBuilder: libraryBuilder, - declarationBuilder: - constructorBuilder.declarationBuilder - as SourceExtensionTypeDeclarationBuilder, + declarationBuilder: constructorBuilder.declarationBuilder, name: _fragment.name, nameScheme: nameScheme, constructorReferences: constructorReferences, diff --git a/pkg/front_end/lib/src/source/outline_builder.dart b/pkg/front_end/lib/src/source/outline_builder.dart index 236266067f4..87705593869 100644 --- a/pkg/front_end/lib/src/source/outline_builder.dart +++ b/pkg/front_end/lib/src/source/outline_builder.dart @@ -1825,123 +1825,126 @@ class OutlineBuilder extends StackListenerImpl { name = identifier.name; } - if (!forExtensionType) { + int? startOffset = constKeyword?.charOffset ?? nameOffset ?? formalsOffset; + + // TODO(johnniwinther): Handle declaring parameters. + if (forExtensionType) { + bool inExtensionType = + declarationContext == DeclarationContext.ExtensionType; + if (formals != null) { + int requiredPositionalCount = 0; + int? firstNamedParameterOffset; + int? firstOptionalPositionalParameterOffset; + for (int i = 0; i < formals.length; i++) { + FormalParameterBuilder formal = formals[i]; + if (inExtensionType) { + TypeBuilder type = formal.type; + if (type is FunctionTypeBuilder && + type.hasFunctionFormalParameterSyntax) { + _compilationUnit.addProblem( + // ignore: lines_longer_than_80_chars + codeExtensionTypePrimaryConstructorFunctionFormalParameterSyntax, + formal.fileOffset, + formal.name.length, + formal.fileUri, + ); + } + if (type is ImplicitTypeBuilder) { + _compilationUnit.addProblem( + codeExpectedRepresentationType, + formal.fileOffset, + formal.name.length, + formal.fileUri, + ); + formal.type = new InvalidTypeBuilderImpl( + formal.fileUri, + formal.fileOffset, + ); + } + if (formal.modifiers.containsSyntacticModifiers( + ignoreCovariant: true, + ignoreRequired: true, + )) { + _compilationUnit.addProblem( + codeRepresentationFieldModifier, + formal.fileOffset, + formal.name.length, + formal.fileUri, + ); + } + if (formal.isInitializingFormal) { + _compilationUnit.addProblem( + codeExtensionTypePrimaryConstructorWithInitializingFormal, + formal.fileOffset, + formal.name.length, + formal.fileUri, + ); + } + } + + if (formal.isPositional) { + if (formal.isOptionalPositional) { + firstOptionalPositionalParameterOffset = formal.fileOffset; + } else { + requiredPositionalCount++; + } + } + if (formal.isNamed) { + firstNamedParameterOffset = formal.fileOffset; + } + _builderFactory.addPrimaryConstructorField( + // TODO(johnniwinther): Support annotations on annotations on fields + // defined through a primary constructor. This is not needed for + // extension types where the field is not part of the AST but will + // be needed when primary constructors are generally supported. + metadata: null, + type: formal.type, + name: formal.name, + nameOffset: formal.fileOffset, + ); + formals[i] = formal.forPrimaryConstructor(_builderFactory); + } + if (inExtensionType) { + if (firstOptionalPositionalParameterOffset != null) { + _compilationUnit.addProblem( + codeOptionalParametersInExtensionTypeDeclaration, + firstOptionalPositionalParameterOffset, + 1, + uri, + ); + } else if (firstNamedParameterOffset != null) { + _compilationUnit.addProblem( + codeNamedParametersInExtensionTypeDeclaration, + firstNamedParameterOffset, + 1, + uri, + ); + } else if (requiredPositionalCount == 0) { + _compilationUnit.addProblem( + codeExpectedRepresentationField, + charOffset, + 1, + uri, + ); + } else if (formals.length > 1) { + _compilationUnit.addProblem( + codeMultipleRepresentationFields, + charOffset, + 1, + uri, + ); + } + } + } + } else { reportIfNotEnabled( libraryFeatures.declaringConstructors, beginToken.charOffset, noLength, ); - // TODO(johnniwinther): Support primary constructors in general. - return; - } - - int? startOffset = constKeyword?.charOffset ?? nameOffset ?? formalsOffset; - - bool inExtensionType = - declarationContext == DeclarationContext.ExtensionType; - if (formals != null) { - int requiredPositionalCount = 0; - int? firstNamedParameterOffset; - int? firstOptionalPositionalParameterOffset; - for (int i = 0; i < formals.length; i++) { - FormalParameterBuilder formal = formals[i]; - if (inExtensionType) { - TypeBuilder type = formal.type; - if (type is FunctionTypeBuilder && - type.hasFunctionFormalParameterSyntax) { - _compilationUnit.addProblem( - // ignore: lines_longer_than_80_chars - codeExtensionTypePrimaryConstructorFunctionFormalParameterSyntax, - formal.fileOffset, - formal.name.length, - formal.fileUri, - ); - } - if (type is ImplicitTypeBuilder) { - _compilationUnit.addProblem( - codeExpectedRepresentationType, - formal.fileOffset, - formal.name.length, - formal.fileUri, - ); - formal.type = new InvalidTypeBuilderImpl( - formal.fileUri, - formal.fileOffset, - ); - } - if (formal.modifiers.containsSyntacticModifiers( - ignoreCovariant: true, - ignoreRequired: true, - )) { - _compilationUnit.addProblem( - codeRepresentationFieldModifier, - formal.fileOffset, - formal.name.length, - formal.fileUri, - ); - } - if (formal.isInitializingFormal) { - _compilationUnit.addProblem( - codeExtensionTypePrimaryConstructorWithInitializingFormal, - formal.fileOffset, - formal.name.length, - formal.fileUri, - ); - } - } - - if (formal.isPositional) { - if (formal.isOptionalPositional) { - firstOptionalPositionalParameterOffset = formal.fileOffset; - } else { - requiredPositionalCount++; - } - } - if (formal.isNamed) { - firstNamedParameterOffset = formal.fileOffset; - } - _builderFactory.addPrimaryConstructorField( - // TODO(johnniwinther): Support annotations on annotations on fields - // defined through a primary constructor. This is not needed for - // extension types where the field is not part of the AST but will - // be needed when primary constructors are generally supported. - metadata: null, - type: formal.type, - name: formal.name, - nameOffset: formal.fileOffset, - ); - formals[i] = formal.forPrimaryConstructor(_builderFactory); - } - if (inExtensionType) { - if (firstOptionalPositionalParameterOffset != null) { - _compilationUnit.addProblem( - codeOptionalParametersInExtensionTypeDeclaration, - firstOptionalPositionalParameterOffset, - 1, - uri, - ); - } else if (firstNamedParameterOffset != null) { - _compilationUnit.addProblem( - codeNamedParametersInExtensionTypeDeclaration, - firstNamedParameterOffset, - 1, - uri, - ); - } else if (requiredPositionalCount == 0) { - _compilationUnit.addProblem( - codeExpectedRepresentationField, - charOffset, - 1, - uri, - ); - } else if (formals.length > 1) { - _compilationUnit.addProblem( - codeMultipleRepresentationFields, - charOffset, - 1, - uri, - ); - } + if (declarationContext == DeclarationContext.Enum) { + // TODO(johnniwinther): Support primary constructors in enums. + return; } } diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart new file mode 100644 index 00000000000..f5c01432ff6 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2025, 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. + +class C1() {} + +class const C2() {} + +class C3() { + final int? i; // Error +} + +class const C4() { // Error + int? i; +} diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.expect b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.expect new file mode 100644 index 00000000000..0fb3b502359 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.expect @@ -0,0 +1,41 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:10:14: Error: Final field 'i' is not initialized. +// Try to initialize the field in the declaration or in every constructor. +// final int? i; // Error +// ^ +// +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:13:15: Error: Constructor is marked 'const' so all fields must be final. +// class const C4() { // Error +// ^ +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:14:8: Context: Field isn't final, but constructor is 'const'. +// int? i; +// ^ +// +import self as self; +import "dart:core" as core; + +class C1 extends core::Object { + constructor •() → self::C1 + : super core::Object::•() + ; +} +class C2 extends core::Object /*hasConstConstructor*/ { + const constructor •() → self::C2 + : super core::Object::•() + ; +} +class C3 extends core::Object { + final field core::int? i = null; + constructor •() → self::C3 + : super core::Object::•() + ; +} +class C4 extends core::Object /*hasConstConstructor*/ { + field core::int? i = null; + const constructor •() → self::C4 + : super core::Object::•() + ; +} diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.modular.expect b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.modular.expect new file mode 100644 index 00000000000..0fb3b502359 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.modular.expect @@ -0,0 +1,41 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:10:14: Error: Final field 'i' is not initialized. +// Try to initialize the field in the declaration or in every constructor. +// final int? i; // Error +// ^ +// +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:13:15: Error: Constructor is marked 'const' so all fields must be final. +// class const C4() { // Error +// ^ +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:14:8: Context: Field isn't final, but constructor is 'const'. +// int? i; +// ^ +// +import self as self; +import "dart:core" as core; + +class C1 extends core::Object { + constructor •() → self::C1 + : super core::Object::•() + ; +} +class C2 extends core::Object /*hasConstConstructor*/ { + const constructor •() → self::C2 + : super core::Object::•() + ; +} +class C3 extends core::Object { + final field core::int? i = null; + constructor •() → self::C3 + : super core::Object::•() + ; +} +class C4 extends core::Object /*hasConstConstructor*/ { + field core::int? i = null; + const constructor •() → self::C4 + : super core::Object::•() + ; +} diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.outline.expect b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.outline.expect new file mode 100644 index 00000000000..ba997af5616 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.outline.expect @@ -0,0 +1,24 @@ +library; +import self as self; +import "dart:core" as core; + +class C1 extends core::Object { + constructor •() → self::C1 + ; +} +class C2 extends core::Object /*hasConstConstructor*/ { + const constructor •() → self::C2 + : super core::Object::•() + ; +} +class C3 extends core::Object { + final field core::int? i; + constructor •() → self::C3 + ; +} +class C4 extends core::Object /*hasConstConstructor*/ { + field core::int? i; + const constructor •() → self::C4 + : super core::Object::•() + ; +} diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.transformed.expect b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.transformed.expect new file mode 100644 index 00000000000..0fb3b502359 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.strong.transformed.expect @@ -0,0 +1,41 @@ +library; +// +// Problems in library: +// +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:10:14: Error: Final field 'i' is not initialized. +// Try to initialize the field in the declaration or in every constructor. +// final int? i; // Error +// ^ +// +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:13:15: Error: Constructor is marked 'const' so all fields must be final. +// class const C4() { // Error +// ^ +// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:14:8: Context: Field isn't final, but constructor is 'const'. +// int? i; +// ^ +// +import self as self; +import "dart:core" as core; + +class C1 extends core::Object { + constructor •() → self::C1 + : super core::Object::•() + ; +} +class C2 extends core::Object /*hasConstConstructor*/ { + const constructor •() → self::C2 + : super core::Object::•() + ; +} +class C3 extends core::Object { + final field core::int? i = null; + constructor •() → self::C3 + : super core::Object::•() + ; +} +class C4 extends core::Object /*hasConstConstructor*/ { + field core::int? i = null; + const constructor •() → self::C4 + : super core::Object::•() + ; +} diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.textual_outline.expect b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.textual_outline.expect new file mode 100644 index 00000000000..bed589dc0e1 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.textual_outline.expect @@ -0,0 +1,11 @@ +class C1() {} + +class const C2() {} + +class C3() { + final int? i; +} + +class const C4() { + int? i; +} diff --git a/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.textual_outline_modelled.expect b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.textual_outline_modelled.expect new file mode 100644 index 00000000000..1eaf42f3f78 --- /dev/null +++ b/pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart.textual_outline_modelled.expect @@ -0,0 +1,11 @@ +class C1() {} + +class C3() { + final int? i; +} + +class const C2() {} + +class const C4() { + int? i; +} diff --git a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.expect b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.expect index 1d19136490d..6197de3c01d 100644 --- a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.expect +++ b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.expect @@ -18,6 +18,9 @@ import "dart:core" as core; import "org-dartlang-testcase:///main_lib.dart"; class A extends core::Object { + constructor •(core::String foo) → self::A + : super core::Object::•() + ; static factory redirectingFactoryToSubtype() → self::A return invalid-expression "pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart:8:45: Error: The constructor function type 'E Function()' isn't a subtype of 'A Function()'. - 'A' is from 'pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart'. diff --git a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.modular.expect b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.modular.expect index 0a58f090b50..d713dae1642 100644 --- a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.modular.expect +++ b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.modular.expect @@ -18,6 +18,9 @@ import "dart:core" as core; import "org-dartlang-testcase:///main_lib.dart"; class A extends core::Object { + constructor •(core::String foo) → self::A + : super core::Object::•() + ; static factory redirectingFactoryToSubtype() → self::A return invalid-expression "pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart:8:45: Error: The constructor function type 'E Function()' isn't a subtype of 'A Function()'. - 'A' is from 'pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart'. diff --git a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.outline.expect b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.outline.expect index 701c51f3b83..6c745939d25 100644 --- a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.outline.expect +++ b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.outline.expect @@ -18,6 +18,8 @@ import "dart:core" as core; import "org-dartlang-testcase:///main_lib.dart"; class A extends core::Object { + constructor •(core::String foo) → self::A + ; static factory redirectingFactoryToSubtype() → self::A return invalid-expression "pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart:8:45: Error: The constructor function type 'E Function()' isn't a subtype of 'A Function()'. - 'A' is from 'pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart'. diff --git a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.transformed.expect b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.transformed.expect index 1d19136490d..6197de3c01d 100644 --- a/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.transformed.expect +++ b/pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart.strong.transformed.expect @@ -18,6 +18,9 @@ import "dart:core" as core; import "org-dartlang-testcase:///main_lib.dart"; class A extends core::Object { + constructor •(core::String foo) → self::A + : super core::Object::•() + ; static factory redirectingFactoryToSubtype() → self::A return invalid-expression "pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart:8:45: Error: The constructor function type 'E Function()' isn't a subtype of 'A Function()'. - 'A' is from 'pkg/front_end/testcases/extension_types/with_dependencies/issue53209_2/main.dart'.