Extract VariableDeclarationResolver.

Change-Id: Ib88682ae56b3e1dd9717957d6f01571de5a685a3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/159600
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
new file mode 100644
index 0000000..ce9ed26
--- /dev/null
+++ b/pkg/analyzer/lib/src/dart/resolver/variable_declaration_resolver.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2020, 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/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/src/dart/ast/ast.dart';
+import 'package:analyzer/src/dart/constant/utilities.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/dart/element/type_demotion.dart';
+import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/resolver.dart';
+import 'package:meta/meta.dart';
+
+/// Helper for resolving [VariableDeclaration]s.
+class VariableDeclarationResolver {
+  final ResolverVisitor _resolver;
+  final FlowAnalysisHelper _flowAnalysis;
+  final bool _strictInference;
+
+  VariableDeclarationResolver({
+    @required ResolverVisitor resolver,
+    @required FlowAnalysisHelper flowAnalysis,
+    @required bool strictInference,
+  })  : _resolver = resolver,
+        _flowAnalysis = flowAnalysis,
+        _strictInference = strictInference;
+
+  void resolve(VariableDeclarationImpl node) {
+    var parent = node.parent as VariableDeclarationList;
+
+    var initializer = node.initializer;
+
+    if (initializer == null) {
+      if (_strictInference && parent.type == null) {
+        _resolver.errorReporter.reportErrorForNode(
+          HintCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE,
+          node,
+          [node.name.name],
+        );
+      }
+      return;
+    }
+
+    var element = node.declaredElement;
+    var isTopLevel =
+        element is FieldElement || element is TopLevelVariableElement;
+
+    InferenceContext.setTypeFromNode(initializer, node);
+    if (isTopLevel) {
+      _flowAnalysis?.topLevelDeclaration_enter(node, null, null);
+    }
+
+    initializer.accept(_resolver);
+    initializer = node.initializer;
+
+    if (parent.type == null) {
+      _setInferredType(element, initializer.staticType);
+    }
+
+    if (element.initializer != null) {
+      var initializerFunction = element.initializer as FunctionElementImpl;
+      initializerFunction.returnType = initializer.staticType;
+    }
+
+    if (isTopLevel) {
+      _flowAnalysis?.topLevelDeclaration_exit();
+    }
+
+    // Note: in addition to cloning the initializers for const variables, we
+    // have to clone the initializers for non-static final fields (because if
+    // they occur in a class with a const constructor, they will be needed to
+    // evaluate the const constructor).
+    if (element is ConstVariableElement) {
+      (element as ConstVariableElement).constantInitializer =
+          ConstantAstCloner().cloneNode(initializer);
+    }
+  }
+
+  void _setInferredType(VariableElement element, DartType initializerType) {
+    if (element is LocalVariableElementImpl) {
+      if (initializerType.isDartCoreNull) {
+        initializerType = DynamicTypeImpl.instance;
+      }
+
+      var inferredType = demoteType(
+        _resolver.definingLibrary,
+        initializerType,
+      );
+      element.type = inferredType;
+    }
+  }
+}
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 5966e9e..32fd184 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -38,6 +38,7 @@
 import 'package:analyzer/src/dart/resolver/scope.dart';
 import 'package:analyzer/src/dart/resolver/type_property_resolver.dart';
 import 'package:analyzer/src/dart/resolver/typed_literal_resolver.dart';
+import 'package:analyzer/src/dart/resolver/variable_declaration_resolver.dart';
 import 'package:analyzer/src/dart/resolver/yield_statement_resolver.dart';
 import 'package:analyzer/src/error/bool_expression_verifier.dart';
 import 'package:analyzer/src/error/codes.dart';
@@ -45,6 +46,7 @@
 import 'package:analyzer/src/error/nullable_dereference_verifier.dart';
 import 'package:analyzer/src/generated/constant.dart';
 import 'package:analyzer/src/generated/element_resolver.dart';
+import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/migratable_ast_info_provider.dart';
 import 'package:analyzer/src/generated/migration.dart';
 import 'package:analyzer/src/generated/source.dart';
@@ -171,6 +173,7 @@
   ForResolver _forResolver;
   PostfixExpressionResolver _postfixExpressionResolver;
   PrefixExpressionResolver _prefixExpressionResolver;
+  VariableDeclarationResolver _variableDeclarationResolver;
   YieldStatementResolver _yieldStatementResolver;
 
   NullSafetyDeadCodeVerifier nullSafetyDeadCodeVerifier;
