blob: def0f8de23b7f05e82bf3a419def388a12861b97 [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/standard_ast_factory.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
import 'package:analyzer/src/summary2/ast_resolver.dart';
import 'package:analyzer/src/summary2/builder/source_library_builder.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/reference.dart';
DartType _dynamicIfNull(DartType type) {
if (type == null || type.isBottom || type.isDartCoreNull) {
return DynamicTypeImpl.instance;
}
return type;
}
class TopLevelInference {
final Linker linker;
final Reference libraryRef;
final UnitBuilder unit;
TopLevelInference(this.linker, this.libraryRef, this.unit);
void infer() {
_inferFieldsTemporary();
_inferConstructorFieldFormals();
}
void _inferConstructorFieldFormals() {
_visitClassList((unitDeclaration) {
var members = unitDeclaration.classOrMixinDeclaration_members;
var fields = <String, LinkedNodeType>{};
_visitClassFields(unitDeclaration, (field) {
var name = unit.context.getVariableName(field);
var type = field.variableDeclaration_type2;
if (type == null) {
throw StateError('Field $name should have a type.');
}
fields[name] = type;
});
for (var member in members) {
if (member.kind == LinkedNodeKind.constructorDeclaration) {
for (var parameter in member.constructorDeclaration_parameters
.formalParameterList_parameters) {
if (parameter.kind == LinkedNodeKind.fieldFormalParameter &&
parameter.fieldFormalParameter_type2 == null) {
var name = unit.context.getSimpleName(
parameter.normalFormalParameter_identifier,
);
var type = fields[name];
if (type == null) {
type = LinkedNodeTypeBuilder(
kind: LinkedNodeTypeKind.dynamic_,
);
}
parameter.fieldFormalParameter_type2 = type;
}
}
}
}
});
}
void _inferFieldsTemporary() {
var unitDeclarations = unit.node.compilationUnit_declarations;
for (LinkedNodeBuilder unitDeclaration in unitDeclarations) {
if (unitDeclaration.kind == LinkedNodeKind.classDeclaration) {
_visitClassFields(unitDeclaration, (field) {
var name = unit.context.getVariableName(field);
// TODO(scheglov) Use inheritance
if (field.variableDeclaration_type2 == null) {
field.variableDeclaration_type2 = LinkedNodeTypeBuilder(
kind: LinkedNodeTypeKind.dynamic_,
);
}
});
var members = unitDeclaration.classOrMixinDeclaration_members;
for (var member in members) {
if (member.kind == LinkedNodeKind.methodDeclaration) {
// TODO(scheglov) Use inheritance
if (member.methodDeclaration_returnType2 == null) {
if (unit.context.isSetter(member)) {
member.methodDeclaration_returnType2 = LinkedNodeTypeBuilder(
kind: LinkedNodeTypeKind.void_,
);
} else {
member.methodDeclaration_returnType2 = LinkedNodeTypeBuilder(
kind: LinkedNodeTypeKind.dynamic_,
);
}
}
}
}
} else if (unitDeclaration.kind == LinkedNodeKind.functionDeclaration) {
if (unit.context.isSetter(unitDeclaration)) {
unitDeclaration.functionDeclaration_returnType2 =
LinkedNodeTypeBuilder(
kind: LinkedNodeTypeKind.void_,
);
}
} else if (unitDeclaration.kind ==
LinkedNodeKind.topLevelVariableDeclaration) {
var variableList =
unitDeclaration.topLevelVariableDeclaration_variableList;
for (var variable in variableList.variableDeclarationList_variables) {
// TODO(scheglov) infer in the correct order
if (variable.variableDeclaration_type2 == null ||
unit.context.isConst(variable)) {
_inferVariableTypeFromInitializerTemporary(variable);
}
}
}
}
}
void _inferVariableTypeFromInitializerTemporary(LinkedNodeBuilder node) {
var unresolvedNode = node.variableDeclaration_initializer;
if (unresolvedNode == null) {
node.variableDeclaration_type2 = LinkedNodeTypeBuilder(
kind: LinkedNodeTypeKind.dynamic_,
);
return;
}
var expression = unit.context.readInitializer(node);
astFactory.expressionFunctionBody(null, null, expression, null);
// TODO(scheglov) can be shared for the whole library
var astResolver = AstResolver(linker, libraryRef);
var resolvedNode = astResolver.resolve(unit, expression);
node.variableDeclaration_initializer = resolvedNode;
if (node.variableDeclaration_type2 == null) {
var initializerType = expression.staticType;
initializerType = _dynamicIfNull(initializerType);
var linkingBundleContext = linker.linkingBundleContext;
node.variableDeclaration_type2 = linkingBundleContext.writeType(
initializerType,
);
}
}
void _visitClassFields(
LinkedNode class_, void Function(LinkedNodeBuilder) f) {
var members = class_.classOrMixinDeclaration_members;
for (var member in members) {
if (member.kind == LinkedNodeKind.fieldDeclaration) {
var variableList = member.fieldDeclaration_fields;
for (var field in variableList.variableDeclarationList_variables) {
f(field);
}
}
}
}
void _visitClassList(void Function(LinkedNodeBuilder) f) {
var unitDeclarations = unit.node.compilationUnit_declarations;
for (LinkedNodeBuilder unitDeclaration in unitDeclarations) {
if (unitDeclaration.kind == LinkedNodeKind.classDeclaration) {
f(unitDeclaration);
}
}
}
}