Generate an error if writing to a potentially-already-assigned final var.

Change-Id: Iee4dccbc2c11290cdf56b3a95259f6401a123620
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164246
Reviewed-by: Johnni Winther <johnniwinther@google.com>
diff --git a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
index 552baa0..7c00414 100644
--- a/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
+++ b/pkg/_fe_analyzer_shared/lib/src/messages/codes_generated.dart
@@ -4039,6 +4039,32 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            name)> templateFinalPossiblyAssignedError = const Template<
+        Message Function(String name)>(
+    messageTemplate:
+        r"""Final variable '#name' might already be assigned at this point.""",
+    withArguments: _withArgumentsFinalPossiblyAssignedError);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeFinalPossiblyAssignedError =
+    const Code<Message Function(String name)>(
+        "FinalPossiblyAssignedError", templateFinalPossiblyAssignedError,
+        analyzerCodes: <String>["ASSIGNMENT_TO_FINAL_LOCAL"]);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFinalPossiblyAssignedError(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeFinalPossiblyAssignedError,
+      message:
+          """Final variable '${name}' might already be assigned at this point.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeForInLoopExactlyOneVariable =
     messageForInLoopExactlyOneVariable;
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
index 878aad2..49becf5 100644
--- a/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/inference_visitor.dart
@@ -5630,8 +5630,10 @@
       VariableSet node, DartType typeContext) {
     VariableDeclarationImpl variable = node.variable;
     bool isDefinitelyAssigned = false;
+    bool isDefinitelyUnassigned = false;
     if (inferrer.isNonNullableByDefault) {
       isDefinitelyAssigned = inferrer.flowAnalysis.isAssigned(variable);
+      isDefinitelyUnassigned = inferrer.flowAnalysis.isUnassigned(variable);
     }
     DartType declaredOrInferredType = variable.lateType ?? variable.type;
     DartType promotedType;
@@ -5677,6 +5679,17 @@
                     node.fileOffset,
                     node.variable.name.length));
           }
+        } else if (variable.isStaticLate) {
+          if (!isDefinitelyUnassigned) {
+            return new ExpressionInferenceResult(
+                resultType,
+                inferrer.helper.wrapInProblem(
+                    resultExpression,
+                    templateFinalPossiblyAssignedError
+                        .withArguments(node.variable.name),
+                    node.fileOffset,
+                    node.variable.name.length));
+          }
         }
       }
     }
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 2d71510..6138394 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -4230,6 +4230,17 @@
       t = 0;
     }
 
+FinalPossiblyAssignedError:
+  template: "Final variable '#name' might already be assigned at this point."
+  analyzerCode: ASSIGNMENT_TO_FINAL_LOCAL
+  configuration: nnbd-strong
+  script: |
+    method() {
+      final int i;
+      i = 0;
+      i = 0;
+    }
+
 NonAgnosticConstant:
   template: "Constant value is not strong/weak mode agnostic."