mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:58:29 +00:00
Report not initialized potentially non-nullable instance fields.
R=brianwilkerson@google.com, paulberry@google.com Change-Id: Id762e0cf651b2f2d7199f3447b5bf305b1a584c8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107567 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Paul Berry <paulberry@google.com>
This commit is contained in:
parent
030e60e0ef
commit
48662bdb94
|
@ -245,6 +245,8 @@ const List<ErrorCode> errorCodeValues = const [
|
|||
CompileTimeErrorCode.NON_SYNC_FACTORY,
|
||||
CompileTimeErrorCode.NOT_ASSIGNED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
|
||||
CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS,
|
||||
CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD,
|
||||
CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR,
|
||||
CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD,
|
||||
CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
|
||||
CompileTimeErrorCode.NOT_ITERABLE_SPREAD,
|
||||
|
|
|
@ -2554,6 +2554,43 @@ class CompileTimeErrorCode extends ErrorCode {
|
|||
"{0} required argument(s) expected, but {1} found.",
|
||||
correction: "Try adding the missing arguments.");
|
||||
|
||||
/**
|
||||
* It is an error if an instance field with potentially non-nullable type has
|
||||
* no initializer expression and is not initialized in a constructor via an
|
||||
* initializing formal or an initializer list entry, unless the field is
|
||||
* marked with the `late` modifier.
|
||||
*
|
||||
* Parameters:
|
||||
* 0: the name of the field that is not initialized
|
||||
*/
|
||||
static const CompileTimeErrorCode
|
||||
NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD = const CompileTimeErrorCode(
|
||||
'NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD',
|
||||
"Non-nullable instance field '{0}' must be initialized.",
|
||||
correction: "Try adding an initializer expression, "
|
||||
"or a generative constructor that initializes it, "
|
||||
"or mark it 'late'.");
|
||||
|
||||
/**
|
||||
* It is an error if an instance field with potentially non-nullable type has
|
||||
* no initializer expression and is not initialized in a constructor via an
|
||||
* initializing formal or an initializer list entry, unless the field is
|
||||
* marked with the `late` modifier.
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* Parameters:
|
||||
* 0: the name of the field that is not initialized
|
||||
*/
|
||||
static const CompileTimeErrorCode
|
||||
NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR =
|
||||
const CompileTimeErrorCode(
|
||||
'NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR',
|
||||
"Non-nullable instance field '{0}' must be initialized.",
|
||||
correction: "Try adding an initializer expression, "
|
||||
"or add a field initializer in this constructor, "
|
||||
"or mark it 'late'.");
|
||||
|
||||
/**
|
||||
* It is an error if a static variable with potentially non-nullable type has
|
||||
* no initializer expression.
|
||||
|
|
|
@ -2013,15 +2013,22 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
}
|
||||
// Prepare a list of not initialized fields.
|
||||
|
||||
// Prepare lists of not initialized fields.
|
||||
List<FieldElement> notInitFinalFields = <FieldElement>[];
|
||||
fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) {
|
||||
if (state == INIT_STATE.NOT_INIT) {
|
||||
if (fieldElement.isFinal && !fieldElement.isLate) {
|
||||
notInitFinalFields.add(fieldElement);
|
||||
}
|
||||
List<FieldElement> notInitNonNullableFields = <FieldElement>[];
|
||||
fieldElementsMap.forEach((FieldElement field, INIT_STATE state) {
|
||||
if (state != INIT_STATE.NOT_INIT) return;
|
||||
if (field.isLate) return;
|
||||
|
||||
if (field.isFinal) {
|
||||
notInitFinalFields.add(field);
|
||||
} else if (_isNonNullable &&
|
||||
_typeSystem.isPotentiallyNonNullable(field.type)) {
|
||||
notInitNonNullableFields.add(field);
|
||||
}
|
||||
});
|
||||
|
||||
// Visit all of the states in the map to ensure that none were never
|
||||
// initialized.
|
||||
fieldElementsMap.forEach((FieldElement fieldElement, INIT_STATE state) {
|
||||
|
@ -2055,6 +2062,17 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
|
|||
[names[0], names[1], names.length - 2]);
|
||||
}
|
||||
}
|
||||
|
||||
if (notInitNonNullableFields.isNotEmpty) {
|
||||
var names = notInitNonNullableFields.map((f) => f.name).toList()..sort();
|
||||
for (var name in names) {
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode
|
||||
.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR,
|
||||
constructor.returnType,
|
||||
[name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3419,7 +3437,9 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
|
|||
}
|
||||
for (ClassMember classMember in members) {
|
||||
if (classMember is FieldDeclaration) {
|
||||
_checkForFinalNotInitialized(classMember.fields);
|
||||
var fields = classMember.fields;
|
||||
_checkForFinalNotInitialized(fields);
|
||||
_checkForNotInitializedNonNullableInstanceFields(classMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4708,6 +4728,31 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
|
|||
}
|
||||
}
|
||||
|
||||
void _checkForNotInitializedNonNullableInstanceFields(
|
||||
FieldDeclaration fieldDeclaration,
|
||||
) {
|
||||
if (!_isNonNullable) return;
|
||||
|
||||
if (fieldDeclaration.isStatic) return;
|
||||
var fields = fieldDeclaration.fields;
|
||||
|
||||
if (fields.isLate) return;
|
||||
if (fields.isFinal) return;
|
||||
|
||||
for (var field in fields.variables) {
|
||||
if (field.initializer != null) continue;
|
||||
|
||||
var type = field.declaredElement.type;
|
||||
if (!_typeSystem.isPotentiallyNonNullable(type)) continue;
|
||||
|
||||
_errorReporter.reportErrorForNode(
|
||||
CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD,
|
||||
field,
|
||||
[field.name.name],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _checkForNotInitializedNonNullableStaticField(FieldDeclaration node) {
|
||||
if (!_isNonNullable) return;
|
||||
|
||||
|
|
|
@ -2937,6 +2937,10 @@ class TypeSystemTest extends AbstractTypeSystemTest {
|
|||
expect(typeSystem.isNonNullable(starType), true);
|
||||
}
|
||||
|
||||
test_isNonNullable_Never() {
|
||||
expect(typeSystem.isNonNullable(neverType), true);
|
||||
}
|
||||
|
||||
test_isNonNullable_never() {
|
||||
expect(typeSystem.isNonNullable(neverType), true);
|
||||
}
|
||||
|
@ -3115,6 +3119,10 @@ class TypeSystemTest extends AbstractTypeSystemTest {
|
|||
expect(typeSystem.isNullable(starType), false);
|
||||
}
|
||||
|
||||
test_isNullable_Never() {
|
||||
expect(typeSystem.isNullable(neverType), false);
|
||||
}
|
||||
|
||||
test_isNullable_never() {
|
||||
expect(typeSystem.isNullable(neverType), false);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,184 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'package:analyzer/src/dart/analysis/experiments.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/driver_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(NotInitializedNonNullableInstanceFieldTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class NotInitializedNonNullableInstanceFieldTest extends DriverResolutionTest {
|
||||
@override
|
||||
AnalysisOptionsImpl get analysisOptions =>
|
||||
AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
|
||||
|
||||
test_constructorFieldInitializer() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
int x;
|
||||
|
||||
A() : x = 0;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_factoryConstructor() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
int x = 0;
|
||||
|
||||
A(this.x);
|
||||
|
||||
factory A.named() => A(0);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_fieldFormal() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
int x;
|
||||
|
||||
A(this.x);
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_futureOr_questionArgument_none() async {
|
||||
await assertNoErrorsInCode('''
|
||||
import 'dart:async';
|
||||
|
||||
class A {
|
||||
FutureOr<int?> x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_hasInitializer() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
int x = 0;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_inferredType() async {
|
||||
await assertErrorsInCode('''
|
||||
abstract class A {
|
||||
int get x;
|
||||
}
|
||||
|
||||
class B extends A {
|
||||
var x;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD,
|
||||
61, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_notAllConstructors() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
int x;
|
||||
|
||||
A.a(this.x);
|
||||
|
||||
A.b();
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode
|
||||
.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR,
|
||||
38,
|
||||
1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_notAllFields() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
int x, y, z;
|
||||
|
||||
A() : x = 0, z = 2;
|
||||
}
|
||||
''', [
|
||||
error(
|
||||
CompileTimeErrorCode
|
||||
.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD_CONSTRUCTOR,
|
||||
28,
|
||||
1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_nullable() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
int? x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_type_dynamic() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
dynamic x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_type_dynamic_implicit() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
var x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_type_never() async {
|
||||
await assertErrorsInCode('''
|
||||
class A {
|
||||
Never x;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD,
|
||||
18, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_type_void() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A {
|
||||
void x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_typeParameter() async {
|
||||
await assertErrorsInCode('''
|
||||
class A<T> {
|
||||
T x;
|
||||
}
|
||||
''', [
|
||||
error(CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_INSTANCE_FIELD,
|
||||
17, 1),
|
||||
]);
|
||||
}
|
||||
|
||||
test_typeParameter_nullable() async {
|
||||
await assertNoErrorsInCode('''
|
||||
class A<T> {
|
||||
T? x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
|
@ -138,6 +138,8 @@ import 'non_constant_spread_expression_from_deferred_library_test.dart'
|
|||
import 'non_null_opt_out_test.dart' as non_null_opt_out;
|
||||
import 'not_assigned_potentially_non_nullable_local_variable_test.dart'
|
||||
as not_assigned_potentially_non_nullable_local_variable;
|
||||
import 'not_initialized_non_nullable_instance_field_test.dart'
|
||||
as not_initialized_non_nullable_instance_field;
|
||||
import 'not_initialized_non_nullable_static_field_test.dart'
|
||||
as not_initialized_non_nullable_static_field;
|
||||
import 'not_initialized_non_nullable_top_level_variable_test.dart'
|
||||
|
@ -319,6 +321,7 @@ main() {
|
|||
non_constant_spread_expression_from_deferred_library.main();
|
||||
non_null_opt_out.main();
|
||||
not_assigned_potentially_non_nullable_local_variable.main();
|
||||
not_initialized_non_nullable_instance_field.main();
|
||||
not_initialized_non_nullable_static_field.main();
|
||||
not_initialized_non_nullable_top_level_variable.main();
|
||||
not_iterable_spread.main();
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
// SharedOptions=--enable-experiment=non-nullable
|
||||
|
||||
// Test that it is an error if an instance field with potentially non-nullable
|
||||
// type has no initializer expression and is not initialized in a constructor
|
||||
// via an initializing formal or an initializer list entry, unless the field is
|
||||
// marked with the `late` modifier.
|
||||
void main() {}
|
||||
|
||||
class A {
|
||||
int v = 0; //# 01: ok
|
||||
int? v; //# 02: ok
|
||||
int? v = 0; //# 03: ok
|
||||
dynamic v; //# 04: ok
|
||||
var v; //# 05: ok
|
||||
void v; //# 06: ok
|
||||
|
||||
int v; A(this.v); //# 07: ok
|
||||
|
||||
int v; A() : v = 0; //# 08: ok
|
||||
|
||||
int v; //# 09: compile-time error
|
||||
|
||||
Never v; //# 10: compile-time error
|
||||
|
||||
int v; A(); //# 11: compile-time error
|
||||
|
||||
int v; A(this.v); A.second(); //# 12: compile-time error
|
||||
}
|
||||
|
||||
class B<T> {
|
||||
T v; //# 13: compile-time error
|
||||
|
||||
T? v; //# 14: ok
|
||||
|
||||
T v; B(this.v); //# 15: ok
|
||||
}
|
|
@ -15,7 +15,7 @@ int f1(
|
|||
required //# 03: compile-time error
|
||||
class C1 {
|
||||
required //# 04: compile-time error
|
||||
int f2;
|
||||
int f2 = 0;
|
||||
}
|
||||
|
||||
// Duplicate modifier
|
||||
|
|
Loading…
Reference in a new issue