blob: 2a00807563e258ceec04f77ebf47b34b11cee7fc [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/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/summary2/lazy_ast.dart';
import 'package:analyzer/src/summary2/type_builder.dart';
/// The type builder for a [GenericFunctionType].
class FunctionTypeBuilder extends TypeBuilder {
static DynamicTypeImpl get _dynamicType => DynamicTypeImpl.instance;
final List<TypeParameterElement> typeFormals;
final List<ParameterElement> parameters;
final DartType returnType;
final NullabilitySuffix nullabilitySuffix;
/// The node for which this builder is created, or `null` if the builder
/// was detached from its node, e.g. during computing default types for
/// type parameters.
final GenericFunctionTypeImpl node;
/// The actual built type, not a [TypeBuilder] anymore.
///
/// When [build] is called, the type is built, stored into this field,
/// and set for the [node].
DartType _type;
FunctionTypeBuilder(
this.typeFormals,
this.parameters,
this.returnType,
this.nullabilitySuffix, {
this.node,
});
/// [isNNBD] indicates whether the containing library is opted into NNBD.
factory FunctionTypeBuilder.of(
bool isNNBD,
GenericFunctionType node,
NullabilitySuffix nullabilitySuffix,
) {
return FunctionTypeBuilder(
_getTypeParameters(node.typeParameters),
_getParameters(isNNBD, node.parameters),
_getNodeType(node.returnType),
nullabilitySuffix,
node: node,
);
}
@override
Element get element => null;
@override
DartType build() {
if (_type != null) {
return _type;
}
for (TypeParameterElementImpl typeParameter in typeFormals) {
typeParameter.bound = _buildType(typeParameter.bound);
}
for (ParameterElementImpl parameter in parameters) {
parameter.type = _buildType(parameter.type);
}
var builtReturnType = _buildType(returnType);
_type = FunctionTypeImpl.synthetic(
builtReturnType,
typeFormals,
parameters,
nullabilitySuffix: nullabilitySuffix,
);
if (node != null) {
node.type = _type;
LazyAst.setReturnType(node, builtReturnType ?? _dynamicType);
}
return _type;
}
@override
String toString() {
var buffer = StringBuffer();
if (typeFormals.isNotEmpty) {
buffer.write('<');
buffer.write(typeFormals.join(', '));
buffer.write('>');
}
buffer.write('(');
buffer.write(parameters.join(', '));
buffer.write(')');
buffer.write(' → ');
buffer.write(returnType);
return buffer.toString();
}
/// If the [type] is a [TypeBuilder], build it; otherwise return as is.
static DartType _buildType(DartType type) {
if (type is TypeBuilder) {
return type.build();
} else {
return type;
}
}
/// Return the type of the [node] as is, possibly a [TypeBuilder].
static DartType _getNodeType(TypeAnnotation node) {
if (node == null) {
return _dynamicType;
} else {
return node.type;
}
}
/// [isNNBD] indicates whether the containing library is opted into NNBD.
static List<ParameterElementImpl> _getParameters(
bool isNNBD,
FormalParameterList node,
) {
return node.parameters.map((parameter) {
return ParameterElementImpl.synthetic(
parameter.identifier?.name ?? '',
_getParameterType(isNNBD, parameter),
// ignore: deprecated_member_use_from_same_package
parameter.kind,
);
}).toList();
}
/// Return the type of the [node] as is, possibly a [TypeBuilder].
///
/// [isNNBD] indicates whether the containing library is opted into NNBD.
static DartType _getParameterType(bool isNNBD, FormalParameter node) {
if (node is DefaultFormalParameter) {
return _getParameterType(isNNBD, node.parameter);
} else if (node is SimpleFormalParameter) {
return _getNodeType(node.type);
} else if (node is FunctionTypedFormalParameter) {
NullabilitySuffix nullabilitySuffix;
if (node.question != null) {
nullabilitySuffix = NullabilitySuffix.question;
} else if (isNNBD) {
nullabilitySuffix = NullabilitySuffix.none;
} else {
nullabilitySuffix = NullabilitySuffix.question;
}
return FunctionTypeBuilder(
_getTypeParameters(node.typeParameters),
_getParameters(isNNBD, node.parameters),
_getNodeType(node.returnType),
nullabilitySuffix,
);
} else {
throw UnimplementedError('(${node.runtimeType}) $node');
}
}
static List<TypeParameterElement> _getTypeParameters(TypeParameterList node) {
if (node == null) return const [];
return node.typeParameters.map((n) => n.declaredElement).toList();
}
}