]>
Commit | Line | Data |
---|---|---|
04454e1e FG |
1 | use proc_macro::{Diagnostic, Level, MultiSpan}; |
2 | use proc_macro2::TokenStream; | |
3 | use quote::quote; | |
4 | use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta}; | |
5 | ||
6 | #[derive(Debug)] | |
064997fb | 7 | pub(crate) enum DiagnosticDeriveError { |
04454e1e FG |
8 | SynError(SynError), |
9 | ErrorHandled, | |
10 | } | |
11 | ||
064997fb | 12 | impl DiagnosticDeriveError { |
04454e1e FG |
13 | pub(crate) fn to_compile_error(self) -> TokenStream { |
14 | match self { | |
064997fb FG |
15 | DiagnosticDeriveError::SynError(e) => e.to_compile_error(), |
16 | DiagnosticDeriveError::ErrorHandled => { | |
04454e1e FG |
17 | // Return ! to avoid having to create a blank DiagnosticBuilder to return when an |
18 | // error has already been emitted to the compiler. | |
19 | quote! { | |
20 | { unreachable!(); } | |
21 | } | |
22 | } | |
23 | } | |
24 | } | |
25 | } | |
26 | ||
064997fb | 27 | impl From<SynError> for DiagnosticDeriveError { |
04454e1e | 28 | fn from(e: SynError) -> Self { |
064997fb | 29 | DiagnosticDeriveError::SynError(e) |
04454e1e FG |
30 | } |
31 | } | |
32 | ||
33 | /// Helper function for use with `throw_*` macros - constraints `$f` to an `impl FnOnce`. | |
34 | pub(crate) fn _throw_err( | |
35 | diag: Diagnostic, | |
36 | f: impl FnOnce(Diagnostic) -> Diagnostic, | |
064997fb | 37 | ) -> DiagnosticDeriveError { |
04454e1e | 38 | f(diag).emit(); |
064997fb FG |
39 | DiagnosticDeriveError::ErrorHandled |
40 | } | |
41 | ||
42 | /// Helper function for printing `syn::Path` - doesn't handle arguments in paths and these are | |
43 | /// unlikely to come up much in use of the macro. | |
44 | fn path_to_string(path: &syn::Path) -> String { | |
45 | let mut out = String::new(); | |
46 | for (i, segment) in path.segments.iter().enumerate() { | |
47 | if i > 0 || path.leading_colon.is_some() { | |
48 | out.push_str("::"); | |
49 | } | |
50 | out.push_str(&segment.ident.to_string()); | |
51 | } | |
52 | out | |
04454e1e FG |
53 | } |
54 | ||
55 | /// Returns an error diagnostic on span `span` with msg `msg`. | |
56 | pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic { | |
57 | Diagnostic::spanned(span, Level::Error, msg) | |
58 | } | |
59 | ||
60 | /// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration | |
61 | /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. | |
62 | /// | |
064997fb | 63 | /// For methods that return a `Result<_, DiagnosticDeriveError>`: |
04454e1e FG |
64 | macro_rules! throw_span_err { |
65 | ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }}; | |
66 | ($span:expr, $msg:expr, $f:expr) => {{ | |
67 | let diag = span_err($span, $msg); | |
68 | return Err(crate::diagnostics::error::_throw_err(diag, $f)); | |
69 | }}; | |
70 | } | |
71 | ||
72 | pub(crate) use throw_span_err; | |
73 | ||
74 | /// Returns an error diagnostic for an invalid attribute. | |
75 | pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic { | |
76 | let span = attr.span().unwrap(); | |
064997fb | 77 | let path = path_to_string(&attr.path); |
04454e1e | 78 | match meta { |
9c376795 | 79 | Meta::Path(_) => span_err(span, &format!("`#[{path}]` is not a valid attribute")), |
04454e1e | 80 | Meta::NameValue(_) => { |
9c376795 | 81 | span_err(span, &format!("`#[{path} = ...]` is not a valid attribute")) |
04454e1e | 82 | } |
9c376795 | 83 | Meta::List(_) => span_err(span, &format!("`#[{path}(...)]` is not a valid attribute")), |
04454e1e FG |
84 | } |
85 | } | |
86 | ||
487cf647 | 87 | /// Emit an error diagnostic for an invalid attribute (optionally performing additional decoration |
04454e1e FG |
88 | /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. |
89 | /// | |
064997fb | 90 | /// For methods that return a `Result<_, DiagnosticDeriveError>`: |
04454e1e FG |
91 | macro_rules! throw_invalid_attr { |
92 | ($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }}; | |
93 | ($attr:expr, $meta:expr, $f:expr) => {{ | |
94 | let diag = crate::diagnostics::error::invalid_attr($attr, $meta); | |
95 | return Err(crate::diagnostics::error::_throw_err(diag, $f)); | |
96 | }}; | |
97 | } | |
98 | ||
99 | pub(crate) use throw_invalid_attr; | |
100 | ||
101 | /// Returns an error diagnostic for an invalid nested attribute. | |
102 | pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diagnostic { | |
103 | let name = attr.path.segments.last().unwrap().ident.to_string(); | |
104 | let name = name.as_str(); | |
105 | ||
106 | let span = nested.span().unwrap(); | |
107 | let meta = match nested { | |
108 | syn::NestedMeta::Meta(meta) => meta, | |
109 | syn::NestedMeta::Lit(_) => { | |
9c376795 | 110 | return span_err(span, &format!("`#[{name}(\"...\")]` is not a valid attribute")); |
04454e1e FG |
111 | } |
112 | }; | |
113 | ||
114 | let span = meta.span().unwrap(); | |
064997fb | 115 | let path = path_to_string(meta.path()); |
04454e1e | 116 | match meta { |
064997fb | 117 | Meta::NameValue(..) => { |
9c376795 | 118 | span_err(span, &format!("`#[{name}({path} = ...)]` is not a valid attribute")) |
04454e1e | 119 | } |
9c376795 | 120 | Meta::Path(..) => span_err(span, &format!("`#[{name}({path})]` is not a valid attribute")), |
04454e1e | 121 | Meta::List(..) => { |
9c376795 | 122 | span_err(span, &format!("`#[{name}({path}(...))]` is not a valid attribute")) |
04454e1e FG |
123 | } |
124 | } | |
125 | } | |
126 | ||
487cf647 | 127 | /// Emit an error diagnostic for an invalid nested attribute (optionally performing additional |
04454e1e FG |
128 | /// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`. |
129 | /// | |
064997fb | 130 | /// For methods that return a `Result<_, DiagnosticDeriveError>`: |
04454e1e FG |
131 | macro_rules! throw_invalid_nested_attr { |
132 | ($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }}; | |
133 | ($attr:expr, $nested_attr:expr, $f:expr) => {{ | |
134 | let diag = crate::diagnostics::error::invalid_nested_attr($attr, $nested_attr); | |
135 | return Err(crate::diagnostics::error::_throw_err(diag, $f)); | |
136 | }}; | |
137 | } | |
138 | ||
139 | pub(crate) use throw_invalid_nested_attr; |