about summary refs log tree commit diff
path: root/compiler/rustc_macros/src/diagnostics
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-16 20:31:42 +0000
committerbors <bors@rust-lang.org>2022-07-16 20:31:42 +0000
commitd5e7f4782e4b699728d0a08200ecd1a54d56a85d (patch)
tree2ecfa500407be289cf19a91c39d2ce7bdd511731 /compiler/rustc_macros/src/diagnostics
parent7210e46dc69a4b197a313d093fe145722c248b7d (diff)
parent6277ac2fb8bf97cd910a0a841c6924b246d32c44 (diff)
downloadrust-d5e7f4782e4b699728d0a08200ecd1a54d56a85d.tar.gz
rust-d5e7f4782e4b699728d0a08200ecd1a54d56a85d.zip
Auto merge of #99346 - matthiaskrgr:rollup-p4dl1qt, r=matthiaskrgr
Rollup of 10 pull requests

Successful merges:

 - #98582 (Allow destructuring opaque types in their defining scopes)
 - #99213 (migrate some of `rustc_passes::check_attr`'s diagnostics and derive improvements)
 - #99258 (Provide structured suggestion for dropped temp value)
 - #99259 (interpret/visitor: support visiting with a PlaceTy)
 - #99287 ([rustdoc-json] JSON no longer inlines)
 - #99290 (Revert "Highlight conflicting param-env candidates")
 - #99316 (docs: add missing word)
 - #99317 (Borrow Vec<T, A> as [T])
 - #99323 (Fix flakyness of GUI tests)
 - #99342 (Avoid some `Symbol` to `String` conversions)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler/rustc_macros/src/diagnostics')
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs2
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs190
-rw-r--r--compiler/rustc_macros/src/diagnostics/fluent.rs6
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs4
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs8
5 files changed, 131 insertions, 79 deletions
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index 027f377b0ac..6b5b8b59320 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -59,7 +59,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
                         return DiagnosticDeriveError::ErrorHandled.to_compile_error();
                     }
                     (Some(DiagnosticDeriveKind::Lint), _) => {
-                        span_err(span, "only `#[error(..)]` and `#[warn(..)]` are supported")
+                        span_err(span, "only `#[error(..)]` and `#[warning(..)]` are supported")
                             .help("use the `#[error(...)]` attribute to create a error")
                             .emit();
                         return DiagnosticDeriveError::ErrorHandled.to_compile_error();
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 74ce1ab08c2..5c5275b7cfb 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -8,12 +8,13 @@ use crate::diagnostics::utils::{
     report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
     Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
 };
-use proc_macro2::{Ident, TokenStream};
+use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote};
 use std::collections::HashMap;
 use std::str::FromStr;
 use syn::{
-    parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type,
+    parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta,
+    Path, Type,
 };
 use synstructure::{BindingInfo, Structure};
 
