1
Fork 0

macros: add helper functions for invalid attrs

Remove some duplicated code between both diagnostic derives by
introducing helper functions for reporting an error in case of a invalid
attribute.

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-04-27 04:06:13 +01:00
parent 071f07274b
commit 36a396ce51
7 changed files with 226 additions and 292 deletions

View file

@ -1,6 +1,9 @@
#![deny(unused_must_use)]
use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
use crate::diagnostics::error::{
invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
SessionDiagnosticDeriveError,
};
use crate::diagnostics::utils::{
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo, HasFieldMap,
};
@ -292,39 +295,24 @@ impl SessionDiagnosticDeriveBuilder {
}
let nested = match meta {
Meta::List(MetaList { nested, .. }) => nested,
Meta::Path(..) => throw_span_err!(
span,
&format!("`#[{}]` is not a valid `SessionDiagnostic` struct attribute", name)
),
Meta::NameValue(..) => throw_span_err!(
span,
&format!("`#[{} = ...]` is not a valid `SessionDiagnostic` struct attribute", name)
),
Meta::List(MetaList { ref nested, .. }) => nested,
_ => throw_invalid_attr!(attr, &meta),
};
let kind = match name {
"error" => SessionDiagnosticKind::Error,
"warning" => SessionDiagnosticKind::Warn,
other => throw_span_err!(
span,
&format!("`#[{}(...)]` is not a valid `SessionDiagnostic` struct attribute", other)
),
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help("only `error` and `warning` are valid attributes")
}),
};
self.set_kind_once(kind, span)?;
let mut tokens = Vec::new();
for attr in nested {
let span = attr.span().unwrap();
let meta = match attr {
for nested_attr in nested {
let meta = match nested_attr {
syn::NestedMeta::Meta(meta) => meta,
syn::NestedMeta::Lit(_) => throw_span_err!(
span,
&format!(
"`#[{}(\"...\")]` is not a valid `SessionDiagnostic` struct attribute",
name
)
),
_ => throw_invalid_nested_attr!(attr, &nested_attr),
};
let path = meta.path();
@ -340,49 +328,12 @@ impl SessionDiagnosticDeriveBuilder {
"code" => {
tokens.push(self.set_code_once(s.value(), s.span().unwrap()));
}
other => {
let diag = span_err(
span,
&format!(
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
name, other
),
);
diag.emit();
}
_ => invalid_nested_attr(attr, &nested_attr)
.help("only `slug` and `code` are valid nested attributes")
.emit(),
}
}
Meta::NameValue(..) => {
span_err(
span,
&format!(
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
name, nested_name
),
)
.help("value must be a string")
.emit();
}
Meta::Path(..) => {
span_err(
span,
&format!(
"`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
name, nested_name
),
)
.emit();
}
Meta::List(..) => {
span_err(
span,
&format!(
"`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
name, nested_name
),
)
.emit();
}
_ => invalid_nested_attr(attr, &nested_attr).emit(),
}
}
@ -478,7 +429,6 @@ impl SessionDiagnosticDeriveBuilder {
info: FieldInfo<'_>,
) -> Result<TokenStream, SessionDiagnosticDeriveError> {
let diag = &self.diag;
let span = attr.span().unwrap();
let field_binding = &info.binding.binding;
let name = attr.path.segments.last().unwrap().ident.to_string();
@ -502,43 +452,31 @@ impl SessionDiagnosticDeriveBuilder {
report_error_if_not_applied_to_span(attr, &info)?;
Ok(self.add_subdiagnostic(field_binding, name, name))
}
other => throw_span_err!(
span,
&format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
),
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag
.help("only `skip_arg`, `primary_span`, `label`, `note` and `help` are valid field attributes")
}),
},
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
"label" | "note" | "help" => {
report_error_if_not_applied_to_span(attr, &info)?;
Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
}
other => throw_span_err!(
span,
&format!(
"`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
other
)
),
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help("only `label`, `note` and `help` are valid field attributes")
}),
},
Meta::NameValue(_) => throw_span_err!(
span,
&format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
|diag| diag.help("value must be a string")
),
Meta::List(MetaList { path, nested, .. }) => {
Meta::List(MetaList { ref path, ref nested, .. }) => {
let name = path.segments.last().unwrap().ident.to_string();
let name = name.as_ref();
match name {
"suggestion" | "suggestion_short" | "suggestion_hidden"
| "suggestion_verbose" => (),
other => throw_span_err!(
span,
&format!(
"`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
other
)
),
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag
.help("only `suggestion{,_short,_hidden,_verbose}` are valid field attributes")
}),
};
let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
@ -546,22 +484,14 @@ impl SessionDiagnosticDeriveBuilder {
let mut msg = None;
let mut code = None;
for attr in nested {
let meta = match attr {
syn::NestedMeta::Meta(meta) => meta,
syn::NestedMeta::Lit(_) => throw_span_err!(
span,
&format!(
"`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
name
)
),
for nested_attr in nested {
let meta = match nested_attr {
syn::NestedMeta::Meta(ref meta) => meta,
syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr),
};
let span = meta.span().unwrap();
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
match meta {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
match nested_name {
@ -572,37 +502,14 @@ impl SessionDiagnosticDeriveBuilder {
let formatted_str = self.build_format(&s.value(), s.span());
code = Some(formatted_str);
}
other => throw_span_err!(
span,
&format!(
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
name, other
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help(
"only `message` and `code` are valid field attributes",
)
),
}),
}
}
Meta::NameValue(..) => throw_span_err!(
span,
&format!(
"`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
name, nested_name
),
|diag| diag.help("value must be a string")
),
Meta::Path(..) => throw_span_err!(
span,
&format!(
"`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
name, nested_name
)
),
Meta::List(..) => throw_span_err!(
span,
&format!(
"`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
name, nested_name
)
),
_ => throw_invalid_nested_attr!(attr, &nested_attr),
}
}
@ -619,6 +526,7 @@ impl SessionDiagnosticDeriveBuilder {
Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
}
_ => throw_invalid_attr!(attr, &meta),
}
}

