2
2
// for details. All rights reserved. Use of this source code is governed by a
3
3
// BSD-style license that can be found in the LICENSE file.
4
4
5
- import 'dart:mirrors' ;
5
+ import 'dart:mirrors' hide SourceLocation ;
6
6
7
7
import 'package:analyzer/dart/constant/value.dart' ;
8
8
import 'package:analyzer/dart/element/element.dart' ;
9
9
import 'package:analyzer/dart/element/type.dart' ;
10
+ // TODO(https://github.com/dart-lang/sdk/issues/32454):
11
+ // ignore: implementation_imports
12
+ import 'package:analyzer/src/dart/element/element.dart' ;
13
+ import 'package:source_span/source_span.dart' ;
10
14
11
15
import 'utils.dart' ;
12
16
@@ -74,7 +78,8 @@ abstract class TypeChecker {
74
78
75
79
/// Returns the first constant annotating [element] that is exactly this type.
76
80
///
77
- /// Throws on unresolved annotations unless [throwOnUnresolved] is `false` .
81
+ /// Throws [UnresolvedAnnotationException] on unresolved annotations unless
82
+ /// [throwOnUnresolved] is explicitly set to `false` (default is `true` ).
78
83
DartObject firstAnnotationOfExact (Element element, {bool throwOnUnresolved}) {
79
84
if (element.metadata.isEmpty) {
80
85
return null ;
@@ -86,42 +91,70 @@ abstract class TypeChecker {
86
91
87
92
/// Returns if a constant annotating [element] is exactly this type.
88
93
///
89
- /// Throws on unresolved annotations unless [throwOnUnresolved] is `false` .
94
+ /// Throws [UnresolvedAnnotationException] on unresolved annotations unless
95
+ /// [throwOnUnresolved] is explicitly set to `false` (default is `true` ).
90
96
bool hasAnnotationOfExact (Element element, {bool throwOnUnresolved}) =>
91
97
firstAnnotationOfExact (element, throwOnUnresolved: throwOnUnresolved) !=
92
98
null ;
93
99
94
- DartObject _computeConstantValue (ElementAnnotation annotation,
95
- {bool throwOnUnresolved}) {
100
+ DartObject _computeConstantValue (
101
+ Element element,
102
+ int annotationIndex, {
103
+ bool throwOnUnresolved,
104
+ }) {
96
105
throwOnUnresolved ?? = true ;
106
+ final annotation = element.metadata[annotationIndex];
97
107
final result = annotation.computeConstantValue ();
98
108
if (result == null && throwOnUnresolved) {
99
- throw new StateError (
100
- 'Could not resolve $annotation . An import or dependency may be '
101
- 'missing or invalid.' );
109
+ throw new UnresolvedAnnotationException ._from (element, annotationIndex);
102
110
}
103
111
return result;
104
112
}
105
113
106
114
/// Returns annotating constants on [element] assignable to this type.
107
115
///
108
- /// Throws on unresolved annotations unless [throwOnUnresolved] is `false` .
109
- Iterable <DartObject > annotationsOf (Element element,
110
- {bool throwOnUnresolved}) =>
111
- element.metadata
112
- .map ((annotation) => _computeConstantValue (annotation,
113
- throwOnUnresolved: throwOnUnresolved))
114
- .where ((a) => a? .type != null && isAssignableFromType (a.type));
116
+ /// Throws [UnresolvedAnnotationException] on unresolved annotations unless
117
+ /// [throwOnUnresolved] is explicitly set to `false` (default is `true` ).
118
+ Iterable <DartObject > annotationsOf (
119
+ Element element, {
120
+ bool throwOnUnresolved,
121
+ }) =>
122
+ _annotationsWhere (
123
+ element,
124
+ isAssignableFromType,
125
+ throwOnUnresolved: throwOnUnresolved,
126
+ );
127
+
128
+ Iterable <DartObject > _annotationsWhere (
129
+ Element element,
130
+ bool Function (DartType ) predicate, {
131
+ bool throwOnUnresolved,
132
+ }) sync * {
133
+ for (var i = 0 ; i < element.metadata.length; i++ ) {
134
+ final value = _computeConstantValue (
135
+ element,
136
+ i,
137
+ throwOnUnresolved: throwOnUnresolved,
138
+ );
139
+ if (value? .type != null && predicate (value.type)) {
140
+ yield value;
141
+ }
142
+ }
143
+ }
115
144
116
145
/// Returns annotating constants on [element] of exactly this type.
117
146
///
118
- /// Throws on unresolved annotations unless [throwOnUnresolved] is `false` .
119
- Iterable <DartObject > annotationsOfExact (Element element,
120
- {bool throwOnUnresolved}) =>
121
- element.metadata
122
- .map ((annotation) => _computeConstantValue (annotation,
123
- throwOnUnresolved: throwOnUnresolved))
124
- .where ((a) => a? .type != null && isExactlyType (a.type));
147
+ /// Throws [UnresolvedAnnotationException] on unresolved annotations unless
148
+ /// [throwOnUnresolved] is explicitly set to `false` (default is `true` ).
149
+ Iterable <DartObject > annotationsOfExact (
150
+ Element element, {
151
+ bool throwOnUnresolved,
152
+ }) =>
153
+ _annotationsWhere (
154
+ element,
155
+ isExactlyType,
156
+ throwOnUnresolved: throwOnUnresolved,
157
+ );
125
158
126
159
/// Returns `true` if the type of [element] can be assigned to this type.
127
160
bool isAssignableFrom (Element element) =>
@@ -242,3 +275,52 @@ class _AnyChecker extends TypeChecker {
242
275
@override
243
276
bool isExactly (Element element) => _checkers.any ((c) => c.isExactly (element));
244
277
}
278
+
279
+ /// Exception thrown when [TypeChecker] fails to resolve a metadata annotation.
280
+ ///
281
+ /// Methods such as [TypeChecker.firstAnnotationOf] may throw this exception
282
+ /// when one or more annotations are not resolvable. This is usually a sign that
283
+ /// something was misspelled, an import is missing, or a dependency was not
284
+ /// defined (for build systems such as Bazel).
285
+ class UnresolvedAnnotationException implements Exception {
286
+ /// Element that was annotated with something we could not resolve.
287
+ final Element annotatedElement;
288
+
289
+ /// Source span of the annotation that was not resolved.
290
+ final SourceSpan annotationSource;
291
+
292
+ // TODO: Remove internal API once ElementAnnotation has source information.
293
+ // https://github.com/dart-lang/sdk/issues/32454
294
+ static SourceSpan _getSourceSpanFrom (ElementAnnotation annotation) {
295
+ final internals = annotation as ElementAnnotationImpl ;
296
+ final astNode = internals.annotationAst;
297
+ final contents = annotation.source.contents.data;
298
+ final start = astNode.offset;
299
+ final end = start + astNode.length;
300
+ return new SourceSpan (
301
+ new SourceLocation (start, sourceUrl: annotation.source.uri),
302
+ new SourceLocation (end, sourceUrl: annotation.source.uri),
303
+ contents.substring (start, end),
304
+ );
305
+ }
306
+
307
+ /// Creates an exception from an annotation ([annotationIndex] ) that was not
308
+ /// resolvable while traversing [Element.metadata] on [annotatedElement] .
309
+ factory UnresolvedAnnotationException ._from (
310
+ Element annotatedElement,
311
+ int annotationIndex,
312
+ ) {
313
+ final annotation = annotatedElement.metadata[annotationIndex];
314
+ final sourceSpan = _getSourceSpanFrom (annotation);
315
+ return new UnresolvedAnnotationException ._(annotatedElement, sourceSpan);
316
+ }
317
+
318
+ const UnresolvedAnnotationException ._(
319
+ this .annotatedElement,
320
+ this .annotationSource,
321
+ );
322
+
323
+ @override
324
+ String toString () => annotationSource
325
+ .message ('Could not resolve annotation for $annotatedElement ' );
326
+ }
0 commit comments