@@ -80,8 +81,8 @@ impl DiagnosticDeriveBuilder {
     }
 
     pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) {
-        // Keep track of which fields are subdiagnostics or have no attributes.
-        let mut subdiagnostics_or_empty = std::collections::HashSet::new();
+        // Keep track of which fields need to be handled with a by-move binding.
+        let mut needs_moved = std::collections::HashSet::new();
 
         // Generates calls to `span_label` and similar functions based on the attributes
         // on fields. Code for suggestions uses formatting machinery and the value of
@@ -92,16 +93,11 @@ impl DiagnosticDeriveBuilder {
         let attrs = structure
             .clone()
             .filter(|field_binding| {
-                let attrs = &field_binding.ast().attrs;
-
-                (!attrs.is_empty()
-                    && attrs.iter().all(|attr| {
-                        "subdiagnostic" != attr.path.segments.last().unwrap().ident.to_string()
-                    }))
-                    || {
-                        subdiagnostics_or_empty.insert(field_binding.binding.clone());
-                        false
-                    }
+                let ast = &field_binding.ast();
+                !self.needs_move(ast) || {
+                    needs_moved.insert(field_binding.binding.clone());
+                    false
+                }
             })
             .each(|field_binding| self.generate_field_attrs_code(field_binding));
 
@@ -111,12 +107,41 @@ impl DiagnosticDeriveBuilder {
         // attributes or a `#[subdiagnostic]` attribute then it must be passed as an
         // argument to the diagnostic so that it can be referred to by Fluent messages.
         let args = structure
-            .filter(|field_binding| subdiagnostics_or_empty.contains(&field_binding.binding))
+            .filter(|field_binding| needs_moved.contains(&field_binding.binding))
             .each(|field_binding| self.generate_field_attrs_code(field_binding));
 
         (attrs, args)
     }
 
+    /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
+    /// call (like `span_label`).
+    fn should_generate_set_arg(&self, field: &Field) -> bool {
+        field.attrs.is_empty()
+    }
+
+    /// Returns `true` if `field` needs to have code generated in the by-move branch of the
+    /// generated derive rather than the by-ref branch.
+    fn needs_move(&self, field: &Field) -> bool {
+        let generates_set_arg = self.should_generate_set_arg(field);
+        let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]);
+        // FIXME(davidtwco): better support for one field needing to be in the by-move and
+        // by-ref branches.
+        let is_subdiagnostic = field
+            .attrs
+            .iter()
+            .map(|attr| attr.path.segments.last().unwrap().ident.to_string())
+            .any(|attr| attr == "subdiagnostic");
+
+        // `set_arg` calls take their argument by-move..
+        generates_set_arg
+            // If this is a `MultiSpan` field then it needs to be moved to be used by any
+            // attribute..
+            || is_multispan
+            // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is
+            // unlikely to be `Copy`..
+            || is_subdiagnostic
+    }
+
     /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
     /// attributes like `#[error(..)`, such as the diagnostic kind and slug. Generates
     /// diagnostic builder calls for setting error code and creating note/help messages.
