From 97762fb9acac4129d56593a92e7389fec517a5ce Mon Sep 17 00:00:00 2001 From: Matan Lurey Date: Thu, 6 Jul 2017 09:29:23 -0700 Subject: [PATCH] Nicer failures when an annotation is not resolvable (dart-lang/source_gen#201) * Add annotationsOfExact. * Add type checks. * Update CHANGELOG. --- source_gen/source_gen/CHANGELOG.md | 4 ++++ source_gen/source_gen/lib/src/type_checker.dart | 17 ++++++++++++++--- .../source_gen/test/type_checker_test.dart | 15 +++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/source_gen/source_gen/CHANGELOG.md b/source_gen/source_gen/CHANGELOG.md index bd7803aaf..2a22470f5 100644 --- a/source_gen/source_gen/CHANGELOG.md +++ b/source_gen/source_gen/CHANGELOG.md @@ -5,6 +5,10 @@ result we've added `#annotationsOfExact|firstAnnotationOfExact` which has the old behavior for precise checks. +* `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. + ## 0.5.10+1 * Update minimum `analyzer` package to `0.29.10`. diff --git a/source_gen/source_gen/lib/src/type_checker.dart b/source_gen/source_gen/lib/src/type_checker.dart index 5e91470e5..087c4807c 100644 --- a/source_gen/source_gen/lib/src/type_checker.dart +++ b/source_gen/source_gen/lib/src/type_checker.dart @@ -58,16 +58,27 @@ abstract class TypeChecker { return results.isEmpty ? null : results.first; } + DartObject _checkedConstantValue(ElementAnnotation annotation) { + final result = annotation.computeConstantValue(); + if (result == null) { + throw new StateError( + 'Could not resolve $annotation. An import or dependency may be ' + 'missing or invalid.'); + } + return result; + } + /// Returns annotating constants on [element] assignable to this type. Iterable annotationsOf(Element element) => element.metadata - .map((a) => a.computeConstantValue()) + .map(_checkedConstantValue) .where((a) => isAssignableFromType(a.type)); /// Returns annotating constants on [element] of exactly this type. Iterable annotationsOfExact(Element element) => element.metadata - .map((a) => a.computeConstantValue()) - .where((a) => isExactlyType(a.type)); + .map(_checkedConstantValue) + .where((a) => a?.type != null && isAssignableFromType(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); diff --git a/source_gen/source_gen/test/type_checker_test.dart b/source_gen/source_gen/test/type_checker_test.dart index d5e6d8bc8..ae127b6c6 100644 --- a/source_gen/source_gen/test/type_checker_test.dart +++ b/source_gen/source_gen/test/type_checker_test.dart @@ -179,4 +179,19 @@ void main() { checkGeneratorForAnnotation: () => const TypeChecker.fromUrl( 'package:source_gen/src/generator_for_annotation.dart#GeneratorForAnnotation')); }); + + test('should gracefully when something is not resolvable', () async { + final resolver = await resolveSource(r''' + library _test; + + @depracated // Intentionally mispelled. + class X {} + '''); + final lib = resolver.getLibraryByName('_test'); + final classX = lib.getType('X'); + final $deprecated = const TypeChecker.fromRuntime(Deprecated); + + expect(() => $deprecated.annotationsOf(classX), throwsStateError, + reason: 'deprecated was spelled wrong; no annotation can be resolved'); + }); }