tracers_codegen/
error.rs

1//! Defines an error type which can represent all the various kinds of failures the code gen
2//! process can encounter.  More specifically to this use case, the error type can also produce a
3//! `TokenStream` which, when consumed by the `rustc` compiler, causes the compile to fail with a
4//! compiler error with the text of whatever the error message was.  That's how the proc macros
5//! communicate failure details back to the compiler at compile time.
6
7use darling::Error as DarlingError;
8use failure::{Error, Fail};
9use proc_macro2::Span;
10use proc_macro2::TokenStream;
11use quote::ToTokens;
12use std::fmt;
13use std::fmt::Display;
14use std::path::PathBuf;
15use std::sync::{Arc, Mutex};
16
17#[derive(Debug, Fail)]
18pub enum TracersError {
19    InvalidProvider {
20        message: String,
21        #[fail(cause)]
22        syn_error: Error,
23    },
24
25    SynError {
26        message: String,
27        #[fail(cause)]
28        syn_error: Error,
29    },
30
31    DarlingError {
32        message: String,
33        darling_error: Arc<Mutex<DarlingError>>,
34    },
35
36    InvalidCallExpression {
37        message: String,
38        #[fail(cause)]
39        syn_error: Error,
40    },
41
42    OtherError {
43        message: String,
44        #[fail(cause)]
45        error: Error,
46    },
47
48    MissingCallInBuildRs,
49
50    BuildInfoReadError {
51        message: String,
52        build_info_path: String,
53        #[fail(cause)]
54        error: Error,
55    },
56
57    BuildInfoWriteError {
58        message: String,
59        build_info_path: String,
60        #[fail(cause)]
61        error: Error,
62    },
63
64    ProviderTraitNotProcessedError {
65        message: String,
66        trait_name: String,
67        #[fail(cause)]
68        error: Error,
69    },
70
71    CodeGenerationError {
72        message: String,
73    },
74
75    NativeCodeGenerationError {
76        message: String,
77        #[fail(cause)]
78        error: Error,
79    },
80}
81
82impl Display for TracersError {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        match self {
85            TracersError::InvalidProvider { message, .. } => write!(f, "{}", message),
86            TracersError::SynError { message, .. } => write!(f, "{}", message),
87            TracersError::DarlingError { message, .. } => write!(f, "{}", message),
88            TracersError::InvalidCallExpression { message, .. } => write!(f, "{}", message),
89            TracersError::OtherError { message, .. } => write!(f, "{}", message),
90            TracersError::MissingCallInBuildRs => write!(f, "Build environment is incomplete; make sure you are calling `tracers_build::build()` in your `build.rs` build script"),
91            TracersError::BuildInfoReadError { message, .. } => write!(f, "{}", message),
92            TracersError::BuildInfoWriteError { message, .. } => write!(f, "{}", message),
93            TracersError::ProviderTraitNotProcessedError { message,.. } => write!(f, "{}", message),
94            TracersError::CodeGenerationError { message } => write!(f, "Error generating probing code: {}", message),
95            TracersError::NativeCodeGenerationError { message, .. } => write!(f, "Error generating or compiling native C++ wrapper code: {}", message)
96        }
97    }
98}
99
100unsafe impl Send for TracersError {}
101unsafe impl Sync for TracersError {}
102
103impl PartialEq<TracersError> for TracersError {
104    fn eq(&self, other: &TracersError) -> bool {
105        //There are a lot of types that don't support equality.  To keep it easy, just compare the
106        //display version
107        self.to_string() == other.to_string()
108    }
109}
110
111impl TracersError {
112    pub fn invalid_provider<T: ToTokens>(message: impl AsRef<str>, element: T) -> TracersError {
113        let message = format!(
114            "There's a problem with this provider trait: {}",
115            message.as_ref()
116        );
117        let e = Self::new_syn_error(&message, element);
118
119        TracersError::InvalidProvider {
120            message,
121            syn_error: e,
122        }
123    }
124
125    pub fn syn_error(message: impl AsRef<str>, e: syn::Error) -> TracersError {
126        // When this happens, it means we got a `syn::Error` back from the `syn` library when we
127        // asked it to parse a token stream.  The message in this error will be something generic,
128        // where as our `message` will have important details that will help the user understand
129        // what went wrong.  So we need to construct our own `syn::Error` which includes this
130        // context, while using the `span` from `e` so it is attached to the right part of the
131        // input code.
132        let message = format!("Parse error: {}\nDetails: {}", message.as_ref(), e);
133        let e = syn::Error::new(e.span(), &message);
134        let e = Self::wrap_syn_error(e);
135
136        TracersError::SynError {
137            message,
138            syn_error: e,
139        }
140    }
141
142    /// When we need to raise an error that is attached to a `syn` type (meaning the span of the
143    /// error will correctly be associated with that type), this method is used.  There is no
144    /// actual `syn` error, we're just reporting a logic error of our own while processing some
145    /// `syn` types.
146    pub fn syn_like_error<T: ToTokens, U: Display>(message: U, tokens: T) -> TracersError {
147        let message = message.to_string();
148
149        Self::syn_error(&message, syn::Error::new_spanned(tokens, &message))
150    }
151
152    pub fn darling_error(e: DarlingError) -> TracersError {
153        let message = e.to_string();
154
155        TracersError::DarlingError {
156            message,
157            darling_error: Arc::new(Mutex::new(e)),
158        }
159    }
160
161    pub fn invalid_call_expression<T: ToTokens>(
162        message: impl AsRef<str>,
163        element: T,
164    ) -> TracersError {
165        let message = format!("Invalid call expression: {}", message.as_ref());
166        let e = Self::new_syn_error(&message, element);
167        TracersError::InvalidCallExpression {
168            message,
169            syn_error: e,
170        }
171    }
172
173    pub fn other_error<D: Display + Send + Sync + 'static>(e: failure::Context<D>) -> TracersError {
174        TracersError::OtherError {
175            message: Self::fail_string(&e),
176            error: e.into(),
177        }
178    }
179
180    pub fn missing_call_in_build_rs() -> TracersError {
181        TracersError::MissingCallInBuildRs
182    }
183
184    pub fn build_info_read_error(build_info_path: PathBuf, e: Error) -> TracersError {
185        let message = format!("Unable to read build info from '{}'.\nAre you sure you're calling `tracers_build::build()` in your `build.rs`?\nError cause: {}",
186            build_info_path.display(),
187            Self::error_string(&e));
188        TracersError::BuildInfoReadError {
189            message,
190            build_info_path: build_info_path.display().to_string(),
191            error: e,
192        }
193    }
194
195    pub fn build_info_write_error(build_info_path: PathBuf, e: Error) -> TracersError {
196        let message = format!("Unable to write build info from '{}'.\nAre you sure you're calling `tracers_build::build()` in your `build.rs`?\nError cause: {}",
197            build_info_path.display(),
198            Self::error_string(&e));
199        TracersError::BuildInfoWriteError {
200            message,
201            build_info_path: build_info_path.display().to_string(),
202            error: e,
203        }
204    }
205
206    pub fn provider_trait_not_processed_error<T: AsRef<str>, E: Into<Error> + Display>(
207        trait_name: T,
208        e: E,
209    ) -> TracersError {
210        TracersError::ProviderTraitNotProcessedError {
211            message: format!("The provider trait '{}' couldn't be processed by the code generator: {}\nCheck your build output for errors.  Tracing will be disabled for this provider",
212                             trait_name.as_ref(),
213                             e),
214            trait_name: trait_name.as_ref().to_owned(),
215            error: e.into()
216        }
217    }
218
219    pub fn code_generation_error<S: AsRef<str>>(message: S) -> TracersError {
220        TracersError::CodeGenerationError {
221            message: message.as_ref().to_owned(),
222        }
223    }
224
225    pub fn native_code_generation_error<S: AsRef<str>, E: Into<Error> + Display>(
226        message: S,
227        e: E,
228    ) -> TracersError {
229        TracersError::NativeCodeGenerationError {
230            message: format!("{}: {}", message.as_ref(), e),
231            error: e.into(),
232        }
233    }
234
235    /// Converts this error type into a `syn::Error`, preserving context from spans and elements if
236    /// any were given
237    pub fn into_syn_error(self) -> syn::Error {
238        match self {
239            TracersError::InvalidProvider { syn_error, .. } => Self::error_as_syn_error(syn_error),
240            TracersError::SynError { syn_error, .. } => Self::error_as_syn_error(syn_error),
241            TracersError::InvalidCallExpression { syn_error, .. } => {
242                Self::error_as_syn_error(syn_error)
243            }
244            others => syn::Error::new(Span::call_site(), others.to_string()),
245        }
246    }
247
248    /// Convert this error into a `TokenStream` such that when the compiler consumes the token
249    /// stream it will evaluate to a compile error, with the span corresponding to whatever element
250    /// was used to report the error.  For those error types that don't have a corresponding
251    /// element, the call site of the macro will be used
252    pub fn into_compiler_error(self) -> TokenStream {
253        //Darling's error type already produces a token stream for errors with useful context into,
254        //so preserve that, otherwise build our own compiler error
255        if let TracersError::DarlingError { darling_error, .. } = self {
256            //Unwrap this error object from Arc and Mutex (it's not Send + Sync natively)
257            //and use the write_error() method to generate a helpful TokenStream
258            let lock =
259                Arc::try_unwrap(darling_error).expect("Somehow a lock is still held on this error");
260            let darling_error = lock
261                .into_inner()
262                .expect("The mutex can't possibly be locked");
263
264            darling_error.write_errors()
265        } else {
266            self.into_syn_error().to_compile_error()
267        }
268    }
269
270    fn new_syn_error<T: ToTokens, U: Display>(message: U, tokens: T) -> Error {
271        Self::wrap_syn_error(syn::Error::new_spanned(tokens, message))
272    }
273
274    fn wrap_syn_error(e: syn::Error) -> Error {
275        Self::wrap_error(e)
276    }
277
278    /// Given a `failure` `Error` type, tests to see if it wraps a real `syn::Error`, and if it
279    /// doesn't, creates a `syn::Error` with the same message
280    fn error_as_syn_error(e: Error) -> syn::Error {
281        e.downcast::<syn::Error>()
282            .unwrap_or_else(|e| syn::Error::new(Span::call_site(), e.to_string()))
283    }
284
285    fn wrap_error(e: impl std::error::Error + Sync + Send + 'static) -> Error {
286        e.into()
287    }
288
289    /// Builds an error string with all the relevant info from a `Fail` implementation
290    #[allow(clippy::redundant_closure)] //clippy's proposed alterstatic won't compile here
291    fn error_string(e: &Error) -> String {
292        let causes: Vec<_> = e.iter_chain().map(|c| c.to_string()).collect();
293        causes.join(": ")
294    }
295
296    #[allow(clippy::redundant_closure)] //clippy's proposed alterstatic won't compile here
297    fn fail_string(f: &dyn Fail) -> String {
298        let causes: Vec<_> = f.iter_chain().map(|c| c.to_string()).collect();
299        causes.join(": ")
300    }
301}
302
303/// Implement conversion from regular errors into a TracersError, but only if the error has been
304/// given a context message using the `.context()` extension method provided by `failure`
305impl<D: Display + Send + Sync + 'static> From<failure::Context<D>> for TracersError {
306    fn from(failure: failure::Context<D>) -> TracersError {
307        TracersError::other_error(failure)
308    }
309}
310
311pub type TracersResult<T> = std::result::Result<T, TracersError>;