Skip to content

Commit de2737a

Browse files
johnniwintherCommit Queue
authored andcommitted
[cfe] Support primary constructors in classes
Part of #61700 Change-Id: I73800eec8ede6f7ca4c1959de2bcd098c974c74c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/457760 Reviewed-by: Konstantin Shcheglov <[email protected]> Commit-Queue: Johnni Winther <[email protected]>
1 parent dc2a0aa commit de2737a

13 files changed

+302
-107
lines changed

pkg/front_end/lib/src/fragment/constructor/declaration.dart

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import '../../source/check_helper.dart';
3131
import '../../source/name_scheme.dart';
3232
import '../../source/source_class_builder.dart';
3333
import '../../source/source_constructor_builder.dart';
34-
import '../../source/source_extension_type_declaration_builder.dart';
3534
import '../../source/source_function_builder.dart';
3635
import '../../source/source_library_builder.dart';
3736
import '../../source/source_loader.dart';
@@ -1337,9 +1336,7 @@ class PrimaryConstructorDeclaration
13371336
f,
13381337
constructorBuilder: constructorBuilder,
13391338
libraryBuilder: libraryBuilder,
1340-
declarationBuilder:
1341-
constructorBuilder.declarationBuilder
1342-
as SourceExtensionTypeDeclarationBuilder,
1339+
declarationBuilder: constructorBuilder.declarationBuilder,
13431340
name: _fragment.name,
13441341
nameScheme: nameScheme,
13451342
constructorReferences: constructorReferences,

pkg/front_end/lib/src/source/outline_builder.dart

Lines changed: 106 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -1825,123 +1825,126 @@ class OutlineBuilder extends StackListenerImpl {
18251825
name = identifier.name;
18261826
}
18271827

1828-
if (!forExtensionType) {
1829-
reportIfNotEnabled(
1830-
libraryFeatures.declaringConstructors,
1831-
beginToken.charOffset,
1832-
noLength,
1833-
);
1834-
// TODO(johnniwinther): Support primary constructors in general.
1835-
return;
1836-
}
1837-
18381828
int? startOffset = constKeyword?.charOffset ?? nameOffset ?? formalsOffset;
18391829

