mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
macros: spanless subdiagnostics from ()
fields
Type attributes could previously be used to support spanless subdiagnostics but these couldn't easily be made optional in the same way that spanned subdiagnostics could by using a field attribute on a field with an `Option<Span>` type. Spanless subdiagnostics can now be specified on fields with `()` type or `Option<()>` type. Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
parent
1d2ea98cff
commit
7b7061dd89
@ -5,10 +5,10 @@ use crate::diagnostics::error::{
|
|||||||
SessionDiagnosticDeriveError,
|
SessionDiagnosticDeriveError,
|
||||||
};
|
};
|
||||||
use crate::diagnostics::utils::{
|
use crate::diagnostics::utils::{
|
||||||
report_error_if_not_applied_to_span, type_matches_path, Applicability, FieldInfo, FieldInnerTy,
|
report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path,
|
||||||
HasFieldMap, SetOnce,
|
Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce,
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::{Ident, TokenStream};
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
@ -388,7 +388,8 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
|
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
|
||||||
let diag = &self.diag;
|
let diag = &self.diag;
|
||||||
|
|
||||||
let name = attr.path.segments.last().unwrap().ident.to_string();
|
let ident = &attr.path.segments.last().unwrap().ident;
|
||||||
|
let name = ident.to_string();
|
||||||
let name = name.as_str();
|
let name = name.as_str();
|
||||||
|
|
||||||
let meta = attr.parse_meta()?;
|
let meta = attr.parse_meta()?;
|
||||||
@ -405,9 +406,18 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
#diag.set_span(#binding);
|
#diag.set_span(#binding);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
"label" | "note" | "help" => {
|
"label" => {
|
||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
Ok(self.add_subdiagnostic(binding, name, name))
|
Ok(self.add_spanned_subdiagnostic(binding, ident, name))
|
||||||
|
}
|
||||||
|
"note" | "help" => {
|
||||||
|
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
||||||
|
Ok(self.add_spanned_subdiagnostic(binding, ident, name))
|
||||||
|
} else if type_is_unit(&info.ty) {
|
||||||
|
Ok(self.add_subdiagnostic(ident, name))
|
||||||
|
} else {
|
||||||
|
report_type_error(attr, "`Span` or `()`")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
|
"subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }),
|
||||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
@ -416,9 +426,18 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
|
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
|
||||||
"label" | "note" | "help" => {
|
"label" => {
|
||||||
report_error_if_not_applied_to_span(attr, &info)?;
|
report_error_if_not_applied_to_span(attr, &info)?;
|
||||||
Ok(self.add_subdiagnostic(binding, name, &s.value()))
|
Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
|
||||||
|
}
|
||||||
|
"note" | "help" => {
|
||||||
|
if type_matches_path(&info.ty, &["rustc_span", "Span"]) {
|
||||||
|
Ok(self.add_spanned_subdiagnostic(binding, ident, &s.value()))
|
||||||
|
} else if type_is_unit(&info.ty) {
|
||||||
|
Ok(self.add_subdiagnostic(ident, &s.value()))
|
||||||
|
} else {
|
||||||
|
report_type_error(attr, "`Span` or `()`")?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||||
diag.help("only `label`, `note` and `help` are valid field attributes")
|
diag.help("only `label`, `note` and `help` are valid field attributes")
|
||||||
@ -510,12 +529,12 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
|
/// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug
|
||||||
/// `fluent_attr_identifier`.
|
/// and `fluent_attr_identifier`.
|
||||||
fn add_subdiagnostic(
|
fn add_spanned_subdiagnostic(
|
||||||
&self,
|
&self,
|
||||||
field_binding: TokenStream,
|
field_binding: TokenStream,
|
||||||
kind: &str,
|
kind: &Ident,
|
||||||
fluent_attr_identifier: &str,
|
fluent_attr_identifier: &str,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let diag = &self.diag;
|
let diag = &self.diag;
|
||||||
@ -531,6 +550,16 @@ impl SessionDiagnosticDeriveBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug
|
||||||
|
/// and `fluent_attr_identifier`.
|
||||||
|
fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: &str) -> TokenStream {
|
||||||
|
let diag = &self.diag;
|
||||||
|
let slug = self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or("missing-slug");
|
||||||
|
quote! {
|
||||||
|
#diag.#kind(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn span_and_applicability_of_ty(
|
fn span_and_applicability_of_ty(
|
||||||
&self,
|
&self,
|
||||||
info: FieldInfo<'_>,
|
info: FieldInfo<'_>,
|
||||||
|
@ -4,7 +4,7 @@ use proc_macro2::TokenStream;
|
|||||||
use quote::{format_ident, quote, ToTokens};
|
use quote::{format_ident, quote, ToTokens};
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
|
use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple, Visibility};
|
||||||
use synstructure::BindingInfo;
|
use synstructure::BindingInfo;
|
||||||
|
|
||||||
/// Checks whether the type name of `ty` matches `name`.
|
/// Checks whether the type name of `ty` matches `name`.
|
||||||
@ -25,22 +25,23 @@ pub(crate) fn type_matches_path(ty: &Type, name: &[&str]) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reports an error if the field's type is not `Applicability`.
|
/// Checks whether the type `ty` is `()`.
|
||||||
fn report_error_if_not_applied_to_ty(
|
pub(crate) fn type_is_unit(ty: &Type) -> bool {
|
||||||
|
if let Type::Tuple(TypeTuple { elems, .. }) = ty { elems.is_empty() } else { false }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reports a type error for field with `attr`.
|
||||||
|
pub(crate) fn report_type_error(
|
||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
info: &FieldInfo<'_>,
|
|
||||||
path: &[&str],
|
|
||||||
ty_name: &str,
|
ty_name: &str,
|
||||||
) -> Result<(), SessionDiagnosticDeriveError> {
|
) -> Result<!, SessionDiagnosticDeriveError> {
|
||||||
if !type_matches_path(&info.ty, path) {
|
|
||||||
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()?;
|
let meta = attr.parse_meta()?;
|
||||||
|
|
||||||
throw_span_err!(
|
throw_span_err!(
|
||||||
attr.span().unwrap(),
|
attr.span().unwrap(),
|
||||||
&format!(
|
&format!(
|
||||||
"the `#[{}{}]` attribute can only be applied to fields of type `{}`",
|
"the `#[{}{}]` attribute can only be applied to fields of type {}",
|
||||||
name,
|
name,
|
||||||
match meta {
|
match meta {
|
||||||
Meta::Path(_) => "",
|
Meta::Path(_) => "",
|
||||||
@ -52,6 +53,17 @@ fn report_error_if_not_applied_to_ty(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Reports an error if the field's type does not match `path`.
|
||||||
|
fn report_error_if_not_applied_to_ty(
|
||||||
|
attr: &Attribute,
|
||||||
|
info: &FieldInfo<'_>,
|
||||||
|
path: &[&str],
|
||||||
|
ty_name: &str,
|
||||||
|
) -> Result<(), SessionDiagnosticDeriveError> {
|
||||||
|
if !type_matches_path(&info.ty, path) {
|
||||||
|
report_type_error(attr, ty_name)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +76,7 @@ pub(crate) fn report_error_if_not_applied_to_applicability(
|
|||||||
attr,
|
attr,
|
||||||
info,
|
info,
|
||||||
&["rustc_errors", "Applicability"],
|
&["rustc_errors", "Applicability"],
|
||||||
"Applicability",
|
"`Applicability`",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +85,7 @@ pub(crate) fn report_error_if_not_applied_to_span(
|
|||||||
attr: &Attribute,
|
attr: &Attribute,
|
||||||
info: &FieldInfo<'_>,
|
info: &FieldInfo<'_>,
|
||||||
) -> Result<(), SessionDiagnosticDeriveError> {
|
) -> Result<(), SessionDiagnosticDeriveError> {
|
||||||
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
|
report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "`Span`")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Inner type of a field and type of wrapper.
|
/// Inner type of a field and type of wrapper.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![feature(allow_internal_unstable)]
|
#![feature(allow_internal_unstable)]
|
||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
|
#![feature(never_type)]
|
||||||
#![feature(proc_macro_diagnostic)]
|
#![feature(proc_macro_diagnostic)]
|
||||||
#![allow(rustc::default_hash_types)]
|
#![allow(rustc::default_hash_types)]
|
||||||
#![recursion_limit = "128"]
|
#![recursion_limit = "128"]
|
||||||
|
@ -482,3 +482,25 @@ struct VecField {
|
|||||||
#[label]
|
#[label]
|
||||||
spans: Vec<Span>,
|
spans: Vec<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(SessionDiagnostic)]
|
||||||
|
#[error(code = "E0123", slug = "foo")]
|
||||||
|
struct UnitField {
|
||||||
|
#[primary_span]
|
||||||
|
spans: Span,
|
||||||
|
#[help]
|
||||||
|
foo: (),
|
||||||
|
#[help = "a"]
|
||||||
|
bar: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(SessionDiagnostic)]
|
||||||
|
#[error(code = "E0123", slug = "foo")]
|
||||||
|
struct OptUnitField {
|
||||||
|
#[primary_span]
|
||||||
|
spans: Span,
|
||||||
|
#[help]
|
||||||
|
foo: Option<()>,
|
||||||
|
#[help = "a"]
|
||||||
|
bar: Option<()>,
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user