1 #![deny(unused_must_use)]
2 use proc_macro::Diagnostic;
3 use quote::{format_ident, quote};
4 use syn::spanned::Spanned;
6 use std::collections::{BTreeSet, HashMap};
8 /// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
9 /// independent from the actual diagnostics emitting code.
11 /// ```ignore (pseudo-rust)
12 /// # extern crate rustc_errors;
13 /// # use rustc_errors::Applicability;
14 /// # extern crate rustc_span;
15 /// # use rustc_span::{symbol::Ident, Span};
16 /// # extern crate rust_middle;
17 /// # use rustc_middle::ty::Ty;
18 /// #[derive(SessionDiagnostic)]
19 /// #[error(code = "E0505", slug = "move-out-of-borrow-error")]
20 /// pub struct MoveOutOfBorrowError<'tcx> {
24 /// #[label = "cannot move out of borrow"]
26 /// #[label = "`{ty}` first borrowed here"]
27 /// pub other_span: Span,
28 /// #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
29 /// pub opt_sugg: Option<(Span, Applicability)>
33 /// Then, later, to emit the error:
35 /// ```ignore (pseudo-rust)
36 /// sess.emit_err(MoveOutOfBorrowError {
41 /// opt_sugg: Some(suggestion, Applicability::MachineApplicable),
44 pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
45 // Names for the diagnostic we build and the session we build it from.
46 let diag = format_ident!("diag");
47 let sess = format_ident!("sess");
49 SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
52 /// Checks whether the type name of `ty` matches `name`.
54 /// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
55 /// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro.
56 fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool {
57 if let syn::Type::Path(ty) = ty {
61 .map(|s| s.ident.to_string())
63 .zip(name.iter().rev())
64 .all(|(x, y)| &x.as_str() == y)
70 /// The central struct for constructing the `as_error` method from an annotated struct.
71 struct SessionDiagnosticDerive<'a> {
72 structure: synstructure::Structure<'a>,
73 builder: SessionDiagnosticDeriveBuilder<'a>,
76 impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError {
77 fn from(e: syn::Error) -> Self {
78 SessionDiagnosticDeriveError::SynError(e)
83 enum SessionDiagnosticDeriveError {
88 impl SessionDiagnosticDeriveError {
89 fn to_compile_error(self) -> proc_macro2::TokenStream {
91 SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
92 SessionDiagnosticDeriveError::ErrorHandled => {
93 // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
94 // error has already been emitted to the compiler.
103 fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic {
104 Diagnostic::spanned(span, proc_macro::Level::Error, msg)
107 /// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
109 /// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
110 /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
111 macro_rules! throw_span_err {
112 ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
113 ($span:expr, $msg:expr, $f:expr) => {{
114 return Err(_throw_span_err($span, $msg, $f));
118 /// When possible, prefer using `throw_span_err!` over using this function directly. This only
119 /// exists as a function to constrain `f` to an `impl FnOnce`.
121 span: impl proc_macro::MultiSpan,
123 f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic,
124 ) -> SessionDiagnosticDeriveError {
125 let diag = span_err(span, msg);
127 SessionDiagnosticDeriveError::ErrorHandled
130 impl<'a> SessionDiagnosticDerive<'a> {
131 fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self {
132 // Build the mapping of field names to fields. This allows attributes to peek values from
134 let mut fields_map = HashMap::new();
136 // Convenience bindings.
137 let ast = structure.ast();
139 if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
140 for field in fields.iter() {
141 if let Some(ident) = &field.ident {
142 fields_map.insert(ident.to_string(), field);
148 builder: SessionDiagnosticDeriveBuilder {
160 fn into_tokens(self) -> proc_macro2::TokenStream {
161 let SessionDiagnosticDerive { mut structure, mut builder } = self;
163 let ast = structure.ast();
164 let attrs = &ast.attrs;
166 let (implementation, param_ty) = {
167 if let syn::Data::Struct(..) = ast.data {
169 let preamble = attrs.iter().map(|attr| {
171 .generate_structure_code(attr)
172 .unwrap_or_else(|v| v.to_compile_error())
180 // Generates calls to `span_label` and similar functions based on the attributes
181 // on fields. Code for suggestions uses formatting machinery and the value of
182 // other fields - because any given field can be referenced multiple times, it
183 // should be accessed through a borrow. When passing fields to `set_arg` (which
184 // happens below) for Fluent, we want to move the data, so that has to happen
185 // in a separate pass over the fields.
186 let attrs = structure.each(|field_binding| {
187 let field = field_binding.ast();
188 let result = field.attrs.iter().map(|attr| {
190 .generate_field_attr_code(
194 binding: field_binding,
199 .unwrap_or_else(|v| v.to_compile_error())
202 quote! { #(#result);* }
205 // When generating `set_arg` calls, move data rather than borrow it to avoid
206 // requiring clones - this must therefore be the last use of each field (for
207 // example, any formatting machinery that might refer to a field should be
208 // generated already).
209 structure.bind_with(|_| synstructure::BindStyle::Move);
210 let args = structure.each(|field_binding| {
211 let field = field_binding.ast();
212 // When a field has attributes like `#[label]` or `#[note]` then it doesn't
213 // need to be passed as an argument to the diagnostic. But when a field has no
214 // attributes then it must be passed as an argument to the diagnostic so that
215 // it can be referred to by Fluent messages.
216 if field.attrs.is_empty() {
217 let diag = &builder.diag;
218 let ident = field_binding.ast().ident.as_ref().unwrap();
222 #field_binding.into_diagnostic_arg()
230 let span = ast.span().unwrap();
231 let (diag, sess) = (&builder.diag, &builder.sess);
232 let init = match (builder.kind, builder.slug) {
234 span_err(span, "diagnostic kind not specified")
235 .help("use the `#[error(...)]` attribute to create an error")
237 return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
239 (Some((kind, _)), None) => {
240 span_err(span, "`slug` not specified")
241 .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr()))
243 return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
245 (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => {
247 let mut #diag = #sess.struct_err(
248 rustc_errors::DiagnosticMessage::fluent(#slug),
252 (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => {
254 let mut #diag = #sess.struct_warn(
255 rustc_errors::DiagnosticMessage::fluent(#slug),
261 let implementation = quote! {
272 let param_ty = match builder.kind {
273 Some((SessionDiagnosticKind::Error, _)) => {
274 quote! { rustc_errors::ErrorGuaranteed }
276 Some((SessionDiagnosticKind::Warn, _)) => quote! { () },
280 (implementation, param_ty)
284 "`#[derive(SessionDiagnostic)]` can only be used on structs",
288 let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
289 let param_ty = quote! { rustc_errors::ErrorGuaranteed };
290 (implementation, param_ty)
294 let sess = &builder.sess;
295 structure.gen_impl(quote! {
296 gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
301 #sess: &'__session_diagnostic_sess rustc_session::Session
302 ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
303 use rustc_errors::IntoDiagnosticArg;
311 /// Field information passed to the builder. Deliberately omits attrs to discourage the
312 /// `generate_*` methods from walking the attributes themselves.
313 struct FieldInfo<'a> {
314 vis: &'a syn::Visibility,
315 binding: &'a synstructure::BindingInfo<'a>,
317 span: &'a proc_macro2::Span,
320 /// What kind of session diagnostic is being derived - an error or a warning?
321 #[derive(Copy, Clone)]
322 enum SessionDiagnosticKind {
329 impl SessionDiagnosticKind {
330 /// Returns human-readable string corresponding to the kind.
331 fn descr(&self) -> &'static str {
333 SessionDiagnosticKind::Error => "error",
334 SessionDiagnosticKind::Warn => "warning",
339 /// Tracks persistent information required for building up the individual calls to diagnostic
340 /// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive`
341 /// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
342 /// double mut borrow later on.
343 struct SessionDiagnosticDeriveBuilder<'a> {
344 /// Name of the session parameter that's passed in to the `as_error` method.
346 /// The identifier to use for the generated `DiagnosticBuilder` instance.
349 /// Store a map of field name to its corresponding field. This is built on construction of the
351 fields: HashMap<String, &'a syn::Field>,
353 /// Kind of diagnostic requested via the struct attribute.
354 kind: Option<(SessionDiagnosticKind, proc_macro::Span)>,
355 /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
356 /// has the actual diagnostic message.
357 slug: Option<(String, proc_macro::Span)>,
358 /// Error codes are a optional part of the struct attribute - this is only set to detect
359 /// multiple specifications.
360 code: Option<proc_macro::Span>,
363 impl<'a> SessionDiagnosticDeriveBuilder<'a> {
364 /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct
365 /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates
366 /// diagnostic builder calls for setting error code and creating note/help messages.
367 fn generate_structure_code(
369 attr: &syn::Attribute,
370 ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
371 let span = attr.span().unwrap();
373 let name = attr.path.segments.last().unwrap().ident.to_string();
374 let name = name.as_str();
375 let meta = attr.parse_meta()?;
377 if matches!(name, "help" | "note")
378 && matches!(meta, syn::Meta::Path(_) | syn::Meta::NameValue(_))
380 let diag = &self.diag;
381 let slug = match &self.slug {
382 Some((slug, _)) => slug.as_str(),
383 None => throw_span_err!(
386 "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
389 syn::Meta::Path(_) => "",
390 syn::Meta::NameValue(_) => " = ...",
396 let id = match meta {
397 syn::Meta::Path(..) => quote! { #name },
398 syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
403 let fn_name = proc_macro2::Ident::new(name, attr.span());
406 #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
410 let nested = match meta {
411 syn::Meta::List(syn::MetaList { nested, .. }) => nested,
412 syn::Meta::Path(..) => throw_span_err!(
414 &format!("`#[{}]` is not a valid `SessionDiagnostic` struct attribute", name)
416 syn::Meta::NameValue(..) => throw_span_err!(
418 &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` struct attribute", name)
422 let kind = match name {
423 "error" => SessionDiagnosticKind::Error,
424 "warning" => SessionDiagnosticKind::Warn,
425 other => throw_span_err!(
427 &format!("`#[{}(...)]` is not a valid `SessionDiagnostic` struct attribute", other)
430 self.set_kind_once(kind, span)?;
432 let mut tokens = Vec::new();
434 let span = attr.span().unwrap();
435 let meta = match attr {
436 syn::NestedMeta::Meta(meta) => meta,
437 syn::NestedMeta::Lit(_) => throw_span_err!(
440 "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` struct attribute",
446 let path = meta.path();
447 let nested_name = path.segments.last().unwrap().ident.to_string();
449 // Struct attributes are only allowed to be applied once, and the diagnostic
450 // changes will be set in the initialisation code.
451 syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
452 match nested_name.as_str() {
454 self.set_slug_once(s.value(), s.span().unwrap());
457 tokens.push(self.set_code_once(s.value(), s.span().unwrap()));
463 "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
471 syn::Meta::NameValue(..) => {
475 "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
479 .help("value must be a string")
482 syn::Meta::Path(..) => {
486 "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
492 syn::Meta::List(..) => {
496 "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
505 Ok(tokens.drain(..).collect())
511 kind: SessionDiagnosticKind,
512 span: proc_macro::Span,
513 ) -> Result<(), SessionDiagnosticDeriveError> {
516 self.kind = Some((kind, span));
519 Some((prev_kind, prev_span)) => {
520 let existing = prev_kind.descr();
521 let current = kind.descr();
523 let msg = if current == existing {
524 format!("`{}` specified multiple times", existing)
526 format!("`{}` specified when `{}` was already specified", current, existing)
528 throw_span_err!(span, &msg, |diag| diag
529 .span_note(prev_span, "previously specified here"));
534 fn set_code_once(&mut self, code: String, span: proc_macro::Span) -> proc_macro2::TokenStream {
537 self.code = Some(span);
540 span_err(span, "`code` specified multiple times")
541 .span_note(prev_span, "previously specified here")
546 let diag = &self.diag;
547 quote! { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); }
550 fn set_slug_once(&mut self, slug: String, span: proc_macro::Span) {
553 self.slug = Some((slug, span));
555 Some((_, prev_span)) => {
556 span_err(span, "`slug` specified multiple times")
557 .span_note(prev_span, "previously specified here")
563 fn generate_field_attr_code(
565 attr: &syn::Attribute,
567 ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
568 let field_binding = &info.binding.binding;
569 let option_ty = option_inner_ty(&info.ty);
570 let generated_code = self.generate_non_option_field_code(
574 binding: info.binding,
575 ty: option_ty.unwrap_or(&info.ty),
580 if option_ty.is_none() {
581 Ok(quote! { #generated_code })
584 if let Some(#field_binding) = #field_binding {
591 fn generate_non_option_field_code(
593 attr: &syn::Attribute,
595 ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
596 let diag = &self.diag;
597 let span = attr.span().unwrap();
598 let field_binding = &info.binding.binding;
600 let name = attr.path.segments.last().unwrap().ident.to_string();
601 let name = name.as_str();
603 let meta = attr.parse_meta()?;
605 syn::Meta::Path(_) => match name {
607 // Don't need to do anything - by virtue of the attribute existing, the
608 // `set_arg` call will not be generated.
612 self.report_error_if_not_applied_to_span(attr, info)?;
614 #diag.set_span(*#field_binding);
617 "label" | "note" | "help" => {
618 self.report_error_if_not_applied_to_span(attr, info)?;
619 Ok(self.add_subdiagnostic(field_binding, name, name))
621 other => throw_span_err!(
623 &format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
626 syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
627 "label" | "note" | "help" => {
628 self.report_error_if_not_applied_to_span(attr, info)?;
629 Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
631 other => throw_span_err!(
634 "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
639 syn::Meta::NameValue(_) => throw_span_err!(
641 &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
642 |diag| diag.help("value must be a string")
644 syn::Meta::List(syn::MetaList { path, nested, .. }) => {
645 let name = path.segments.last().unwrap().ident.to_string();
646 let name = name.as_ref();
649 "suggestion" | "suggestion_short" | "suggestion_hidden"
650 | "suggestion_verbose" => (),
651 other => throw_span_err!(
654 "`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
660 let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
666 let meta = match attr {
667 syn::NestedMeta::Meta(meta) => meta,
668 syn::NestedMeta::Lit(_) => throw_span_err!(
671 "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
677 let span = meta.span().unwrap();
678 let nested_name = meta.path().segments.last().unwrap().ident.to_string();
679 let nested_name = nested_name.as_str();
682 syn::Meta::NameValue(syn::MetaNameValue {
683 lit: syn::Lit::Str(s), ..
684 }) => match nested_name {
686 msg = Some(s.value());
689 let formatted_str = self.build_format(&s.value(), s.span());
690 code = Some(formatted_str);
692 other => throw_span_err!(
695 "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
700 syn::Meta::NameValue(..) => throw_span_err!(
703 "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
706 |diag| diag.help("value must be a string")
708 syn::Meta::Path(..) => throw_span_err!(
711 "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
715 syn::Meta::List(..) => throw_span_err!(
718 "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
725 let method = format_ident!("span_{}", name);
730 .map(|(slug, _)| slug.as_str())
731 .unwrap_or_else(|| "missing-slug");
732 let msg = msg.as_deref().unwrap_or("suggestion");
733 let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
734 let code = code.unwrap_or_else(|| quote! { String::new() });
736 Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
741 /// Reports an error if the field's type is not `Span`.
742 fn report_error_if_not_applied_to_span(
744 attr: &syn::Attribute,
746 ) -> Result<(), SessionDiagnosticDeriveError> {
747 if !type_matches_path(&info.ty, &["rustc_span", "Span"]) {
748 let name = attr.path.segments.last().unwrap().ident.to_string();
749 let name = name.as_str();
750 let meta = attr.parse_meta()?;
753 attr.span().unwrap(),
755 "the `#[{}{}]` attribute can only be applied to fields of type `Span`",
758 syn::Meta::Path(_) => "",
759 syn::Meta::NameValue(_) => " = ...",
760 syn::Meta::List(_) => "(...)",
769 /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
770 /// `fluent_attr_identifier`.
771 fn add_subdiagnostic(
773 field_binding: &proc_macro2::Ident,
775 fluent_attr_identifier: &str,
776 ) -> proc_macro2::TokenStream {
777 let diag = &self.diag;
780 self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
781 let fn_name = format_ident!("span_{}", kind);
785 rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
790 fn span_and_applicability_of_ty(
793 ) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), SessionDiagnosticDeriveError>
796 // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
797 ty @ syn::Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
798 let binding = &info.binding.binding;
799 Ok((quote!(*#binding), quote!(rustc_errors::Applicability::Unspecified)))
801 // If `ty` is `(Span, Applicability)` then return tokens accessing those.
802 syn::Type::Tuple(tup) => {
803 let mut span_idx = None;
804 let mut applicability_idx = None;
806 for (idx, elem) in tup.elems.iter().enumerate() {
807 if type_matches_path(elem, &["rustc_span", "Span"]) {
808 if span_idx.is_none() {
809 span_idx = Some(syn::Index::from(idx));
813 "type of field annotated with `#[suggestion(...)]` contains more than one `Span`"
816 } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
817 if applicability_idx.is_none() {
818 applicability_idx = Some(syn::Index::from(idx));
822 "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
828 if let Some(span_idx) = span_idx {
829 let binding = &info.binding.binding;
830 let span = quote!(#binding.#span_idx);
831 let applicability = applicability_idx
832 .map(|applicability_idx| quote!(#binding.#applicability_idx))
833 .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
835 return Ok((span, applicability));
838 throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
839 diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`")
842 // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error.
843 _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| {
844 diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`")
849 /// In the strings in the attributes supplied to this macro, we want callers to be able to
850 /// reference fields in the format string. For example:
852 /// ```ignore (not-usage-example)
854 /// #[error = "Expected a point greater than ({x}, {y})"]
860 /// We want to automatically pick up that `{x}` refers `self.x` and `{y}` refers to `self.y`,
861 /// then generate this call to `format!`:
863 /// ```ignore (not-usage-example)
864 /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y)
867 /// This function builds the entire call to `format!`.
868 fn build_format(&self, input: &str, span: proc_macro2::Span) -> proc_macro2::TokenStream {
869 // This set is used later to generate the final format string. To keep builds reproducible,
870 // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead
872 let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
874 // At this point, we can start parsing the format string.
875 let mut it = input.chars().peekable();
876 // Once the start of a format string has been found, process the format string and spit out
877 // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the
878 // next call to `it.next()` retrieves the next character.
879 while let Some(c) = it.next() {
880 if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
881 let mut eat_argument = || -> Option<String> {
882 let mut result = String::new();
883 // Format specifiers look like
884 // format := '{' [ argument ] [ ':' format_spec ] '}' .
885 // Therefore, we only need to eat until ':' or '}' to find the argument.
886 while let Some(c) = it.next() {
888 let next = *it.peek().unwrap_or(&'\0');
891 } else if next == ':' {
892 // Eat the ':' character.
893 assert_eq!(it.next().unwrap(), ':');
897 // Eat until (and including) the matching '}'
898 while it.next()? != '}' {
904 if let Some(referenced_field) = eat_argument() {
905 referenced_fields.insert(referenced_field);
909 // At this point, `referenced_fields` contains a set of the unique fields that were
910 // referenced in the format string. Generate the corresponding "x = self.x" format
911 // string parameters:
912 let args = referenced_fields.into_iter().map(|field: String| {
913 let field_ident = format_ident!("{}", field);
914 let value = if self.fields.contains_key(&field) {
919 // This field doesn't exist. Emit a diagnostic.
922 proc_macro::Level::Error,
923 format!("`{}` doesn't refer to a field on this type", field),
931 #field_ident = #value
935 format!(#input #(,#args)*)
940 /// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
941 fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> {
942 if type_matches_path(ty, &["std", "option", "Option"]) {
943 if let syn::Type::Path(ty_path) = ty {
944 let path = &ty_path.path;
945 let ty = path.segments.iter().last().unwrap();
946 if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
947 if bracketed.args.len() == 1 {
948 if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {