22// for details. All rights reserved. Use of this source code is governed by a 
33// BSD-style license that can be found in the LICENSE file. 
44
5- import  'dart:mirrors' ;
5+ import  'dart:mirrors'   hide  SourceLocation ;
66
77import  'package:analyzer/dart/constant/value.dart' ;
88import  'package:analyzer/dart/element/element.dart' ;
99import  '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' ;
1014
1115import  'utils.dart' ;
1216
@@ -74,7 +78,8 @@ abstract class TypeChecker {
7478
7579  /// Returns the first constant annotating [element]  that is exactly this type. 
7680  /// 
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` ). 
7883DartObject  firstAnnotationOfExact (Element  element, {bool  throwOnUnresolved}) {
7984    if  (element.metadata.isEmpty) {
8085      return  null ;
@@ -86,42 +91,70 @@ abstract class TypeChecker {
8691
8792  /// Returns if a constant annotating [element]  is exactly this type. 
8893  /// 
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` ). 
9096bool  hasAnnotationOfExact (Element  element, {bool  throwOnUnresolved}) => 
9197      firstAnnotationOfExact (element, throwOnUnresolved:  throwOnUnresolved) != 
9298      null ;
9399
94-   DartObject  _computeConstantValue (ElementAnnotation  annotation,
95-       {bool  throwOnUnresolved}) {
100+   DartObject  _computeConstantValue (
101+     Element  element,
102+     int  annotationIndex, {
103+     bool  throwOnUnresolved,
104+   }) {
96105    throwOnUnresolved ?? =  true ;
106+     final  annotation =  element.metadata[annotationIndex];
97107    final  result =  annotation.computeConstantValue ();
98108    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);
102110    }
103111    return  result;
104112  }
105113
106114  /// Returns annotating constants on [element]  assignable to this type. 
107115  /// 
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+   }
115144
116145  /// Returns annotating constants on [element]  of exactly this type. 
117146  /// 
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+       );
125158
126159  /// Returns `true`  if the type of [element]  can be assigned to this type. 
127160bool  isAssignableFrom (Element  element) => 
@@ -242,3 +275,52 @@ class _AnyChecker extends TypeChecker {
242275  @override 
243276  bool  isExactly (Element  element) =>  _checkers.any ((c) =>  c.isExactly (element));
244277}
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