1#![doc(html_root_url = "https://docs.rs/no-panic/0.1.37")]
136#![allow(
137 clippy::doc_markdown,
138 clippy::match_same_arms,
139 clippy::missing_panics_doc,
140 clippy::uninlined_format_args
141)]
142#![cfg_attr(all(test, exhaustive), feature(non_exhaustive_omitted_patterns_lint))]
143
144extern crate proc_macro;
145
146use proc_macro::TokenStream;
147use proc_macro2::{Span, TokenStream as TokenStream2};
148use quote::quote;
149use std::mem;
150use syn::parse::{Error, Nothing, Result};
151use syn::{
152 parse_quote, FnArg, GenericArgument, Ident, ItemFn, Pat, PatType, Path, PathArguments,
153 ReturnType, Token, Type, TypeInfer, TypeParamBound,
154};
155
156#[proc_macro_attribute]
157pub fn no_panic(args: TokenStream, input: TokenStream) -> TokenStream {
158 let args = TokenStream2::from(args);
159 let input = TokenStream2::from(input);
160 TokenStream::from(match parse(args, input.clone()) {
161 Ok(function) => {
162 let expanded = expand_no_panic(function);
163 {
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "cfg");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "not");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "doc");
_s
});
_s
});
_s
});
::quote::ToTokens::to_tokens(&expanded, &mut _s);
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "cfg");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "doc");
_s
});
_s
});
::quote::ToTokens::to_tokens(&input, &mut _s);
_s
}quote! {
164 #[cfg(not(doc))]
165 #expanded
166 #[cfg(doc)]
168 #input
169 }
170 }
171 Err(parse_error) => {
172 let compile_error = parse_error.to_compile_error();
173 {
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&compile_error, &mut _s);
::quote::ToTokens::to_tokens(&input, &mut _s);
_s
}quote! {
174 #compile_error
175 #input
176 }
177 }
178 })
179}
180
181fn parse(args: TokenStream2, input: TokenStream2) -> Result<ItemFn> {
182 let function: ItemFn = syn::parse2(input)?;
183 let _: Nothing = syn::parse2::<Nothing>(args)?;
184 if function.sig.constness.is_some() {
185 return Err(Error::new(
186 Span::call_site(),
187 "no_panic attribute on const fn is not supported",
188 ));
189 }
190 if function.sig.asyncness.is_some() {
191 return Err(Error::new(
192 Span::call_site(),
193 "no_panic attribute on async fn is not supported",
194 ));
195 }
196 Ok(function)
197}
198
199fn make_impl_trait_wild(ret: &mut Type) {
201 match ret {
202 #![cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
203 Type::ImplTrait(impl_trait) => {
204 *ret = Type::Infer(TypeInfer {
205 attrs: Vec::new(),
206 underscore_token: ::syn::token::UnderscoreToken,
207 });
208 }
209 Type::Array(ret) => make_impl_trait_wild(&mut ret.elem),
210 Type::Group(ret) => make_impl_trait_wild(&mut ret.elem),
211 Type::Paren(ret) => make_impl_trait_wild(&mut ret.elem),
212 Type::Path(ret) => make_impl_trait_wild_in_path(&mut ret.path),
213 Type::Ptr(ret) => make_impl_trait_wild(&mut ret.elem),
214 Type::Reference(ret) => make_impl_trait_wild(&mut ret.elem),
215 Type::Slice(ret) => make_impl_trait_wild(&mut ret.elem),
216 Type::TraitObject(ret) => {
217 for bound in &mut ret.bounds {
218 if let TypeParamBound::Trait(bound) = bound {
219 make_impl_trait_wild_in_path(&mut bound.path);
220 }
221 }
222 }
223 Type::Tuple(ret) => ret.elems.iter_mut().for_each(make_impl_trait_wild),
224 Type::FnPtr(_) | Type::Infer(_) | Type::Macro(_) | Type::Never(_) | Type::Verbatim(_) => {}
225 _ => {}
226 }
227}
228
229fn make_impl_trait_wild_in_path(path: &mut Path) {
230 for segment in &mut path.segments {
231 if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments {
232 for arg in &mut bracketed.args {
233 if let GenericArgument::Type(arg) = arg {
234 make_impl_trait_wild(arg);
235 }
236 }
237 }
238 }
239}
240
241fn expand_no_panic(mut function: ItemFn) -> TokenStream2 {
242 let mut move_self = None;
243 let mut arg_attrs = Vec::new();
244 let mut arg_pat = Vec::new();
245 let mut arg_val = Vec::new();
246 for (i, input) in function.sig.inputs.iter_mut().enumerate() {
247 match input {
248 FnArg::Typed(PatType { attrs, pat, .. })
249 if match pat.as_ref() {
250 Pat::Ident(pat) => pat.ident != "self",
251 _ => true,
252 } =>
253 {
254 let arg_name = if let Pat::Ident(original_name) = &**pat {
255 original_name.ident.clone()
256 } else {
257 Ident::new(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("__arg{0}", i))
})format!("__arg{}", i), Span::call_site())
258 };
259 arg_attrs.push(attrs);
260 arg_pat.push(mem::replace(&mut *pat, ::syn::__private::parse_quote({
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "mut");
::quote::ToTokens::to_tokens(&arg_name, &mut _s);
_s
})parse_quote!(mut #arg_name)));
261 arg_val.push(arg_name);
262 }
263 FnArg::Typed(_) | FnArg::Receiver(_) => {
264 move_self = Some({
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "if");
::quote::__private::push_ident(&mut _s, "false");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "loop");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
::quote::__private::TokenStream::new());
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "allow");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "unreachable_code");
_s
});
_s
});
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "let");
::quote::__private::push_ident(&mut _s, "__self");
::quote::__private::push_eq(&mut _s);
::quote::__private::push_ident(&mut _s, "self");
::quote::__private::push_semi(&mut _s);
_s
});
_s
});
_s
}quote! {
265 if false {
266 loop {}
267 #[allow(unreachable_code)]
268 {
269 let __self = self;
270 }
271 }
272 });
273 }
274 }
275 }
276
277 let has_inline = function
278 .attrs
279 .iter()
280 .any(|attr| attr.path().is_ident("inline"));
281 if !has_inline {
282 function.attrs.push(::syn::__private::parse_quote({
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "inline");
_s
});
_s
})parse_quote!(#[inline]));
283 }
284
285 let ret = match &function.sig.output {
286 ReturnType::Default => {
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_rarrow(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
::quote::__private::TokenStream::new());
_s
}quote!(-> ()),
287 ReturnType::Type(arrow, output) => {
288 let mut output = output.clone();
289 make_impl_trait_wild(&mut output);
290 {
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&arrow, &mut _s);
::quote::ToTokens::to_tokens(&output, &mut _s);
_s
}quote!(#arrow #output)
291 }
292 };
293 let stmts = function.block.stmts;
294 let message = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("\n\nERROR[no-panic]: detected panic in function `{0}`\n",
function.sig.ident))
})format!(
295 "\n\nERROR[no-panic]: detected panic in function `{}`\n",
296 function.sig.ident,
297 );
298 let unsafe_extern = if falsecfg!(no_unsafe_extern_blocks) {
299 None
300 } else {
301 Some(::syn::token::UnsafeToken))
302 };
303 *function.block = ::syn::__private::parse_quote({
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "struct");
::quote::__private::push_ident(&mut _s, "__NoPanic");
::quote::__private::push_semi(&mut _s);
::quote::ToTokens::to_tokens(&unsafe_extern, &mut _s);
::quote::__private::push_ident(&mut _s, "extern");
::quote::__private::parse(&mut _s, "\"C\"");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_pound(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Bracket,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "link_name");
::quote::__private::push_eq(&mut _s);
::quote::ToTokens::to_tokens(&message, &mut _s);
_s
});
::quote::__private::push_ident(&mut _s, "fn");
::quote::__private::push_ident(&mut _s, "trigger");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
::quote::__private::TokenStream::new());
::quote::__private::push_rarrow(&mut _s);
::quote::__private::push_bang(&mut _s);
::quote::__private::push_semi(&mut _s);
_s
});
::quote::__private::push_ident(&mut _s, "impl");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "core");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "ops");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "Drop");
::quote::__private::push_ident(&mut _s, "for");
::quote::__private::push_ident(&mut _s, "__NoPanic");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "fn");
::quote::__private::push_ident(&mut _s, "drop");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_and(&mut _s);
::quote::__private::push_ident(&mut _s, "mut");
::quote::__private::push_ident(&mut _s, "self");
_s
});
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "unsafe");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "trigger");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
::quote::__private::TokenStream::new());
::quote::__private::push_semi(&mut _s);
_s
});
_s
});
_s
});
::quote::__private::push_ident(&mut _s, "let");
::quote::__private::push_ident(&mut _s, "__guard");
::quote::__private::push_eq(&mut _s);
::quote::__private::push_ident(&mut _s, "__NoPanic");
::quote::__private::push_semi(&mut _s);
::quote::__private::push_ident(&mut _s, "let");
::quote::__private::push_ident(&mut _s, "__result");
::quote::__private::push_eq(&mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "move");
::quote::__private::push_or_or(&mut _s);
::quote::ToTokens::to_tokens(&ret, &mut _s);
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Brace,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&move_self, &mut _s);
{
use ::quote::__private::ext::*;
let has_iter = ::quote::__private::HasIterator::<false>;
#[allow(unused_mut)]
let (mut arg_attrs, i) = arg_attrs.quote_into_iter();
let has_iter = has_iter | i;
#[allow(unused_mut)]
let (mut arg_pat, i) = arg_pat.quote_into_iter();
let has_iter = has_iter | i;
#[allow(unused_mut)]
let (mut arg_val, i) = arg_val.quote_into_iter();
let has_iter = has_iter | i;
<_ as
::quote::__private::CheckHasIterator<true>>::check(has_iter);
while true {
let arg_attrs =
match arg_attrs.next() {
Some(_x) => ::quote::__private::RepInterp(_x),
None => break,
};
let arg_pat =
match arg_pat.next() {
Some(_x) => ::quote::__private::RepInterp(_x),
None => break,
};
let arg_val =
match arg_val.next() {
Some(_x) => ::quote::__private::RepInterp(_x),
None => break,
};
{
use ::quote::__private::ext::*;
let has_iter = ::quote::__private::HasIterator::<false>;
#[allow(unused_mut)]
let (mut arg_attrs, i) = arg_attrs.quote_into_iter();
let has_iter = has_iter | i;
<_ as
::quote::__private::CheckHasIterator<true>>::check(has_iter);
while true {
let arg_attrs =
match arg_attrs.next() {
Some(_x) => ::quote::__private::RepInterp(_x),
None => break,
};
::quote::ToTokens::to_tokens(&arg_attrs, &mut _s);
}
}
::quote::__private::push_ident(&mut _s, "let");
::quote::ToTokens::to_tokens(&arg_pat, &mut _s);
::quote::__private::push_eq(&mut _s);
::quote::ToTokens::to_tokens(&arg_val, &mut _s);
::quote::__private::push_semi(&mut _s);
}
}
{
use ::quote::__private::ext::*;
let has_iter = ::quote::__private::HasIterator::<false>;
#[allow(unused_mut)]
let (mut stmts, i) = stmts.quote_into_iter();
let has_iter = has_iter | i;
<_ as
::quote::__private::CheckHasIterator<true>>::check(has_iter);
while true {
let stmts =
match stmts.next() {
Some(_x) => ::quote::__private::RepInterp(_x),
None => break,
};
::quote::ToTokens::to_tokens(&stmts, &mut _s);
}
}
_s
});
_s
});
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
::quote::__private::TokenStream::new());
::quote::__private::push_semi(&mut _s);
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "core");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "mem");
::quote::__private::push_colon2(&mut _s);
::quote::__private::push_ident(&mut _s, "forget");
::quote::__private::push_group(&mut _s,
::quote::__private::Delimiter::Parenthesis,
{
let mut _s = ::quote::__private::TokenStream::new();
::quote::__private::push_ident(&mut _s, "__guard");
_s
});
::quote::__private::push_semi(&mut _s);
::quote::__private::push_ident(&mut _s, "__result");
_s
});
_s
})parse_quote!({
304 struct __NoPanic;
305 #unsafe_extern extern "C" {
306 #[link_name = #message]
307 fn trigger() -> !;
308 }
309 impl ::core::ops::Drop for __NoPanic {
310 fn drop(&mut self) {
311 unsafe {
312 trigger();
313 }
314 }
315 }
316 let __guard = __NoPanic;
317 let __result = (move || #ret {
318 #move_self
319 #(
320 #(#arg_attrs)*
321 let #arg_pat = #arg_val;
322 )*
323 #(#stmts)*
324 })();
325 ::core::mem::forget(__guard);
326 __result
327 });
328
329 {
let mut _s = ::quote::__private::TokenStream::new();
::quote::ToTokens::to_tokens(&function, &mut _s);
_s
}quote!(#function)
330}