From 4547b622d8d29df964fa2914213088b148c498fc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:18:32 +0200 Subject: Merging upstream version 1.67.1+dfsg1. Signed-off-by: Daniel Baumann --- .../rustc_macros/src/diagnostics/diagnostic.rs | 72 +++++++++-- .../src/diagnostics/diagnostic_builder.rs | 14 +- compiler/rustc_macros/src/diagnostics/error.rs | 4 +- compiler/rustc_macros/src/diagnostics/mod.rs | 2 +- .../rustc_macros/src/diagnostics/subdiagnostic.rs | 4 +- compiler/rustc_macros/src/diagnostics/utils.rs | 142 ++++++++++++++++----- 6 files changed, 188 insertions(+), 50 deletions(-) (limited to 'compiler/rustc_macros/src/diagnostics') diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index ef1985b96..684835d8c 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -5,6 +5,7 @@ use crate::diagnostics::error::{span_err, DiagnosticDeriveError}; use crate::diagnostics::utils::SetOnce; use proc_macro2::TokenStream; use quote::quote; +use syn::spanned::Spanned; use synstructure::Structure; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. @@ -28,8 +29,8 @@ impl<'a> DiagnosticDerive<'a> { let DiagnosticDerive { mut structure, mut builder } = self; let implementation = builder.each_variant(&mut structure, |mut builder, variant| { - let preamble = builder.preamble(&variant); - let body = builder.body(&variant); + let preamble = builder.preamble(variant); + let body = builder.body(variant); let diag = &builder.parent.diag; let DiagnosticDeriveKind::Diagnostic { handler } = &builder.parent.kind else { @@ -38,13 +39,24 @@ impl<'a> DiagnosticDerive<'a> { let init = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") - .help(&format!( + .help(format!( "specify the slug as the first argument to the `#[diag(...)]` \ attribute, such as `#[diag(hir_analysis_example_error)]`", )) .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } + Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { + span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") + .note(format!( + "slug is `{slug_name}` but the crate name is `{crate_name}`" + )) + .help(format!( + "expected a slug starting with `{slug_prefix}_...`" + )) + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + } Some(slug) => { quote! { let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug); @@ -69,6 +81,8 @@ impl<'a> DiagnosticDerive<'a> { for @Self where G: rustc_errors::EmissionGuarantee { + + #[track_caller] fn into_diagnostic( self, #handler: &'__diagnostic_handler_sess rustc_errors::Handler @@ -99,8 +113,8 @@ impl<'a> LintDiagnosticDerive<'a> { let LintDiagnosticDerive { mut structure, mut builder } = self; let implementation = builder.each_variant(&mut structure, |mut builder, variant| { - let preamble = builder.preamble(&variant); - let body = builder.body(&variant); + let preamble = builder.preamble(variant); + let body = builder.body(variant); let diag = &builder.parent.diag; let formatting_init = &builder.formatting_init; @@ -114,25 +128,41 @@ impl<'a> LintDiagnosticDerive<'a> { let msg = builder.each_variant(&mut structure, |mut builder, variant| { // Collect the slug by generating the preamble. - let _ = builder.preamble(&variant); + let _ = builder.preamble(variant); match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") - .help(&format!( + .help(format!( "specify the slug as the first argument to the attribute, such as \ `#[diag(compiletest_example)]`", )) .emit(); - return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + DiagnosticDeriveError::ErrorHandled.to_compile_error() + } + Some(slug) if let Some( Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { + span_err(slug.span().unwrap(), "diagnostic slug and crate name do not match") + .note(format!( + "slug is `{slug_name}` but the crate name is `{crate_name}`" + )) + .help(format!( + "expected a slug starting with `{slug_prefix}_...`" + )) + .emit(); + DiagnosticDeriveError::ErrorHandled.to_compile_error() + } + Some(slug) => { + quote! { + rustc_errors::fluent::#slug.into() + } } - Some(slug) => quote! { rustc_errors::fluent::#slug.into() }, } }); let diag = &builder.diag; structure.gen_impl(quote! { gen impl<'__a> rustc_errors::DecorateLint<'__a, ()> for @Self { + #[track_caller] fn decorate_lint<'__b>( self, #diag: &'__b mut rustc_errors::DiagnosticBuilder<'__a, ()> @@ -148,3 +178,27 @@ impl<'a> LintDiagnosticDerive<'a> { }) } } + +struct Mismatch { + slug_name: String, + crate_name: String, + slug_prefix: String, +} + +impl Mismatch { + /// Checks whether the slug starts with the crate name it's in. + fn check(slug: &syn::Path) -> Option { + // If this is missing we're probably in a test, so bail. + let crate_name = std::env::var("CARGO_CRATE_NAME").ok()?; + + // If we're not in a "rustc_" crate, bail. + let Some(("rustc", slug_prefix)) = crate_name.split_once("_") else { return None }; + + let slug_name = slug.segments.first()?.ident.to_string(); + if !slug_name.starts_with(slug_prefix) { + Some(Mismatch { slug_name, slug_prefix: slug_prefix.to_string(), crate_name }) + } else { + None + } + } +} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 3ea83fd09..9f2ac5112 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -100,7 +100,7 @@ impl DiagnosticDeriveBuilder { _ => variant.ast().ident.span().unwrap(), }; let builder = DiagnosticDeriveVariantBuilder { - parent: &self, + parent: self, span, field_map: build_field_mapping(variant), formatting_init: TokenStream::new(), @@ -211,7 +211,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { nested_iter.next(); } Some(NestedMeta::Meta(Meta::NameValue { .. })) => {} - Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| diag + 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")), @@ -227,13 +227,13 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { .. })) => (value, path), NestedMeta::Meta(Meta::Path(_)) => { - invalid_nested_attr(attr, &nested_attr) + invalid_nested_attr(attr, nested_attr) .help("diagnostic slug must be the first argument") .emit(); continue; } _ => { - invalid_nested_attr(attr, &nested_attr).emit(); + invalid_nested_attr(attr, nested_attr).emit(); continue; } }; @@ -251,7 +251,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); }); } - _ => invalid_nested_attr(attr, &nested_attr) + _ => invalid_nested_attr(attr, nested_attr) .help("only `code` is a valid nested attributes following the slug") .emit(), } @@ -427,9 +427,9 @@ 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, &["rustc_span", "Span"]) { + if type_matches_path(info.ty, &["rustc_span", "Span"]) { Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) - } else if type_is_unit(&info.ty) { + } else if type_is_unit(info.ty) { Ok(self.add_subdiagnostic(&fn_ident, slug)) } else { report_type_error(attr, "`Span` or `()`")? diff --git a/compiler/rustc_macros/src/diagnostics/error.rs b/compiler/rustc_macros/src/diagnostics/error.rs index 0b1ededa7..4612f54e4 100644 --- a/compiler/rustc_macros/src/diagnostics/error.rs +++ b/compiler/rustc_macros/src/diagnostics/error.rs @@ -84,7 +84,7 @@ pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { } } -/// Emit a error diagnostic for an invalid attribute (optionally performing additional decoration +/// Emit an error diagnostic for an invalid attribute (optionally performing additional decoration /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. /// /// For methods that return a `Result<_, DiagnosticDeriveError>`: @@ -126,7 +126,7 @@ pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diag } } -/// Emit a error diagnostic for an invalid nested attribute (optionally performing additional +/// Emit an error diagnostic for an invalid nested attribute (optionally performing additional /// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. /// /// For methods that return a `Result<_, DiagnosticDeriveError>`: diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 860340b43..78df0cd1d 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -129,7 +129,7 @@ pub fn lint_diagnostic_derive(s: Structure<'_>) -> TokenStream { /// } /// /// #[derive(Subdiagnostic)] -/// #[suggestion_verbose(parser::raw_identifier)] +/// #[suggestion(style = "verbose",parser::raw_identifier)] /// pub struct RawIdentifierSuggestion<'tcx> { /// #[primary_span] /// span: Span, diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index fa0ca5a52..446aebe4f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -409,7 +409,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut code = None; for nested_attr in list.nested.iter() { let NestedMeta::Meta(ref meta) = nested_attr else { - throw_invalid_nested_attr!(attr, &nested_attr); + throw_invalid_nested_attr!(attr, nested_attr); }; let span = meta.span().unwrap(); @@ -427,7 +427,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { ); code.set_once((code_field, formatting_init), span); } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + _ => throw_invalid_nested_attr!(attr, nested_attr, |diag| { diag.help("`code` is the only valid nested attribute") }), } diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 374c795d0..da9023352 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -12,7 +12,7 @@ use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple}; use syn::{MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, VariantInfo}; -use super::error::invalid_nested_attr; +use super::error::{invalid_attr, invalid_nested_attr}; thread_local! { pub static CODE_IDENT_COUNT: RefCell = RefCell::new(0); @@ -80,7 +80,7 @@ fn report_error_if_not_applied_to_ty( path: &[&str], ty_name: &str, ) -> Result<(), DiagnosticDeriveError> { - if !type_matches_path(&info.ty, path) { + if !type_matches_path(info.ty, path) { report_type_error(attr, ty_name)?; } @@ -105,8 +105,8 @@ pub(crate) fn report_error_if_not_applied_to_span( attr: &Attribute, info: &FieldInfo<'_>, ) -> Result<(), DiagnosticDeriveError> { - if !type_matches_path(&info.ty, &["rustc_span", "Span"]) - && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"]) + if !type_matches_path(info.ty, &["rustc_span", "Span"]) + && !type_matches_path(info.ty, &["rustc_errors", "MultiSpan"]) { report_type_error(attr, "`Span` or `MultiSpan`")?; } @@ -472,16 +472,13 @@ pub(super) fn build_suggestion_code( } /// Possible styles for suggestion subdiagnostics. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub(super) enum SuggestionKind { - /// `#[suggestion]` Normal, - /// `#[suggestion_short]` Short, - /// `#[suggestion_hidden]` Hidden, - /// `#[suggestion_verbose]` Verbose, + ToolOnly, } impl FromStr for SuggestionKind { @@ -489,15 +486,28 @@ impl FromStr for SuggestionKind { fn from_str(s: &str) -> Result { match s { - "" => Ok(SuggestionKind::Normal), - "_short" => Ok(SuggestionKind::Short), - "_hidden" => Ok(SuggestionKind::Hidden), - "_verbose" => Ok(SuggestionKind::Verbose), + "normal" => Ok(SuggestionKind::Normal), + "short" => Ok(SuggestionKind::Short), + "hidden" => Ok(SuggestionKind::Hidden), + "verbose" => Ok(SuggestionKind::Verbose), + "tool-only" => Ok(SuggestionKind::ToolOnly), _ => Err(()), } } } +impl fmt::Display for SuggestionKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SuggestionKind::Normal => write!(f, "normal"), + SuggestionKind::Short => write!(f, "short"), + SuggestionKind::Hidden => write!(f, "hidden"), + SuggestionKind::Verbose => write!(f, "verbose"), + SuggestionKind::ToolOnly => write!(f, "tool-only"), + } + } +} + impl SuggestionKind { pub fn to_suggestion_style(&self) -> TokenStream { match self { @@ -513,6 +523,19 @@ impl SuggestionKind { SuggestionKind::Verbose => { quote! { rustc_errors::SuggestionStyle::ShowAlways } } + SuggestionKind::ToolOnly => { + quote! { rustc_errors::SuggestionStyle::CompletelyHidden } + } + } + } + + fn from_suffix(s: &str) -> Option { + match s { + "" => Some(SuggestionKind::Normal), + "_short" => Some(SuggestionKind::Short), + "_hidden" => Some(SuggestionKind::Hidden), + "_verbose" => Some(SuggestionKind::Verbose), + _ => None, } } } @@ -565,25 +588,49 @@ impl SubdiagnosticKind { let name = name.as_str(); let meta = attr.parse_meta()?; + let mut kind = match name { "label" => SubdiagnosticKind::Label, "note" => SubdiagnosticKind::Note, "help" => SubdiagnosticKind::Help, "warning" => SubdiagnosticKind::Warn, _ => { + // Recover old `#[(multipart_)suggestion_*]` syntaxes + // FIXME(#100717): remove if let Some(suggestion_kind) = - name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) + name.strip_prefix("suggestion").and_then(SuggestionKind::from_suffix) { + if suggestion_kind != SuggestionKind::Normal { + invalid_attr(attr, &meta) + .help(format!( + r#"Use `#[suggestion(..., style = "{}")]` instead"#, + suggestion_kind + )) + .emit(); + } + SubdiagnosticKind::Suggestion { - suggestion_kind, + suggestion_kind: SuggestionKind::Normal, applicability: None, code_field: new_code_ident(), code_init: TokenStream::new(), } } else if let Some(suggestion_kind) = - name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) + name.strip_prefix("multipart_suggestion").and_then(SuggestionKind::from_suffix) { - SubdiagnosticKind::MultipartSuggestion { suggestion_kind, applicability: None } + if suggestion_kind != SuggestionKind::Normal { + invalid_attr(attr, &meta) + .help(format!( + r#"Use `#[multipart_suggestion(..., style = "{}")]` instead"#, + suggestion_kind + )) + .emit(); + } + + SubdiagnosticKind::MultipartSuggestion { + suggestion_kind: SuggestionKind::Normal, + applicability: None, + } } else { throw_invalid_attr!(attr, &meta); } @@ -621,6 +668,7 @@ impl SubdiagnosticKind { }; let mut code = None; + let mut suggestion_kind = None; let mut nested_iter = nested.into_iter().peekable(); @@ -638,7 +686,7 @@ impl SubdiagnosticKind { let meta = match nested_attr { NestedMeta::Meta(ref meta) => meta, NestedMeta::Lit(_) => { - invalid_nested_attr(attr, &nested_attr).emit(); + invalid_nested_attr(attr, nested_attr).emit(); continue; } }; @@ -650,7 +698,7 @@ impl SubdiagnosticKind { let string_value = match meta { Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => Some(value), - Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + Meta::Path(_) => throw_invalid_nested_attr!(attr, nested_attr, |diag| { diag.help("a diagnostic slug must be the first argument to the attribute") }), _ => None, @@ -672,7 +720,7 @@ impl SubdiagnosticKind { | SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. }, ) => { let Some(value) = string_value else { - invalid_nested_attr(attr, &nested_attr).emit(); + invalid_nested_attr(attr, nested_attr).emit(); continue; }; @@ -682,26 +730,56 @@ impl SubdiagnosticKind { }); applicability.set_once(value, span); } + ( + "style", + SubdiagnosticKind::Suggestion { .. } + | SubdiagnosticKind::MultipartSuggestion { .. }, + ) => { + let Some(value) = string_value else { + invalid_nested_attr(attr, nested_attr).emit(); + continue; + }; + + let value = value.value().parse().unwrap_or_else(|()| { + span_err(value.span().unwrap(), "invalid suggestion style") + .help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`") + .emit(); + SuggestionKind::Normal + }); + + suggestion_kind.set_once(value, span); + } // Invalid nested attribute (_, SubdiagnosticKind::Suggestion { .. }) => { - invalid_nested_attr(attr, &nested_attr) - .help("only `code` and `applicability` are valid nested attributes") + invalid_nested_attr(attr, nested_attr) + .help( + "only `style`, `code` and `applicability` are valid nested attributes", + ) .emit(); } (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { - invalid_nested_attr(attr, &nested_attr) - .help("only `applicability` is a valid nested attributes") + invalid_nested_attr(attr, nested_attr) + .help("only `style` and `applicability` are valid nested attributes") .emit() } _ => { - invalid_nested_attr(attr, &nested_attr).emit(); + invalid_nested_attr(attr, nested_attr).emit(); } } } match kind { - SubdiagnosticKind::Suggestion { ref code_field, ref mut code_init, .. } => { + SubdiagnosticKind::Suggestion { + ref code_field, + ref mut code_init, + suggestion_kind: ref mut kind_field, + .. + } => { + if let Some(kind) = suggestion_kind.value() { + *kind_field = kind; + } + *code_init = if let Some(init) = code.value() { init } else { @@ -709,11 +787,17 @@ impl SubdiagnosticKind { quote! { let #code_field = std::iter::empty(); } }; } + SubdiagnosticKind::MultipartSuggestion { + suggestion_kind: ref mut kind_field, .. + } => { + if let Some(kind) = suggestion_kind.value() { + *kind_field = kind; + } + } SubdiagnosticKind::Label | SubdiagnosticKind::Note | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::MultipartSuggestion { .. } => {} + | SubdiagnosticKind::Warn => {} } Ok(Some((kind, slug))) @@ -746,5 +830,5 @@ pub(super) fn should_generate_set_arg(field: &Field) -> bool { } pub(super) fn is_doc_comment(attr: &Attribute) -> bool { - attr.path.segments.last().unwrap().ident.to_string() == "doc" + attr.path.segments.last().unwrap().ident == "doc" } -- cgit v1.2.3