View file

@ -1,11 +1,11 @@
use proc_macro::{Diagnostic, Level, MultiSpan};
use proc_macro2::TokenStream;
use quote::quote;
use syn;
use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta};
#[derive(Debug)]
pub(crate) enum SessionDiagnosticDeriveError {
SynError(syn::Error),
SynError(SynError),
ErrorHandled,
}
@ -24,37 +24,109 @@ impl SessionDiagnosticDeriveError {
}
}
impl From<SynError> for SessionDiagnosticDeriveError {
fn from(e: SynError) -> Self {
SessionDiagnosticDeriveError::SynError(e)
}
}
/// Helper function for use with `throw_*` macros - constraints `$f` to an `impl FnOnce`.
pub(crate) fn _throw_err(
diag: Diagnostic,
f: impl FnOnce(Diagnostic) -> Diagnostic,
) -> SessionDiagnosticDeriveError {
f(diag).emit();
SessionDiagnosticDeriveError::ErrorHandled
}
/// Returns an error diagnostic on span `span` with msg `msg`.
pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic {
Diagnostic::spanned(span, Level::Error, msg)
}
/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
///
/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
///
/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
macro_rules! throw_span_err {
($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
($span:expr, $msg:expr, $f:expr) => {{
return Err(crate::diagnostics::error::_throw_span_err($span, $msg, $f));
let diag = span_err($span, $msg);
return Err(crate::diagnostics::error::_throw_err(diag, $f));
}};
}
pub(crate) use throw_span_err;
/// When possible, prefer using `throw_span_err!` over using this function directly. This only
/// exists as a function to constrain `f` to an `impl FnOnce`.
pub(crate) fn _throw_span_err(
span: impl MultiSpan,
msg: &str,
f: impl FnOnce(Diagnostic) -> Diagnostic,
) -> SessionDiagnosticDeriveError {
let diag = span_err(span, msg);
f(diag).emit();
SessionDiagnosticDeriveError::ErrorHandled
}
/// Returns an error diagnostic for an invalid attribute.
pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic {
let span = attr.span().unwrap();
let name = attr.path.segments.last().unwrap().ident.to_string();
let name = name.as_str();
impl From<syn::Error> for SessionDiagnosticDeriveError {
fn from(e: syn::Error) -> Self {
SessionDiagnosticDeriveError::SynError(e)
match meta {
Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", name)),
Meta::NameValue(_) => {
span_err(span, &format!("`#[{} = ...]` is not a valid attribute", name))
}
Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", name)),
}
}
/// Emit a 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<_, SessionDiagnosticDeriveError>`:
macro_rules! throw_invalid_attr {
($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }};
($attr:expr, $meta:expr, $f:expr) => {{
let diag = crate::diagnostics::error::invalid_attr($attr, $meta);
return Err(crate::diagnostics::error::_throw_err(diag, $f));
}};
}
pub(crate) use throw_invalid_attr;
/// Returns an error diagnostic for an invalid nested attribute.
pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diagnostic {
let name = attr.path.segments.last().unwrap().ident.to_string();
let name = name.as_str();
let span = nested.span().unwrap();
let meta = match nested {
syn::NestedMeta::Meta(meta) => meta,
syn::NestedMeta::Lit(_) => {
return span_err(span, &format!("`#[{}(\"...\")]` is not a valid attribute", name));
}
};
let span = meta.span().unwrap();
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
match meta {
Meta::NameValue(..) => span_err(
span,
&format!("`#[{}({} = ...)]` is not a valid attribute", name, nested_name),
),
Meta::Path(..) => {
span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, nested_name))
}
Meta::List(..) => {
span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, nested_name))
}
}
}
/// Emit a 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<_, SessionDiagnosticDeriveError>`:
macro_rules! throw_invalid_nested_attr {
($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }};
($attr:expr, $nested_attr:expr, $f:expr) => {{
let diag = crate::diagnostics::error::invalid_nested_attr($attr, $nested_attr);
return Err(crate::diagnostics::error::_throw_err(diag, $f));
}};
}
pub(crate) use throw_invalid_nested_attr;

