From 6a844481aa2345c3e343b39ac39b6197e796d287 Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 6 Jul 2017 09:53:10 -0700 Subject: [PATCH] Add TypeChecker.any (#202) * Add annotationsOfExact. * Add type checks. * Update CHANGELOG. * Add TypeChecker.any. * CHANGELOG. * . --- CHANGELOG.md | 3 +++ lib/src/type_checker.dart | 28 +++++++++++++++++++++++++--- pubspec.yaml | 2 +- test/type_checker_test.dart | 10 +++++++++- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a22470f..e574e3b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ * `TypeChecker#annotations...`-methods now throw a `StateError` if one or more annotations on an element are not resolvable. This is usually a sign of a mispelling, missing import, or missing dependency. + +* Added `TypeChecker.any`, which delegates to multiple other `TypeChecker` + implementations when making a type check. ## 0.5.10+1 diff --git a/lib/src/type_checker.dart b/lib/src/type_checker.dart index 087c4807..e1d4a8e5 100644 --- a/lib/src/type_checker.dart +++ b/lib/src/type_checker.dart @@ -14,6 +14,20 @@ import 'utils.dart'; abstract class TypeChecker { const TypeChecker._(); + /// Creates a new [TypeChecker] that delegates to other [checkers]. + /// + /// This implementation will return `true` for type checks if _any_ of the + /// provided type checkers return true, which is useful for deprecating an + /// API: + /// ```dart + /// const $Foo = const TypeChecker.fromRuntime(Foo); + /// const $Bar = const TypeChecker.fromRuntime(Bar); + /// + /// // Used until $Foo is deleted. + /// const $FooOrBar = const TypeChecker.forAny(const [$Foo, $Bar]); + /// ``` + const factory TypeChecker.any(Iterable checkers) = _AnyChecker; + /// Create a new [TypeChecker] backed by a runtime [type]. /// /// This implementation uses `dart:mirrors` (runtime reflection). @@ -71,14 +85,13 @@ abstract class TypeChecker { /// Returns annotating constants on [element] assignable to this type. Iterable annotationsOf(Element element) => element.metadata .map(_checkedConstantValue) - .where((a) => isAssignableFromType(a.type)); + .where((a) => a?.type != null && isAssignableFromType(a.type)); /// Returns annotating constants on [element] of exactly this type. Iterable annotationsOfExact(Element element) => element.metadata .map(_checkedConstantValue) - .where((a) => a?.type != null && isAssignableFromType(a.type)); + .where((a) => a?.type != null && isExactlyType(a.type)); - /// Returns `true` if the type of [element] can be assigned to this type. /// Returns `true` if the type of [element] can be assigned to this type. bool isAssignableFrom(Element element) => isExactly(element) || _getAllSupertypes(element).any(isExactlyType); @@ -210,3 +223,12 @@ class _UriTypeChecker extends TypeChecker { @override String toString() => '${uri}'; } + +class _AnyChecker extends TypeChecker { + final Iterable _checkers; + + const _AnyChecker(this._checkers) : super._(); + + @override + bool isExactly(Element element) => _checkers.any((c) => c.isExactly(element)); +} diff --git a/pubspec.yaml b/pubspec.yaml index 4e73c121..3a252fbd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: source_gen -version: 0.6.0-dev +version: 0.6.0 author: Dart Team description: Automated source code generation for Dart. homepage: https://github.com/dart-lang/source_gen diff --git a/test/type_checker_test.dart b/test/type_checker_test.dart index ae127b6c..da5bd0d0 100644 --- a/test/type_checker_test.dart +++ b/test/type_checker_test.dart @@ -180,7 +180,7 @@ void main() { 'package:source_gen/src/generator_for_annotation.dart#GeneratorForAnnotation')); }); - test('should gracefully when something is not resolvable', () async { + test('should fail gracefully when something is not resolvable', () async { final resolver = await resolveSource(r''' library _test; @@ -194,4 +194,12 @@ void main() { expect(() => $deprecated.annotationsOf(classX), throwsStateError, reason: 'deprecated was spelled wrong; no annotation can be resolved'); }); + + test('should check multiple checkers', () { + final listOrMap = const TypeChecker.any(const [ + const TypeChecker.fromRuntime(List), + const TypeChecker.fromRuntime(Map), + ]); + expect(listOrMap.isExactlyType(staticMap), isTrue); + }); }