blob: 9b4f698c523e9581041d8c977b44dc01c27b1a53 [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/analysis/features.dart';
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/dart/element/type_visitor.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_system.dart';
import 'package:analyzer/src/dart/element/type_visitor.dart';
import 'package:analyzer/src/summary2/link.dart';
import 'package:analyzer/src/summary2/type_builder.dart';
/// The type builder for a [NamedType].
class NamedTypeBuilder extends TypeBuilder {
/// TODO(scheglov) Replace with `DartType` in `TypeAliasElementImpl`.
static const _aliasedTypeKey = '_aliasedType';
static DynamicTypeImpl get _dynamicType => DynamicTypeImpl.instance;
/// The linker that contains this type.
final Linker linker;
/// The type system of the library with the type name.
final TypeSystemImpl typeSystem;
@override
final Element element;
final List<DartType> arguments;
@override
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 NamedTypeImpl? 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;
NamedTypeBuilder(this.linker, this.typeSystem, this.element, this.arguments,
this.nullabilitySuffix,
{this.node});
factory NamedTypeBuilder.of(
Linker linker,
TypeSystemImpl typeSystem,
NamedTypeImpl node,
Element element,
NullabilitySuffix nullabilitySuffix,
) {
List<DartType> arguments;
var argumentList = node.typeArguments;
if (argumentList != null) {
arguments = argumentList.arguments.map((n) => n.typeOrThrow).toList();
} else {
arguments = <DartType>[];
}
return NamedTypeBuilder(
linker, typeSystem, element, arguments, nullabilitySuffix,
node: node);
}
/// TODO(scheglov) Only when enabled both in the element, and target?
bool get _isNonFunctionTypeAliasesEnabled {
return element.library!.featureSet.isEnabled(
Feature.nonfunction_type_aliases,
);
}
@override
R accept<R>(TypeVisitor<R> visitor) {
if (visitor is LinkingTypeVisitor<R>) {
var visitor2 = visitor as LinkingTypeVisitor<R>;
return visitor2.visitNamedTypeBuilder(this);
} else {
throw StateError('Should not happen outside linking.');
}
}
@override
DartType build() {
if (_type != null) {
return _type!;
}
final element = this.element;
if (element is ClassElement) {
var parameters = element.typeParameters;
var arguments = _buildArguments(parameters);
var type = element.instantiate(
typeArguments: arguments,
nullabilitySuffix: nullabilitySuffix,
);
type = typeSystem.toLegacyTypeIfOptOut(type) as InterfaceType;
_type = type;
} else if (element is TypeAliasElementImpl) {
var aliasedType = _getAliasedType(element);
var parameters = element.typeParameters;
var arguments = _buildArguments(parameters);
element.aliasedType = aliasedType;
var type = element.instantiate(
typeArguments: arguments,
nullabilitySuffix: nullabilitySuffix,
);
type = typeSystem.toLegacyTypeIfOptOut(type);
_type = type;
} else if (element is NeverElementImpl) {
if (typeSystem.isNonNullableByDefault) {
_type = NeverTypeImpl.instance.withNullability(nullabilitySuffix);
} else {
_type = typeSystem.typeProvider.nullType;
}
} else if (element is TypeParameterElement) {
_type = TypeParameterTypeImpl(
element: element,
nullabilitySuffix: nullabilitySuffix,
);
} else {
_type = _dynamicType;
}
node?.type = _type;
return _type!;
}
@override
String toString() {
var buffer = StringBuffer();
buffer.write(element.displayName);
if (arguments.isNotEmpty) {
buffer.write('<');
buffer.write(arguments.join(', '));
buffer.write('>');
}
return buffer.toString();
}
@override
TypeImpl withNullability(NullabilitySuffix nullabilitySuffix) {
if (this.nullabilitySuffix == nullabilitySuffix) {
return this;
}
return NamedTypeBuilder(
linker, typeSystem, element, arguments, nullabilitySuffix,
node: node);
}
DartType _buildAliasedType(TypeAnnotation? node) {
if (_isNonFunctionTypeAliasesEnabled) {
if (node != null) {
return _buildType(node.typeOrThrow);
} else {
return _dynamicType;
}
} else {
if (node is GenericFunctionType) {
return _buildType(node.typeOrThrow);
} else {
return FunctionTypeImpl(
typeFormals: const <TypeParameterElement>[],
parameters: const <ParameterElement>[],
returnType: _dynamicType,
nullabilitySuffix: NullabilitySuffix.none,
);
}
}
}
/// Build arguments that correspond to the type [parameters].
List<DartType> _buildArguments(List<TypeParameterElement> parameters) {
if (parameters.isEmpty) {
return const <DartType>[];
} else if (arguments.isNotEmpty) {
if (arguments.length == parameters.length) {
return List.generate(arguments.length, (index) {
var type = arguments[index];
return _buildType(type);
});
} else {
return _listOfDynamic(parameters.length);
}
} else {
return List.generate(parameters.length, (index) {
var parameter = parameters[index] as TypeParameterElementImpl;
var defaultType = parameter.defaultType!;
return _buildType(defaultType);
});
}
}
DartType _buildFormalParameterType(FormalParameter node) {
if (node is DefaultFormalParameter) {
return _buildFormalParameterType(node.parameter);
} else if (node is FunctionTypedFormalParameter) {
return _buildFunctionType(
typeParameterList: node.typeParameters,
returnTypeNode: node.returnType,
parameterList: node.parameters,
hasQuestion: node.question != null,
);
} else if (node is SimpleFormalParameter) {
return _buildNodeType(node.type);
} else {
throw UnimplementedError('(${node.runtimeType}) $node');
}
}
FunctionType _buildFunctionType({
required TypeParameterList? typeParameterList,
required TypeAnnotation? returnTypeNode,
required FormalParameterList parameterList,
required bool hasQuestion,
}) {
var returnType = _buildNodeType(returnTypeNode);
var typeParameters = _typeParameters(typeParameterList);
var formalParameters = _formalParameters(parameterList);
return FunctionTypeImpl(
typeFormals: typeParameters,
parameters: formalParameters,
returnType: returnType,
nullabilitySuffix: _getNullabilitySuffix(hasQuestion),
);
}
DartType _buildNodeType(TypeAnnotation? node) {
if (node == null) {
return _dynamicType;
} else {
return _buildType(node.typeOrThrow);
}
}
List<ParameterElementImpl> _formalParameters(FormalParameterList node) {
return node.parameters.asImpl.map((parameter) {
return ParameterElementImpl.synthetic(
parameter.identifier?.name ?? '',
_buildFormalParameterType(parameter),
parameter.kind,
);
}).toList();
}
DartType _getAliasedType(TypeAliasElementImpl element) {
var typedefNode = linker.getLinkingNode(element);
// If the element is not being linked, the types have already been built.
if (typedefNode == null) {
return element.aliasedType;
}
// Break a possible recursion.
var existing = typedefNode.getProperty(_aliasedTypeKey) as DartType?;
if (existing != null) {
return existing;
} else {
_setAliasedType(typedefNode, _dynamicType);
}
if (typedefNode is FunctionTypeAlias) {
var result = _buildFunctionType(
typeParameterList: null,
returnTypeNode: typedefNode.returnType,
parameterList: typedefNode.parameters,
hasQuestion: false,
);
_setAliasedType(typedefNode, result);
return result;
} else if (typedefNode is GenericTypeAlias) {
var aliasedTypeNode = typedefNode.type;
var aliasedType = _buildAliasedType(aliasedTypeNode);
_setAliasedType(typedefNode, aliasedType);
return aliasedType;
} else {
throw StateError('(${element.runtimeType}) $element');
}
}
NullabilitySuffix _getNullabilitySuffix(bool hasQuestion) {
if (hasQuestion) {
return NullabilitySuffix.question;
} else if (typeSystem.isNonNullableByDefault) {
return NullabilitySuffix.none;
} else {
return NullabilitySuffix.star;
}
}
/// 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;
}
}
static List<DartType> _listOfDynamic(int length) {
return List<DartType>.filled(length, _dynamicType);
}
static void _setAliasedType(AstNode node, DartType type) {
node.setProperty(_aliasedTypeKey, type);
}
static List<TypeParameterElement> _typeParameters(TypeParameterList? node) {
if (node != null) {
return node.typeParameters
.map<TypeParameterElement>((p) => p.declaredElement!)
.toList();
} else {
return const <TypeParameterElement>[];
}
}
}