]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_macros/src/diagnostics/error.rs
Auto merge of #107843 - bjorn3:sync_cg_clif-2023-02-09, r=bjorn3
[rust.git] / compiler / rustc_macros / src / diagnostics / error.rs
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)]
7 pub(crate) enum DiagnosticDeriveError {
8     SynError(SynError),
9     ErrorHandled,
10 }
11
12 impl DiagnosticDeriveError {
13     pub(crate) fn to_compile_error(self) -> TokenStream {
14         match self {
15             DiagnosticDeriveError::SynError(e) => e.to_compile_error(),
16             DiagnosticDeriveError::ErrorHandled => {
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
27 impl From<SynError> for DiagnosticDeriveError {
28     fn from(e: SynError) -> Self {
29         DiagnosticDeriveError::SynError(e)
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,
37 ) -> DiagnosticDeriveError {
38     f(diag).emit();
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
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 ///
63 /// For methods that return a `Result<_, DiagnosticDeriveError>`:
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();
77     let path = path_to_string(&attr.path);
78     match meta {
79         Meta::Path(_) => span_err(span, &format!("`#[{path}]` is not a valid attribute")),
80         Meta::NameValue(_) => {
81             span_err(span, &format!("`#[{path} = ...]` is not a valid attribute"))
82         }
83         Meta::List(_) => span_err(span, &format!("`#[{path}(...)]` is not a valid attribute")),
84     }
85 }
86
87 /// Emit an error diagnostic for an invalid attribute (optionally performing additional decoration
88 /// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
89 ///
90 /// For methods that return a `Result<_, DiagnosticDeriveError>`:
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(_) => {
110             return span_err(span, &format!("`#[{name}(\"...\")]` is not a valid attribute"));
111         }
112     };
113
114     let span = meta.span().unwrap();
115     let path = path_to_string(meta.path());
116     match meta {
117         Meta::NameValue(..) => {
118             span_err(span, &format!("`#[{name}({path} = ...)]` is not a valid attribute"))
119         }
120         Meta::Path(..) => span_err(span, &format!("`#[{name}({path})]` is not a valid attribute")),
121         Meta::List(..) => {
122             span_err(span, &format!("`#[{name}({path}(...))]` is not a valid attribute"))
123         }
124     }
125 }
126
127 /// Emit an error diagnostic for an invalid nested attribute (optionally performing additional
128 /// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
129 ///
130 /// For methods that return a `Result<_, DiagnosticDeriveError>`:
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;