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() {