blob: 993aa693a4ba07c735d6de5ae3da4ffc0a27ba62 [file] [log] [blame]
// Copyright (c) 2019, 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/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/dart/element/type.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.dart';
import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/summary/link.dart' as graph
show DependencyWalker, Node;
import 'package:analyzer/src/summary2/ast_resolver.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linking_node_scope.dart';
import 'package:analyzer/src/task/inference_error.dart';
import 'package:analyzer/src/task/strong_mode.dart';
import 'package:analyzer/src/util/collection.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:collection/collection.dart';
/// Resolver for typed constant top-level variables and fields initializers.
///
/// Initializers of untyped variables are resolved during [TopLevelInference].
class ConstantInitializersResolver {
final Linker linker;
late CompilationUnitElementImpl _unitElement;
late LibraryElement _library;
bool _enclosingClassHasConstConstructor = false;
late Scope _scope;
ConstantInitializersResolver(this.linker);
void perform() {
for (var builder in linker.builders.values) {
_library = builder.element;
for (var unit in _library.units) {
_unitElement = unit as CompilationUnitElementImpl;
unit.classes.forEach(_resolveClassFields);
unit.enums.forEach(_resolveClassFields);
unit.extensions.forEach(_resolveExtensionFields);
unit.mixins.forEach(_resolveClassFields);
_scope = builder.element.scope;
unit.topLevelVariables.forEach(_resolveVariable);
}
}
}
void _resolveClassFields(ClassElement class_) {
_enclosingClassHasConstConstructor =
class_.constructors.any((c) => c.isConst);
var node = linker.getLinkingNode(class_)!;
_scope = LinkingNodeContext.get(node).scope;
for (var element in class_.fields) {
_resolveVariable(element);
}
_enclosingClassHasConstConstructor = false;
}
void _resolveExtensionFields(ExtensionElement extension_) {
var node = linker.getLinkingNode(extension_)!;
_scope = LinkingNodeContext.get(node).scope;
for (var element in extension_.fields) {
_resolveVariable(element);
}
}
void _resolveVariable(PropertyInducingElement element) {
element as PropertyInducingElementImpl;
var variable = linker.getLinkingNode(element);
if (variable is! VariableDeclaration) return;
if (variable.initializer == null) return;
var declarationList = variable.parent as VariableDeclarationList;
var typeNode = declarationList.type;
DartType contextType;
if (element.hasTypeInferred) {
contextType = element.type;
} else if (typeNode != null) {
contextType = typeNode.typeOrThrow;
} else {
contextType = DynamicTypeImpl.instance;
}
if (declarationList.isConst ||
declarationList.isFinal && _enclosingClassHasConstConstructor) {
var astResolver = AstResolver(linker, _unitElement, _scope);
astResolver.resolveExpression(() => variable.initializer!,
contextType: contextType);
}
if (element is ConstVariableElement) {
var constElement = element as ConstVariableElement;
constElement.constantInitializer = variable.initializer;
}
}
}
class TopLevelInference {
final Linker linker;
TopLevelInference(this.linker);
void infer() {
var initializerInference = _InitializerInference(linker);
initializerInference.createNodes();
_performOverrideInference();
initializerInference.perform();
}
void _performOverrideInference() {
var inferrer = InstanceMemberInferrer(linker.inheritance);
for (var builder in linker.builders.values) {
for (var unit in builder.element.units) {
inferrer.inferCompilationUnit(unit);
}
}
}
}
/// Information about a base constructor of a mixin application.
class _BaseConstructor {
final InterfaceType superType;
final ConstructorElement element;
_BaseConstructor(this.superType, this.element);
}
class _ConstructorInferenceNode extends _InferenceNode {
final _InferenceWalker _walker;
final ConstructorElement _constructor;
final List<_FieldFormalParameter> _fieldParameters = [];
final List<_SuperFormalParameter> _superParameters = [];
/// If this node is a constructor of a mixin application, this field
/// is the corresponding constructor of the superclass.
_BaseConstructor? _baseConstructor;
@override
bool isEvaluated = false;
_ConstructorInferenceNode(this._walker, this._constructor) {
for (var parameter in _constructor.parameters) {
if (parameter is FieldFormalParameterElementImpl) {
if (parameter.hasImplicitType) {
var field = parameter.field;
if (field != null) {
_fieldParameters.add(
_FieldFormalParameter(parameter, field),
);
}
}
} else if (parameter is SuperFormalParameterElementImpl) {
if (parameter.hasImplicitType) {
var superParameter = parameter.superConstructorParameter;
if (superParameter != null) {
_superParameters.add(
_SuperFormalParameter(parameter, superParameter),
);
}
}
}
}
var classElement = _constructor.enclosingElement;
if (classElement.isMixinApplication) {
var superType = classElement.supertype;
if (superType != null) {
var index = classElement.constructors.indexOf(_constructor);
var superConstructors = superType.element.constructors
.where((element) => element.isAccessibleIn(classElement.library))
.toList();
if (index < superConstructors.length) {
_baseConstructor = _BaseConstructor(
superType,
superConstructors[index],
);
}
}
}
}
@override
String get displayName => '$_constructor';
@override
List<_InferenceNode> computeDependencies() {
var dependencies = [
..._fieldParameters.map((e) => _walker.getNode(e.field)).whereNotNull(),
..._superParameters
.map((e) => _walker.getNode(e.superParameter))
.whereNotNull(),
];
dependencies.addIfNotNull(
_walker.getNode(_baseConstructor?.element),
);
return dependencies;
}
@override
void evaluate() {
for (var fieldParameter in _fieldParameters) {
fieldParameter.parameter.type = fieldParameter.field.type;
}
for (var superParameter in _superParameters) {
superParameter.parameter.type = superParameter.superParameter.type;
}
// We have inferred formal parameter types of the base constructor.
// Update types of a mixin application constructor formal parameters.
var baseConstructor = _baseConstructor;
if (baseConstructor != null) {
var constructor = _constructor as ConstructorElementImpl;
var substitution = Substitution.fromInterfaceType(
baseConstructor.superType,
);
forCorrespondingPairs<ParameterElement, ParameterElement>(
constructor.parameters,
baseConstructor.element.parameters,
(parameter, baseParameter) {
var type = substitution.substituteType(baseParameter.type);
(parameter as ParameterElementImpl).type = type;
},
);
// Update arguments of `SuperConstructorInvocation` to have the types
// (which we have just set) of the corresponding formal parameters.
// MixinApp(x, y) : super(x, y);
var initializers = constructor.constantInitializers;
var initializer = initializers.single as SuperConstructorInvocation;
forCorrespondingPairs<ParameterElement, Expression>(
constructor.parameters,
initializer.argumentList.arguments,
(parameter, argument) {
(argument as SimpleIdentifierImpl).staticType = parameter.type;
},
);
}
isEvaluated = true;
}
@override
void markCircular(List<_InferenceNode> cycle) {
for (var fieldParameter in _fieldParameters) {
fieldParameter.parameter.type = DynamicTypeImpl.instance;
}
for (var superParameter in _superParameters) {
superParameter.parameter.type = DynamicTypeImpl.instance;
}
isEvaluated = true;
}
}
/// A field formal parameter with a non-nullable field.
class _FieldFormalParameter {
final FieldFormalParameterElementImpl parameter;
final FieldElement field;
_FieldFormalParameter(this.parameter, this.field);
}
class _InferenceDependenciesCollector extends RecursiveAstVisitor<void> {
final Set<Element> _set = Set.identity();
@override
void visitInstanceCreationExpression(InstanceCreationExpression node) {
var element = node.constructorName.staticElement?.declaration;
if (element == null) return;
_set.add(element);
if (element.enclosingElement.typeParameters.isNotEmpty) {
node.argumentList.accept(this);
}
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
var element = node.staticElement?.declaration;
if (element is PropertyAccessorElement && element.isGetter) {
_set.add(element.variable);
}
}
}
abstract class _InferenceNode extends graph.Node<_InferenceNode> {
String get displayName;
void evaluate();
void markCircular(List<_InferenceNode> cycle);
}
class _InferenceWalker extends graph.DependencyWalker<_InferenceNode> {
final Linker _linker;
final Map<Element, _InferenceNode> _nodes = Map.identity();
_InferenceWalker(this._linker);
@override
void evaluate(_InferenceNode v) {
v.evaluate();
}
@override
void evaluateScc(List<_InferenceNode> scc) {
for (var node in scc) {
node.markCircular(scc);
}
}
_InferenceNode? getNode(Element? element) {
return _nodes[element];
}
void walkNodes() {
for (var node in _nodes.values) {
if (!node.isEvaluated) {
walk(node);
}
}
}
}
class _InitializerInference {
final Linker _linker;
final _InferenceWalker _walker;
late CompilationUnitElementImpl _unitElement;
late Scope _scope;
_InitializerInference(this._linker) : _walker = _InferenceWalker(_linker);
void createNodes() {
for (var builder in _linker.builders.values) {
for (var unit in builder.element.units) {
_unitElement = unit as CompilationUnitElementImpl;
unit.classes.forEach(_addClassConstructorFieldFormals);
unit.classes.forEach(_addClassElementFields);
unit.enums.forEach(_addClassConstructorFieldFormals);
unit.enums.forEach(_addClassElementFields);
unit.extensions.forEach(_addExtensionElementFields);
unit.mixins.forEach(_addClassElementFields);
_scope = builder.element.scope;
for (var element in unit.topLevelVariables) {
_addVariableNode(element);
}
}
}
}
void perform() {
_walker.walkNodes();
}
void _addClassConstructorFieldFormals(ClassElement class_) {
for (var constructor in class_.constructors) {
var inferenceNode = _ConstructorInferenceNode(_walker, constructor);
_walker._nodes[constructor] = inferenceNode;
}
}
void _addClassElementFields(ClassElement class_) {
var node = _linker.getLinkingNode(class_)!;
_scope = LinkingNodeContext.get(node).scope;
for (var element in class_.fields) {
_addVariableNode(element);
}
}
void _addExtensionElementFields(ExtensionElement extension_) {
var node = _linker.getLinkingNode(extension_)!;
_scope = LinkingNodeContext.get(node).scope;
for (var element in extension_.fields) {
_addVariableNode(element);
}
}
void _addVariableNode(PropertyInducingElement element) {
element as PropertyInducingElementImpl;
if (element.isSynthetic) return;
var node = _linker.getLinkingNode(element) as VariableDeclaration;
var variableList = node.parent as VariableDeclarationList;
if (variableList.type != null) {
return;
}
if (node.initializer != null) {
var inferenceNode =
_VariableInferenceNode(_walker, _unitElement, _scope, element, node);
_walker._nodes[element] = inferenceNode;
element.typeInference =
_PropertyInducingElementTypeInference(inferenceNode);
} else {
element.type = DynamicTypeImpl.instance;
}
}
}
class _PropertyInducingElementTypeInference
implements PropertyInducingElementTypeInference {
final _VariableInferenceNode _node;
_PropertyInducingElementTypeInference(this._node);
@override
void perform() {
if (!_node.isEvaluated) {
_node._walker.walk(_node);
}
}
}
/// A super formal parameter with a non-nullable super-constructor parameter.
class _SuperFormalParameter {
final SuperFormalParameterElementImpl parameter;
final ParameterElement superParameter;
_SuperFormalParameter(this.parameter, this.superParameter);
}
class _VariableInferenceNode extends _InferenceNode {
final _InferenceWalker _walker;
final CompilationUnitElementImpl _unitElement;
final TypeSystemImpl _typeSystem;
final Scope _scope;
final PropertyInducingElementImpl _element;
final VariableDeclaration _node;
@override
bool isEvaluated = false;
_VariableInferenceNode(
this._walker,
this._unitElement,
this._scope,
this._element,
this._node,
) : _typeSystem = _unitElement.library.typeSystem;
@override
String get displayName {
return _node.name.name;
}
@override
List<_InferenceNode> computeDependencies() {
if (_element.hasTypeInferred) {
return const <_InferenceNode>[];
}
_resolveInitializer(forDependencies: true);
var collector = _InferenceDependenciesCollector();
_node.initializer!.accept(collector);
if (collector._set.isEmpty) {
return const <_InferenceNode>[];
}
return collector._set.map(_walker.getNode).whereNotNull().toList();
}
@override
void evaluate() {
if (_element.hasTypeInferred) {
return;
}
_resolveInitializer(forDependencies: false);
var initializerType = _node.initializer!.typeOrThrow;
initializerType = _refineType(initializerType);
_element.type = initializerType;
_element.hasTypeInferred = true;
isEvaluated = true;
}
@override
void markCircular(List<_InferenceNode> cycle) {
_element.type = DynamicTypeImpl.instance;
_element.hasTypeInferred = true;
var cycleNames = <String>{};
for (var inferenceNode in cycle) {
cycleNames.add(inferenceNode.displayName);
}
_element.typeInferenceError = TopLevelInferenceError(
kind: TopLevelInferenceErrorKind.dependencyCycle,
arguments: cycleNames.toList(),
);
isEvaluated = true;
}
DartType _refineType(DartType type) {
if (type.isDartCoreNull) {
return DynamicTypeImpl.instance;
}
if (_typeSystem.isNonNullableByDefault) {
return _typeSystem.nonNullifyLegacy(type);
} else {
if (type.isBottom) {
return DynamicTypeImpl.instance;
}
return type;
}
}
void _resolveInitializer({required bool forDependencies}) {
var enclosingElement = _element.enclosingElement;
var enclosingClassElement =
enclosingElement is ClassElement ? enclosingElement : null;
var astResolver = AstResolver(_walker._linker, _unitElement, _scope,
enclosingClassElement: enclosingClassElement);
astResolver.resolveExpression(() => _node.initializer!,
buildElements: forDependencies);
}
}