Skip to content

Commit b6bd805

Browse files
authored
Deprecate TypeChecker.fromRuntime, add TypeChecker.typeNamed as recommended replacement. (#776)
* Deprecated TypeChecker.fromRuntime, add TypeChecker.typeNamed as recommended replacement. * Address review comments.
1 parent 25f62a8 commit b6bd805

File tree

7 files changed

+160
-10
lines changed

7 files changed

+160
-10
lines changed

source_gen/CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
## 3.0.1-wip
2-
1+
## 3.1.0-wip
2+
3+
- Prepare to stop using `dart:mirrors`: deprecate `TypeChecker.fromRuntime`.
4+
It will be removed in version `4.0.0`. Add `TypeChecker.typeNamed` which is
5+
the recommended replacement.
6+
- Prepare to stop using `dart:mirrors`: add `inPackage` and `inSdk` parameters
7+
to `GenerateForAnnotation`. These will start being used in version `4.0.0`
8+
when it switches to `TypeChecker.typeNamed`.
39
- Allow `analyzer: '>=7.4.0 <9.0.0'`.
410

511
## 3.0.0

source_gen/lib/src/generator_for_annotation.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,32 @@ import 'type_checker.dart';
4343
abstract class GeneratorForAnnotation<T> extends Generator {
4444
final bool throwOnUnresolved;
4545

46+
/// Annotation package for [TypeChecker.typeNamed].
47+
///
48+
/// Currently unused, will be used from `source_gen` 4.0.0.
49+
final String? inPackage;
50+
51+
/// Annotation package type for [TypeChecker.typeNamed].
52+
///
53+
/// Currently unused, will be used from `source_gen` 4.0.0.
54+
final bool? inSdk;
55+
4656
/// By default, this generator will throw if it encounters unresolved
4757
/// annotations. You can override this by setting [throwOnUnresolved] to
4858
/// `false`.
49-
const GeneratorForAnnotation({this.throwOnUnresolved = true});
59+
///
60+
/// With `source_gen` 4.0.0 this class will stop using mirrors for matching
61+
/// annotations and will fall back to comparing the name of `T`. Pass
62+
/// [inPackage] and [inSdk] to tighten the check; see [TypeChecker.typeNamed].
63+
/// To use a custom annotation check, override [typeChecker].
64+
const GeneratorForAnnotation({
65+
this.throwOnUnresolved = true,
66+
this.inPackage,
67+
this.inSdk,
68+
});
5069

70+
// This will switch to `typeNamed` in 4.0.0.
71+
// ignore: deprecated_member_use_from_same_package
5172
TypeChecker get typeChecker => TypeChecker.fromRuntime(T);
5273

5374
@override

source_gen/lib/src/type_checker.dart

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,27 @@ abstract class TypeChecker {
3535
/// Create a new [TypeChecker] backed by a runtime [type].
3636
///
3737
/// This implementation uses `dart:mirrors` (runtime reflection).
38+
@Deprecated('''
39+
Will be removed in 4.0.0 to drop `dart:mirrors` dependency.
40+
41+
Recommended: replace `fromRuntime(Foo)` with
42+
`typeNamed(Foo, inPackage: 'foo_package')`. This is a slighly weaker check than
43+
`fromRuntime(Foo)` as it matches any annotation named `Foo` in
44+
`package:foo_package`.
45+
46+
If you need an exact match, use `fromUrl`.''')
3847
const factory TypeChecker.fromRuntime(Type type) = _MirrorTypeChecker;
3948

49+
/// Create a new [TypeChecker] for types matching the name of [type].
50+
///
51+
/// Optionally, also pass [inPackage] to restrict to a specific package by
52+
/// name. Set [inSdk] if it's a `dart` package.
53+
const factory TypeChecker.typeNamed(
54+
Type type, {
55+
String? inPackage,
56+
bool? inSdk,
57+
}) = _NameTypeChecker;
58+
4059
/// Create a new [TypeChecker] backed by a static [type].
4160
const factory TypeChecker.fromStatic(DartType type) = _LibraryTypeChecker;
4261

@@ -269,6 +288,38 @@ class _MirrorTypeChecker extends TypeChecker {
269288
String toString() => _computed.toString();
270289
}
271290

291+
// Checks a runtime type name and optional package against a static type.
292+
class _NameTypeChecker extends TypeChecker {
293+
final Type _type;
294+
295+
final String? _inPackage;
296+
final bool _inSdk;
297+
298+
const _NameTypeChecker(this._type, {String? inPackage, bool? inSdk})
299+
: _inPackage = inPackage,
300+
_inSdk = inSdk ?? false,
301+
super._();
302+
303+
String get _typeName {
304+
final result = _type.toString();
305+
return result.contains('<')
306+
? result.substring(0, result.indexOf('<'))
307+
: result;
308+
}
309+
310+
@override
311+
bool isExactly(Element2 element) {
312+
final uri = element.library2!.uri;
313+
return element.name3 == _typeName &&
314+
(_inPackage == null ||
315+
(((uri.scheme == 'dart') == _inSdk) &&
316+
uri.pathSegments.first == _inPackage));
317+
}
318+
319+
@override
320+
String toString() => _inPackage == null ? '$_type' : '$_inPackage#$_type';
321+
}
322+
272323
// Checks a runtime type against an Uri and Symbol.
273324
class _UriTypeChecker extends TypeChecker {
274325
final String _url;

source_gen/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: source_gen
2-
version: 3.0.1-wip
2+
version: 3.1.0-wip
33
description: >-
44
Source code generation builders and utilities for the Dart build system
55
repository: https://github.com/dart-lang/source_gen/tree/master/source_gen

source_gen/test/constants_test.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,11 @@ void main() {
199199

200200
test('should compare using TypeChecker', () {
201201
final $deprecated = constants[8];
202-
const check = TypeChecker.fromRuntime(Deprecated);
202+
const check = TypeChecker.typeNamed(
203+
Deprecated,
204+
inPackage: 'core',
205+
inSdk: true,
206+
);
203207
expect($deprecated.instanceOf(check), isTrue, reason: '$deprecated');
204208
});
205209
});

source_gen/test/external_only_type_checker_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ void main() {
7676
'TypeChecker.forRuntime',
7777
() {
7878
commonTests(
79-
checkNonPublic: () => const TypeChecker.fromRuntime(NonPublic),
79+
checkNonPublic:
80+
() =>
81+
const TypeChecker.typeNamed(NonPublic, inPackage: 'source_gen'),
8082
);
8183
},
8284
onPlatform: const {

source_gen/test/type_checker_test.dart

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -335,15 +335,77 @@ void main() {
335335

336336
group('TypeChecker.forRuntime', () {
337337
commonTests(
338+
// ignore: deprecated_member_use_from_same_package
338339
checkIterable: () => const TypeChecker.fromRuntime(Iterable),
340+
// ignore: deprecated_member_use_from_same_package
339341
checkEnum: () => const TypeChecker.fromRuntime(Enum),
342+
// ignore: deprecated_member_use_from_same_package
340343
checkEnumMixin: () => const TypeChecker.fromRuntime(MyEnumMixin),
344+
// ignore: deprecated_member_use_from_same_package
341345
checkMap: () => const TypeChecker.fromRuntime(Map),
346+
// ignore: deprecated_member_use_from_same_package
342347
checkMapMixin: () => const TypeChecker.fromRuntime(MyMapMixin),
348+
// ignore: deprecated_member_use_from_same_package
343349
checkHashMap: () => const TypeChecker.fromRuntime(HashMap),
350+
// ignore: deprecated_member_use_from_same_package
344351
checkGenerator: () => const TypeChecker.fromRuntime(Generator),
352+
353+
checkGeneratorForAnnotation:
354+
// ignore: deprecated_member_use_from_same_packages
355+
() => const TypeChecker.typeNamed(
356+
GeneratorForAnnotation,
357+
inPackage: 'source_gen',
358+
),
359+
);
360+
});
361+
362+
group('TypeChecker.typeNamed without package', () {
363+
commonTests(
364+
checkIterable: () => const TypeChecker.typeNamed(Iterable),
365+
checkEnum: () => const TypeChecker.typeNamed(Enum),
366+
checkEnumMixin: () => const TypeChecker.typeNamed(MyEnumMixin),
367+
checkMap: () => const TypeChecker.typeNamed(Map),
368+
checkMapMixin: () => const TypeChecker.typeNamed(MyMapMixin),
369+
checkHashMap: () => const TypeChecker.typeNamed(HashMap),
370+
checkGenerator: () => const TypeChecker.typeNamed(Generator),
345371
checkGeneratorForAnnotation:
346-
() => const TypeChecker.fromRuntime(GeneratorForAnnotation),
372+
() => const TypeChecker.typeNamed(GeneratorForAnnotation),
373+
);
374+
});
375+
376+
group('TypeChecker.typeNamed with package', () {
377+
commonTests(
378+
checkIterable:
379+
() => const TypeChecker.typeNamed(
380+
Iterable,
381+
inPackage: 'core',
382+
inSdk: true,
383+
),
384+
checkEnum:
385+
() =>
386+
const TypeChecker.typeNamed(Enum, inPackage: 'core', inSdk: true),
387+
checkEnumMixin:
388+
() =>
389+
const TypeChecker.typeNamed(MyEnumMixin, inPackage: 'source_gen'),
390+
checkMap:
391+
() =>
392+
const TypeChecker.typeNamed(Map, inPackage: 'core', inSdk: true),
393+
checkMapMixin:
394+
() =>
395+
const TypeChecker.typeNamed(MyMapMixin, inPackage: 'source_gen'),
396+
checkHashMap:
397+
() => const TypeChecker.typeNamed(
398+
HashMap,
399+
inPackage: 'collection',
400+
inSdk: true,
401+
),
402+
checkGenerator:
403+
() => const TypeChecker.typeNamed(Generator, inPackage: 'source_gen'),
404+
checkGeneratorForAnnotation:
405+
() => const TypeChecker.typeNamed(
406+
GeneratorForAnnotation,
407+
inPackage: 'source_gen',
408+
),
347409
);
348410
});
349411

@@ -393,7 +455,11 @@ void main() {
393455
class X {}
394456
''', (resolver) async => (await resolver.findLibraryByName('_test'))!);
395457
final classX = library.getClass2('X')!;
396-
const $deprecated = TypeChecker.fromRuntime(Deprecated);
458+
const $deprecated = TypeChecker.typeNamed(
459+
Deprecated,
460+
inPackage: 'core',
461+
inSdk: true,
462+
);
397463

398464
expect(
399465
() => $deprecated.annotationsOf(classX),
@@ -412,8 +478,8 @@ void main() {
412478

413479
test('should check multiple checkers', () {
414480
const listOrMap = TypeChecker.any([
415-
TypeChecker.fromRuntime(List),
416-
TypeChecker.fromRuntime(Map),
481+
TypeChecker.typeNamed(List, inPackage: 'core', inSdk: true),
482+
TypeChecker.typeNamed(Map, inPackage: 'core', inSdk: true),
417483
]);
418484
expect(listOrMap.isExactlyType(staticMap), isTrue);
419485
});

0 commit comments

Comments
 (0)