summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_macros/src/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_macros/src/diagnostics')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs72
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs14
-rw-r--r--compiler/rustc_macros/src/diagnostics/error.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/mod.rs2
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs142
6 files changed, 188 insertions, 50 deletions
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<Mismatch> {
+ // 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<u32> = 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<Self, Self::Err> {
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<Self> {
+ 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"
}