Report NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD.

Change-Id: I0e2b46df212d2534498a181b36066cd05f635700
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/107260
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Leaf Petersen <leafp@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 515584d..e59f4c9 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -237,6 +237,7 @@
   CompileTimeErrorCode.NON_SYNC_FACTORY,
   CompileTimeErrorCode.NOT_ASSIGNED_POTENTIALLY_NON_NULLABLE_LOCAL_VARIABLE,
   CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS,
+  CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD,
   CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_TOP_LEVEL_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 d1312e3..12c0a1b 100644
--- a/pkg/analyzer/lib/src/error/codes.dart
+++ b/pkg/analyzer/lib/src/error/codes.dart
@@ -2471,6 +2471,18 @@
           correction: "Try adding the missing arguments.");
 
   /**
+   * It is an error if a static variable with potentially non-nullable type has
+   * no initializer expression.
+   *
+   * Parameters:
+   * 0: the name of the field that is invalid
+   */
+  static const CompileTimeErrorCode NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD =
+      const CompileTimeErrorCode('NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD',
+          "Non-nullable static field '{0}' must be initialized.",
+          correction: "Try adding an initializer expression.");
+
+  /**
    * It is an error if a top level variable <cut> with potentially non-nullable
    * type has no initializer expression <cut>.
    *
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index 880545a..f618d02 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -718,6 +718,7 @@
       }
     }
     try {
+      _checkForNotInitializedNonNullableStaticField(node);
       super.visitFieldDeclaration(node);
     } finally {
       _isInStaticVariableDeclaration = false;
@@ -4718,18 +4719,42 @@
     }
   }
 
+  void _checkForNotInitializedNonNullableStaticField(FieldDeclaration node) {
+    if (!_isNonNullable) return;
+
+    if (!node.isStatic) return;
+
+    var fields = node.fields;
+
+    // Const and final checked separately.
+    if (fields.isConst || fields.isFinal) return;
+
+    if (fields.type == null) return;
+    var type = fields.type.type;
+
+    if (!_typeSystem.isPotentiallyNonNullable(type)) return;
+
+    for (var variable in fields.variables) {
+      if (variable.initializer == null) {
+        _errorReporter.reportErrorForNode(
+          CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD,
+          variable.name,
+          [variable.name.name],
+        );
+      }
+    }
+  }
+
   void _checkForNotInitializedNonNullableTopLevelVariable(
     VariableDeclarationList node,
   ) {
+    if (!_isNonNullable) return;
+
     // Const and final checked separately.
     if (node.isConst || node.isFinal) {
       return;
     }
 
-    if (!_isNonNullable) {
-      return;
-    }
-
     if (node.type == null) {
       return;
     }
diff --git a/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_static_field_test.dart b/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_static_field_test.dart
new file mode 100644
index 0000000..233eb2b
--- /dev/null
+++ b/pkg/analyzer/test/src/diagnostics/not_initialized_non_nullable_static_field_test.dart
@@ -0,0 +1,95 @@
+// 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(NotInitializedNonNullableStaticFieldTest);
+  });
+}
+
+@reflectiveTest
+class NotInitializedNonNullableStaticFieldTest extends DriverResolutionTest {
+  @override
+  AnalysisOptionsImpl get analysisOptions =>
+      AnalysisOptionsImpl()..enabledExperiments = [EnableString.non_nullable];
+
+  test_futureOr_questionArgument_none() async {
+    assertNoErrorsInCode('''
+import 'dart:async';
+
+class A {
+  static FutureOr<int?> v;
+}
+''');
+  }
+
+  test_hasInitializer() async {
+    assertNoErrorsInCode('''
+class A {
+  static int v = 0;
+}
+''');
+  }
+
+  test_noInitializer() async {
+    assertErrorsInCode('''
+class A {
+  static int x = 0, y, z = 2;
+}
+''', [
+      error(CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD, 30,
+          1),
+    ]);
+  }
+
+  test_nullable() async {
+    assertNoErrorsInCode('''
+class A {
+  static int? v;
+}
+''');
+  }
+
+  test_type_dynamic() async {
+    assertNoErrorsInCode('''
+class A {
+  static dynamic v;
+}
+''');
+  }
+
+  test_type_dynamic_implicit() async {
+    assertNoErrorsInCode('''
+class A {
+  static var v;
+}
+''');
+  }
+
+  test_type_never() async {
+    assertErrorsInCode('''
+class A {
+  static Never v;
+}
+''', [
+      error(CompileTimeErrorCode.NOT_INITIALIZED_NON_NULLABLE_STATIC_FIELD, 25,
+          1),
+    ]);
+  }
+
+  test_type_void() async {
+    assertNoErrorsInCode('''
+class A {
+  static void v;
+}
+''');
+  }
+}
diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart
index bfd21cf..810392d 100644
--- a/pkg/analyzer/test/src/diagnostics/test_all.dart
+++ b/pkg/analyzer/test/src/diagnostics/test_all.dart
@@ -98,6 +98,8 @@
 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_static_field_test.dart'
+    as not_initialized_non_nullable_static_field;
 import 'not_initialized_non_nullable_top_level_variable_test.dart'
     as not_initialized_non_nullable_top_level_variable;
 import 'not_iterable_spread_test.dart' as not_iterable_spread;
@@ -240,6 +242,7 @@
     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_static_field.main();
     not_initialized_non_nullable_top_level_variable.main();
     not_iterable_spread.main();
     not_map_spread.main();
diff --git a/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_static_field_test.dart b/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_static_field_test.dart
new file mode 100644
index 0000000..a70142e
--- /dev/null
+++ b/tests/language_2/nnbd/static_errors/not_initialized_non_nullable_static_field_test.dart
@@ -0,0 +1,20 @@
+// 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 static field non-nullable type has no
+// initializer expression.
+void main() {}
+
+class A {
+  static int v; //# 01: compile-time error
+  static int v = 0; //# 02: ok
+  static int? v; //# 03: ok
+  static int? v = 0; //# 04: ok
+  static dynamic v; //# 05: ok
+  static var v; //# 06: ok
+  static void v; //# 07: ok
+  static Never v; //# 08: compile-time error
+}