2022-04-27 02:57:44 +01:00
|
|
|
use proc_macro::{Diagnostic, Level, MultiSpan};
|
|
|
|
use proc_macro2::TokenStream;
|
|
|
|
use quote::quote;
|
2022-04-27 04:06:13 +01:00
|
|
|
use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta};
|
2022-04-27 02:57:44 +01:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
2022-06-30 08:57:45 +01:00
|
|
|
pub(crate) enum DiagnosticDeriveError {
|
2022-04-27 04:06:13 +01:00
|
|
|
SynError(SynError),
|
2022-04-27 02:57:44 +01:00
|
|
|
ErrorHandled,
|
|
|
|
}
|
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
impl DiagnosticDeriveError {
|
2022-04-27 02:57:44 +01:00
|
|
|
pub(crate) fn to_compile_error(self) -> TokenStream {
|
|
|
|
match self {
|
2022-06-30 08:57:45 +01:00
|
|
|
DiagnosticDeriveError::SynError(e) => e.to_compile_error(),
|
|
|
|
DiagnosticDeriveError::ErrorHandled => {
|
2022-04-27 02:57:44 +01:00
|
|
|
// Return ! to avoid having to create a blank DiagnosticBuilder to return when an
|
|
|
|
// error has already been emitted to the compiler.
|
|
|
|
quote! {
|
|
|
|
{ unreachable!(); }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-30 08:57:45 +01:00
|
|
|
impl From<SynError> for DiagnosticDeriveError {
|
2022-04-27 04:06:13 +01:00
|
|
|
fn from(e: SynError) -> Self {
|
2022-06-30 08:57:45 +01:00
|
|
|
DiagnosticDeriveError::SynError(e)
|
2022-04-27 04:06:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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,
|
2022-06-30 08:57:45 +01:00
|
|
|
) -> DiagnosticDeriveError {
|
2022-04-27 04:06:13 +01:00
|
|
|
f(diag).emit();
|
2022-06-30 08:57:45 +01:00
|
|
|
DiagnosticDeriveError::ErrorHandled
|
2022-04-27 04:06:13 +01:00
|
|
|
}
|
|
|
|
|
2022-06-23 14:51:44 +01:00
|
|
|
/// Helper function for printing `syn::Path` - doesn't handle arguments in paths and these are
|
|
|
|
/// unlikely to come up much in use of the macro.
|
|
|
|
fn path_to_string(path: &syn::Path) -> String {
|
|
|
|
let mut out = String::new();
|
|
|
|
for (i, segment) in path.segments.iter().enumerate() {
|
|
|
|
if i > 0 || path.leading_colon.is_some() {
|
|
|
|
out.push_str("::");
|
|
|
|
}
|
|
|
|
out.push_str(&segment.ident.to_string());
|
|
|
|
}
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
2022-04-27 04:06:13 +01:00
|
|
|
/// Returns an error diagnostic on span `span` with msg `msg`.
|
2022-04-27 02:57:44 +01:00
|
|
|
pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic {
|
|
|
|
Diagnostic::spanned(span, Level::Error, msg)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
|
|
|
|
/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
|
2022-04-27 04:06:13 +01:00
|
|
|
///
|
2022-06-30 08:57:45 +01:00
|
|
|
/// For methods that return a `Result<_, DiagnosticDeriveError>`:
|
2022-04-27 02:57:44 +01:00
|
|
|
macro_rules! throw_span_err {
|
|
|
|
($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
|
|
|
|
($span:expr, $msg:expr, $f:expr) => {{
|
2022-04-27 04:06:13 +01:00
|
|
|
let diag = span_err($span, $msg);
|
|
|
|
return Err(crate::diagnostics::error::_throw_err(diag, $f));
|
2022-04-27 02:57:44 +01:00
|
|
|
}};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) use throw_span_err;
|
|
|
|
|
2022-04-27 04:06:13 +01:00
|
|
|
/// Returns an error diagnostic for an invalid attribute.
|
|
|
|
pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic {
|
|
|
|
let span = attr.span().unwrap();
|
2022-06-23 14:51:44 +01:00
|
|
|
let path = path_to_string(&attr.path);
|
2022-04-27 04:06:13 +01:00
|
|
|
match meta {
|
2022-06-23 14:51:44 +01:00
|
|
|
Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", path)),
|
2022-04-27 04:06:13 +01:00
|
|
|
Meta::NameValue(_) => {
|
2022-06-23 14:51:44 +01:00
|
|
|
span_err(span, &format!("`#[{} = ...]` is not a valid attribute", path))
|
2022-04-27 04:06:13 +01:00
|
|
|
}
|
2022-06-23 14:51:44 +01:00
|
|
|
Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", path)),
|
2022-04-27 04:06:13 +01:00
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
|
2022-04-27 04:06:13 +01:00
|
|
|
/// Emit a error diagnostic for an invalid attribute (optionally performing additional decoration
|
|
|
|
/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
|
|
|
|
///
|
2022-06-30 08:57:45 +01:00
|
|
|
/// For methods that return a `Result<_, DiagnosticDeriveError>`:
|
2022-04-27 04:06:13 +01:00
|
|
|
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();
|
2022-06-23 14:51:44 +01:00
|
|
|
let path = path_to_string(meta.path());
|
2022-04-27 04:06:13 +01:00
|
|
|
match meta {
|
2022-06-23 14:51:44 +01:00
|
|
|
Meta::NameValue(..) => {
|
|
|
|
span_err(span, &format!("`#[{}({} = ...)]` is not a valid attribute", name, path))
|
|
|
|
}
|
2022-04-27 04:06:13 +01:00
|
|
|
Meta::Path(..) => {
|
2022-06-23 14:51:44 +01:00
|
|
|
span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, path))
|
2022-04-27 04:06:13 +01:00
|
|
|
}
|
|
|
|
Meta::List(..) => {
|
2022-06-23 14:51:44 +01:00
|
|
|
span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, path))
|
2022-04-27 04:06:13 +01:00
|
|
|
}
|
2022-04-27 02:57:44 +01:00
|
|
|
}
|
|
|
|
}
|
2022-04-27 04:06:13 +01:00
|
|
|
|
|
|
|
/// Emit a error diagnostic for an invalid nested attribute (optionally performing additional
|
|
|
|
/// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
|
|
|
|
///
|
2022-06-30 08:57:45 +01:00
|
|
|
/// For methods that return a `Result<_, DiagnosticDeriveError>`:
|
2022-04-27 04:06:13 +01:00
|
|
|
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;
|