blob: c5b38e4aff370dec2076f3e5fd28fbb0d2fa2060 [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/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/resolver/scope.dart';
import 'package:analyzer/src/summary2/ast_resolver.dart';
import 'package:analyzer/src/summary2/lazy_ast.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/linked_unit_context.dart';
import 'package:analyzer/src/summary2/reference.dart';
DartType _dynamicIfNull(DartType type) {
if (type == null || type.isBottom || type.isDartCoreNull) {
return DynamicTypeImpl.instance;
}
return type;
}
/// TODO(scheglov) This is not a valid implementation of top-level inference.
/// See https://bit.ly/2HYfAKg
///
/// In general inference of constructor field formal parameters should be
/// interleaved with inference of fields. There are resynthesis tests that
/// fail because of this limitation.
class TopLevelInference {
final Linker linker;
LibraryElementImpl _libraryElement;
Scope _libraryScope;
Scope _nameScope;
LinkedUnitContext _linkedContext;
CompilationUnitElementImpl unitElement;
TopLevelInference(this.linker, Reference libraryRef) {
_libraryElement = linker.elementFactory.elementOfReference(libraryRef);
_libraryScope = LibraryScope(_libraryElement);
_nameScope = _libraryScope;
}
DynamicTypeImpl get _dynamicType {
return DynamicTypeImpl.instance;
}
VoidTypeImpl get _voidType => VoidTypeImpl.instance;
void infer() {
_setOmittedReturnTypes();
_inferFieldsTemporary();
_inferConstructorFieldFormals();
}
void _inferConstructorFieldFormals() {
for (CompilationUnitElementImpl unit in _libraryElement.units) {
this.unitElement = unit;
_linkedContext = unit.linkedContext;
for (var class_ in unit.types) {
var fields = <String, DartType>{};
for (FieldElementImpl field in class_.fields) {
if (field.isSynthetic) continue;
var name = field.name;
var type = field.type;
if (type == null) {
throw StateError('Field $name should have a type.');
}
fields[name] ??= type;
}
for (ConstructorElementImpl constructor in class_.constructors) {
for (ParameterElementImpl parameter in constructor.parameters) {
if (parameter is FieldFormalParameterElement) {
FormalParameter node = parameter.linkedNode;
if (node is DefaultFormalParameter) {
var defaultParameter = node as DefaultFormalParameter;
node = defaultParameter.parameter;
}
if (node is FieldFormalParameter &&
node.type == null &&
node.parameters == null) {
var name = parameter.name;
var type = fields[name] ?? _dynamicType;
LazyAst.setType(node, type);
}
}
}
}
}
}
}
void _inferFieldsTemporary() {
for (CompilationUnitElementImpl unit in _libraryElement.units) {
this.unitElement = unit;
_linkedContext = unit.linkedContext;
for (var class_ in unit.types) {
_inferFieldsTemporaryClass(class_);
}
for (var mixin_ in unit.mixins) {
_inferFieldsTemporaryClass(mixin_);
}
for (TopLevelVariableElementImpl variable in unit.topLevelVariables) {
if (variable.isSynthetic) continue;
VariableDeclaration node = variable.linkedNode;
VariableDeclarationList parent = node.parent;
if (parent.type == null || _linkedContext.isConst(node)) {
_inferVariableTypeFromInitializerTemporary(node);
}
}
}
}
void _inferFieldsTemporaryClass(ClassElement class_) {
var prevScope = _nameScope;
_nameScope = TypeParameterScope(_nameScope, class_);
_nameScope = ClassScope(_nameScope, class_);
for (FieldElementImpl field in class_.fields) {
if (field.isSynthetic) continue;
VariableDeclaration node = field.linkedNode;
VariableDeclarationList parent = node.parent;
// TODO(scheglov) Use inheritance
// TODO(scheglov) infer in the correct order
if (parent.type == null || parent.isConst) {
_inferVariableTypeFromInitializerTemporary(node);
}
}
_nameScope = prevScope;
}
void _inferVariableTypeFromInitializerTemporary(VariableDeclaration node) {
var initializer = node.initializer;
if (initializer == null) {
LazyAst.setType(node, _dynamicType);
return;
}
var astResolver = AstResolver(linker, _libraryElement, _nameScope);
astResolver.resolve(initializer);
VariableDeclarationList parent = node.parent;
if (parent.type == null) {
var initializerType = initializer.staticType;
initializerType = _dynamicIfNull(initializerType);
LazyAst.setType(node, initializerType);
}
}
void _setOmittedReturnTypes() {
for (CompilationUnitElementImpl unit in _libraryElement.units) {
this.unitElement = unit;
_linkedContext = unit.linkedContext;
for (var class_ in unit.types) {
_setOmittedReturnTypesClass(class_);
}
for (var mixin_ in unit.mixins) {
_setOmittedReturnTypesClass(mixin_);
}
for (FunctionElementImpl function in unit.functions) {
FunctionDeclaration node = function.linkedNode;
if (node.returnType == null) {
LazyAst.setReturnType(node, _dynamicType);
}
}
for (PropertyAccessorElementImpl accessor in unit.accessors) {
if (accessor.isSynthetic) continue;
if (accessor.isSetter) {
LazyAst.setReturnType(accessor.linkedNode, _voidType);
}
}
}
}
void _setOmittedReturnTypesClass(ClassElement class_) {
for (MethodElementImpl method in class_.methods) {
MethodDeclaration node = method.linkedNode;
if (node.returnType == null) {
LazyAst.setReturnType(node, _dynamicType);
}
}
for (PropertyAccessorElementImpl accessor in class_.accessors) {
if (accessor.isSynthetic) continue;
MethodDeclaration node = accessor.linkedNode;
if (node.returnType != null) continue;
if (accessor.isSetter) {
LazyAst.setReturnType(node, _voidType);
} else {
LazyAst.setReturnType(node, _dynamicType);
}
}
}
}