blob: bfe5f5324e11bf9c6166a76862369e5ce1b805d0 [file]
// 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:_fe_analyzer_shared/src/types/shared_type.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/extensions.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type_schema.dart';
import 'package:analyzer/src/dart/resolver/type_analyzer.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart' as diag;
import 'package:analyzer/src/error/listener.dart';
import 'package:analyzer/src/generated/error_detection_helpers.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
/// Helper for resolving [VariableDeclaration]s.
class VariableDeclarationResolver {
final TypeAnalyzer _typeAnalyzer;
final bool _strictInference;
VariableDeclarationResolver({
required TypeAnalyzer typeAnalyzer,
required bool strictInference,
}) : _typeAnalyzer = typeAnalyzer,
_strictInference = strictInference;
void resolve(VariableDeclarationImpl node) {
var parent = node.parent2 as VariableDeclarationList;
var initializer = node.initializer2;
if (initializer == null) {
if (_strictInference && parent.type == null) {
_typeAnalyzer.diagnosticReporter.report(
diag.inferenceFailureOnUninitializedVariable
.withArguments(variable: node.name.lexeme)
.at(node),
);
}
return;
}
var element = node.declaredFragment!.element;
bool isTopLevel;
bool bindsThis;
if (element is FieldElementImpl) {
isTopLevel = true;
bindsThis = !element.isStatic && element.isLate;
} else if (element is TopLevelVariableElement) {
isTopLevel = true;
bindsThis = false;
} else {
isTopLevel = false;
bindsThis = false;
}
List<FormalParameterElementImpl>? inScopePrimaryConstructorParameters;
if (element is FieldElementImpl &&
element.isInstanceField &&
!element.isLate) {
inScopePrimaryConstructorParameters = element.enclosingElement
.tryCast<InterfaceElementImpl>()
?.primaryConstructor
?.formalParameters;
}
var beforeInitializerOffset = node.equals!.offset;
if (isTopLevel) {
_typeAnalyzer.flowAnalysis.flowAnalysisRoot_enter(
node,
inScopePrimaryConstructorParameters,
offset: beforeInitializerOffset,
);
if (inScopePrimaryConstructorParameters != null) {
_typeAnalyzer.flowAnalysis.declarePrimaryConstructorParameters(
inScopePrimaryConstructorParameters,
offset: beforeInitializerOffset,
);
}
} else if (element.isLate) {
_typeAnalyzer.flowAnalysis.flow?.lateInitializer_begin(
node,
offset: beforeInitializerOffset,
);
}
if (bindsThis) {
_typeAnalyzer.flowAnalysis.flow?.thisBinding_begin(
null,
thisType: SharedTypeView(_typeAnalyzer.thisType!),
offset: initializer.offset,
);
}
var contextType =
element is PropertyInducingElementImpl &&
element.isTypeInferredFromInitializer
? UnknownInferredType.instance
: element.type;
_typeAnalyzer.withThisAccessibility(
bindsThis || _typeAnalyzer.isThisAccessible,
() => _typeAnalyzer.analyzeExpression(
initializer!,
SharedTypeSchemaView(contextType),
),
);
initializer = _typeAnalyzer.popRewrite()!;
var whyNotPromoted = _typeAnalyzer.flowAnalysis.flow?.whyNotPromoted(
_typeAnalyzer.flowAnalysis.getExpressionInfo(initializer),
);
var initializerType = initializer.typeOrThrow;
if (parent.type == null && element is LocalVariableElementImpl) {
element.type = _typeAnalyzer
.variableTypeFromInitializerType(SharedTypeView(initializerType))
.unwrapTypeView();
}
if (bindsThis) {
_typeAnalyzer.flowAnalysis.flow?.thisBinding_end(offset: initializer.end);
}
if (isTopLevel) {
_typeAnalyzer.flowAnalysis.flowAnalysisRoot_exit();
_typeAnalyzer.nullSafetyDeadCodeVerifier.flowEnd(node);
} else if (element.isLate) {
_typeAnalyzer.flowAnalysis.flow?.lateInitializer_end(offset: node.end);
}
// Initializers of top-level variables and fields are already included
// into elements during linking.
if (element is LocalVariableElementImpl && element.isConst) {
var fragment = element.firstFragment;
fragment.constantInitializer2 = initializer;
}
_typeAnalyzer.checkForAssignableExpressionAtType(
initializer,
initializerType,
element.type,
const NonAssignabilityReporterForAssignment(),
whyNotPromoted: whyNotPromoted,
);
}
}