Macro. Introspect MethodDeclaration node. Change-Id: I2d790cd82586db8686c92b30b7599c332b73a2be Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/331842 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/summary2/macro_declarations.dart b/pkg/analyzer/lib/src/summary2/macro_declarations.dart index f779dcc..8fd21bf 100644 --- a/pkg/analyzer/lib/src/summary2/macro_declarations.dart +++ b/pkg/analyzer/lib/src/summary2/macro_declarations.dart
@@ -35,7 +35,10 @@ } class DeclarationBuilder { - final DeclarationBuilderFromNode fromNode = DeclarationBuilderFromNode(); + final Map<Uri, List<ast.Annotation>> libraryMetadataMap = {}; + + late final DeclarationBuilderFromNode fromNode = + DeclarationBuilderFromNode(this); final DeclarationBuilderFromElement fromElement = DeclarationBuilderFromElement(); @@ -44,7 +47,6 @@ /// corresponding elements. So, we can access them uniformly via interfaces, /// mixins, etc. void transferToElements() { - // TODO(scheglov) Make sure that these are only declarations? for (final entry in fromNode._namedTypeMap.entries) { final element = entry.key.element; if (element != null) { @@ -61,9 +63,13 @@ for (final entry in fromNode._classMap.entries) { final element = entry.key.declaredElement!; final declaration = entry.value; - declaration.element = element; fromElement._classMap[element] = declaration; } + for (final entry in fromNode._methodMap.entries) { + final element = entry.key.declaredElement!; + final declaration = entry.value; + fromElement._methodMap[element] = declaration; + } } } @@ -76,6 +82,9 @@ final Map<FieldElement, FieldDeclarationImpl> _fieldMap = Map.identity(); + final Map<ExecutableElement, MethodDeclarationImpl> _methodMap = + Map.identity(); + final Map<TypeParameterElement, macro.TypeParameterDeclarationImpl> _typeParameterMap = Map.identity(); @@ -103,8 +112,7 @@ id: macro.RemoteInstance.uniqueId, languageVersion: macro.LanguageVersionImpl(version.major, version.minor), - // TODO: Provide metadata annotations. - metadata: const [], + metadata: _buildMetadata(element), uri: element.library!.source.uri, element: element); _libraryMap[element.library!] = library; @@ -112,12 +120,21 @@ return library; } + macro.MethodDeclarationImpl methodElement(MethodElement element) { + return _methodMap[element] ??= _methodElement(element); + } + macro.TypeParameterDeclarationImpl typeParameter( TypeParameterElement element, ) { return _typeParameterMap[element] ??= _typeParameter(element); } + List<macro.MetadataAnnotationImpl> _buildMetadata(Element element) { + // TODO: Provide metadata annotations. + return const []; + } + macro.TypeAnnotationImpl _dartType(DartType type) { switch (type) { case InterfaceType(): @@ -129,20 +146,28 @@ 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) { - assert(!_fieldMap.containsKey(element)); final enclosingClass = element.enclosingElement as ClassElement; return FieldDeclarationImpl( id: macro.RemoteInstance.uniqueId, identifier: identifier(element), library: library(element), - // TODO: Provide metadata annotations. - metadata: const [], + metadata: _buildMetadata(element), hasExternal: element.isExternal, hasFinal: element.isFinal, hasLate: element.isLate, @@ -163,13 +188,11 @@ IntrospectableClassDeclarationImpl _introspectableClassElement( ClassElement element) { - assert(!_classMap.containsKey(element)); return IntrospectableClassDeclarationImpl._( id: macro.RemoteInstance.uniqueId, identifier: identifier(element), library: library(element), - // TODO: Provide metadata annotations. - metadata: const [], + metadata: _buildMetadata(element), typeParameters: element.typeParameters.map(_typeParameter).toList(), interfaces: element.interfaces.map(_interfaceType).toList(), hasAbstract: element.isAbstract, @@ -181,7 +204,37 @@ hasSealed: element.isSealed, mixins: element.mixins.map(_interfaceType).toList(), superclass: element.supertype.mapOrNull(_interfaceType), - )..element = element; + 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( @@ -191,14 +244,15 @@ id: macro.RemoteInstance.uniqueId, identifier: identifier(element), library: library(element), - // TODO: Provide metadata annotations. - metadata: const [], + metadata: _buildMetadata(element), bound: element.bound.mapOrNull(_dartType), ); } } class DeclarationBuilderFromNode { + final DeclarationBuilder declarationBuilder; + final Map<ast.NamedType, IdentifierImpl> _namedTypeMap = Map.identity(); final Map<Element, IdentifierImpl> _declaredIdentifierMap = Map.identity(); @@ -210,6 +264,8 @@ final Map<ast.MethodDeclaration, MethodDeclarationImpl> _methodMap = Map.identity(); + DeclarationBuilderFromNode(this.declarationBuilder); + macro.ClassDeclarationImpl classDeclaration( ast.ClassDeclaration node, ) { @@ -217,20 +273,26 @@ } 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), - // TODO: Provide metadata annotations. - metadata: const [], - uri: element.library!.source.uri, - element: element); - _libraryMap[element.library!] = library; + final library = element.library!; + + if (_libraryMap[library] case final result?) { + return result; } - return library; + + final version = library.languageVersion.effective; + final uri = library.source.uri; + final metadataNodes = declarationBuilder.libraryMetadataMap[uri] ?? []; + + return _libraryMap[library] = LibraryImplFromElement( + id: macro.RemoteInstance.uniqueId, + languageVersion: macro.LanguageVersionImpl( + version.major, + version.minor, + ), + metadata: _buildMetadata(metadataNodes), + uri: uri, + element: library, + ); } macro.MethodDeclarationImpl methodDeclaration( @@ -239,6 +301,13 @@ return _methodMap[node] ??= _methodDeclaration(node); } + List<macro.MetadataAnnotationImpl> _buildMetadata( + List<ast.Annotation> elements, + ) { + // TODO: Provide metadata annotations. + return const []; + } + macro.IdentifierImpl _declaredIdentifier(Token name, Element element) { return _declaredIdentifierMap[element] ??= _DeclaredIdentifierImpl( id: macro.RemoteInstance.uniqueId, @@ -247,7 +316,32 @@ ); } - macro.FunctionTypeParameterImpl _formalParameter( + 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(node.metadata), + type: typeAnnotation, + ); + } + + macro.FunctionTypeParameterImpl _functionTypeFormalParameter( ast.FormalParameter node, ) { if (node is ast.DefaultFormalParameter) { @@ -265,9 +359,8 @@ id: macro.RemoteInstance.uniqueId, isNamed: node.isNamed, isRequired: node.isRequired, + metadata: _buildMetadata(node.metadata), name: node.name?.lexeme, - // TODO: Provide metadata annotations. - metadata: const [], type: typeAnnotation, ); } @@ -275,13 +368,12 @@ IntrospectableClassDeclarationImpl _introspectableClassDeclaration( ast.ClassDeclaration node, ) { - assert(!_classMap.containsKey(node)); + final element = node.declaredElement!; return IntrospectableClassDeclarationImpl._( id: macro.RemoteInstance.uniqueId, - identifier: _declaredIdentifier(node.name, node.declaredElement!), - library: library(node.declaredElement!), - // TODO: Provide metadata annotations. - metadata: const [], + identifier: _declaredIdentifier(node.name, element), + library: library(element), + metadata: _buildMetadata(node.metadata), typeParameters: _typeParameters(node.typeParameters), interfaces: _namedTypes(node.implementsClause?.interfaces), hasAbstract: node.abstractKeyword != null, @@ -293,27 +385,28 @@ hasSealed: node.sealedKeyword != null, mixins: _namedTypes(node.withClause?.mixinTypes), superclass: node.extendsClause?.superclass.mapOrNull(_namedType), + element: element, ); } MethodDeclarationImpl _methodDeclaration( ast.MethodDeclaration node, ) { - assert(!_methodMap.containsKey(node)); - // TODO(scheglov) other parents final parentNode = node.parent as ast.ClassDeclaration; final parentElement = parentNode.declaredElement!; final typeElement = parentElement.augmentationTarget ?? parentElement; final definingType = _declaredIdentifier(parentNode.name, typeElement); + final element = node.declaredElement!; + return MethodDeclarationImpl._( - element: node.declaredElement as MethodElement, id: macro.RemoteInstance.uniqueId, - identifier: _declaredIdentifier(node.name, node.declaredElement!), - library: library(node.declaredElement!), - // TODO: Provide metadata annotations. - metadata: const [], + definingType: definingType, + element: element, + identifier: _declaredIdentifier(node.name, element), + library: library(element), + metadata: _buildMetadata(node.metadata), hasAbstract: false, hasBody: node.body is! ast.EmptyFunctionBody, hasExternal: node.externalKeyword != null, @@ -321,14 +414,26 @@ isOperator: node.isOperator, isSetter: node.isSetter, isStatic: node.isStatic, - namedParameters: [], // TODO(scheglov) implement - positionalParameters: [], // TODO(scheglov) implement + namedParameters: _namedFormalParameters(node.parameters), + positionalParameters: _positionalFormalParameters(node.parameters), returnType: _typeAnnotation(node.returnType), typeParameters: _typeParameters(node.typeParameters), - definingType: definingType, ); } + 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, @@ -356,6 +461,19 @@ } } + 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: @@ -368,11 +486,11 @@ isNullable: node.question != null, namedParameters: node.parameters.parameters .where((e) => e.isNamed) - .map(_formalParameter) + .map(_functionTypeFormalParameter) .toList(), positionalParameters: node.parameters.parameters .where((e) => e.isPositional) - .map(_formalParameter) + .map(_functionTypeFormalParameter) .toList(), returnType: _typeAnnotation(node.returnType), typeParameters: _typeParameters(node.typeParameters), @@ -402,8 +520,7 @@ id: macro.RemoteInstance.uniqueId, identifier: _declaredIdentifier(node.name, node.declaredElement!), library: library(node.declaredElement!), - // TODO: Provide metadata annotations. - metadata: const [], + metadata: _buildMetadata(node.metadata), bound: node.bound.mapOrNull(_typeAnnotation), ); } @@ -456,7 +573,7 @@ class IntrospectableClassDeclarationImpl extends macro.IntrospectableClassDeclarationImpl { - late final ClassElement element; + final ClassElement element; IntrospectableClassDeclarationImpl._({ required super.id, @@ -474,6 +591,7 @@ required super.hasSealed, required super.mixins, required super.superclass, + required this.element, }); } @@ -502,7 +620,7 @@ } class MethodDeclarationImpl extends macro.MethodDeclarationImpl { - final MethodElement element; + final ExecutableElement element; MethodDeclarationImpl._({ required super.id, @@ -515,12 +633,12 @@ 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 super.isStatic, required this.element, }); }
diff --git a/pkg/analyzer/test/src/summary/macro/declaration_text.dart b/pkg/analyzer/test/src/summary/macro/declaration_text.dart index f0f228d..ea8744a 100644 --- a/pkg/analyzer/test/src/summary/macro/declaration_text.dart +++ b/pkg/analyzer/test/src/summary/macro/declaration_text.dart
@@ -8,7 +8,8 @@ import 'introspect_shared.dart'; -/*macro*/ class DeclarationTextMacro implements ClassTypesMacro { +/*macro*/ class DeclarationTextMacro + implements ClassTypesMacro, MethodTypesMacro { const DeclarationTextMacro(); @override @@ -24,6 +25,20 @@ ), ); } + + @override + FutureOr<void> buildTypesForMethod(method, builder) { + final printer = _DeclarationPrinter(); + printer.writeMethodDeclaration(method); + final text = printer._sink.toString(); + + builder.declareType( + 'x', + DeclarationCode.fromString( + 'const x = r"""$text""";', + ), + ); + } } class _DeclarationPrinter { @@ -48,6 +63,43 @@ }); } + void writeIndent() { + _sink.write(_indent); + } + + /// TODO(scheglov) Copy TreeStringSink here + void writeIndentedLine(void Function() f) { + writeIndent(); + f(); + writeln(); + } + + void writeln([Object? object = '']) { + _sink.writeln(object); + } + + void writeMethodDeclaration(MethodDeclaration e) { + _writelnWithIndent(e.identifier.name); + + _withIndent(() { + _writeFlags({ + 'hasAbstract': e.hasAbstract, + 'hasBody': e.hasBody, + 'hasExternal': e.hasExternal, + 'isGetter': e.isGetter, + 'isOperator': e.isOperator, + 'isSetter': e.isSetter, + 'isStatic': e.isStatic, + }); + + _writeMetadata(e); + _writeNamedFormalParameters(e.namedParameters); + _writePositionalFormalParameters(e.positionalParameters); + _writeTypeAnnotation('returnType', e.returnType); + _writeTypeParameters(e.typeParameters); + }); + } + void _withIndent(void Function() f) { var savedIndent = _indent; _indent = '$savedIndent '; @@ -70,6 +122,30 @@ } } + void _writeFlags(Map<String, bool> flags) { + if (flags.values.any((flag) => flag)) { + writeIndentedLine(() { + _sink.write('flags:'); + for (final entry in flags.entries) { + if (entry.value) { + _sink.write(' ${entry.key}'); + } + } + }); + } + } + + void _writeFormalParameter(ParameterDeclaration e) { + _writelnWithIndent(e.identifier.name); + _withIndent(() { + _writeFlags({ + 'isNamed': e.isNamed, + 'isRequired': e.isRequired, + }); + _writeTypeAnnotation('type', e.type); + }); + } + void _writeIf(bool flag, String str) { if (flag) { _sink.write(str); @@ -85,6 +161,26 @@ _sink.writeln(line); } + void _writeMetadata(Annotatable e) { + _writeElements('metadata', e.metadata, _writeMetadataAnnotation); + } + + void _writeMetadataAnnotation(MetadataAnnotation e) { + // TODO(scheglov) implement + } + + void _writeNamedFormalParameters( + Iterable<ParameterDeclaration> elements, + ) { + _writeElements('namedParameters', elements, _writeFormalParameter); + } + + void _writePositionalFormalParameters( + Iterable<ParameterDeclaration> elements, + ) { + _writeElements('positionalParameters', elements, _writeFormalParameter); + } + void _writeTypeAnnotation(String name, TypeAnnotation? type) { _sink.write(_indent); _sink.write('$name: ');
diff --git a/pkg/analyzer/test/src/summary/macro_test.dart b/pkg/analyzer/test/src/summary/macro_test.dart index 460ecb6..dd6c89a 100644 --- a/pkg/analyzer/test/src/summary/macro_test.dart +++ b/pkg/analyzer/test/src/summary/macro_test.dart
@@ -1412,8 +1412,116 @@ return code.replaceAll('/*macro*/', 'macro'); } + test_class_methodDeclaration_getter() async { + await _assertTypesPhaseIntrospectionText(r''' +abstract class A { + @DeclarationTextMacro() + int get foo => 0; +} +''', r''' +foo + flags: hasBody isGetter + returnType: int +'''); + } + + test_class_methodDeclaration_method_hasBody_false() async { + await _assertTypesPhaseIntrospectionText(r''' +abstract class A { + @DeclarationTextMacro() + void foo(); +} +''', r''' +foo + returnType: void +'''); + } + + test_class_methodDeclaration_method_hasExternal() async { + await _assertTypesPhaseIntrospectionText(r''' +abstract class A { + @DeclarationTextMacro() + external void foo(); +} +''', r''' +foo + flags: hasExternal + returnType: void +'''); + } + + test_class_methodDeclaration_method_isStatic() async { + await _assertTypesPhaseIntrospectionText(r''' +class A { + @DeclarationTextMacro() + static void foo() {} +} +''', r''' +foo + flags: hasBody isStatic + returnType: void +'''); + } + + test_class_methodDeclaration_method_namedParameters() async { + await _assertTypesPhaseIntrospectionText(r''' +abstract class A { + @DeclarationTextMacro() + void foo({required int a, String? b}]) {} +} +''', r''' +foo + flags: hasBody + namedParameters + a + flags: isNamed isRequired + type: int + b + flags: isNamed + type: String? + returnType: void +'''); + } + + test_class_methodDeclaration_method_positionalParameters() async { + await _assertTypesPhaseIntrospectionText(r''' +abstract class A { + @DeclarationTextMacro() + void foo(int a, [String? b]) {} +} +''', r''' +foo + flags: hasBody + positionalParameters + a + flags: isRequired + type: int + b + type: String? + returnType: void +'''); + } + + test_class_methodDeclaration_setter() async { + await _assertTypesPhaseIntrospectionText(r''' +abstract class A { + @DeclarationTextMacro() + set foo(int value) {} +} +''', r''' +foo + flags: hasBody isSetter + positionalParameters + value + flags: isRequired + type: int + returnType: OmittedType +'''); + } + test_classDeclaration_interfaces() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A implements B, C<int, String> {} ''', r''' class A @@ -1425,6 +1533,7 @@ test_classDeclaration_isAbstract() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() abstract class A {} ''', r''' abstract class A @@ -1433,6 +1542,7 @@ test_classDeclaration_mixins() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A with B, C<int, String> {} ''', r''' class A @@ -1444,6 +1554,7 @@ test_classDeclaration_superclass() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B {} ''', r''' class A @@ -1453,6 +1564,7 @@ test_classDeclaration_superclass_nullable() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<int?> {} ''', r''' class A @@ -1462,6 +1574,7 @@ test_classDeclaration_superclass_typeArguments() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<String, List<int>> {} ''', r''' class A @@ -1471,6 +1584,7 @@ test_classDeclaration_typeParameters() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A<T, U extends List<T>> {} ''', r''' class A @@ -1483,6 +1597,7 @@ test_functionTypeAnnotation_formalParameters_namedOptional_simpleFormalParameter() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function(int a, {int? b, int? c})> {} ''', r''' class A @@ -1492,6 +1607,7 @@ test_functionTypeAnnotation_formalParameters_namedRequired_simpleFormalParameter() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function(int a, {required int b, required int c})> {} ''', r''' class A @@ -1501,6 +1617,7 @@ test_functionTypeAnnotation_formalParameters_positionalOptional_simpleFormalParameter() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function(int a, [int b, int c])> {} ''', r''' class A @@ -1511,6 +1628,7 @@ /// TODO(scheglov) Tests for unnamed positional formal parameters. test_functionTypeAnnotation_formalParameters_positionalRequired_simpleFormalParameter() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function(int a, double b)> {} ''', r''' class A @@ -1520,6 +1638,7 @@ test_functionTypeAnnotation_nullable() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function()?> {} ''', r''' class A @@ -1529,6 +1648,7 @@ test_functionTypeAnnotation_returnType() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function()> {} ''', r''' class A @@ -1538,6 +1658,7 @@ test_functionTypeAnnotation_returnType_omitted() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<Function()> {} ''', r''' class A @@ -1547,6 +1668,7 @@ test_functionTypeAnnotation_typeParameters() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends B<void Function<T, U extends num>()> {} ''', r''' class A @@ -1556,6 +1678,7 @@ test_namedTypeAnnotation_prefixed() async { await _assertTypesPhaseIntrospectionText(r''' +@DeclarationTextMacro() class A extends prefix.B {} ''', r''' class A @@ -1597,7 +1720,6 @@ var library = await buildLibrary(''' import 'declaration_text.dart'; -@DeclarationTextMacro() $declarationCode ''');