summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs176
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(...)]`")