@@ -25,14 +25,14 @@ public AnnotationResolver(PipelineResult pipelineResult)
2525
2626 /// <summary>
2727 /// Attempt to retrieve the <paramref name="symbol"/>'s value for the annotation <paramref name="id"/>. This will check any
28- /// annotation providers that were passed to the constructor , and the internal per-symbol annotation storage.
28+ /// annotation providers that were passed to the resolver , and the internal per-symbol annotation storage.
2929 /// </summary>
3030 /// <typeparam name="TValue">
3131 /// The expected type of the annotation value. If the type does not match, a <see cref="AnnotationTypeException"/> will be thrown.
3232 /// If the annotation allows multiple types for its values, and a type parameter cannot be determined statically,
33- /// use <see cref="TryGetAnnotation (CliSymbol, AnnotationId, out object?)"/> to access the annotation value without checking its type.
33+ /// use <see cref="TryGet (CliSymbol, AnnotationId, out object?)"/> to access the annotation value without checking its type.
3434 /// </typeparam>
35- /// <param name="symbol">The symbol the value is attached to </param>
35+ /// <param name="symbol">The symbol that is annotated </param>
3636 /// <param name="id">
3737 /// The identifier for the annotation value to be retrieved.
3838 /// For example, the annotation identifier for the help description is <see cref="HelpAnnotations.Description">.
@@ -69,9 +69,9 @@ public bool TryGet<TValue>(CliSymbol symbol, AnnotationId annotationId, [NotNull
6969
7070 /// <summary>
7171 /// Attempt to retrieve the <paramref name="symbol"/>'s value for the annotation <paramref name="id"/>. This will check any
72- /// annotation providers that were passed to the constructor , and the internal per-symbol annotation storage.
72+ /// annotation providers that were passed to the resolver , and the internal per-symbol annotation storage.
7373 /// </summary>
74- /// <param name="symbol">The symbol the value is attached to </param>
74+ /// <param name="symbol">The symbol that is annotated </param>
7575 /// <param name="id">
7676 /// The identifier for the annotation value to be retrieved.
7777 /// For example, the annotation identifier for the help description is <see cref="HelpAnnotations.Description">.
@@ -90,6 +90,13 @@ public bool TryGet<TValue>(CliSymbol symbol, AnnotationId annotationId, [NotNull
9090 /// </remarks>
9191 public bool TryGet ( CliSymbol symbol , AnnotationId annotationId , [ NotNullWhen ( true ) ] out object ? value )
9292 {
93+ // a value set directly on the symbol takes precedence over values returned by providers.
94+ if ( symbol . TryGetAnnotation ( annotationId , out value ) )
95+ {
96+ return true ;
97+ }
98+
99+ // Providers are given precedence in the order they were provided to the resolver.
93100 foreach ( var provider in providers )
94101 {
95102 if ( provider . TryGet ( symbol , annotationId , context , out value ) )
@@ -98,7 +105,7 @@ public bool TryGet(CliSymbol symbol, AnnotationId annotationId, [NotNullWhen(tru
98105 }
99106 }
100107
101- return symbol . TryGetAnnotation ( annotationId , out value ) ;
108+ return false ;
102109 }
103110
104111 /// <summary>
@@ -128,4 +135,60 @@ public bool TryGet(CliSymbol symbol, AnnotationId annotationId, [NotNullWhen(tru
128135
129136 return default ;
130137 }
138+
139+ /// <summary>
140+ /// For an annotation <paramref name="id"/> that permits multiple values, enumerate the values associated with
141+ /// the <paramref name="symbol"/>. If the annotation is not set, an empty enumerable will be returned. This will
142+ /// check any annotation providers that were passed to the resolver, and the internal per-symbol annotation storage.
143+ /// </summary>
144+ /// <typeparam name="TValue">
145+ /// The expected type of the annotation value. If the type does not match, a <see cref="AnnotationCollectionTypeException"/>
146+ /// will be thrown.
147+ /// </typeparam>
148+ /// <param name="symbol">The symbol that is annotated</param>
149+ /// <param name="id">
150+ /// The identifier for the annotation value to be retrieved.
151+ /// For example, the annotation identifier for the help description is <see cref="HelpAnnotations.Description">.
152+ /// </param>
153+ /// <param name="value">An out parameter to contain the result</param>
154+ /// <returns>True if successful</returns>
155+ /// <remarks>
156+ /// This is intended for use by developers defining custom annotation IDs. Anyone defining an annotation
157+ /// ID should also define an accessor extension method on <see cref="AnnotationResolver"/> extension method
158+ /// on <see cref="CliSymbol"/> that subsystem authors can use to access the annotation value, such as
159+ /// <see cref="HelpAnnotationExtensions.GetDescription{TSymbol}(AnnotationResolver, TSymbol)"/>.
160+ /// </remarks>
161+ public IEnumerable < TValue > Enumerate < TValue > ( CliSymbol symbol , AnnotationId annotationId )
162+ {
163+ // Values added directly on the symbol take precedence over values returned by providers,
164+ // so they are returned first.
165+ // NOTE: EnumerateAnnotations returns these in the reverse order they were added, which means that
166+ // callers that take the first value of a given subtype will get the most recently added value of
167+ // that subtype that the CLI author added to the symbol.
168+ foreach ( var value in symbol . EnumerateAnnotations < TValue > ( annotationId ) )
169+ {
170+ yield return value ;
171+ }
172+
173+ // Providers are given precedence in the order they were provided to the resolver.
174+ foreach ( var provider in providers )
175+ {
176+ if ( ! provider . TryGet ( symbol , annotationId , context , out object ? rawValue ) )
177+ {
178+ continue ;
179+ }
180+
181+ if ( rawValue is IEnumerable < TValue > expectedTypeValue )
182+ {
183+ foreach ( var value in expectedTypeValue )
184+ {
185+ yield return value ;
186+ }
187+ }
188+ else
189+ {
190+ throw new AnnotationTypeException ( annotationId , typeof ( IEnumerable < TValue > ) , rawValue ? . GetType ( ) , provider ) ;
191+ }
192+ }
193+ }
131194}
0 commit comments