View file

@ -1,6 +1,9 @@
#![deny(unused_must_use)]
use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
use crate::diagnostics::error::{
span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
SessionDiagnosticDeriveError,
};
use crate::diagnostics::utils::{
option_inner_ty, report_error_if_not_applied_to_applicability,
report_error_if_not_applied_to_span, FieldInfo, HasFieldMap, SetOnce,
@ -253,25 +256,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
let meta = attr.parse_meta()?;
let kind = match meta {
Meta::Path(_) => throw_span_err!(
span,
&format!("`#[{}]` is not a valid `SessionSubdiagnostic` attribute", name)
),
Meta::NameValue(_) => throw_span_err!(
span,
&format!("`#[{} = ...]` is not a valid `SessionSubdiagnostic` attribute", name)
),
Meta::List(MetaList { nested, .. }) => {
for attr in nested {
let meta = match attr {
syn::NestedMeta::Meta(meta) => meta,
syn::NestedMeta::Lit(_) => throw_span_err!(
span,
&format!(
"`#[{}(\"...\")]` is not a valid `SessionSubdiagnostic` attribute",
name
)
),
Meta::List(MetaList { ref nested, .. }) => {
for nested_attr in nested {
let meta = match nested_attr {
syn::NestedMeta::Meta(ref meta) => meta,
_ => throw_invalid_nested_attr!(attr, &nested_attr),
};
let span = meta.span().unwrap();
@ -296,51 +285,22 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
};
self.applicability.set_once((quote! { #value }, span));
}
other => throw_span_err!(
span,
&format!(
"`#[{}({} = ...)]` is not a valid `SessionSubdiagnostic` attribute",
name, other
)
),
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help("only `code`, `slug` and `applicability` are valid nested attributes")
}),
}
}
Meta::NameValue(..) => throw_span_err!(
span,
&format!(
"`#[{}({} = ...)]` is not a valid `SessionSubdiagnostic` attribute",
name, nested_name
),
|diag| diag.help("value must be a string")
),
Meta::Path(..) => throw_span_err!(
span,
&format!(
"`#[{}({})]` is not a valid `SessionSubdiagnostic` attribute",
name, nested_name
)
),
Meta::List(..) => throw_span_err!(
span,
&format!(
"`#[{}({}(...))]` is not a valid `SessionSubdiagnostic` attribute",
name, nested_name
)
),
_ => throw_invalid_nested_attr!(attr, &nested_attr),
}
}
let Ok(kind) = SubdiagnosticKind::from_str(name) else {
throw_span_err!(
span,
&format!(
"`#[{}(...)]` is not a valid `SessionSubdiagnostic` attribute",
name
)
);
throw_invalid_attr!(attr, &meta)
};
kind
}
_ => throw_invalid_attr!(attr, &meta),
};
if matches!(
@ -408,31 +368,11 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
"skip_arg" => {
return Ok(quote! {});
}
other => span_err(
span,
&format!(
"`#[{}]` is not a valid `SessionSubdiagnostic` field attribute",
other
),
)
.emit(),
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help("only `primary_span`, `applicability` and `skip_arg` are valid field attributes")
}),
},
Meta::NameValue(_) => span_err(
span,
&format!(
"`#[{} = ...]` is not a valid `SessionSubdiagnostic` field attribute",
name
),
)
.emit(),
Meta::List(_) => span_err(
span,
&format!(
"`#[{}(...)]` is not a valid `SessionSubdiagnostic` field attribute",
name
),
)
.emit(),
_ => throw_invalid_attr!(attr, &meta),
}
}