Report an error when a non-nullable top-level variable is not initialized.
Change-Id: I2e5e33c01d5f2c10d41b7e2f76e93eaacadffff6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106762
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 046a034..7c1fa00 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -236,6 +236,7 @@
CompileTimeErrorCode.NON_GENERATIVE_CONSTRUCTOR,
CompileTimeErrorCode.NON_SYNC_FACTORY,
CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS,
+ CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
CompileTimeErrorCode.NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
CompileTimeErrorCode.NOT_ITERABLE_SPREAD,
CompileTimeErrorCode.NOT_MAP_SPREAD,
diff --git a/pkg/analyzer/lib/src/error/codes.dart b/pkg/analyzer/lib/src/error/codes.dart
index faec666..791135a 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -2453,12 +2453,29 @@
correction: "Try adding the missing arguments.");
/**
+ * It is an error if a top level variable <cut> with potentially non-nullable
+ * type has no initializer expression <cut>.
+ *
+ * Parameters:
+ * 0: the name of the variable that is invalid
+ */
+ static const CompileTimeErrorCode
+ NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE =
+ const CompileTimeErrorCode(
+ 'NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE',
+ "Non-nullable top-level variable '{0}' must be initialized.",
+ correction: "Try adding an initializer expression.");
+
+ /**
* It is an error if a potentially non-nullable local variable which has no
* initializer expression and is not marked `late` is used before it is
* definitely assigned.
*
* TODO(scheglov) Update the code and the message when implement definite
* assignment analysis.
+ *
+ * Parameters:
+ * 0: the name of the variable that is invalid
*/
static const CompileTimeErrorCode
NOT_INITIALIZED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE =
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index bcb76f5..4b370a9 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -1353,6 +1353,7 @@
@override
void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
_checkForFinalNotInitialized(node.variables);
+ _checkForNotInitializedNonNullableTopLevelVariable(node.variables);
super.visitTopLevelVariableDeclaration(node);
}
@@ -3405,6 +3406,38 @@
}
}
+ void _checkForNotInitializedNonNullableTopLevelVariable(
+ VariableDeclarationList node,
+ ) {
+ // Const and final checked separately.
+ if (node.isConst || node.isFinal) {
+ return;
+ }
+
+ if (!_isNonNullable) {
+ return;
+ }
+
+ if (node.type == null) {
+ return;
+ }
+ var type = node.type.type;
+
+ if (!_typeSystem.isPotentiallyNonNullable(type)) {
+ return;
+ }
+
+ for (var variable in node.variables) {
+ if (variable.initializer == null) {
+ _errorReporter.reportErrorForNode(
+ CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+ variable.name,
+ [variable.name.name],
+ );
+ }
+ }
+ }
+
/**
* If there are no constructors in the given [members], verify that all
* final fields are initialized. Cases in which there is at least one
diff --git a/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart b/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart
index b18a85e..679f2ce 100644
--- a/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/missing_default_value_for_parameter_test.dart
@@ -121,7 +121,7 @@
test_genericFunctionType() async {
await assertNoErrorsInCode('''
-void Function({String s}) log;
+void Function({String s})? log;
''');
}
diff --git a/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_top_level_variable_test.dart b/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_top_level_variable_test.dart
new file mode 100644
index 0000000..9399e70
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_top_level_variable_test.dart
@@ -0,0 +1,76 @@
+// 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(NotInitializedNonNullableTopLevelVariableTest);
+ });
+}
+
+@reflectiveTest
+class NotInitializedNonNullableTopLevelVariableTest
+ extends DriverResolutionTest {
+ @override
+ AnalysisOptionsImpl get analysisOptions =>
+ AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
+
+ test_hasInitializer() async {
+ assertNoErrorsInCode('''
+int v = 0;
+''');
+ }
+
+ test_noInitializer() async {
+ assertErrorsInCode('''
+int x = 0, y, z = 2;
+''', [
+ error(
+ CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+ 11,
+ 1),
+ ]);
+ }
+
+ test_nullable() async {
+ assertNoErrorsInCode('''
+int? v;
+''');
+ }
+
+ test_type_dynamic() async {
+ assertNoErrorsInCode('''
+dynamic v;
+''');
+ }
+
+ test_type_dynamic_implicit() async {
+ assertNoErrorsInCode('''
+var v;
+''');
+ }
+
+ test_type_never() async {
+ assertErrorsInCode('''
+Never v;
+''', [
+ error(
+ CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_VARIABLE,
+ 6,
+ 1),
+ ]);
+ }
+
+ test_type_void() async {
+ assertNoErrorsInCode('''
+void v;
+''');
+ }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart b/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
index f9703db..e19ac7b 100644
--- a/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/sdk_version_never_test.dart
@@ -42,7 +42,7 @@
test_lessThan() async {
await verifyVersion('2.3.0', '''
-Never sink;
+Never sink = (throw 42);
''', expectedErrors: [
error(HintCode.SDK_VERSION_NEVER, 0, 5),
]);
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index 2f965be..61643d4 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -96,6 +96,8 @@
import 'non_constant_spread_expression_from_deferred_library_test.dart'
as non_constant_spread_expression_from_deferred_library;
import 'non_null_opt_out_test.dart' as non_null_opt_out;
+import 'not_initialized_non_nullable_top_level_variable_test.dart'
+ as not_initialized_non_nullable_top_level_variable;
import 'not_initialized_potentially_non_nullable_local_variable_test.dart'
as not_initialized_potentially_non_nullable_local_variable;
import 'not_iterable_spread_test.dart' as not_iterable_spread;
@@ -237,6 +239,7 @@
non_constant_set_element_from_deferred_library.main();
non_constant_spread_expression_from_deferred_library.main();
non_null_opt_out.main();
+ not_initialized_non_nullable_top_level_variable.main();
not_initialized_potentially_non_nullable_local_variable.main();
not_iterable_spread.main();
not_map_spread.main();
diff --git a/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart b/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart
index ff664b7..0f72ece 100644
--- a/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart
+++ b/tests/language_2/nnbd/static_errors/missing_default_value_for_parameter_test.dart
@@ -30,7 +30,7 @@
}
typedef void f13({String});
void printToLog(void f({String})) {}
-void Function({String s}) f14;
+void Function({String s})? f14;
class B<T extends Object?> {
// Potentially non-nullable types
diff --git a/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_top_test.dart b/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_top_test.dart
new file mode 100644
index 0000000..28e55e4
--- /dev/null
+++ b/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_top_test.dart
@@ -0,0 +1,18 @@
+// 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 a top level variable with non-nullable type has
+// no initializer expression.
+void main() {}
+
+int v; //# 01: compile-time error
+int v = 0; //# 02: ok
+int? v; //# 03: ok
+int? v = 0; //# 04: ok
+dynamic v; //# 05: ok
+var v; //# 06: ok
+void v; //# 07: ok
+Never v; //# 08: compile-time error