]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
New upstream version 1.65.0+dfsg1
[rustc.git] / compiler / rustc_macros / src / diagnostics / diagnostic_builder.rs
CommitLineData
064997fb
FG
1#![deny(unused_must_use)]
2
3use crate::diagnostics::error::{
4 invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
5 DiagnosticDeriveError,
6};
7use crate::diagnostics::utils::{
8 report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
9 Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
10};
11use proc_macro2::{Ident, Span, TokenStream};
12use quote::{format_ident, quote};
13use std::collections::HashMap;
14use std::str::FromStr;
15use syn::{
16 parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta,
17 Path, Type,
18};
19use synstructure::{BindingInfo, Structure};
20
f2b60f7d
FG
21/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
22#[derive(Copy, Clone, PartialEq, Eq)]
064997fb 23pub(crate) enum DiagnosticDeriveKind {
f2b60f7d
FG
24 SessionDiagnostic,
25 LintDiagnostic,
064997fb
FG
26}
27
28/// Tracks persistent information required for building up individual calls to diagnostic methods
f2b60f7d 29/// for generated diagnostic derives - both `SessionDiagnostic` for fatal/errors/warnings and
064997fb
FG
30/// `LintDiagnostic` for lints.
31pub(crate) struct DiagnosticDeriveBuilder {
32 /// The identifier to use for the generated `DiagnosticBuilder` instance.
33 pub diag: syn::Ident,
34
35 /// Store a map of field name to its corresponding field. This is built on construction of the
36 /// derive builder.
37 pub fields: HashMap<String, TokenStream>,
38
f2b60f7d
FG
39 /// Kind of diagnostic that should be derived.
40 pub kind: DiagnosticDeriveKind,
064997fb
FG
41 /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
42 /// has the actual diagnostic message.
43 pub slug: Option<(Path, proc_macro::Span)>,
44 /// Error codes are a optional part of the struct attribute - this is only set to detect
45 /// multiple specifications.
46 pub code: Option<(String, proc_macro::Span)>,
47}
48
49impl HasFieldMap for DiagnosticDeriveBuilder {
50 fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
51 self.fields.get(field)
52 }
53}
54
55impl DiagnosticDeriveBuilder {
56 pub fn preamble<'s>(&mut self, structure: &Structure<'s>) -> TokenStream {
57 let ast = structure.ast();
58 let attrs = &ast.attrs;
59 let preamble = attrs.iter().map(|attr| {
60 self.generate_structure_code_for_attr(attr).unwrap_or_else(|v| v.to_compile_error())
61 });
62
63 quote! {
64 #(#preamble)*;
65 }
66 }
67
68 pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
69 // Keep track of which fields need to be handled with a by-move binding.
70 let mut needs_moved = std::collections::HashSet::new();
71
72 // Generates calls to `span_label` and similar functions based on the attributes
73 // on fields. Code for suggestions uses formatting machinery and the value of
74 // other fields - because any given field can be referenced multiple times, it
75 // should be accessed through a borrow. When passing fields to `add_subdiagnostic`
76 // or `set_arg` (which happens below) for Fluent, we want to move the data, so that
77 // has to happen in a separate pass over the fields.
78 let attrs = structure
79 .clone()
80 .filter(|field_binding| {
81 let ast = &field_binding.ast();
82 !self.needs_move(ast) || {
83 needs_moved.insert(field_binding.binding.clone());
84 false
85 }
86 })
87 .each(|field_binding| self.generate_field_attrs_code(field_binding));
88
89 structure.bind_with(|_| synstructure::BindStyle::Move);
90 // When a field has attributes like `#[label]` or `#[note]` then it doesn't
91 // need to be passed as an argument to the diagnostic. But when a field has no
92 // attributes or a `#[subdiagnostic]` attribute then it must be passed as an
93 // argument to the diagnostic so that it can be referred to by Fluent messages.
94 let args = structure
95 .filter(|field_binding| needs_moved.contains(&field_binding.binding))
96 .each(|field_binding| self.generate_field_attrs_code(field_binding));
97
98 (attrs, args)
99 }
100
101 /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
102 /// call (like `span_label`).
103 fn should_generate_set_arg(&self, field: &Field) -> bool {
104 field.attrs.is_empty()
105 }
106
107 /// Returns `true` if `field` needs to have code generated in the by-move branch of the
108 /// generated derive rather than the by-ref branch.
109 fn needs_move(&self, field: &Field) -> bool {
110 let generates_set_arg = self.should_generate_set_arg(field);
111 let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
112 // FIXME(davidtwco): better support for one field needing to be in the by-move and
113 // by-ref branches.
114 let is_subdiagnostic = field
115 .attrs
116 .iter()
117 .map(|attr| attr.path.segments.last().unwrap().ident.to_string())
118 .any(|attr| attr == "subdiagnostic");
119
120 // `set_arg` calls take their argument by-move..
121 generates_set_arg
122 // If this is a `MultiSpan` field then it needs to be moved to be used by any
123 // attribute..
124 || is_multispan
125 // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
126 // unlikely to be `Copy`..
127 || is_subdiagnostic
128 }
129
130 /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
f2b60f7d 131 /// attributes like `#[diag(..)]`, such as the slug and error code. Generates
064997fb
FG
132 /// diagnostic builder calls for setting error code and creating note/help messages.
133 fn generate_structure_code_for_attr(
134 &mut self,
135 attr: &Attribute,
136 ) -> Result<TokenStream, DiagnosticDeriveError> {
137 let diag = &self.diag;
138 let span = attr.span().unwrap();
139
140 let name = attr.path.segments.last().unwrap().ident.to_string();
141 let name = name.as_str();
142 let meta = attr.parse_meta()?;
143
f2b60f7d 144 let is_diag = name == "diag";
064997fb
FG
145
146 let nested = match meta {
f2b60f7d 147 // Most attributes are lists, like `#[diag(..)]` for most cases or
064997fb
FG
148 // `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug.
149 Meta::List(MetaList { ref nested, .. }) => nested,
150 // Subdiagnostics without spans can be applied to the type too, and these are just
f2b60f7d
FG
151 // paths: `#[help]`, `#[note]` and `#[warning]`
152 Meta::Path(_) if !is_diag => {
153 let fn_name = if name == "warning" {
064997fb
FG
154 Ident::new("warn", attr.span())
155 } else {
156 Ident::new(name, attr.span())
157 };
158 return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
159 }
160 _ => throw_invalid_attr!(attr, &meta),
161 };
162
163 // Check the kind before doing any further processing so that there aren't misleading
164 // "no kind specified" errors if there are failures later.
165 match name {
f2b60f7d
FG
166 "error" | "lint" => throw_invalid_attr!(attr, &meta, |diag| {
167 diag.help("`error` and `lint` have been replaced by `diag`")
168 }),
169 "warn_" => throw_invalid_attr!(attr, &meta, |diag| {
170 diag.help("`warn_` have been replaced by `warning`")
171 }),
172 "diag" | "help" | "note" | "warning" => (),
064997fb 173 _ => throw_invalid_attr!(attr, &meta, |diag| {
f2b60f7d 174 diag.help("only `diag`, `help`, `note` and `warning` are valid attributes")
064997fb
FG
175 }),
176 }
177
f2b60f7d 178 // First nested element should always be the path, e.g. `#[diag(typeck::invalid)]` or
064997fb
FG
179 // `#[help(typeck::another_help)]`.
180 let mut nested_iter = nested.into_iter();
181 if let Some(nested_attr) = nested_iter.next() {
182 // Report an error if there are any other list items after the path.
f2b60f7d 183 if !is_diag && nested_iter.next().is_some() {
064997fb
FG
184 throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
185 diag.help(
f2b60f7d 186 "`help`, `note` and `warning` struct attributes can only have one argument",
064997fb
FG
187 )
188 });
189 }
190
191 match nested_attr {
064997fb 192 NestedMeta::Meta(Meta::Path(path)) => {
f2b60f7d
FG
193 if is_diag {
194 self.slug.set_once((path.clone(), span));
195 } else {
196 let fn_name = proc_macro2::Ident::new(name, attr.span());
197 return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
198 }
064997fb
FG
199 }
200 NestedMeta::Meta(meta @ Meta::NameValue(_))
f2b60f7d 201 if is_diag && meta.path().segments.last().unwrap().ident == "code" =>
064997fb
FG
202 {
203 // don't error for valid follow-up attributes
204 }
205 nested_attr => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
206 diag.help("first argument of the attribute should be the diagnostic slug")
207 }),
208 };
209 }
210
211 // Remaining attributes are optional, only `code = ".."` at the moment.
212 let mut tokens = Vec::new();
213 for nested_attr in nested_iter {
214 let meta = match nested_attr {
215 syn::NestedMeta::Meta(meta) => meta,
216 _ => throw_invalid_nested_attr!(attr, &nested_attr),
217 };
218
219 let path = meta.path();
220 let nested_name = path.segments.last().unwrap().ident.to_string();
221 // Struct attributes are only allowed to be applied once, and the diagnostic
222 // changes will be set in the initialisation code.
223 if let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) = &meta {
224 let span = s.span().unwrap();
225 match nested_name.as_str() {
226 "code" => {
227 self.code.set_once((s.value(), span));
228 let code = &self.code.as_ref().map(|(v, _)| v);
229 tokens.push(quote! {
230 #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
231 });
232 }
233 _ => invalid_nested_attr(attr, &nested_attr)
234 .help("only `code` is a valid nested attributes following the slug")
235 .emit(),
236 }
237 } else {
238 invalid_nested_attr(attr, &nested_attr).emit()
239 }
240 }
241
f2b60f7d 242 Ok(tokens.into_iter().collect())
064997fb
FG
243 }
244
245 fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
246 let field = binding_info.ast();
247 let field_binding = &binding_info.binding;
248
249 if self.should_generate_set_arg(&field) {
250 let diag = &self.diag;
251 let ident = field.ident.as_ref().unwrap();
252 return quote! {
253 #diag.set_arg(
254 stringify!(#ident),
255 #field_binding
256 );
257 };
258 }
259
260 let needs_move = self.needs_move(&field);
261 let inner_ty = FieldInnerTy::from_type(&field.ty);
262
263 field
264 .attrs
265 .iter()
266 .map(move |attr| {
267 let name = attr.path.segments.last().unwrap().ident.to_string();
268 let needs_clone =
269 name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_));
270 let (binding, needs_destructure) = if needs_clone {
271 // `primary_span` can accept a `Vec<Span>` so don't destructure that.
272 (quote! { #field_binding.clone() }, false)
273 } else if needs_move {
274 (quote! { #field_binding }, true)
275 } else {
276 (quote! { *#field_binding }, true)
277 };
278
279 let generated_code = self
280 .generate_inner_field_code(
281 attr,
282 FieldInfo {
283 binding: binding_info,
284 ty: inner_ty.inner_type().unwrap_or(&field.ty),
285 span: &field.span(),
286 },
287 binding,
288 )
289 .unwrap_or_else(|v| v.to_compile_error());
290
291 if needs_destructure {
292 inner_ty.with(field_binding, generated_code)
293 } else {
294 generated_code
295 }
296 })
297 .collect()
298 }
299
300 fn generate_inner_field_code(
301 &mut self,
302 attr: &Attribute,
303 info: FieldInfo<'_>,
304 binding: TokenStream,
305 ) -> Result<TokenStream, DiagnosticDeriveError> {
306 let meta = attr.parse_meta()?;
307 match meta {
308 Meta::Path(_) => self.generate_inner_field_code_path(attr, info, binding),
309 Meta::List(MetaList { .. }) => self.generate_inner_field_code_list(attr, info, binding),
310 _ => throw_invalid_attr!(attr, &meta),
311 }
312 }
313
314 fn generate_inner_field_code_path(
315 &mut self,
316 attr: &Attribute,
317 info: FieldInfo<'_>,
318 binding: TokenStream,
319 ) -> Result<TokenStream, DiagnosticDeriveError> {
320 assert!(matches!(attr.parse_meta()?, Meta::Path(_)));
321 let diag = &self.diag;
322
323 let meta = attr.parse_meta()?;
324
325 let ident = &attr.path.segments.last().unwrap().ident;
326 let name = ident.to_string();
327 let name = name.as_str();
328 match name {
329 "skip_arg" => {
330 // Don't need to do anything - by virtue of the attribute existing, the
331 // `set_arg` call will not be generated.
332 Ok(quote! {})
333 }
334 "primary_span" => {
f2b60f7d
FG
335 match self.kind {
336 DiagnosticDeriveKind::SessionDiagnostic => {
337 report_error_if_not_applied_to_span(attr, &info)?;
338
339 Ok(quote! {
340 #diag.set_span(#binding);
341 })
342 }
343 DiagnosticDeriveKind::LintDiagnostic => {
344 throw_invalid_attr!(attr, &meta, |diag| {
345 diag.help("the `primary_span` field attribute is not valid for lint diagnostics")
346 })
347 }
348 }
064997fb
FG
349 }
350 "label" => {
351 report_error_if_not_applied_to_span(attr, &info)?;
352 Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
353 }
f2b60f7d 354 "note" | "help" | "warning" => {
064997fb
FG
355 let warn_ident = Ident::new("warn", Span::call_site());
356 let (ident, path) = match name {
357 "note" => (ident, parse_quote! { _subdiag::note }),
358 "help" => (ident, parse_quote! { _subdiag::help }),
f2b60f7d 359 "warning" => (&warn_ident, parse_quote! { _subdiag::warn }),
064997fb
FG
360 _ => unreachable!(),
361 };
362 if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
363 Ok(self.add_spanned_subdiagnostic(binding, ident, path))
364 } else if type_is_unit(&info.ty) {
365 Ok(self.add_subdiagnostic(ident, path))
366 } else {
367 report_type_error(attr, "`Span` or `()`")?
368 }
369 }
370 "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
371 _ => throw_invalid_attr!(attr, &meta, |diag| {
372 diag.help(
373 "only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` \
374 are valid field attributes",
375 )
376 }),
377 }
378 }
379
380 fn generate_inner_field_code_list(
381 &mut self,
382 attr: &Attribute,
383 info: FieldInfo<'_>,
384 binding: TokenStream,
385 ) -> Result<TokenStream, DiagnosticDeriveError> {
386 let meta = attr.parse_meta()?;
387 let Meta::List(MetaList { ref path, ref nested, .. }) = meta else { unreachable!() };
388
389 let ident = &attr.path.segments.last().unwrap().ident;
390 let name = path.segments.last().unwrap().ident.to_string();
391 let name = name.as_ref();
392 match name {
393 "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
394 return self.generate_inner_field_code_suggestion(attr, info);
395 }
f2b60f7d 396 "label" | "help" | "note" | "warning" => (),
064997fb
FG
397 _ => throw_invalid_attr!(attr, &meta, |diag| {
398 diag.help(
399 "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
400 valid field attributes",
401 )
402 }),
403 }
404
405 // For `#[label(..)]`, `#[note(..)]` and `#[help(..)]`, the first nested element must be a
406 // path, e.g. `#[label(typeck::label)]`.
407 let mut nested_iter = nested.into_iter();
408 let msg = match nested_iter.next() {
409 Some(NestedMeta::Meta(Meta::Path(path))) => path.clone(),
410 Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr),
411 None => throw_invalid_attr!(attr, &meta),
412 };
413
414 // None of these attributes should have anything following the slug.
415 if nested_iter.next().is_some() {
416 throw_invalid_attr!(attr, &meta);
417 }
418
419 match name {
420 "label" => {
421 report_error_if_not_applied_to_span(attr, &info)?;
422 Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
423 }
424 "note" | "help" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => {
425 Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
426 }
427 "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
f2b60f7d 428 // `warning` must be special-cased because the attribute `warn` already has meaning and
064997fb 429 // so isn't used, despite the diagnostic API being named `warn`.
f2b60f7d 430 "warning" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
064997fb 431 .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
f2b60f7d 432 "warning" if type_is_unit(&info.ty) => {
064997fb
FG
433 Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
434 }
f2b60f7d 435 "note" | "help" | "warning" => report_type_error(attr, "`Span` or `()`")?,
064997fb
FG
436 _ => unreachable!(),
437 }
438 }
439
440 fn generate_inner_field_code_suggestion(
441 &mut self,
442 attr: &Attribute,
443 info: FieldInfo<'_>,
444 ) -> Result<TokenStream, DiagnosticDeriveError> {
445 let diag = &self.diag;
446
447 let mut meta = attr.parse_meta()?;
448 let Meta::List(MetaList { ref path, ref mut nested, .. }) = meta else { unreachable!() };
449
450 let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
451
452 let mut msg = None;
453 let mut code = None;
454
455 let mut nested_iter = nested.into_iter().peekable();
456 if let Some(nested_attr) = nested_iter.peek() {
457 if let NestedMeta::Meta(Meta::Path(path)) = nested_attr {
458 msg = Some(path.clone());
459 }
460 };
461 // Move the iterator forward if a path was found (don't otherwise so that
462 // code/applicability can be found or an error emitted).
463 if msg.is_some() {
464 let _ = nested_iter.next();
465 }
466
467 for nested_attr in nested_iter {
468 let meta = match nested_attr {
469 syn::NestedMeta::Meta(ref meta) => meta,
470 syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr),
471 };
472
473 let nested_name = meta.path().segments.last().unwrap().ident.to_string();
474 let nested_name = nested_name.as_str();
475 match meta {
476 Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
477 let span = meta.span().unwrap();
478 match nested_name {
479 "code" => {
480 let formatted_str = self.build_format(&s.value(), s.span());
481 code = Some(formatted_str);
482 }
483 "applicability" => {
484 applicability = match applicability {
485 Some(v) => {
486 span_err(
487 span,
488 "applicability cannot be set in both the field and \
489 attribute",
490 )
491 .emit();
492 Some(v)
493 }
494 None => match Applicability::from_str(&s.value()) {
495 Ok(v) => Some(quote! { #v }),
496 Err(()) => {
497 span_err(span, "invalid applicability").emit();
498 None
499 }
500 },
501 }
502 }
503 _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
504 diag.help(
505 "only `message`, `code` and `applicability` are valid field \
506 attributes",
507 )
508 }),
509 }
510 }
511 _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
512 if matches!(meta, Meta::Path(_)) {
513 diag.help("a diagnostic slug must be the first argument to the attribute")
514 } else {
515 diag
516 }
517 }),
518 }
519 }
520
521 let applicability =
522 applicability.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
523
524 let name = path.segments.last().unwrap().ident.to_string();
525 let method = format_ident!("span_{}", name);
526
527 let msg = msg.unwrap_or_else(|| parse_quote! { _subdiag::suggestion });
528 let msg = quote! { rustc_errors::fluent::#msg };
529 let code = code.unwrap_or_else(|| quote! { String::new() });
530
531 Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); })
532 }
533
534 /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug
535 /// and `fluent_attr_identifier`.
536 fn add_spanned_subdiagnostic(
537 &self,
538 field_binding: TokenStream,
539 kind: &Ident,
540 fluent_attr_identifier: Path,
541 ) -> TokenStream {
542 let diag = &self.diag;
543 let fn_name = format_ident!("span_{}", kind);
544 quote! {
545 #diag.#fn_name(
546 #field_binding,
547 rustc_errors::fluent::#fluent_attr_identifier
548 );
549 }
550 }
551
552 /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
553 /// and `fluent_attr_identifier`.
554 fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream {
555 let diag = &self.diag;
556 quote! {
557 #diag.#kind(rustc_errors::fluent::#fluent_attr_identifier);
558 }
559 }
560
561 fn span_and_applicability_of_ty(
562 &self,
563 info: FieldInfo<'_>,
564 ) -> Result<(TokenStream, Option<TokenStream>), DiagnosticDeriveError> {
565 match &info.ty {
566 // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
567 ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
568 let binding = &info.binding.binding;
569 Ok((quote!(*#binding), None))
570 }
571 // If `ty` is `(Span, Applicability)` then return tokens accessing those.
572 Type::Tuple(tup) => {
573 let mut span_idx = None;
574 let mut applicability_idx = None;
575
576 for (idx, elem) in tup.elems.iter().enumerate() {
577 if type_matches_path(elem, &["rustc_span", "Span"]) {
578 if span_idx.is_none() {
579 span_idx = Some(syn::Index::from(idx));
580 } else {
581 throw_span_err!(
582 info.span.unwrap(),
583 "type of field annotated with `#[suggestion(...)]` contains more \
584 than one `Span`"
585 );
586 }
587 } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
588 if applicability_idx.is_none() {
589 applicability_idx = Some(syn::Index::from(idx));
590 } else {
591 throw_span_err!(
592 info.span.unwrap(),
593 "type of field annotated with `#[suggestion(...)]` contains more \
594 than one Applicability"
595 );
596 }
597 }
598 }
599
600 if let Some(span_idx) = span_idx {
601 let binding = &info.binding.binding;
602 let span = quote!(#binding.#span_idx);
603 let applicability = applicability_idx
604 .map(|applicability_idx| quote!(#binding.#applicability_idx))
605 .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
606
607 return Ok((span, Some(applicability)));
608 }
609
610 throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
611 diag.help(
612 "`#[suggestion(...)]` on a tuple field must be applied to fields of type \
613 `(Span, Applicability)`",
614 )
615 });
616 }
617 // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error.
618 _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| {
619 diag.help(
620 "`#[suggestion(...)]` should be applied to fields of type `Span` or \
621 `(Span, Applicability)`",
622 )
623 }),
624 }
625 }
626}