1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
use proc_macro::{Diagnostic, Level, MultiSpan};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{spanned::Spanned, Attribute, Error as SynError, Meta};
#[derive(Debug)]
pub(crate) enum DiagnosticDeriveError {
SynError(SynError),
ErrorHandled,
}
impl DiagnosticDeriveError {
pub(crate) fn to_compile_error(self) -> TokenStream {
match self {
DiagnosticDeriveError::SynError(e) => e.to_compile_error(),
DiagnosticDeriveError::ErrorHandled => {
// Return ! to avoid having to create a blank DiagnosticBuilder to return when an
// error has already been emitted to the compiler.
quote! {
{ unreachable!(); }
}
}
}
}
}
impl From<SynError> for DiagnosticDeriveError {
fn from(e: SynError) -> Self {
DiagnosticDeriveError::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,
) -> DiagnosticDeriveError {
f(diag).emit();
DiagnosticDeriveError::ErrorHandled
}
/// 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
}
/// Returns an error diagnostic on span `span` with msg `msg`.
#[must_use]
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)`.
///
/// For methods that return a `Result<_, DiagnosticDeriveError>`:
macro_rules! throw_span_err {
($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
($span:expr, $msg:expr, $f:expr) => {{
let diag = span_err($span, $msg);
return Err(crate::diagnostics::error::_throw_err(diag, $f));
}};
}
pub(crate) use throw_span_err;
/// Returns an error diagnostic for an invalid attribute.
pub(crate) fn invalid_attr(attr: &Attribute) -> Diagnostic {
let span = attr.span().unwrap();
let path = path_to_string(attr.path());
match attr.meta {
Meta::Path(_) => span_err(span, &format!("`#[{path}]` is not a valid attribute")),
Meta::NameValue(_) => {
span_err(span, &format!("`#[{path} = ...]` is not a valid attribute"))
}
Meta::List(_) => span_err(span, &format!("`#[{path}(...)]` is not a valid attribute")),
}
}
/// Emit an 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<_, DiagnosticDeriveError>`:
macro_rules! throw_invalid_attr {
($attr:expr) => {{ throw_invalid_attr!($attr, |diag| diag) }};
($attr:expr, $f:expr) => {{
let diag = crate::diagnostics::error::invalid_attr($attr);
return Err(crate::diagnostics::error::_throw_err(diag, $f));
}};
}
pub(crate) use throw_invalid_attr;
|