diff --git a/reflectable/lib/src/builder_implementation.dart b/reflectable/lib/src/builder_implementation.dart index fe82c8f6..e3021348 100644 --- a/reflectable/lib/src/builder_implementation.dart +++ b/reflectable/lib/src/builder_implementation.dart @@ -152,7 +152,7 @@ class _ReflectionWorld { await reflector._generateCode(this, importCollector, typedefs); if (typedefs.isNotEmpty) { for (DartType dartType in typedefs.keys) { - String body = await reflector._typeCodeOfTypeArgument( + String body = await _ReflectorDomain._typeCodeOfTypeArgument( dartType, importCollector, typeVariablesInScope, typedefs, useNameOfGenericFunctionType: false); typedefsCode += @@ -1918,7 +1918,7 @@ class _ReflectorDomain { /// Returns true iff the given [type] is not and does not contain a free /// type variable. [typeVariablesInScope] gives the names of type variables /// which are in scope (and hence not free in the relevant context). - bool _hasNoFreeTypeVariables(DartType type, + static bool _hasNoFreeTypeVariables(DartType type, [Set? typeVariablesInScope]) { if (type is TypeParameterType && (typeVariablesInScope == null || @@ -1951,7 +1951,7 @@ class _ReflectorDomain { /// is used to decide whether the output should be a simple `typedef` /// name or a fully spelled-out generic function type (and it has no /// effect when [dartType] is not a generic function type). - Future _typeCodeOfTypeArgument( + static Future _typeCodeOfTypeArgument( DartType dartType, _ImportCollector importCollector, Set typeVariablesInScope, @@ -1963,8 +1963,7 @@ class _ReflectorDomain { 'Attempt to generate code for an ' 'unsupported kind of type: $dartType (${dartType.runtimeType}). ' 'Generating `dynamic`.', - element, - _resolver)); + element)); return 'dynamic'; } @@ -5034,7 +5033,20 @@ Future _extractMetadataCode(Element element, Resolver resolver, if (_isPrivateName(name)) { await _severe('Cannot access private name $name', element); } - metadataParts.add('const $prefix$name($arguments)'); + var typeArguments = annotationNode.typeArguments; + if (typeArguments != null) { + var typedefs = {}; + var typeVariablesInScope = {}; // None at top level. + + final typeArgumentsList = typeArguments.arguments.map((annotation) => annotation.type); + final typeArgumentsString = (await Future.wait(typeArgumentsList.map((type) async => + type != null ? await _ReflectorDomain._typeCodeOfTypeArgument(type, importCollector, typeVariablesInScope, typedefs, useNameOfGenericFunctionType: false) : 'dynamic' + ))).join(', '); + // print(typeArgumentsString); + metadataParts.add('const $prefix$name<$typeArgumentsString>($arguments)'); + } else { + metadataParts.add('const $prefix$name($arguments)'); + } } else { // A field reference. if (_isPrivateName(annotationNode.name.name)) { @@ -5641,7 +5653,7 @@ Future _fine(String message, /// Returns a string containing the given [message] and identifying the /// associated source code location as the location of the given [target]. Future _formatDiagnosticMessage( - String message, Element? target, Resolver resolver) async { + String message, Element? target, [Resolver? resolver]) async { Source? source = target?.source; if (source == null) return message; String locationString = ''; @@ -5651,7 +5663,8 @@ Future _formatDiagnosticMessage( var targetLibrary = target?.library; if (targetLibrary != null && nameOffset != null && - !_isPlatformLibrary(targetLibrary)) { + !_isPlatformLibrary(targetLibrary) && + resolver != null) { final resolvedLibrary = await _getResolvedLibrary(targetLibrary, resolver); if (resolvedLibrary != null) { final targetDeclaration = resolvedLibrary.getElementDeclaration(target!); diff --git a/test_reflectable/test/generic_metadata_test.dart b/test_reflectable/test/generic_metadata_test.dart new file mode 100644 index 00000000..11ed1ab9 --- /dev/null +++ b/test_reflectable/test/generic_metadata_test.dart @@ -0,0 +1,94 @@ +@c +library test_reflectable.test.generic_metadata_test; + +import 'package:reflectable/reflectable.dart'; +import 'package:test/test.dart'; +import 'generic_metadata_test.reflectable.dart'; + +class MyReflectable extends Reflectable { + const MyReflectable() + : super(metadataCapability, instanceInvokeCapability, + staticInvokeCapability, declarationsCapability, libraryCapability); +} + +const myReflectable = MyReflectable(); + +const b = 0; + +const c = [ + Bar({'a': 14}) +]; + +const d = true; + +class K { + static const p = 2; +} + +@myReflectable +@Bar({ + b: deprecated, + c: Deprecated('tomorrow'), + 1 + 2: (d ? 3 : 4), + identical(1, 2): 's', + K.p: 6 +}) +@Bar({}) +@Bar.namedConstructor({}) +@c +class Foo { + @Bar({}) + @Bar>({}) + @Bar.namedConstructor({}) + @Bar.namedConstructor({}) + @c + void foo() {} + var x = 10; +} + +class Bar { + final Map m; + const Bar(this.m); + const Bar.namedConstructor(this.m); + + @override + String toString() => 'Bar<$X>($m)'; +} + +void main() { + initializeReflectable(); + + test('metadata on class', () { + expect(myReflectable.reflectType(Foo).metadata, const [ + MyReflectable(), + Bar({ + b: deprecated, + c: Deprecated('tomorrow'), + 3: 3, + false: 's', + 2: 6, + }), + [ + Bar({'a': 14}) + ], + ]); + + var fooMirror = myReflectable.reflectType(Foo) as ClassMirror; + expect(fooMirror.declarations['foo']!.metadata, const [ + Bar({}), + Bar>>({}), + Bar({}), + Bar({}), + [ + Bar({'a': 14}) + ], + ]); + + // The synthetic accessors do not have metadata. + expect(fooMirror.instanceMembers['x']!.metadata, []); + expect(fooMirror.instanceMembers['x=']!.metadata, []); + + // Test metadata on libraries + expect(myReflectable.reflectType(Foo).owner!.metadata, [c]); + }); +} \ No newline at end of file