1840-
bool inExtensionType =
1841-
declarationContext == DeclarationContext.ExtensionType;
1842-
if (formals != null) {
1843-
int requiredPositionalCount = 0;
1844-
int? firstNamedParameterOffset;
1845-
int? firstOptionalPositionalParameterOffset;
1846-
for (int i = 0; i < formals.length; i++) {
1847-
FormalParameterBuilder formal = formals[i];
1830+
// TODO(johnniwinther): Handle declaring parameters.
1831+
if (forExtensionType) {
1832+
bool inExtensionType =
1833+
declarationContext == DeclarationContext.ExtensionType;
1834+
if (formals != null) {
1835+
int requiredPositionalCount = 0;
1836+
int? firstNamedParameterOffset;
1837+
int? firstOptionalPositionalParameterOffset;
1838+
for (int i = 0; i < formals.length; i++) {
1839+
FormalParameterBuilder formal = formals[i];
1840+
if (inExtensionType) {
1841+
TypeBuilder type = formal.type;
1842+
if (type is FunctionTypeBuilder &&
1843+
type.hasFunctionFormalParameterSyntax) {
1844+
_compilationUnit.addProblem(
1845+
// ignore: lines_longer_than_80_chars
1846+
codeExtensionTypePrimaryConstructorFunctionFormalParameterSyntax,
1847+
formal.fileOffset,
1848+
formal.name.length,
1849+
formal.fileUri,
1850+
);
1851+
}
1852+
if (type is ImplicitTypeBuilder) {
1853+
_compilationUnit.addProblem(
1854+
codeExpectedRepresentationType,
1855+
formal.fileOffset,
1856+
formal.name.length,
1857+
formal.fileUri,
1858+
);
1859+
formal.type = new InvalidTypeBuilderImpl(
1860+
formal.fileUri,
1861+
formal.fileOffset,
1862+
);
1863+
}
1864+
if (formal.modifiers.containsSyntacticModifiers(
1865+
ignoreCovariant: true,
1866+
ignoreRequired: true,
1867+
)) {
1868+
_compilationUnit.addProblem(
1869+
codeRepresentationFieldModifier,
1870+
formal.fileOffset,
1871+
formal.name.length,
1872+
formal.fileUri,
1873+
);
1874+
}
1875+
if (formal.isInitializingFormal) {
1876+
_compilationUnit.addProblem(
1877+
codeExtensionTypePrimaryConstructorWithInitializingFormal,
1878+
formal.fileOffset,
1879+
formal.name.length,
1880+
formal.fileUri,
1881+
);
1882+
}
1883+
}
1884+
1885+
if (formal.isPositional) {
1886+
if (formal.isOptionalPositional) {
1887+
firstOptionalPositionalParameterOffset = formal.fileOffset;
1888+
} else {
1889+
requiredPositionalCount++;
1890+
}
1891+
}
1892+
if (formal.isNamed) {
1893+
firstNamedParameterOffset = formal.fileOffset;
1894+
}
1895+
_builderFactory.addPrimaryConstructorField(
1896+
// TODO(johnniwinther): Support annotations on annotations on fields
1897+
// defined through a primary constructor. This is not needed for
1898+
// extension types where the field is not part of the AST but will
1899+
// be needed when primary constructors are generally supported.
1900+
metadata: null,
1901+
type: formal.type,
1902+
name: formal.name,
1903+
nameOffset: formal.fileOffset,
1904+
);
1905+
formals[i] = formal.forPrimaryConstructor(_builderFactory);
1906+
}
18481907
if (inExtensionType) {
1849-
TypeBuilder type = formal.type;
1850-
if (type is FunctionTypeBuilder &&
1851-
type.hasFunctionFormalParameterSyntax) {
1908+
if (firstOptionalPositionalParameterOffset != null) {
18521909
_compilationUnit.addProblem(
1853-
// ignore: lines_longer_than_80_chars
1854-
codeExtensionTypePrimaryConstructorFunctionFormalParameterSyntax,
1855-
formal.fileOffset,
1856-
formal.name.length,
1857-
formal.fileUri,
1910+
codeOptionalParametersInExtensionTypeDeclaration,
1911+
firstOptionalPositionalParameterOffset,
1912+
1,
1913+
uri,
18581914
);
1859-
}
1860-
if (type is ImplicitTypeBuilder) {
1915+
} else if (firstNamedParameterOffset != null) {
18611916
_compilationUnit.addProblem(
1862-
codeExpectedRepresentationType,
1863-
formal.fileOffset,
1864-
formal.name.length,
1865-
formal.fileUri,
1866-
);
1867-
formal.type = new InvalidTypeBuilderImpl(
1868-
formal.fileUri,
1869-
formal.fileOffset,
1917+
codeNamedParametersInExtensionTypeDeclaration,
1918+
firstNamedParameterOffset,
1919+
1,
1920+
uri,
18701921
);
1871-
}
1872-
if (formal.modifiers.containsSyntacticModifiers(
1873-
ignoreCovariant: true,
1874-
ignoreRequired: true,
1875-
)) {
1922+
} else if (requiredPositionalCount == 0) {
18761923
_compilationUnit.addProblem(
1877-
codeRepresentationFieldModifier,
1878-
formal.fileOffset,
1879-
formal.name.length,
1880-
formal.fileUri,
1924+
codeExpectedRepresentationField,
1925+
charOffset,
1926+
1,
1927+
uri,
18811928
);
1882-
}
1883-
if (formal.isInitializingFormal) {
1929+
} else if (formals.length > 1) {
18841930
_compilationUnit.addProblem(
1885-
codeExtensionTypePrimaryConstructorWithInitializingFormal,
1886-
formal.fileOffset,
1887-
formal.name.length,
1888-
formal.fileUri,
1931+
codeMultipleRepresentationFields,
1932+
charOffset,
1933+
1,
1934+
uri,
18891935
);
18901936
}
18911937
}
1892-
1893-
if (formal.isPositional) {
1894-
if (formal.isOptionalPositional) {
1895-
firstOptionalPositionalParameterOffset = formal.fileOffset;
1896-
} else {
1897-
requiredPositionalCount++;
1898-
}
1899-
}
1900-
if (formal.isNamed) {
1901-
firstNamedParameterOffset = formal.fileOffset;
1902-
}
1903-
_builderFactory.addPrimaryConstructorField(
1904-
// TODO(johnniwinther): Support annotations on annotations on fields
1905-
// defined through a primary constructor. This is not needed for
1906-
// extension types where the field is not part of the AST but will
1907-
// be needed when primary constructors are generally supported.
1908-
metadata: null,
1909-
type: formal.type,
1910-
name: formal.name,
1911-
nameOffset: formal.fileOffset,
1912-
);
1913-
formals[i] = formal.forPrimaryConstructor(_builderFactory);
19141938
}
1915-
if (inExtensionType) {
1916-
if (firstOptionalPositionalParameterOffset != null) {
1917-
_compilationUnit.addProblem(
1918-
codeOptionalParametersInExtensionTypeDeclaration,
1919-
firstOptionalPositionalParameterOffset,
1920-
1,
1921-
uri,
1922-
);
1923-
} else if (firstNamedParameterOffset != null) {
1924-
_compilationUnit.addProblem(
1925-
codeNamedParametersInExtensionTypeDeclaration,
1926-
firstNamedParameterOffset,
1927-
1,
1928-
uri,
1929-
);
1930-
} else if (requiredPositionalCount == 0) {
1931-
_compilationUnit.addProblem(
1932-
codeExpectedRepresentationField,
1933-
charOffset,
1934-
1,
1935-
uri,
1936-
);
1937-
} else if (formals.length > 1) {
1938-
_compilationUnit.addProblem(
1939-
codeMultipleRepresentationFields,
1940-
charOffset,
1941-
1,
1942-
uri,
1943-
);
1944-
}
1939+
} else {
1940+
reportIfNotEnabled(
1941+
libraryFeatures.declaringConstructors,
1942+
beginToken.charOffset,
1943+
noLength,
1944+
);
1945+
if (declarationContext == DeclarationContext.Enum) {
1946+
// TODO(johnniwinther): Support primary constructors in enums.
1947+
return;
19451948
}
19461949
}
19471950

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
class C1() {}
6+
7+
class const C2() {}
8+
9+
class C3() {
10+
final int? i; // Error
11+
}
12+
13+
class const C4() { // Error
14+
int? i;
15+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:10:14: Error: Final field 'i' is not initialized.
6+
// Try to initialize the field in the declaration or in every constructor.
7+
// final int? i; // Error
8+
// ^
9+
//
10+
// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:13:15: Error: Constructor is marked 'const' so all fields must be final.
11+
// class const C4() { // Error
12+
// ^
13+
// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:14:8: Context: Field isn't final, but constructor is 'const'.
14+
// int? i;
15+
// ^
16+
//
17+
import self as self;
18+
import "dart:core" as core;
19+
20+
class C1 extends core::Object {
21+
constructor •() → self::C1
22+
: super core::Object::•()
23+
;
24+
}
25+
class C2 extends core::Object /*hasConstConstructor*/ {
26+
const constructor •() → self::C2
27+
: super core::Object::•()
28+
;
29+
}
30+
class C3 extends core::Object {
31+
final field core::int? i = null;
32+
constructor •() → self::C3
33+
: super core::Object::•()
34+
;
35+
}
36+
class C4 extends core::Object /*hasConstConstructor*/ {
37+
field core::int? i = null;
38+
const constructor •() → self::C4
39+
: super core::Object::•()
40+
;
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
library;
2+
//
3+
// Problems in library:
4+
//
5+
// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:10:14: Error: Final field 'i' is not initialized.
6+
// Try to initialize the field in the declaration or in every constructor.
7+
// final int? i; // Error
8+
// ^
9+
//
10+
// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:13:15: Error: Constructor is marked 'const' so all fields must be final.
11+
// class const C4() { // Error
12+
// ^
13+
// pkg/front_end/testcases/declaring_constructors/class_primary_constructor.dart:14:8: Context: Field isn't final, but constructor is 'const'.
14+
// int? i;
15+
// ^
16+
//
17+
import self as self;
18+
import "dart:core" as core;
19+
20+
class C1 extends core::Object {
21+
constructor •() → self::C1
22+
: super core::Object::•()
23+
;
24+
}
25+
class C2 extends core::Object /*hasConstConstructor*/ {
26+
const constructor •() → self::C2
27+
: super core::Object::•()
28+
;
29+
}
30+
class C3 extends core::Object {
31+
final field core::int? i = null;
32+
constructor •() → self::C3
33+
: super core::Object::•()
34+
;
35+
}
36+
class C4 extends core::Object /*hasConstConstructor*/ {
37+
field core::int? i = null;
38+
const constructor •() → self::C4
39+
: super core::Object::•()
40+
;
41+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
library;
2+
import self as self;
3+
import "dart:core" as core;
4+
5+
class C1 extends core::Object {
6+
constructor •() → self::C1
7+
;
8+
}
9+
class C2 extends core::Object /*hasConstConstructor*/ {
10+
const constructor •() → self::C2
11+
: super core::Object::•()
12+
;
13+
}
14+
class C3 extends core::Object {
15+
final field core::int? i;
16+
constructor •() → self::C3
17+
;
18+
}
19+
class C4 extends core::Object /*hasConstConstructor*/ {
20+
field core::int? i;
21+
const constructor •() → self::C4
22+
: super core::Object::•()
23+
;
24+
}

0 commit comments

Comments
 (0)