Support more parts of ClassDeclaration in DeclarationBuilderFromElement.

Change-Id: I6d6c0344924a19e8b8b01cf54a51b69fd61e0122
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243849
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2022-05-05 21:30:04 +00:00
parent c6173d3686
commit 5a2f5ab33c
6 changed files with 417 additions and 170 deletions

View file

@ -486,7 +486,7 @@ class _ClassIntrospector implements macro.ClassIntrospector {
return null;
}
return declarationBuilder.fromElement.classDeclaration(
return declarationBuilder.fromElement.classElement(
superType.element,
);
}

View file

@ -8,6 +8,8 @@ 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/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
class ClassDeclarationImpl extends macro.ClassDeclarationImpl {
late final ClassElement element;
@ -43,7 +45,7 @@ class DeclarationBuilder {
/// corresponding elements. So, we can access them uniformly via interfaces,
/// mixins, etc.
void transferToElements() {
for (final entry in fromNode._classNodes.entries) {
for (final entry in fromNode._classMap.entries) {
final element = entry.key.declaredElement as ClassElement;
final declaration = entry.value;
declaration.element = element;
@ -54,51 +56,86 @@ class DeclarationBuilder {
class DeclarationBuilderFromElement {
final Map<ClassElement, ClassDeclarationImpl> _classMap = Map.identity();
final Map<TypeParameterElement, macro.TypeParameterDeclarationImpl>
_typeParameterMap = Map.identity();
macro.ClassDeclarationImpl classDeclaration(ClassElement element) {
return _classMap[element] ??= _classDeclaration(element);
macro.ClassDeclarationImpl classElement(ClassElement element) {
return _classMap[element] ??= _classElement(element);
}
ClassDeclarationImpl _classDeclaration(ClassElement element) {
macro.TypeParameterDeclarationImpl typeParameter(
TypeParameterElement element,
) {
return _typeParameterMap[element] ??= _typeParameter(element);
}
ClassDeclarationImpl _classElement(ClassElement element) {
assert(!_classMap.containsKey(element));
return ClassDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _identifierFromName(element.name),
typeParameters: [], // TODO _buildTypeParameters(node.typeParameters),
interfaces: [], // TODO _buildTypeAnnotations(node.implementsClause?.interfaces),
isAbstract: false, // TODO node.abstractKeyword != null,
identifier: _identifier(element.name),
typeParameters: element.typeParameters.map(_typeParameter).toList(),
interfaces: element.interfaces.map(_dartType).toList(),
isAbstract: element.isAbstract,
isExternal: false,
mixins: [], // TODO _buildTypeAnnotations(node.withClause?.mixinTypes),
superclass: null,
// TODO(scheglov) implement
// superclass: node.extendsClause?.superclass.mapOrNull(
// _buildTypeAnnotation,
// ),
mixins: element.mixins.map(_dartType).toList(),
superclass: element.supertype.mapOrNull(_dartType),
)..element = element;
}
macro.IdentifierImpl _identifierFromName(String name) {
macro.TypeAnnotationImpl _dartType(DartType type) {
if (type is InterfaceType) {
return macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
identifier: _identifier(type.element.name),
typeArguments: type.typeArguments.map(_dartType).toList(),
);
} else if (type is TypeParameterType) {
return macro.NamedTypeAnnotationImpl(
id: macro.RemoteInstance.uniqueId,
isNullable: type.nullabilitySuffix == NullabilitySuffix.question,
identifier: _identifier(type.element.name),
typeArguments: const [],
);
} else {
// TODO(scheglov) other types
throw UnimplementedError('(${type.runtimeType}) $type');
}
}
macro.IdentifierImpl _identifier(String name) {
return _IdentifierImpl(
id: macro.RemoteInstance.uniqueId,
name: name,
);
}
macro.TypeParameterDeclarationImpl _typeParameter(
TypeParameterElement element,
) {
return macro.TypeParameterDeclarationImpl(
id: macro.RemoteInstance.uniqueId,
identifier: _identifier(element.name),
bound: element.bound.mapOrNull(_dartType),
);
}
}
class DeclarationBuilderFromNode {
final Map<ast.ClassDeclaration, ClassDeclarationImpl> _classNodes =
final Map<ast.ClassDeclaration, ClassDeclarationImpl> _classMap =
Map.identity();
macro.ClassDeclarationImpl classDeclaration(
ast.ClassDeclaration node,
) {
return _classNodes[node] ??= _classDeclaration(node);
return _classMap[node] ??= _classDeclaration(node);
}
ClassDeclarationImpl _classDeclaration(
ast.ClassDeclaration node,
) {
assert(!_classNodes.containsKey(node));
assert(!_classMap.containsKey(node));
return ClassDeclarationImpl._(
id: macro.RemoteInstance.uniqueId,
identifier: _identifier(node.name),

View file

@ -6,6 +6,8 @@ import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'introspect_shared.dart';
/*macro*/ class DeclarationTextMacro implements ClassTypesMacro {
const DeclarationTextMacro();
@ -88,15 +90,14 @@ class _DeclarationPrinter {
_sink.write('$name: ');
if (type != null) {
_writeln(_typeStr(type));
_writeln(type.asString);
} else {
_writeln('null');
}
}
void _writeTypeAnnotationLine(TypeAnnotation type) {
var typeStr = _typeStr(type);
_writelnWithIndent(typeStr);
_writelnWithIndent(type.asString);
}
void _writeTypeAnnotations(String name, Iterable<TypeAnnotation> elements) {
@ -117,138 +118,4 @@ class _DeclarationPrinter {
void _writeTypeParameters(Iterable<TypeParameterDeclaration> elements) {
_writeElements('typeParameters', elements, _writeTypeParameter);
}
static String _typeStr(TypeAnnotation type) {
final buffer = StringBuffer();
_TypeStringBuilder(buffer).write(type);
return buffer.toString();
}
}
class _TypeStringBuilder {
final StringSink _sink;
_TypeStringBuilder(this._sink);
void write(TypeAnnotation type) {
if (type is FunctionTypeAnnotation) {
_writeFunctionTypeAnnotation(type);
} else if (type is NamedTypeAnnotation) {
_writeNamedTypeAnnotation(type);
} else if (type is OmittedTypeAnnotation) {
_sink.write('OmittedType');
} else {
throw UnimplementedError('(${type.runtimeType}) $type');
}
if (type.isNullable) {
_sink.write('?');
}
}
void _writeFormalParameter(FunctionTypeParameter node) {
final String closeSeparator;
if (node.isNamed) {
_sink.write('{');
closeSeparator = '}';
if (node.isRequired) {
_sink.write('required ');
}
} else if (!node.isRequired) {
_sink.write('[');
closeSeparator = ']';
} else {
closeSeparator = '';
}
write(node.type);
if (node.name != null) {
_sink.write(' ');
_sink.write(node.name);
}
_sink.write(closeSeparator);
}
void _writeFunctionTypeAnnotation(FunctionTypeAnnotation type) {
write(type.returnType);
_sink.write(' Function');
_sink.writeList(
elements: type.typeParameters,
write: _writeTypeParameter,
separator: ', ',
open: '<',
close: '>',
);
_sink.write('(');
var hasFormalParameter = false;
for (final formalParameter in type.positionalParameters) {
if (hasFormalParameter) {
_sink.write(', ');
}
_writeFormalParameter(formalParameter);
hasFormalParameter = true;
}
for (final formalParameter in type.namedParameters) {
if (hasFormalParameter) {
_sink.write(', ');
}
_writeFormalParameter(formalParameter);
hasFormalParameter = true;
}
_sink.write(')');
}
void _writeNamedTypeAnnotation(NamedTypeAnnotation type) {
_sink.write(type.identifier.name);
_sink.writeList(
elements: type.typeArguments,
write: write,
separator: ', ',
open: '<',
close: '>',
);
}
void _writeTypeParameter(TypeParameterDeclaration node) {
_sink.write(node.identifier.name);
final bound = node.bound;
if (bound != null) {
_sink.write(' extends ');
write(bound);
}
}
}
extension on StringSink {
void writeList<T>({
required Iterable<T> elements,
required void Function(T element) write,
required String separator,
String? open,
String? close,
}) {
elements = elements.toList();
if (elements.isEmpty) {
return;
}
if (open != null) {
this.write(open);
}
var isFirst = true;
for (var element in elements) {
if (isFirst) {
isFirst = false;
} else {
this.write(separator);
}
write(element);
}
if (close != null) {
this.write(close);
}
}
}

View file

@ -6,6 +6,8 @@ import 'dart:async';
import 'package:_fe_analyzer_shared/src/macros/api.dart';
import 'introspect_shared.dart';
const introspectMacro = IntrospectDeclarationsPhaseMacro();
/*macro*/ class IntrospectDeclarationsPhaseMacro
@ -19,6 +21,7 @@ const introspectMacro = IntrospectDeclarationsPhaseMacro();
) async {
final printer = _DeclarationPrinter(
classIntrospector: builder,
typeResolver: builder,
);
await printer.writeClassDeclaration(declaration);
final text = printer._sink.toString();
@ -34,11 +37,13 @@ const introspectMacro = IntrospectDeclarationsPhaseMacro();
class _DeclarationPrinter {
final ClassIntrospector classIntrospector;
final TypeResolver typeResolver;
final StringBuffer _sink = StringBuffer();
String _indent = '';
_DeclarationPrinter({
required this.classIntrospector,
required this.typeResolver,
});
Future<void> writeClassDeclaration(ClassDeclaration e) async {
@ -55,10 +60,9 @@ class _DeclarationPrinter {
await _withIndent(() => writeClassDeclaration(superclass));
}
// TODO(scheglov) implement
// _writeTypeParameters(e.typeParameters);
// _writeTypeAnnotations('mixins', e.mixins);
// _writeTypeAnnotations('interfaces', e.interfaces);
await _writeTypeParameters(e.typeParameters);
await _writeTypeAnnotations('mixins', e.mixins);
await _writeTypeAnnotations('interfaces', e.interfaces);
});
}
@ -69,6 +73,21 @@ class _DeclarationPrinter {
_indent = savedIndent;
}
Future<void> _writeElements<T>(
String name,
Iterable<T> elements,
Future<void> Function(T) f,
) async {
if (elements.isNotEmpty) {
_writelnWithIndent(name);
await _withIndent(() async {
for (var element in elements) {
await f(element);
}
});
}
}
void _writeIf(bool flag, String str) {
if (flag) {
_sink.write(str);
@ -83,4 +102,43 @@ class _DeclarationPrinter {
_sink.write(_indent);
_sink.writeln(line);
}
void _writeTypeAnnotation(String name, TypeAnnotation? type) {
_sink.write(_indent);
_sink.write('$name: ');
if (type != null) {
_writeln(type.asString);
} else {
_writeln('null');
}
}
Future<void> _writeTypeAnnotationLine(TypeAnnotation type) async {
_writelnWithIndent(type.asString);
}
Future<void> _writeTypeAnnotations(
String name,
Iterable<TypeAnnotation> elements,
) async {
await _writeElements(name, elements, _writeTypeAnnotationLine);
}
Future<void> _writeTypeParameter(TypeParameterDeclaration e) async {
_writelnWithIndent(e.identifier.name);
await _withIndent(() async {
var bound = e.bound;
if (bound != null) {
_writeTypeAnnotation('bound', bound);
}
});
}
Future<void> _writeTypeParameters(
Iterable<TypeParameterDeclaration> elements,
) async {
await _writeElements('typeParameters', elements, _writeTypeParameter);
}
}

View file

@ -0,0 +1,141 @@
// 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/api.dart';
class _TypeAnnotationStringBuilder {
final StringSink _sink;
_TypeAnnotationStringBuilder(this._sink);
void write(TypeAnnotation type) {
if (type is FunctionTypeAnnotation) {
_writeFunctionTypeAnnotation(type);
} else if (type is NamedTypeAnnotation) {
_writeNamedTypeAnnotation(type);
} else if (type is OmittedTypeAnnotation) {
_sink.write('OmittedType');
} else {
throw UnimplementedError('(${type.runtimeType}) $type');
}
if (type.isNullable) {
_sink.write('?');
}
}
void _writeFormalParameter(FunctionTypeParameter node) {
final String closeSeparator;
if (node.isNamed) {
_sink.write('{');
closeSeparator = '}';
if (node.isRequired) {
_sink.write('required ');
}
} else if (!node.isRequired) {
_sink.write('[');
closeSeparator = ']';
} else {
closeSeparator = '';
}
write(node.type);
if (node.name != null) {
_sink.write(' ');
_sink.write(node.name);
}
_sink.write(closeSeparator);
}
void _writeFunctionTypeAnnotation(FunctionTypeAnnotation type) {
write(type.returnType);
_sink.write(' Function');
_sink.writeList(
elements: type.typeParameters,
write: _writeTypeParameter,
separator: ', ',
open: '<',
close: '>',
);
_sink.write('(');
var hasFormalParameter = false;
for (final formalParameter in type.positionalParameters) {
if (hasFormalParameter) {
_sink.write(', ');
}
_writeFormalParameter(formalParameter);
hasFormalParameter = true;
}
for (final formalParameter in type.namedParameters) {
if (hasFormalParameter) {
_sink.write(', ');
}
_writeFormalParameter(formalParameter);
hasFormalParameter = true;
}
_sink.write(')');
}
void _writeNamedTypeAnnotation(NamedTypeAnnotation type) {
_sink.write(type.identifier.name);
_sink.writeList(
elements: type.typeArguments,
write: write,
separator: ', ',
open: '<',
close: '>',
);
}
void _writeTypeParameter(TypeParameterDeclaration node) {
_sink.write(node.identifier.name);
final bound = node.bound;
if (bound != null) {
_sink.write(' extends ');
write(bound);
}
}
}
extension on StringSink {
void writeList<T>({
required Iterable<T> elements,
required void Function(T element) write,
required String separator,
String? open,
String? close,
}) {
elements = elements.toList();
if (elements.isEmpty) {
return;
}
if (open != null) {
this.write(open);
}
var isFirst = true;
for (var element in elements) {
if (isFirst) {
isFirst = false;
} else {
this.write(separator);
}
write(element);
}
if (close != null) {
this.write(close);
}
}
}
extension E on TypeAnnotation {
String get asString {
final buffer = StringBuffer();
_TypeAnnotationStringBuilder(buffer).write(this);
return buffer.toString();
}
}

View file

@ -65,6 +65,12 @@ class MacroElementsTest extends ElementsBaseTest {
return code.replaceAll('/*macro*/', 'macro');
}
String get _introspectSharedCode {
return MacrosEnvironment.instance.packageAnalyzerFolder
.getChildAssumingFile('test/src/summary/macro/introspect_shared.dart')
.readAsStringSync();
}
Future<void> setUp() async {
writeTestPackageConfig(
PackageConfigFileBuilder(),
@ -1152,6 +1158,133 @@ library
''');
}
test_introspect_declarations_ClassDeclaration_imported_interfaces() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {}
class B {}
class C implements A, B {}
''');
await _assertIntrospectDeclarationsText(r'''
import 'a.dart';
@introspectMacro
class X extends C {}
''', r'''
class X
superclass
class C
superclass
class Object
interfaces
A
B
''');
}
test_introspect_declarations_ClassDeclaration_imported_isAbstract() async {
newFile('$testPackageLibPath/a.dart', r'''
abstract class A {}
''');
await _assertIntrospectDeclarationsText(r'''
import 'a.dart';
@introspectMacro
class X extends A {}
''', r'''
class X
superclass
abstract class A
superclass
class Object
''');
}
test_introspect_declarations_ClassDeclaration_imported_mixins() async {
newFile('$testPackageLibPath/a.dart', r'''
mixin M1 {}
mixin M2 {}
class C with M1, M2 {}
''');
await _assertIntrospectDeclarationsText(r'''
import 'a.dart';
@introspectMacro
class X extends C {}
''', r'''
class X
superclass
class C
superclass
class Object
mixins
M1
M2
''');
}
test_introspect_declarations_ClassDeclaration_imported_superclass() async {
newFile('$testPackageLibPath/a.dart', r'''
class A {}
class B extends A {}
''');
await _assertIntrospectDeclarationsText(r'''
import 'a.dart';
@introspectMacro
class X extends B {}
''', r'''
class X
superclass
class B
superclass
class A
superclass
class Object
''');
}
test_introspect_declarations_ClassDeclaration_imported_typeParameters() async {
newFile('$testPackageLibPath/a.dart', r'''
class A<T, U extends List<T>> {}
''');
await _assertIntrospectDeclarationsText(r'''
import 'a.dart';
@introspectMacro
class X extends A {}
''', r'''
class X
superclass
class A
superclass
class Object
typeParameters
T
U
bound: List<T>
''');
}
test_introspect_declarations_ClassDeclaration_superclassOf() async {
await _assertIntrospectDeclarationsText(r'''
class A {}
@introspectMacro
class X extends A {}
''', r'''
class X
superclass
class A
superclass
class Object
''');
}
test_introspect_declarations_ClassDeclaration_superclassOf_implicit() async {
await _assertIntrospectDeclarationsText(r'''
@introspectMacro
@ -1163,18 +1296,14 @@ class X
''');
}
test_introspect_declarations_ClassDeclaration_superclassOf_local() async {
test_introspect_declarations_ClassDeclaration_superclassOf_unresolved() async {
await _assertIntrospectDeclarationsText(r'''
class A<T> {}
@introspectMacro
class X extends A<int> {}
class X extends A {}
''', r'''
class X
superclass
class A
superclass
class Object
class Object
''');
}
@ -1597,6 +1726,11 @@ class A {}
/// the textual dump of the introspection information for the first
/// declaration.
Future<String> _getDeclarationText(String declarationCode) async {
newFile(
'$testPackageLibPath/introspect_shared.dart',
_introspectSharedCode,
);
newFile(
'$testPackageLibPath/declaration_text.dart',
_declarationTextCode,
@ -1608,7 +1742,10 @@ import 'declaration_text.dart';
@DeclarationTextMacro()
$declarationCode
''', preBuildSequence: [
{'package:test/declaration_text.dart'}
{
'package:test/introspect_shared.dart',
'package:test/declaration_text.dart',
}
]);
_assertNoErrorsForClassElement(
@ -1626,6 +1763,11 @@ $declarationCode
/// that contain textual dump of the introspection information for
/// macro annotated declarations.
Future<String> _getIntrospectDeclarationsText(String declarationCode) async {
newFile(
'$testPackageLibPath/introspect_shared.dart',
_introspectSharedCode,
);
newFile(
'$testPackageLibPath/introspect_declarations_phase.dart',
_introspectDeclarationsPhaseCode,
@ -1633,10 +1775,12 @@ $declarationCode
var library = await buildLibrary('''
import 'introspect_declarations_phase.dart';
$declarationCode
''', preBuildSequence: [
{'package:test/introspect_declarations_phase.dart'}
{
'package:test/introspect_shared.dart',
'package:test/introspect_declarations_phase.dart',
}
]);
for (final class_ in library.definingCompilationUnit.classes) {