mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 00:45:16 +00:00
Support for top-level getters that are instances of macros.
Change-Id: Ieda42930afa965ddc5170cea2755eb2559b8f34a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/243381 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
706e62a1bf
commit
404e568787
|
@ -12,6 +12,7 @@ import 'package:_fe_analyzer_shared/src/macros/executor/remote_instance.dart'
|
|||
as macro;
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/token.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/element/element.dart';
|
||||
import 'package:analyzer/src/summary2/library_builder.dart';
|
||||
import 'package:analyzer/src/summary2/link.dart';
|
||||
|
@ -22,7 +23,7 @@ class LibraryMacroApplier {
|
|||
final MultiMacroExecutor macroExecutor;
|
||||
final LibraryBuilder libraryBuilder;
|
||||
|
||||
final Map<MacroTargetElement, List<MacroApplication>> _applications =
|
||||
final Map<MacroTargetElement, List<_MacroApplication>> _applications =
|
||||
Map.identity();
|
||||
|
||||
final Map<ClassDeclaration, macro.ClassDeclaration> _classDeclarations = {};
|
||||
|
@ -147,36 +148,31 @@ class LibraryMacroApplier {
|
|||
List<Annotation> annotations,
|
||||
macro.Declaration Function() getDeclaration,
|
||||
) async {
|
||||
final applications = <MacroApplication>[];
|
||||
final applications = <_MacroApplication>[];
|
||||
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
final annotation = annotations[i];
|
||||
final macroElement = _importedMacroElement(annotation.name);
|
||||
final argumentsNode = annotation.arguments;
|
||||
if (macroElement is ClassElementImpl && argumentsNode != null) {
|
||||
final importedLibrary = macroElement.library;
|
||||
Future<MacroClassInstance?> instantiateSingle({
|
||||
required ClassElementImpl macroClass,
|
||||
required String constructorName,
|
||||
required ArgumentList argumentsNode,
|
||||
}) async {
|
||||
final importedLibrary = macroClass.library;
|
||||
final macroExecutor = importedLibrary.bundleMacroExecutor;
|
||||
if (macroExecutor != null) {
|
||||
await _runWithCatchingExceptions(
|
||||
return await _runWithCatchingExceptions(
|
||||
() async {
|
||||
final arguments = _buildArguments(
|
||||
annotationIndex: i,
|
||||
node: argumentsNode,
|
||||
);
|
||||
final declaration = getDeclaration();
|
||||
final macroInstance = await macroExecutor.instantiate(
|
||||
libraryUri: macroElement.librarySource.uri,
|
||||
className: macroElement.name,
|
||||
constructorName: '', // TODO
|
||||
return await macroExecutor.instantiate(
|
||||
libraryUri: macroClass.librarySource.uri,
|
||||
className: macroClass.name,
|
||||
constructorName: constructorName,
|
||||
arguments: arguments,
|
||||
identifierResolver: _IdentifierResolver(),
|
||||
declarationKind: macro.DeclarationKind.clazz,
|
||||
declaration: declaration,
|
||||
);
|
||||
applications.add(
|
||||
MacroApplication(
|
||||
annotationIndex: i,
|
||||
instance: macroInstance,
|
||||
),
|
||||
declaration: getDeclaration(),
|
||||
);
|
||||
},
|
||||
annotationIndex: i,
|
||||
|
@ -185,6 +181,43 @@ class LibraryMacroApplier {
|
|||
},
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
final annotation = annotations[i];
|
||||
final macroInstance = await _importedMacroDeclaration(
|
||||
annotation.name,
|
||||
whenClass: ({
|
||||
required macroClass,
|
||||
}) async {
|
||||
final argumentsNode = annotation.arguments;
|
||||
if (argumentsNode != null) {
|
||||
return await instantiateSingle(
|
||||
macroClass: macroClass,
|
||||
constructorName: '', // TODO(scheglov) implement
|
||||
argumentsNode: argumentsNode,
|
||||
);
|
||||
}
|
||||
},
|
||||
whenGetter: ({
|
||||
required macroClass,
|
||||
required instanceCreation,
|
||||
}) async {
|
||||
return await instantiateSingle(
|
||||
macroClass: macroClass,
|
||||
constructorName: '', // TODO(scheglov) implement
|
||||
argumentsNode: instanceCreation.argumentList,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (macroInstance != null) {
|
||||
applications.add(
|
||||
_MacroApplication(
|
||||
annotationIndex: i,
|
||||
instance: macroInstance,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (applications.isNotEmpty) {
|
||||
|
@ -192,8 +225,19 @@ class LibraryMacroApplier {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the macro element referenced by the [node].
|
||||
ElementImpl? _importedMacroElement(Identifier node) {
|
||||
/// If [node] references a macro, invokes the right callback.
|
||||
Future<R?> _importedMacroDeclaration<R>(
|
||||
Identifier node, {
|
||||
required Future<R?> Function({
|
||||
required ClassElementImpl macroClass,
|
||||
})
|
||||
whenClass,
|
||||
required Future<R?> Function({
|
||||
required ClassElementImpl macroClass,
|
||||
required InstanceCreationExpression instanceCreation,
|
||||
})
|
||||
whenGetter,
|
||||
}) async {
|
||||
final String? prefix;
|
||||
final String name;
|
||||
if (node is PrefixedIdentifier) {
|
||||
|
@ -224,8 +268,28 @@ class LibraryMacroApplier {
|
|||
|
||||
final lookupResult = importedLibrary.scope.lookup(name);
|
||||
final element = lookupResult.getter;
|
||||
if (element is ClassElementImpl && element.isMacro) {
|
||||
return element;
|
||||
if (element is ClassElementImpl) {
|
||||
if (element.isMacro) {
|
||||
return await whenClass(macroClass: element);
|
||||
}
|
||||
} else if (element is PropertyAccessorElementImpl &&
|
||||
element.isGetter &&
|
||||
element.isSynthetic) {
|
||||
final variable = element.variable;
|
||||
final variableType = variable.type;
|
||||
if (variable is ConstTopLevelVariableElementImpl &&
|
||||
variableType is InterfaceType) {
|
||||
final macroClass = variableType.element;
|
||||
final initializer = variable.constantInitializer;
|
||||
if (macroClass is ClassElementImpl &&
|
||||
macroClass.isMacro &&
|
||||
initializer is InstanceCreationExpression) {
|
||||
return await whenGetter(
|
||||
macroClass: macroClass,
|
||||
instanceCreation: initializer,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
@ -380,13 +444,13 @@ class LibraryMacroApplier {
|
|||
}
|
||||
|
||||
/// Run the [body], report exceptions as [MacroApplicationError]s to [onError].
|
||||
static Future<void> _runWithCatchingExceptions<T>(
|
||||
static Future<T?> _runWithCatchingExceptions<T>(
|
||||
Future<T> Function() body, {
|
||||
required int annotationIndex,
|
||||
required void Function(MacroApplicationError) onError,
|
||||
}) async {
|
||||
try {
|
||||
await body();
|
||||
return await body();
|
||||
} on MacroApplicationError catch (e) {
|
||||
onError(e);
|
||||
} on macro.RemoteException catch (e) {
|
||||
|
@ -406,21 +470,10 @@ class LibraryMacroApplier {
|
|||
),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class MacroApplication {
|
||||
final int annotationIndex;
|
||||
final MacroClassInstance instance;
|
||||
|
||||
MacroApplication({
|
||||
required this.annotationIndex,
|
||||
required this.instance,
|
||||
});
|
||||
|
||||
bool shouldExecute(macro.Phase phase) => instance.shouldExecute(phase);
|
||||
}
|
||||
|
||||
/// Helper class for evaluating arguments for a single constructor based
|
||||
/// macro application.
|
||||
class _ArgumentEvaluation {
|
||||
|
@ -551,6 +604,18 @@ class _IdentifierResolver extends macro.IdentifierResolver {
|
|||
}
|
||||
}
|
||||
|
||||
class _MacroApplication {
|
||||
final int annotationIndex;
|
||||
final MacroClassInstance instance;
|
||||
|
||||
_MacroApplication({
|
||||
required this.annotationIndex,
|
||||
required this.instance,
|
||||
});
|
||||
|
||||
bool shouldExecute(macro.Phase phase) => instance.shouldExecute(phase);
|
||||
}
|
||||
|
||||
class _TypeResolver implements macro.TypeResolver {
|
||||
@override
|
||||
Future<macro.StaticType> resolve(macro.TypeAnnotationCode type) {
|
||||
|
|
|
@ -63,6 +63,130 @@ class MacroElementsTest extends ElementsBaseTest {
|
|||
);
|
||||
}
|
||||
|
||||
test_application_getter_withoutPrefix_withoutArguments() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
import 'dart:async';
|
||||
import 'package:_fe_analyzer_shared/src/macros/api.dart';
|
||||
|
||||
macro class MyMacro implements ClassTypesMacro {
|
||||
FutureOr<void> buildTypesForClass(clazz, builder) {
|
||||
builder.declareType(
|
||||
'MyClass',
|
||||
DeclarationCode.fromString('class MyClass {}'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const myMacro = MyMacro();
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart';
|
||||
|
||||
@myMacro
|
||||
class A {}
|
||||
''', preBuildSequence: [
|
||||
{'package:test/a.dart'}
|
||||
]);
|
||||
|
||||
checkElementText(
|
||||
library,
|
||||
r'''
|
||||
library
|
||||
imports
|
||||
package:test/a.dart
|
||||
definingUnit
|
||||
classes
|
||||
class A @33
|
||||
metadata
|
||||
Annotation
|
||||
atSign: @ @18
|
||||
name: SimpleIdentifier
|
||||
token: myMacro @19
|
||||
staticElement: package:test/a.dart::@getter::myMacro
|
||||
staticType: null
|
||||
element: package:test/a.dart::@getter::myMacro
|
||||
constructors
|
||||
synthetic @-1
|
||||
parts
|
||||
package:test/_macro_types.dart
|
||||
classes
|
||||
class MyClass @6
|
||||
constructors
|
||||
synthetic @-1
|
||||
exportScope
|
||||
A: package:test/test.dart;A
|
||||
MyClass: package:test/test.dart;package:test/_macro_types.dart;MyClass
|
||||
''',
|
||||
withExportScope: true);
|
||||
}
|
||||
|
||||
test_application_getter_withPrefix_withoutArguments() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
import 'dart:async';
|
||||
import 'package:_fe_analyzer_shared/src/macros/api.dart';
|
||||
|
||||
macro class MyMacro implements ClassTypesMacro {
|
||||
FutureOr<void> buildTypesForClass(clazz, builder) {
|
||||
builder.declareType(
|
||||
'MyClass',
|
||||
DeclarationCode.fromString('class MyClass {}'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const myMacro = MyMacro();
|
||||
''');
|
||||
|
||||
var library = await buildLibrary(r'''
|
||||
import 'a.dart' as prefix;
|
||||
|
||||
@prefix.myMacro
|
||||
class A {}
|
||||
''', preBuildSequence: [
|
||||
{'package:test/a.dart'}
|
||||
]);
|
||||
|
||||
checkElementText(
|
||||
library,
|
||||
r'''
|
||||
library
|
||||
imports
|
||||
package:test/a.dart as prefix @19
|
||||
definingUnit
|
||||
classes
|
||||
class A @50
|
||||
metadata
|
||||
Annotation
|
||||
atSign: @ @28
|
||||
name: PrefixedIdentifier
|
||||
prefix: SimpleIdentifier
|
||||
token: prefix @29
|
||||
staticElement: self::@prefix::prefix
|
||||
staticType: null
|
||||
period: . @35
|
||||
identifier: SimpleIdentifier
|
||||
token: myMacro @36
|
||||
staticElement: package:test/a.dart::@getter::myMacro
|
||||
staticType: null
|
||||
staticElement: package:test/a.dart::@getter::myMacro
|
||||
staticType: null
|
||||
element: package:test/a.dart::@getter::myMacro
|
||||
constructors
|
||||
synthetic @-1
|
||||
parts
|
||||
package:test/_macro_types.dart
|
||||
classes
|
||||
class MyClass @6
|
||||
constructors
|
||||
synthetic @-1
|
||||
exportScope
|
||||
A: package:test/test.dart;A
|
||||
MyClass: package:test/test.dart;package:test/_macro_types.dart;MyClass
|
||||
''',
|
||||
withExportScope: true);
|
||||
}
|
||||
|
||||
test_application_newInstance_withoutPrefix() async {
|
||||
newFile('$testPackageLibPath/a.dart', r'''
|
||||
import 'dart:async';
|
||||
|
|
Loading…
Reference in a new issue