diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index dcbe89251cb..9f7d2661a3e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -5,7 +5,7 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - build_field_mapping, report_error_if_not_applied_to_span, report_type_error, + 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, }; @@ -152,8 +152,12 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { fn parse_subdiag_attribute( &self, attr: &Attribute, - ) -> Result<(SubdiagnosticKind, Path), DiagnosticDeriveError> { - let (subdiag, slug) = SubdiagnosticKind::from_attr(attr, self)?; + ) -> Result, DiagnosticDeriveError> { + let Some((subdiag, slug)) = SubdiagnosticKind::from_attr(attr, self)? else { + // Some attributes aren't errors - like documentation comments - but also aren't + // subdiagnostics. + return Ok(None); + }; if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag { let meta = attr.parse_meta()?; @@ -170,7 +174,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), }); - Ok((subdiag, slug)) + Ok(Some((subdiag, slug))) } /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct @@ -182,6 +186,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { ) -> Result { let diag = &self.parent.diag; + // Always allow documentation comments. + if is_doc_comment(attr) { + return Ok(quote! {}); + } + let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); let meta = attr.parse_meta()?; @@ -250,7 +259,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { return Ok(tokens); } - let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; + let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else { + // Some attributes aren't errors - like documentation comments - but also aren't + // subdiagnostics. + return Ok(quote! {}); + }; let fn_ident = format_ident!("{}", subdiag); match subdiag { SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { @@ -291,6 +304,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { .attrs .iter() .map(move |attr| { + // Always allow documentation comments. + if is_doc_comment(attr) { + return quote! {}; + } + let name = attr.path.segments.last().unwrap().ident.to_string(); let needs_clone = name == "primary_span" && matches!(inner_ty, FieldInnerTy::Vec(_)); @@ -397,8 +415,11 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> { _ => (), } - let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; - + let Some((subdiag, slug)) = self.parse_subdiag_attribute(attr)? else { + // Some attributes aren't errors - like documentation comments - but also aren't + // subdiagnostics. + return Ok(quote! {}); + }; let fn_ident = format_ident!("{}", subdiag); match subdiag { SubdiagnosticKind::Label => { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 3d4c3ab9fd7..4fe02e81607 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -5,9 +5,9 @@ use crate::diagnostics::error::{ DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - build_field_mapping, new_code_ident, report_error_if_not_applied_to_applicability, - report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, - SpannedOption, SubdiagnosticKind, + build_field_mapping, is_doc_comment, new_code_ident, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo, + FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -43,6 +43,11 @@ impl SubdiagnosticDeriveBuilder { if matches!(ast.data, syn::Data::Enum(..)) { for attr in &ast.attrs { + // Always allow documentation comments. + if is_doc_comment(attr) { + continue; + } + span_err( attr.span().unwrap(), "unsupported type attribute for subdiagnostic enum", @@ -173,7 +178,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut kind_slugs = vec![]; for attr in self.variant.ast().attrs { - let (kind, slug) = SubdiagnosticKind::from_attr(attr, self)?; + let Some((kind, slug)) = SubdiagnosticKind::from_attr(attr, self)? else { + // Some attributes aren't errors - like documentation comments - but also aren't + // subdiagnostics. + continue; + }; let Some(slug) = slug else { let name = attr.path.segments.last().unwrap().ident.to_string(); @@ -227,6 +236,11 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { ast.attrs .iter() .map(|attr| { + // Always allow documentation comments. + if is_doc_comment(attr) { + return quote! {}; + } + let info = FieldInfo { binding, ty: inner_ty.inner_type().unwrap_or(&ast.ty), diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 4fd4adc5112..61d5007fc30 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -477,7 +477,12 @@ impl SubdiagnosticKind { pub(super) fn from_attr( attr: &Attribute, fields: &impl HasFieldMap, - ) -> Result<(SubdiagnosticKind, Option), DiagnosticDeriveError> { + ) -> Result)>, DiagnosticDeriveError> { + // Always allow documentation comments. + if is_doc_comment(attr) { + return Ok(None); + } + let span = attr.span().unwrap(); let name = attr.path.segments.last().unwrap().ident.to_string(); @@ -526,7 +531,9 @@ impl SubdiagnosticKind { | SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn - | SubdiagnosticKind::MultipartSuggestion { .. } => return Ok((kind, None)), + | SubdiagnosticKind::MultipartSuggestion { .. } => { + return Ok(Some((kind, None))); + } SubdiagnosticKind::Suggestion { .. } => { throw_span_err!(span, "suggestion without `code = \"...\"`") } @@ -626,7 +633,7 @@ impl SubdiagnosticKind { | SubdiagnosticKind::MultipartSuggestion { .. } => {} } - Ok((kind, slug)) + Ok(Some((kind, slug))) } } @@ -654,3 +661,7 @@ impl quote::IdentFragment for SubdiagnosticKind { pub(super) fn should_generate_set_arg(field: &Field) -> bool { field.attrs.is_empty() } + +pub(super) fn is_doc_comment(attr: &Attribute) -> bool { + attr.path.segments.last().unwrap().ident.to_string() == "doc" +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index e873c36e0b3..b8deb48d49a 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -749,3 +749,12 @@ struct SubdiagnosticEagerSuggestion { #[subdiagnostic(eager)] sub: SubdiagnosticWithSuggestion, } + +/// with a doc comment on the type.. +#[derive(Diagnostic)] +#[diag(compiletest::example, code = "E0123")] +struct WithDocComment { + /// ..and the field + #[primary_span] + span: Span, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 84ee5af42de..31aae7ba12d 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -641,3 +641,24 @@ struct BJ { span: Span, r#type: String, } + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +#[label(parser::add_paren)] +struct BK { + /// ..and the field + #[primary_span] + span: Span, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +enum BL { + /// ..and the variant.. + #[label(parser::add_paren)] + Foo { + /// ..and the field + #[primary_span] + span: Span, + } +}