diff options
Diffstat (limited to 'compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs')
-rw-r--r-- | compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs | 176 |
1 files changed, 79 insertions, 97 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 46068f8c8..427c82c41 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -1,19 +1,17 @@ #![deny(unused_must_use)] use crate::diagnostics::error::{ - invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, - DiagnosticDeriveError, + span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, - should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, FieldMap, - HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + should_generate_set_arg, type_is_bool, type_is_unit, type_matches_path, FieldInfo, + FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; -use syn::{ - parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, -}; +use syn::Token; +use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type}; use synstructure::{BindingInfo, Structure, VariantInfo}; /// What kind of diagnostic is being derived - a fatal/error/warning or a lint? @@ -77,7 +75,7 @@ impl DiagnosticDeriveBuilder { match ast.data { syn::Data::Struct(..) | syn::Data::Enum(..) => (), syn::Data::Union(..) => { - span_err(span, "diagnostic derives can only be used on structs and enums"); + span_err(span, "diagnostic derives can only be used on structs and enums").emit(); } } @@ -121,7 +119,7 @@ impl DiagnosticDeriveBuilder { impl<'a> DiagnosticDeriveVariantBuilder<'a> { /// Generates calls to `code` and similar functions based on the attributes on the type or /// variant. - pub fn preamble<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream { + pub fn preamble(&mut self, variant: &VariantInfo<'_>) -> TokenStream { let ast = variant.ast(); let attrs = &ast.attrs; let preamble = attrs.iter().map(|attr| { @@ -135,7 +133,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { /// Generates calls to `span_label` and similar functions based on the attributes on fields or /// calls to `set_arg` when no attributes are present. - pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream { + pub fn body(&mut self, variant: &VariantInfo<'_>) -> TokenStream { let mut body = quote! {}; // Generate `set_arg` calls first.. for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) { @@ -160,8 +158,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { }; if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag { - let meta = attr.parse_meta()?; - throw_invalid_attr!(attr, &meta, |diag| diag + throw_invalid_attr!(attr, |diag| diag .help("consider creating a `Subdiagnostic` instead")); } @@ -191,71 +188,44 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return Ok(quote! {}); } - let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = attr.path().segments.last().unwrap().ident.to_string(); let name = name.as_str(); - let meta = attr.parse_meta()?; - if name == "diag" { - let Meta::List(MetaList { ref nested, .. }) = meta else { - throw_invalid_attr!( - attr, - &meta - ); - }; + let mut first = true; - let mut nested_iter = nested.into_iter().peekable(); + if name == "diag" { + let mut tokens = TokenStream::new(); + attr.parse_nested_meta(|nested| { + let path = &nested.path; - match nested_iter.peek() { - Some(NestedMeta::Meta(Meta::Path(slug))) => { - self.slug.set_once(slug.clone(), slug.span().unwrap()); - nested_iter.next(); + if first && (nested.input.is_empty() || nested.input.peek(Token![,])) { + self.slug.set_once(path.clone(), path.span().unwrap()); + first = false; + return Ok(()) } - Some(NestedMeta::Meta(Meta::NameValue { .. })) => {} - Some(nested_attr) => throw_invalid_nested_attr!(attr, nested_attr, |diag| diag - .help("a diagnostic slug is required as the first argument")), - None => throw_invalid_attr!(attr, &meta, |diag| diag - .help("a diagnostic slug is required as the first argument")), - }; - // Remaining attributes are optional, only `code = ".."` at the moment. - let mut tokens = TokenStream::new(); - for nested_attr in nested_iter { - let (value, path) = match nested_attr { - NestedMeta::Meta(Meta::NameValue(MetaNameValue { - lit: syn::Lit::Str(value), - path, - .. - })) => (value, path), - NestedMeta::Meta(Meta::Path(_)) => { - invalid_nested_attr(attr, nested_attr) - .help("diagnostic slug must be the first argument") - .emit(); - continue; - } - _ => { - invalid_nested_attr(attr, nested_attr).emit(); - continue; - } + first = false; + + let Ok(nested) = nested.value() else { + span_err(nested.input.span().unwrap(), "diagnostic slug must be the first argument").emit(); + return Ok(()) }; - let nested_name = path.segments.last().unwrap().ident.to_string(); - // Struct attributes are only allowed to be applied once, and the diagnostic - // changes will be set in the initialisation code. - let span = value.span().unwrap(); - match nested_name.as_str() { - "code" => { - self.code.set_once((), span); - - let code = value.value(); - tokens.extend(quote! { - #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); - }); - } - _ => invalid_nested_attr(attr, nested_attr) - .help("only `code` is a valid nested attributes following the slug") - .emit(), + if path.is_ident("code") { + self.code.set_once((), path.span().unwrap()); + + let code = nested.parse::<syn::LitStr>()?; + tokens.extend(quote! { + #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); + }); + } else { + span_err(path.span().unwrap(), "unknown argument").note("only the `code` parameter is valid after the slug").emit(); + + // consume the buffer so we don't have syntax errors from syn + let _ = nested.parse::<TokenStream>(); } - } + Ok(()) + })?; return Ok(tokens); } @@ -270,7 +240,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { Ok(self.add_subdiagnostic(&fn_ident, slug)) } SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { - throw_invalid_attr!(attr, &meta, |diag| diag + throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); } SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), @@ -309,7 +279,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return quote! {}; } - let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = attr.path().segments.last().unwrap().ident.to_string(); let needs_clone = name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); let (binding, needs_destructure) = if needs_clone { @@ -343,11 +313,10 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { binding: TokenStream, ) -> Result<TokenStream, DiagnosticDeriveError> { let diag = &self.parent.diag; - let meta = attr.parse_meta()?; - let ident = &attr.path.segments.last().unwrap().ident; + let ident = &attr.path().segments.last().unwrap().ident; let name = ident.to_string(); - match (&meta, name.as_str()) { + match (&attr.meta, name.as_str()) { // Don't need to do anything - by virtue of the attribute existing, the // `set_arg` call will not be generated. (Meta::Path(_), "skip_arg") => return Ok(quote! {}), @@ -361,7 +330,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { }); } DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, &meta, |diag| { + throw_invalid_attr!(attr, |diag| { diag.help("the `primary_span` field attribute is not valid for lint diagnostics") }) } @@ -378,26 +347,34 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return Ok(quote! { #diag.subdiagnostic(#binding); }); } } - (Meta::List(MetaList { ref nested, .. }), "subdiagnostic") => { - if nested.len() == 1 - && let Some(NestedMeta::Meta(Meta::Path(path))) = nested.first() - && path.is_ident("eager") { - let handler = match &self.parent.kind { - DiagnosticDeriveKind::Diagnostic { handler } => handler, - DiagnosticDeriveKind::LintDiagnostic => { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help("eager subdiagnostics are not supported on lints") - }) - } - }; - return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); - } else { - throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "`eager` is the only supported nested attribute for `subdiagnostic`", - ) - }) + (Meta::List(meta_list), "subdiagnostic") => { + let err = || { + span_err( + meta_list.span().unwrap(), + "`eager` is the only supported nested attribute for `subdiagnostic`", + ) + .emit(); + }; + + let Ok(p): Result<Path, _> = meta_list.parse_args() else { + err(); + return Ok(quote! {}); + }; + + if !p.is_ident("eager") { + err(); + return Ok(quote! {}); } + + let handler = match &self.parent.kind { + DiagnosticDeriveKind::Diagnostic { handler } => handler, + DiagnosticDeriveKind::LintDiagnostic => { + throw_invalid_attr!(attr, |diag| { + diag.help("eager subdiagnostics are not supported on lints") + }) + } + }; + return Ok(quote! { #diag.eager_subdiagnostic(#handler, #binding); }); } _ => (), } @@ -414,12 +391,17 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) } SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { - if type_matches_path(info.ty.inner_type(), &["rustc_span", "Span"]) { + let inner = info.ty.inner_type(); + if type_matches_path(inner, &["rustc_span", "Span"]) + || type_matches_path(inner, &["rustc_span", "MultiSpan"]) + { Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) - } else if type_is_unit(info.ty.inner_type()) { + } else if type_is_unit(inner) + || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner)) + { Ok(self.add_subdiagnostic(&fn_ident, slug)) } else { - report_type_error(attr, "`Span` or `()`")? + report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")? } } SubdiagnosticKind::Suggestion { @@ -429,7 +411,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { code_init, } => { if let FieldInnerTy::Vec(_) = info.ty { - throw_invalid_attr!(attr, &meta, |diag| { + throw_invalid_attr!(attr, |diag| { diag .note("`#[suggestion(...)]` applied to `Vec` field is ambiguous") .help("to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`") |