@@ -131,7 +156,7 @@ impl DiagnosticDeriveBuilder {
         let name = name.as_str();
         let meta = attr.parse_meta()?;
 
-        let is_help_or_note = matches!(name, "help" | "note");
+        let is_help_note_or_warn = matches!(name, "help" | "note" | "warn_");
 
         let nested = match meta {
             // Most attributes are lists, like `#[error(..)]`/`#[warning(..)]` for most cases or
@@ -139,8 +164,12 @@ impl DiagnosticDeriveBuilder {
             Meta::List(MetaList { ref nested, .. }) => nested,
             // Subdiagnostics without spans can be applied to the type too, and these are just
             // paths: `#[help]` and `#[note]`
-            Meta::Path(_) if is_help_or_note => {
-                let fn_name = proc_macro2::Ident::new(name, attr.span());
+            Meta::Path(_) if is_help_note_or_warn => {
+                let fn_name = if name == "warn_" {
+                    Ident::new("warn", attr.span())
+                } else {
+                    Ident::new(name, attr.span())
+                };
                 return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); });
             }
             _ => throw_invalid_attr!(attr, &meta),
@@ -152,9 +181,11 @@ impl DiagnosticDeriveBuilder {
             "error" => self.kind.set_once((DiagnosticDeriveKind::Error, span)),
             "warning" => self.kind.set_once((DiagnosticDeriveKind::Warn, span)),
             "lint" => self.kind.set_once((DiagnosticDeriveKind::Lint, span)),
-            "help" | "note" => (),
+            "help" | "note" | "warn_" => (),
             _ => throw_invalid_attr!(attr, &meta, |diag| {
-                diag.help("only `error`, `warning`, `help` and `note` are valid attributes")
+                diag.help(
+                    "only `error`, `warning`, `help`, `note` and `warn_` are valid attributes",
+                )
             }),
         }
 
@@ -163,14 +194,16 @@ impl DiagnosticDeriveBuilder {
         let mut nested_iter = nested.into_iter();
         if let Some(nested_attr) = nested_iter.next() {
             // Report an error if there are any other list items after the path.
-            if is_help_or_note && nested_iter.next().is_some() {
+            if is_help_note_or_warn && nested_iter.next().is_some() {
                 throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
-                    diag.help("`help` and `note` struct attributes can only have one argument")
+                    diag.help(
+                        "`help`, `note` and `warn_` struct attributes can only have one argument",
+                    )
                 });
             }
 
             match nested_attr {
-                NestedMeta::Meta(Meta::Path(path)) if is_help_or_note => {
+                NestedMeta::Meta(Meta::Path(path)) if is_help_note_or_warn => {
                     let fn_name = proc_macro2::Ident::new(name, attr.span());
                     return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); });
                 }
@@ -178,7 +211,7 @@ impl DiagnosticDeriveBuilder {
                     self.slug.set_once((path.clone(), span));
                 }
                 NestedMeta::Meta(meta @ Meta::NameValue(_))
-                    if !is_help_or_note
+                    if !is_help_note_or_warn
                         && meta.path().segments.last().unwrap().ident.to_string() == "code" =>
                 {
                     // don't error for valid follow-up attributes
@@ -227,57 +260,55 @@ impl DiagnosticDeriveBuilder {
         let field = binding_info.ast();
         let field_binding = &binding_info.binding;
 
-        let inner_ty = FieldInnerTy::from_type(&field.ty);
-
-        // When generating `set_arg` or `add_subdiagnostic` calls, move data rather than
-        // borrow it to avoid requiring clones - this must therefore be the last use of
-        // each field (for example, any formatting machinery that might refer to a field
-        // should be generated already).
-        if field.attrs.is_empty() {
+        if self.should_generate_set_arg(&field) {
             let diag = &self.diag;
             let ident = field.ident.as_ref().unwrap();
-            quote! {
+            return quote! {
                 #diag.set_arg(
                     stringify!(#ident),
                     #field_binding
                 );
-            }
-        } else {
-            field
-                .attrs
-                .iter()
-                .map(move |attr| {
-                    let name = attr.path.segments.last().unwrap().ident.to_string();
-                    let (binding, needs_destructure) = match (name.as_str(), &inner_ty) {
-                        // `primary_span` can accept a `Vec<Span>` so don't destructure that.
-                        ("primary_span", FieldInnerTy::Vec(_)) => {
-                            (quote! { #field_binding.clone() }, false)
-                        }
-                        // `subdiagnostics` are not derefed because they are bound by value.
-                        ("subdiagnostic", _) => (quote! { #field_binding }, true),
-                        _ => (quote! { *#field_binding }, true),
-                    };
-
-                    let generated_code = self
-                        .generate_inner_field_code(
-                            attr,
-                            FieldInfo {
-                                binding: binding_info,
-                                ty: inner_ty.inner_type().unwrap_or(&field.ty),
-                                span: &field.span(),
-                            },
-                            binding,
-                        )
-                        .unwrap_or_else(|v| v.to_compile_error());
-
-                    if needs_destructure {
-                        inner_ty.with(field_binding, generated_code)
-                    } else {
-                        generated_code
-                    }
-                })
-                .collect()
+            };
         }
+
+        let needs_move = self.needs_move(&field);
+        let inner_ty = FieldInnerTy::from_type(&field.ty);
+
+        field
+            .attrs
+            .iter()
+            .map(move |attr| {
+                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 {
+                    // `primary_span` can accept a `Vec<Span>` so don't destructure that.
+                    (quote! { #field_binding.clone() }, false)
+                } else if needs_move {
+                    (quote! { #field_binding }, true)
+                } else {
+                    (quote! { *#field_binding }, true)
+                };
+
+                let generated_code = self
+                    .generate_inner_field_code(
+                        attr,
+                        FieldInfo {
+                            binding: binding_info,
+                            ty: inner_ty.inner_type().unwrap_or(&field.ty),
+                            span: &field.span(),
+                        },
+                        binding,
+                    )
+                    .unwrap_or_else(|v| v.to_compile_error());
+
+                if needs_destructure {
+                    inner_ty.with(field_binding, generated_code)
+                } else {
+                    generated_code
+                }
+            })
+            .collect()
     }
 
     fn generate_inner_field_code(
@@ -324,10 +355,12 @@ impl DiagnosticDeriveBuilder {
                 report_error_if_not_applied_to_span(attr, &info)?;
                 Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label }))
             }
-            "note" | "help" => {
-                let path = match name {
-                    "note" => parse_quote! { _subdiag::note },
-                    "help" => parse_quote! { _subdiag::help },
+            "note" | "help" | "warn_" => {
+                let warn_ident = Ident::new("warn", Span::call_site());
+                let (ident, path) = match name {
+                    "note" => (ident, parse_quote! { _subdiag::note }),
+                    "help" => (ident, parse_quote! { _subdiag::help }),
+                    "warn_" => (&warn_ident, parse_quote! { _subdiag::warn }),
                     _ => unreachable!(),
                 };
                 if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
@@ -364,10 +397,10 @@ impl DiagnosticDeriveBuilder {
             "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => {
                 return self.generate_inner_field_code_suggestion(attr, info);
             }
-            "label" | "help" | "note" => (),
+            "label" | "help" | "note" | "warn_" => (),
             _ => throw_invalid_attr!(attr, &meta, |diag| {
                 diag.help(
-                    "only `label`, `note`, `help` or `suggestion{,_short,_hidden,_verbose}` are \
+                    "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \
                      valid field attributes",
                 )
             }),
@@ -396,7 +429,14 @@ impl DiagnosticDeriveBuilder {
                 Ok(self.add_spanned_subdiagnostic(binding, ident, msg))
             }
             "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)),
-            "note" | "help" => report_type_error(attr, "`Span` or `()`")?,
+            // `warn_` must be special-cased because the attribute `warn` already has meaning and
+            // so isn't used, despite the diagnostic API being named `warn`.
+            "warn_" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self
+                .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)),
+            "warn_" if type_is_unit(&info.ty) => {
+                Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg))
+            }
+            "note" | "help" | "warn_" => report_type_error(attr, "`Span` or `()`")?,
             _ => unreachable!(),
         }
     }
diff --git a/compiler/rustc_macros/src/diagnostics/fluent.rs b/compiler/rustc_macros/src/diagnostics/fluent.rs
index 2758fcd1310..1170d2b3c59 100644
--- a/compiler/rustc_macros/src/diagnostics/fluent.rs
+++ b/compiler/rustc_macros/src/diagnostics/fluent.rs
@@ -260,10 +260,12 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
             #generated
 
             pub mod _subdiag {
-                pub const note: crate::SubdiagnosticMessage =
-                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
                 pub const help: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("help"));
+                pub const note: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("note"));
+                pub const warn: crate::SubdiagnosticMessage =
+                    crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("warn"));
                 pub const label: crate::SubdiagnosticMessage =
                     crate::SubdiagnosticMessage::FluentAttr(std::borrow::Cow::Borrowed("label"));
                 pub const suggestion: crate::SubdiagnosticMessage =
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 2a5b6beba94..edf4dbed985 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -37,6 +37,8 @@ enum SubdiagnosticKind {
     Note,
     /// `#[help(...)]`
     Help,
+    /// `#[warn_(...)]`
+    Warn,
     /// `#[suggestion{,_short,_hidden,_verbose}]`
     Suggestion(SubdiagnosticSuggestionKind),
 }
@@ -49,6 +51,7 @@ impl FromStr for SubdiagnosticKind {
             "label" => Ok(SubdiagnosticKind::Label),
             "note" => Ok(SubdiagnosticKind::Note),
             "help" => Ok(SubdiagnosticKind::Help),
+            "warn_" => Ok(SubdiagnosticKind::Warn),
             "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
             "suggestion_short" => {
                 Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
@@ -70,6 +73,7 @@ impl quote::IdentFragment for SubdiagnosticKind {
             SubdiagnosticKind::Label => write!(f, "label"),
             SubdiagnosticKind::Note => write!(f, "note"),
             SubdiagnosticKind::Help => write!(f, "help"),
+            SubdiagnosticKind::Warn => write!(f, "warn"),
             SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
                 write!(f, "suggestion")
             }
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 8977db4606c..002abb152f7 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -85,7 +85,13 @@ pub(crate) fn report_error_if_not_applied_to_span(
     attr: &Attribute,
     info: &FieldInfo<'_>,
 ) -> Result<(), DiagnosticDeriveError> {
-    report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
+    if !type_matches_path(&info.ty, &["rustc_span", "Span"])
+        && !type_matches_path(&info.ty, &["rustc_errors", "MultiSpan"])
+    {
+        report_type_error(attr, "`Span` or `MultiSpan`")?;
+    }
+
+    Ok(())
 }
 
 /// Inner type of a field and type of wrapper.