@@ -290,6 +293,10 @@
         super(definingLibrary, source, typeProvider, errorListener,
             nameScope: nameScope) {
     _promoteManager = TypePromotionManager(typeSystem);
+
+    var analysisOptions =
+        definingLibrary.context.analysisOptions as AnalysisOptionsImpl;
+
     nullableDereferenceVerifier = NullableDereferenceVerifier(
       typeSystem: typeSystem,
       errorReporter: errorReporter,
@@ -341,6 +348,11 @@
       resolver: this,
       flowAnalysis: _flowAnalysis,
     );
+    _variableDeclarationResolver = VariableDeclarationResolver(
+      resolver: this,
+      flowAnalysis: _flowAnalysis,
+      strictInference: analysisOptions.strictInference,
+    );
     _yieldStatementResolver = YieldStatementResolver(
       resolver: this,
     );
@@ -1720,29 +1732,10 @@
 
   @override
   void visitVariableDeclaration(VariableDeclaration node) {
-    var grandParent = node.parent.parent;
-    bool isTopLevel = grandParent is FieldDeclaration ||
-        grandParent is TopLevelVariableDeclaration;
-    InferenceContext.setTypeFromNode(node.initializer, node);
-    if (isTopLevel) {
-      _flowAnalysis?.topLevelDeclaration_enter(node, null, null);
-    }
-    super.visitVariableDeclaration(node);
-    if (isTopLevel) {
-      _flowAnalysis?.topLevelDeclaration_exit();
-    }
-    VariableElement element = node.declaredElement;
-    if (element.initializer != null && node.initializer != null) {
-      var initializer = element.initializer as FunctionElementImpl;
-      initializer.returnType = node.initializer.staticType;
-    }
-    // Note: in addition to cloning the initializers for const variables, we
-    // have to clone the initializers for non-static final fields (because if
-    // they occur in a class with a const constructor, they will be needed to
-    // evaluate the const constructor).
-    if (element is ConstVariableElement) {
-      (element as ConstVariableElement).constantInitializer =
-          _createCloner().cloneNode(node.initializer);
+    _variableDeclarationResolver.resolve(node);
+
+    if (node.parent.parent is ForParts) {
+      _define(node.declaredElement);
     }
   }
 
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 2ccef51..3f662eb 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -12,12 +12,10 @@
 import 'package:analyzer/src/dart/element/element.dart';
 import 'package:analyzer/src/dart/element/member.dart' show ConstructorMember;
 import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_demotion.dart';
 import 'package:analyzer/src/dart/element/type_provider.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
 import 'package:analyzer/src/dart/resolver/flow_analysis_visitor.dart';
 import 'package:analyzer/src/error/codes.dart';
-import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/migration.dart';
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/variable_type_provider.dart';
@@ -49,9 +47,6 @@
   /// The type representing the type 'dynamic'.
   DartType _dynamicType;
 
-  /// True if inference failures should be reported, otherwise false.
-  bool _strictInference;
-
   /// The object providing promoted or declared types of variables.
   LocalVariableTypeProvider _localVariableTypeProvider;
 
@@ -67,9 +62,6 @@
     _typeSystem = _resolver.typeSystem;
     _dynamicType = _typeProvider.dynamicType;
     _localVariableTypeProvider = _resolver.localVariableTypeProvider;
-    AnalysisOptionsImpl analysisOptions =
-        _resolver.definingLibrary.context.analysisOptions;
-    _strictInference = analysisOptions.strictInference;
   }
 
   /// Is `true` if the library being analyzed is non-nullable by default.
@@ -583,11 +575,6 @@
     recordStaticType(node, _typeProvider.bottomType);
   }
 
-  @override
-  void visitVariableDeclaration(VariableDeclaration node) {
-    _inferLocalVariableType(node, node.initializer);
-  }
-
   /// Set the static type of [node] to be the least upper bound of the static
   /// types of subexpressions [expr1] and [expr2].
   void _analyzeLeastUpperBound(
@@ -751,39 +738,6 @@
     }
   }
 
-  /// Given a local variable declaration and its initializer, attempt to infer
-  /// a type for the local variable declaration based on the initializer.
-  /// Inference is only done if an explicit type is not present, and if
-  /// inferring a type improves the type.
-  void _inferLocalVariableType(
-      VariableDeclaration node, Expression initializer) {
-    AstNode parent = node.parent;
-    if (initializer != null) {
-      if (parent is VariableDeclarationList && parent.type == null) {
-        DartType type = initializer.staticType;
-        if (type != null && !type.isDartCoreNull) {
-          VariableElement element = node.declaredElement;
-          if (element is LocalVariableElementImpl) {
-            var initializerType = initializer.staticType;
-            var inferredType = demoteType(
-              _resolver.definingLibrary,
-              initializerType,
-            );
-            element.type = inferredType;
-          }
-        }
-      }
-    } else if (_strictInference) {
-      if (parent is VariableDeclarationList && parent.type == null) {
-        _resolver.errorReporter.reportErrorForNode(
-          HintCode.INFERENCE_FAILURE_ON_UNINITIALIZED_VARIABLE,
-          node,
-          [node.name.name],
-        );
-      }
-    }
-  }
-
   /// Given a property access [node] with static type [nodeType],
   /// and [id] is the property name being accessed, infer a type for the
   /// access itself and its constituent components if the access is to one of the
diff --git a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
index 33b8193..a173cf5 100644
--- a/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/undefined_identifier_test.dart
@@ -57,6 +57,18 @@
     ]);
   }
 
+  test_forStatement_ForPartsWithDeclarations_initializer() async {
+    await assertErrorsInCode('''
+void f() {
+  for (var x = x;;) {
+    x;
+  }
+}
+''', [
+      error(CompileTimeErrorCode.UNDEFINED_IDENTIFIER, 26, 1),
+    ]);
+  }
+
   test_forStatement_inBody() async {
     await assertNoErrorsInCode('''
 f() {