Migration: add support for local variable declarations.

Change-Id: Ifead9354c583a070a7240d158823eafe1be3d875
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/104840
Commit-Queue: Paul Berry <paulberry@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/nullability/graph_builder.dart b/pkg/analysis_server/lib/src/nullability/graph_builder.dart
index c2f4bfa..1cf5c85 100644
--- a/pkg/analysis_server/lib/src/nullability/graph_builder.dart
+++ b/pkg/analysis_server/lib/src/nullability/graph_builder.dart
@@ -439,6 +439,17 @@
     return DecoratedType(typeName.type, NullabilityNode.never);
   }
 
+  @override
+  DecoratedType visitVariableDeclaration(VariableDeclaration node) {
+    var destinationType = getOrComputeElementType(node.declaredElement);
+    var initializer = node.initializer;
+    if (initializer == null) {
+      throw UnimplementedError('TODO(paulberry)');
+    } else {
+      _handleAssignment(destinationType, initializer);
+    }
+  }
+
   /// Creates the necessary constraint(s) for an assignment from [sourceType] to
   /// [destinationType].  [expression] is the expression whose type is
   /// [sourceType]; it is the expression we will have to null-check in the case
diff --git a/pkg/analysis_server/lib/src/nullability/node_builder.dart b/pkg/analysis_server/lib/src/nullability/node_builder.dart
index e1c6939..8827aee 100644
--- a/pkg/analysis_server/lib/src/nullability/node_builder.dart
+++ b/pkg/analysis_server/lib/src/nullability/node_builder.dart
@@ -85,14 +85,14 @@
   @override
   DecoratedType visitFunctionDeclaration(FunctionDeclaration node) {
     _handleExecutableDeclaration(node.declaredElement, node.returnType,
-        node.functionExpression.parameters, node);
+        node.functionExpression.parameters, node.functionExpression.body, node);
     return null;
   }
 
   @override
   DecoratedType visitMethodDeclaration(MethodDeclaration node) {
-    _handleExecutableDeclaration(
-        node.declaredElement, node.returnType, node.parameters, node);
+    _handleExecutableDeclaration(node.declaredElement, node.returnType,
+        node.parameters, node.body, node);
     return null;
   }
 
@@ -169,11 +169,21 @@
     return null;
   }
 
+  @override
+  DecoratedType visitVariableDeclarationList(VariableDeclarationList node) {
+    var type = decorateType(node.type, node);
+    for (var variable in node.variables) {
+      _variables.recordDecoratedElementType(variable.declaredElement, type);
+    }
+    return null;
+  }
+
   /// Common handling of function and method declarations.
   void _handleExecutableDeclaration(
       ExecutableElement declaredElement,
       TypeAnnotation returnType,
       FormalParameterList parameters,
+      FunctionBody body,
       AstNode enclosingNode) {
     var decoratedReturnType = decorateType(returnType, enclosingNode);
     var previousFunctionType = _currentFunctionType;
@@ -187,6 +197,7 @@
     _currentFunctionType = functionType;
     try {
       parameters?.accept(this);
+      body?.accept(this);
     } finally {
       _currentFunctionType = previousFunctionType;
     }
diff --git a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
index 115dc45..5851f82 100644
--- a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
+++ b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
@@ -821,6 +821,17 @@
 ''');
     assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node);
   }
+
+  test_variableDeclaration() async {
+    await analyze('''
+void f(int i) {
+  int j = i;
+}
+''');
+    assertEdge(decoratedTypeAnnotation('int i').node,
+        decoratedTypeAnnotation('int j').node,
+        hard: true);
+  }
 }
 
 class MigrationVisitorTestBase extends AbstractSingleUnitTest {
@@ -1041,6 +1052,16 @@
     expect(bound.node.isNullable, isTrue);
     expect(bound.type, same(typeProvider.objectType));
   }
+
+  test_variableDeclaration_type_simple() async {
+    await analyze('''
+main() {
+  int i;
+}
+''');
+    var decoratedType = decoratedTypeAnnotation('int');
+    expect(decoratedType.node, TypeMatcher<NullabilityNodeMutable>());
+  }
 }
 
 /// Mock representation of constraint variables.
diff --git a/pkg/analysis_server/test/src/nullability/provisional_api_test.dart b/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
index ba01ca0..47140d6 100644
--- a/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
+++ b/pkg/analysis_server/test/src/nullability/provisional_api_test.dart
@@ -286,6 +286,26 @@
     await _checkSingleFileChanges(content, expected);
   }
 
+  test_data_flow_local_declaration() async {
+    var content = '''
+void f(int i) {
+  int j = i;
+}
+main() {
+  f(null);
+}
+''';
+    var expected = '''
+void f(int? i) {
+  int? j = i;
+}
+main() {
+  f(null);
+}
+''';
+    await _checkSingleFileChanges(content, expected);
+  }
+
   test_data_flow_outward() async {
     var content = '''
 int f(int i) => null;