Migration: better handle a variable declaration statement with multiple variables.

This fix addresses the case where a variable declaration statement
declares multiple variables with different types; previously, we were
applying the type of the first variable to all the variables, leading
to possible crashes.

There are still some other issues with variable declaration statements
that declare multiple variables; I'll address them in follow-up CLs.

Bug: https://github.com/dart-lang/sdk/issues/47669
Change-Id: Iba82ef5ccc0b22382f30bf5a6ed391f4874f7443
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/220042
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 653d36b..2dad8d1 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -660,7 +660,7 @@
   DecoratedType? visitVariableDeclarationList(VariableDeclarationList node) {
     node.metadata.accept(this);
     var typeAnnotation = node.type;
-    var type = _pushNullabilityNodeTarget(
+    var declaredType = _pushNullabilityNodeTarget(
         NullabilityNodeTarget.element(
             node.variables.first.declaredElement!, _getLineInfo),
         () => typeAnnotation?.accept(this));
@@ -671,9 +671,11 @@
     if (hint != null && hint.kind == HintCommentKind.lateFinal) {
       _variables!.recordLateHint(source, node, hint);
     }
+    var parent = node.parent;
     for (var variable in node.variables) {
       variable.metadata.accept(this);
       var declaredElement = variable.declaredElement;
+      var type = declaredType;
       if (type == null) {
         var target =
             NullabilityNodeTarget.element(declaredElement!, _getLineInfo);
@@ -683,11 +685,11 @@
       }
       _variables!.recordDecoratedElementType(declaredElement, type);
       variable.initializer?.accept(this);
-    }
-    var parent = node.parent;
-    if (parent is FieldDeclaration) {
-      if (_hasAngularChildAnnotation(parent.metadata)) {
-        _graph.makeNullable(type!.node!, AngularAnnotationOrigin(source, node));
+      if (parent is FieldDeclaration) {
+        if (_hasAngularChildAnnotation(parent.metadata)) {
+          _graph.makeNullable(
+              type.node!, AngularAnnotationOrigin(source, node));
+        }
       }
     }
     return null;
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index de7d447..69a8936 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -8927,6 +8927,31 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  Future<void> test_var_with_different_types() async {
+    // Based on https://github.com/dart-lang/sdk/issues/47669
+    var content = '''
+class C<T> {
+  T m() => throw 'foo';
+}
+f(bool b, List<C<int>> cs) {
+  var x = !b,
+      y = cs.first,
+      z = y.m();
+}
+''';
+    var expected = '''
+class C<T> {
+  T m() => throw 'foo';
+}
+f(bool b, List<C<int>> cs) {
+  var x = !b,
+      y = cs.first,
+      z = y.m();
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   Future<void> test_weak_if_visit_weak_subexpression() async {
     var content = '''
 int f(int x, int/*?*/ y) {