blob: 3ca0a2ff4f6119b064570b6d4ab07f4b7f4a951c [file] [log] [blame]
// Copyright (c) 2022, 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:_fe_analyzer_shared/src/macros/executor/introspection_impls.dart'
as macro;
import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
as macro;
import 'package:analyzer/dart/ast/ast.dart' as ast;
import 'package:analyzer/dart/ast/token.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' as ast;
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/utilities/extensions/collection.dart';
import 'package:analyzer/src/utilities/extensions/element.dart';
import 'package:collection/collection.dart';
class ClassDeclarationImpl extends macro.ClassDeclarationImpl {
late final ClassElement element;
ClassDeclarationImpl._({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.typeParameters,
required super.interfaces,
required super.hasAbstract,
required super.hasBase,
required super.hasExternal,
required super.hasFinal,
required super.hasInterface,
required super.hasMixin,
required super.hasSealed,
required super.mixins,
required super.superclass,
});
}
class DeclarationBuilder {
final ast.AstNode? Function(Element?) nodeOfElement;
final Map<Element, IdentifierImpl> _identifierMap = Map.identity();
late final DeclarationBuilderFromNode fromNode =
DeclarationBuilderFromNode(this);
late final DeclarationBuilderFromElement fromElement =
DeclarationBuilderFromElement(this);
DeclarationBuilder({
required this.nodeOfElement,
});
List<macro.MetadataAnnotationImpl> _buildMetadata(Element element) {
return element.withAugmentations
.expand((current) => current.metadata)
.map(_buildMetadataElement)
.whereNotNull()
.toList();
}
macro.MetadataAnnotationImpl? _buildMetadataElement(
ElementAnnotation annotation,
) {
annotation as ElementAnnotationImpl;
final node = annotation.annotationAst;
final importPrefixNames = annotation.library.libraryImports
.map((e) => e.prefix?.element.name)
.whereNotNull()
.toSet();
final identifiers = <ast.SimpleIdentifier>[];
switch (node.name) {
case ast.PrefixedIdentifier node:
identifiers.add(node.prefix);
identifiers.add(node.identifier);
case ast.SimpleIdentifier node:
identifiers.add(node);
default:
return null;
}
identifiers.addIfNotNull(node.constructorName);
var nextIndex = 0;
if (importPrefixNames.contains(identifiers.first.name)) {
nextIndex++;
}
final identifierName = identifiers[nextIndex++];
final constructorName = identifiers.elementAtOrNull(nextIndex);
final identifierMacro = IdentifierImplFromNode(
id: macro.RemoteInstance.uniqueId,
name: identifierName.name,
getElement: () => identifierName.staticElement,
);
final arguments = node.arguments;
if (arguments != null) {
return macro.ConstructorMetadataAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
constructor: IdentifierImplFromNode(
id: macro.RemoteInstance.uniqueId,
name: constructorName?.name ?? '',
getElement: () => node.element,
),
type: identifierMacro,
);
} else {
return macro.IdentifierMetadataAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: identifierMacro,
);
}
}
}
class DeclarationBuilderFromElement {
final DeclarationBuilder declarationBuilder;
final Map<Element, LibraryImpl> _libraryMap = Map.identity();
final Map<ClassElement, IntrospectableClassDeclarationImpl> _classMap =
Map.identity();
final Map<MixinElement, IntrospectableMixinDeclarationImpl> _mixinMap =
Map.identity();
final Map<FieldElement, FieldDeclarationImpl> _fieldMap = Map.identity();
final Map<ExecutableElement, MethodDeclarationImpl> _methodMap =
Map.identity();
final Map<TypeParameterElement, macro.TypeParameterDeclarationImpl>
_typeParameterMap = Map.identity();
DeclarationBuilderFromElement(this.declarationBuilder);
macro.IntrospectableClassDeclarationImpl classElement(ClassElement element) {
return _classMap[element] ??= _introspectableClassElement(element);
}
macro.FieldDeclarationImpl fieldElement(FieldElement element) {
return _fieldMap[element] ??= _fieldElement(element);
}
macro.IdentifierImpl identifier(Element element) {
final map = declarationBuilder._identifierMap;
return map[element] ??= IdentifierImplFromElement(
id: macro.RemoteInstance.uniqueId,
name: element.name!,
element: element,
);
}
macro.LibraryImpl library(Element element) {
var library = _libraryMap[element.library];
if (library == null) {
final version = element.library!.languageVersion.effective;
library = LibraryImplFromElement(
id: macro.RemoteInstance.uniqueId,
languageVersion:
macro.LanguageVersionImpl(version.major, version.minor),
metadata: _buildMetadata(element),
uri: element.library!.source.uri,
element: element);
_libraryMap[element.library!] = library;
}
return library;
}
macro.MethodDeclarationImpl methodElement(MethodElement element) {
return _methodMap[element] ??= _methodElement(element);
}
macro.IntrospectableMixinDeclarationImpl mixinElement(MixinElement element) {
return _mixinMap[element] ??= _introspectableMixinElement(element);
}
macro.TypeParameterDeclarationImpl typeParameter(
TypeParameterElement element,
) {
return _typeParameterMap[element] ??= _typeParameter(element);
}
List<macro.MetadataAnnotationImpl> _buildMetadata(Element element) {
return declarationBuilder._buildMetadata(element);
}
macro.TypeAnnotationImpl _dartType(DartType type) {
switch (type) {
case InterfaceType():
return _interfaceType(type);
case TypeParameterType():
return macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
identifier: identifier(type.element),
typeArguments: const [],
);
case VoidType():
return macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: macro.IdentifierImpl(
id: macro.RemoteInstance.uniqueId,
name: 'void',
),
isNullable: false,
typeArguments: const [],
);
default:
throw UnimplementedError('(${type.runtimeType}) $type');
}
}
FieldDeclarationImpl _fieldElement(FieldElement element) {
final enclosingElement = element.enclosingElement;
return FieldDeclarationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
library: library(element),
metadata: _buildMetadata(element),
hasExternal: element.isExternal,
hasFinal: element.isFinal,
hasLate: element.isLate,
type: _dartType(element.type),
definingType: identifier(enclosingElement),
isStatic: element.isStatic,
);
}
macro.NamedTypeAnnotationImpl _interfaceType(InterfaceType type) {
return macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
identifier: identifier(type.element),
typeArguments: type.typeArguments.map(_dartType).toList(),
);
}
IntrospectableClassDeclarationImpl _introspectableClassElement(
ClassElement element) {
return IntrospectableClassDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: element.typeParameters.map(_typeParameter).toList(),
interfaces: element.interfaces.map(_interfaceType).toList(),
hasAbstract: element.isAbstract,
hasBase: element.isBase,
hasExternal: false,
hasFinal: element.isFinal,
hasInterface: element.isInterface,
hasMixin: element.isMixinClass,
hasSealed: element.isSealed,
mixins: element.mixins.map(_interfaceType).toList(),
superclass: element.supertype.mapOrNull(_interfaceType),
element: element,
);
}
IntrospectableMixinDeclarationImpl _introspectableMixinElement(
MixinElement element) {
return IntrospectableMixinDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: element.typeParameters.map(_typeParameter).toList(),
hasBase: element.isBase,
interfaces: element.interfaces.map(_interfaceType).toList(),
superclassConstraints:
element.superclassConstraints.map(_interfaceType).toList(),
element: element,
);
}
MethodDeclarationImpl _methodElement(MethodElement element) {
final enclosingClass = element.enclosingElement as ClassElement;
return MethodDeclarationImpl._(
element: element,
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
library: library(element),
metadata: _buildMetadata(element),
hasAbstract: false,
// hasBody: node.body is! ast.EmptyFunctionBody,
hasBody: true,
// hasExternal: node.externalKeyword != null,
hasExternal: false,
// isGetter: node.isGetter,
isGetter: false,
// isOperator: node.isOperator,
isOperator: false,
// isSetter: node.isSetter,
isSetter: false,
// isStatic: node.isStatic,
isStatic: element.isStatic,
namedParameters: [], // TODO(scheglov) implement
positionalParameters: [], // TODO(scheglov) implement
returnType: _dartType(element.returnType),
typeParameters: element.typeParameters.map(_typeParameter).toList(),
definingType: identifier(enclosingClass),
);
}
macro.TypeParameterDeclarationImpl _typeParameter(
TypeParameterElement element,
) {
return macro.TypeParameterDeclarationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: identifier(element),
library: library(element),
metadata: _buildMetadata(element),
bound: element.bound.mapOrNull(_dartType),
);
}
}
class DeclarationBuilderFromNode {
final DeclarationBuilder declarationBuilder;
final Map<ast.NamedType, IdentifierImpl> _namedTypeMap = Map.identity();
final Map<Element, LibraryImpl> _libraryMap = Map.identity();
DeclarationBuilderFromNode(this.declarationBuilder);
macro.ClassDeclarationImpl classDeclaration(
ast.ClassDeclaration node,
) {
return _introspectableClassDeclaration(node);
}
macro.LibraryImpl library(Element element) {
final library = element.library!;
if (_libraryMap[library] case final result?) {
return result;
}
final version = library.languageVersion.effective;
final uri = library.source.uri;
return _libraryMap[library] = LibraryImplFromElement(
id: macro.RemoteInstance.uniqueId,
languageVersion: macro.LanguageVersionImpl(
version.major,
version.minor,
),
metadata: _buildMetadata(element),
uri: uri,
element: library,
);
}
macro.MethodDeclarationImpl methodDeclaration(
ast.MethodDeclaration node,
) {
return _methodDeclaration(node);
}
macro.MixinDeclarationImpl mixinDeclaration(
ast.MixinDeclaration node,
) {
return _introspectableMixinDeclaration(node);
}
List<macro.MetadataAnnotationImpl> _buildMetadata(Element element) {
return declarationBuilder._buildMetadata(element);
}
macro.IdentifierImpl _declaredIdentifier(Token name, Element element) {
final map = declarationBuilder._identifierMap;
return map[element] ??= _DeclaredIdentifierImpl(
id: macro.RemoteInstance.uniqueId,
name: name.lexeme,
element: element,
);
}
macro.IdentifierImpl _definingType(ast.AstNode node) {
final parentNode = node.parent;
switch (parentNode) {
case ast.ClassDeclaration():
final parentElement = parentNode.declaredElement!;
final typeElement = parentElement.augmentationTarget ?? parentElement;
return _declaredIdentifier(parentNode.name, typeElement);
case ast.MixinDeclaration():
final parentElement = parentNode.declaredElement!;
final typeElement = parentElement.augmentationTarget ?? parentElement;
return _declaredIdentifier(parentNode.name, typeElement);
default:
// TODO(scheglov) other parents
throw UnimplementedError('(${parentNode.runtimeType}) $parentNode');
}
}
macro.ParameterDeclarationImpl _formalParameter(ast.FormalParameter node) {
if (node is ast.DefaultFormalParameter) {
node = node.parameter;
}
final macro.TypeAnnotationImpl typeAnnotation;
if (node is ast.SimpleFormalParameter) {
typeAnnotation = _typeAnnotation(node.type);
} else {
throw UnimplementedError('(${node.runtimeType}) $node');
}
final element = node.declaredElement!;
return macro.ParameterDeclarationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name!, element),
isNamed: node.isNamed,
isRequired: node.isRequired,
library: library(element),
metadata: _buildMetadata(element),
type: typeAnnotation,
);
}
macro.FunctionTypeParameterImpl _functionTypeFormalParameter(
ast.FormalParameter node,
) {
if (node is ast.DefaultFormalParameter) {
node = node.parameter;
}
final element = node.declaredElement!;
final macro.TypeAnnotationImpl typeAnnotation;
if (node is ast.SimpleFormalParameter) {
typeAnnotation = _typeAnnotation(node.type);
} else {
throw UnimplementedError('(${node.runtimeType}) $node');
}
return macro.FunctionTypeParameterImpl(
id: macro.RemoteInstance.uniqueId,
isNamed: node.isNamed,
isRequired: node.isRequired,
metadata: _buildMetadata(element),
name: node.name?.lexeme,
type: typeAnnotation,
);
}
IntrospectableClassDeclarationImpl _introspectableClassDeclaration(
ast.ClassDeclaration node,
) {
final element = node.declaredElement as ClassElementImpl;
final interfaceNodes = <ast.NamedType>[];
final mixinNodes = <ast.NamedType>[];
for (var current = node;;) {
if (current.implementsClause case final clause?) {
interfaceNodes.addAll(clause.interfaces);
}
if (current.withClause case final clause?) {
mixinNodes.addAll(clause.mixinTypes);
}
final nextElement = current.declaredElement?.augmentation;
final nextNode = declarationBuilder.nodeOfElement(nextElement);
if (nextNode is! ast.ClassDeclaration) {
break;
}
current = nextNode;
}
return IntrospectableClassDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
interfaces: _namedTypes(interfaceNodes),
hasAbstract: node.abstractKeyword != null,
hasBase: node.baseKeyword != null,
hasExternal: false,
hasFinal: node.finalKeyword != null,
hasInterface: node.interfaceKeyword != null,
hasMixin: node.mixinKeyword != null,
hasSealed: node.sealedKeyword != null,
mixins: _namedTypes(mixinNodes),
superclass: node.extendsClause?.superclass.mapOrNull(_namedType),
element: element,
);
}
IntrospectableMixinDeclarationImpl _introspectableMixinDeclaration(
ast.MixinDeclaration node,
) {
final element = node.declaredElement as MixinElementImpl;
final onNodes = <ast.NamedType>[];
final interfaceNodes = <ast.NamedType>[];
for (var current = node;;) {
if (current.onClause case final clause?) {
onNodes.addAll(clause.superclassConstraints);
}
if (current.implementsClause case final clause?) {
interfaceNodes.addAll(clause.interfaces);
}
final nextElement = current.declaredElement?.augmentation;
final nextNode = declarationBuilder.nodeOfElement(nextElement);
if (nextNode is! ast.MixinDeclaration) {
break;
}
current = nextNode;
}
return IntrospectableMixinDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
typeParameters: _typeParameters(node.typeParameters),
hasBase: node.baseKeyword != null,
interfaces: _namedTypes(interfaceNodes),
superclassConstraints: _namedTypes(onNodes),
element: element,
);
}
MethodDeclarationImpl _methodDeclaration(
ast.MethodDeclaration node,
) {
final definingType = _definingType(node);
final element = node.declaredElement!;
return MethodDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
definingType: definingType,
element: element,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
hasAbstract: false,
hasBody: node.body is! ast.EmptyFunctionBody,
hasExternal: node.externalKeyword != null,
isGetter: node.isGetter,
isOperator: node.isOperator,
isSetter: node.isSetter,
isStatic: node.isStatic,
namedParameters: _namedFormalParameters(node.parameters),
positionalParameters: _positionalFormalParameters(node.parameters),
returnType: _typeAnnotation(node.returnType),
typeParameters: _typeParameters(node.typeParameters),
);
}
List<macro.ParameterDeclarationImpl> _namedFormalParameters(
ast.FormalParameterList? node,
) {
if (node != null) {
return node.parameters
.where((e) => e.isNamed)
.map(_formalParameter)
.toList();
} else {
return const [];
}
}
macro.NamedTypeAnnotationImpl _namedType(ast.NamedType node) {
return macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: _namedTypeIdentifier(node),
isNullable: node.question != null,
typeArguments: _typeAnnotations(node.typeArguments?.arguments),
);
}
macro.IdentifierImpl _namedTypeIdentifier(ast.NamedType node) {
return _namedTypeMap[node] ??= _NamedTypeIdentifierImpl(
id: macro.RemoteInstance.uniqueId,
name: node.name2.lexeme,
node: node,
);
}
List<macro.NamedTypeAnnotationImpl> _namedTypes(
List<ast.NamedType>? elements,
) {
if (elements != null) {
return elements.map(_namedType).toList();
} else {
return const [];
}
}
List<macro.ParameterDeclarationImpl> _positionalFormalParameters(
ast.FormalParameterList? node,
) {
if (node != null) {
return node.parameters
.where((e) => e.isPositional)
.map(_formalParameter)
.toList();
} else {
return const [];
}
}
macro.TypeAnnotationImpl _typeAnnotation(ast.TypeAnnotation? node) {
switch (node) {
case null:
return macro.OmittedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
);
case ast.GenericFunctionType():
return macro.FunctionTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
isNullable: node.question != null,
namedParameters: node.parameters.parameters
.where((e) => e.isNamed)
.map(_functionTypeFormalParameter)
.toList(),
positionalParameters: node.parameters.parameters
.where((e) => e.isPositional)
.map(_functionTypeFormalParameter)
.toList(),
returnType: _typeAnnotation(node.returnType),
typeParameters: _typeParameters(node.typeParameters),
);
case ast.NamedType():
return _namedType(node);
default:
throw UnimplementedError('(${node.runtimeType}) $node');
}
}
List<macro.TypeAnnotationImpl> _typeAnnotations(
List<ast.TypeAnnotation>? elements,
) {
if (elements != null) {
return List.generate(
elements.length, (i) => _typeAnnotation(elements[i]));
} else {
return const [];
}
}
macro.TypeParameterDeclarationImpl _typeParameter(
ast.TypeParameter node,
) {
final element = node.declaredElement!;
return macro.TypeParameterDeclarationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: _declaredIdentifier(node.name, element),
library: library(element),
metadata: _buildMetadata(element),
bound: node.bound.mapOrNull(_typeAnnotation),
);
}
List<macro.TypeParameterDeclarationImpl> _typeParameters(
ast.TypeParameterList? typeParameterList,
) {
if (typeParameterList != null) {
return typeParameterList.typeParameters.map(_typeParameter).toList();
} else {
return const [];
}
}
}
class FieldDeclarationImpl extends macro.FieldDeclarationImpl {
FieldDeclarationImpl({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.hasExternal,
required super.hasFinal,
required super.hasLate,
required super.type,
required super.definingType,
required super.isStatic,
});
}
abstract class IdentifierImpl extends macro.IdentifierImpl {
IdentifierImpl({
required super.id,
required super.name,
});
Element? get element;
}
class IdentifierImplFromElement extends IdentifierImpl {
@override
final Element element;
IdentifierImplFromElement({
required super.id,
required super.name,
required this.element,
});
}
class IdentifierImplFromNode extends IdentifierImpl {
final Element? Function() getElement;
IdentifierImplFromNode({
required super.id,
required super.name,
required this.getElement,
});
@override
Element? get element => getElement();
}
class IntrospectableClassDeclarationImpl
extends macro.IntrospectableClassDeclarationImpl {
final ClassElement element;
IntrospectableClassDeclarationImpl._({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.typeParameters,
required super.interfaces,
required super.hasAbstract,
required super.hasBase,
required super.hasFinal,
required super.hasExternal,
required super.hasInterface,
required super.hasMixin,
required super.hasSealed,
required super.mixins,
required super.superclass,
required this.element,
});
}
class IntrospectableMixinDeclarationImpl
extends macro.IntrospectableMixinDeclarationImpl {
final MixinElement element;
IntrospectableMixinDeclarationImpl._({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.typeParameters,
required super.hasBase,
required super.interfaces,
required super.superclassConstraints,
required this.element,
});
}
abstract class LibraryImpl extends macro.LibraryImpl {
LibraryImpl({
required super.id,
required super.languageVersion,
required super.metadata,
required super.uri,
});
Element? get element;
}
class LibraryImplFromElement extends LibraryImpl {
@override
final Element element;
LibraryImplFromElement({
required super.id,
required super.languageVersion,
required super.metadata,
required super.uri,
required this.element,
});
}
class MethodDeclarationImpl extends macro.MethodDeclarationImpl {
final ExecutableElement element;
MethodDeclarationImpl._({
required super.id,
required super.identifier,
required super.library,
required super.metadata,
required super.hasAbstract,
required super.hasBody,
required super.hasExternal,
required super.isGetter,
required super.isOperator,
required super.isSetter,
required super.isStatic,
required super.namedParameters,
required super.positionalParameters,
required super.returnType,
required super.typeParameters,
required super.definingType,
required this.element,
});
}
class _DeclaredIdentifierImpl extends IdentifierImpl {
@override
final Element element;
_DeclaredIdentifierImpl({
required super.id,
required super.name,
required this.element,
});
}
class _NamedTypeIdentifierImpl extends IdentifierImpl {
final ast.NamedType node;
_NamedTypeIdentifierImpl({
required super.id,
required super.name,
required this.node,
});
@override
Element? get element => node.element;
}
extension<T> on T? {
R? mapOrNull<R>(R Function(T) mapper) {
final self = this;
return self != null ? mapper(self) : null;
}
}