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