[cfe] Support primary constructors in classes
Part of https://github.com/dart-lang/sdk/issues/61700 Change-Id: I73800eec8ede6f7ca4c1959de2bcd098c974c74c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/457760 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
committed by
Commit Queue
parent
dc2a0aae7d
commit
de2737affe
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
+41
@@ -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::•()
|
||||
;
|
||||
}
|
||||
+41
@@ -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::•()
|
||||
;
|
||||
}
|
||||
+24
@@ -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::•()
|
||||
;
|
||||
}
|
||||
+41
@@ -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::•()
|
||||
;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
class C1() {}
|
||||
|
||||
class const C2() {}
|
||||
|
||||
class C3() {
|
||||
final int? i;
|
||||
}
|
||||
|
||||
class const C4() {
|
||||
int? i;
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
class C1() {}
|
||||
|
||||
class C3() {
|
||||
final int? i;
|
||||
}
|
||||
|
||||
class const C2() {}
|
||||
|
||||
class const C4() {
|
||||
int? i;
|
||||
}
|
||||
+3
@@ -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'.
|
||||
|
||||
+3
@@ -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'.
|
||||
|
||||
+2
@@ -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'.
|
||||
|
||||
+3
@@ -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'.
|
||||
|
||||
Reference in New Issue
Block a user