DeCo. Report initializingFormalForNonExistentField for primary constructors.

Change-Id: I42d2516902a0d7c2a3eb914d39afe01a10d4bf86
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/482640
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Konstantin Shcheglov
2026-02-23 08:12:48 -08:00
parent b18b1c598a
commit f2de280b40
6 changed files with 237 additions and 80 deletions
@@ -6141,63 +6141,51 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
}
void _checkForValidField(FieldFormalParameter parameter) {
var parent2 = parameter.parent?.parent;
if (parent2 is! ConstructorDeclaration &&
parent2?.parent is! ConstructorDeclaration) {
var constructor = parameter.parentFormalParameterList.parent;
if (constructor is PrimaryConstructorDeclaration &&
constructor.parent is ExtensionTypeDeclaration) {
return;
}
if (constructor is! ConstructorDeclaration &&
constructor is! PrimaryConstructorDeclaration) {
return;
}
var element = parameter.declaredFragment?.element;
if (element is FieldFormalParameterElementImpl) {
var fieldElement = element.field;
if (fieldElement == null || fieldElement.isOriginGetterSetter) {
diagnosticReporter.report(
diag.initializingFormalForNonExistentField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
} else {
var parameterElement = parameter.declaredFragment?.element;
if (parameterElement is FieldFormalParameterElementImpl) {
var declaredType = parameterElement.type;
var fieldType = fieldElement.type;
if (fieldElement.isOriginGetterSetter) {
diagnosticReporter.report(
diag.initializingFormalForNonExistentField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
} else if (fieldElement.isStatic) {
diagnosticReporter.report(
diag.initializerForStaticField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
} else if (!typeSystem.isSubtypeOf(declaredType, fieldType)) {
diagnosticReporter.report(
diag.fieldInitializingFormalNotAssignable
.withArguments(
formalParameterType: declaredType,
fieldType: fieldType,
)
.at(parameter),
);
}
} else {
if (fieldElement.isOriginGetterSetter) {
diagnosticReporter.report(
diag.initializingFormalForNonExistentField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
} else if (fieldElement.isStatic) {
diagnosticReporter.report(
diag.initializerForStaticField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
}
}
}
if (element is! FieldFormalParameterElementImpl) {
return;
}
var fieldElement = element.field;
if (fieldElement == null || fieldElement.isOriginGetterSetter) {
diagnosticReporter.report(
diag.initializingFormalForNonExistentField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
return;
}
if (fieldElement.isStatic) {
diagnosticReporter.report(
diag.initializerForStaticField
.withArguments(formalName: parameter.name.lexeme)
.at(parameter),
);
return;
}
var elementType = element.type;
var fieldType = fieldElement.type;
if (!typeSystem.isSubtypeOf(elementType, fieldType)) {
diagnosticReporter.report(
diag.fieldInitializingFormalNotAssignable
.withArguments(
formalParameterType: elementType,
fieldType: fieldType,
)
.at(parameter),
);
}
// else {
// TODO(jwren): Report error, constructor initializer variable is a top level element
@@ -6431,7 +6431,10 @@ class const A(this.x) {
const a = A(1);
''',
[error(diag.initializerForNonExistentField, 33, 5)],
[
error(diag.initializingFormalForNonExistentField, 14, 6),
error(diag.initializerForNonExistentField, 33, 5),
],
);
assertDartObjectText(_topLevelVar('a'), r'''
A
@@ -15,7 +15,18 @@ main() {
@reflectiveTest
class InitializerForStaticFieldTest extends PubPackageResolutionTest {
test_fieldFormalParameter() async {
test_class_primaryConstructor_fieldFormalParameter() async {
await assertErrorsInCode(
r'''
class A(this.x) {
static int? x;
}
''',
[error(diag.initializerForStaticField, 8, 6)],
);
}
test_class_secondaryConstructor_fieldFormalParameter() async {
await assertErrorsInCode(
r'''
class A {
@@ -27,7 +38,7 @@ class A {
);
}
test_initializer() async {
test_class_secondaryConstructor_initializerList() async {
await assertErrorsInCode(
r'''
class A {
@@ -40,4 +51,82 @@ class A {
],
);
}
test_enum_primaryConstructor_fieldFormalParameter() async {
await assertErrorsInCode(
r'''
enum E(this.x) {
v(0);
static int? x;
}
''',
[error(diag.initializerForStaticField, 7, 6)],
);
}
test_enum_secondaryConstructor_fieldFormalParameter() async {
await assertErrorsInCode(
r'''
enum E {
v(0);
static int x = 0;
const E(this.x);
}
''',
[error(diag.initializerForStaticField, 47, 6)],
);
}
test_enum_secondaryConstructor_initializerList() async {
await assertErrorsInCode(
r'''
enum E {
v;
static int x = 1;
const E() : x = 0;
}
''',
[
error(diag.initializerForStaticField, 48, 5, messageContains: ["'x'"]),
],
);
}
test_extensionType_primaryConstructor_fieldFormalParameter_notReportedHere() async {
await assertErrorsInCode(
r'''
extension type E(this.x) {
static int? x;
}
''',
[error(diag.expectedRepresentationField, 17, 4)],
);
}
test_extensionType_secondaryConstructor_fieldFormalParameter() async {
await assertErrorsInCode(
r'''
extension type E(int it) {
static int x = 0;
E.named(this.x) : this.it = 0;
}
''',
[error(diag.initializerForStaticField, 57, 6)],
);
}
test_extensionType_secondaryConstructor_initializerList() async {
await assertErrorsInCode(
r'''
extension type E(int it) {
static int x = 1;
E.named() : x = 0, this.it = 0;
}
''',
[
error(diag.initializerForStaticField, 61, 5, messageContains: ["'x'"]),
],
);
}
}
@@ -16,7 +16,33 @@ main() {
@reflectiveTest
class InitializingFormalForNonExistentFieldTest
extends PubPackageResolutionTest {
test_class_nonExistent() async {
test_class_primary_fieldExists() async {
await assertNoErrorsInCode(r'''
class C(this.x) {
final int x;
}
''');
}
test_class_primary_fieldMissing() async {
await assertErrorsInCode(
r'''
class C(this.x) {}
''',
[error(diag.initializingFormalForNonExistentField, 8, 6)],
);
}
test_class_secondary_fieldExists() async {
await assertNoErrorsInCode(r'''
class C {
final int x;
C(this.x);
}
''');
}
test_class_secondary_fieldMissing() async {
await assertErrorsInCode(
r'''
class A {
@@ -27,7 +53,19 @@ class A {
);
}
test_class_notInEnclosingClass() async {
test_class_secondary_fieldMissing_getter() async {
await assertErrorsInCode(
r'''
class A {
int get x => 1;
A(this.x) {}
}
''',
[error(diag.initializingFormalForNonExistentField, 32, 6)],
);
}
test_class_secondary_notInEnclosingClass() async {
await assertErrorsInCode(
r'''
class A {
@@ -41,7 +79,7 @@ class B extends A {
);
}
test_class_optional() async {
test_class_secondary_optionalPositional_fieldMissing() async {
await assertErrorsInCode(
r'''
class A {
@@ -52,19 +90,28 @@ class A {
);
}
test_class_synthetic() async {
test_enum_primary_fieldExists() async {
await assertNoErrorsInCode(r'''
enum E(this.x) {
v(0);
final int x;
}
''');
}
test_enum_primary_fieldMissing() async {
await assertErrorsInCode(
r'''
class A {
int get x => 1;
A(this.x) {}
enum E(this.x) {
v(0);
}
''',
[error(diag.initializingFormalForNonExistentField, 32, 6)],
[error(diag.initializingFormalForNonExistentField, 7, 6)],
);
}
test_enum_existing() async {
test_enum_secondary_fieldExists() async {
await assertNoErrorsInCode(r'''
enum E {
v(0);
@@ -74,7 +121,32 @@ enum E {
''');
}
test_enum_optional() async {
test_enum_secondary_fieldMissing() async {
await assertErrorsInCode(
r'''
enum E {
v(0);
const E(this.x);
}
''',
[error(diag.initializingFormalForNonExistentField, 27, 6)],
);
}
test_enum_secondary_fieldMissing_getter() async {
await assertErrorsInCode(
r'''
enum E {
v(0);
const E(this.x);
int get x => 1;
}
''',
[error(diag.initializingFormalForNonExistentField, 27, 6)],
);
}
test_enum_secondary_optionalPositional_fieldMissing() async {
await assertErrorsInCode(
r'''
enum E {
@@ -89,28 +161,31 @@ enum E {
);
}
test_enum_required() async {
test_extensionType_primary_fieldMissing_notReportedHere() async {
await assertErrorsInCode(
r'''
enum E {
v(0);
const E(this.x);
}
extension type E(this.x) {}
''',
[error(diag.initializingFormalForNonExistentField, 27, 6)],
[error(diag.expectedRepresentationField, 17, 4)],
);
}
test_enum_synthetic() async {
test_extensionType_secondary_fieldExists() async {
await assertNoErrorsInCode(r'''
extension type E(int it) {
E.named(this.it);
}
''');
}
test_extensionType_secondary_fieldMissing() async {
await assertErrorsInCode(
r'''
enum E {
v(0);
const E(this.x);
int get x => 1;
extension type E(int it) {
E.named(this.x) : this.it = 0;
}
''',
[error(diag.initializingFormalForNonExistentField, 27, 6)],
[error(diag.initializingFormalForNonExistentField, 37, 6)],
);
}
}
@@ -50,7 +50,7 @@ class Invalid {
test_requiredParam_declaring() async {
await assertNoDiagnostics(r'''
class A({required var String this.s});
class A({required var String s});
''');
}
@@ -101,6 +101,8 @@ enum E(final String _v, [final String? v2]) {
enum E(this._v, [this._v2]) {
a('a'),
b('b', 'c');
final String _v;
final String? _v2;
}
''');
}