#[macro_use] extern crate proc_macro_error; extern crate proc_macro; use proc_macro2::{Span, TokenStream}; use proc_macro_error::{ abort, abort_call_site, diagnostic, emit_call_site_warning, emit_error, emit_warning, proc_macro_error, set_dummy, Diagnostic, Level, OptionExt, ResultExt, SpanRange, }; use syn::{parse_macro_input, spanned::Spanned}; // Macros and Diagnostic #[proc_macro] #[proc_macro_error] pub fn abort_from(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); abort!( span, syn::Error::new(Span::call_site(), "abort!(span, from) test") ) } #[proc_macro] #[proc_macro_error] pub fn abort_to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); abort!(span, "abort!(span, single_expr) test") } #[proc_macro] #[proc_macro_error] pub fn abort_format(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); abort!(span, "abort!(span, expr1, {}) test", "expr2") } #[proc_macro] #[proc_macro_error] pub fn abort_call_site_test(_: proc_macro::TokenStream) -> proc_macro::TokenStream { abort_call_site!("abort_call_site! test") } #[proc_macro] #[proc_macro_error] pub fn direct_abort(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); Diagnostic::spanned(span.into(), Level::Error, "Diagnostic::abort() test".into()).abort() } #[proc_macro] #[proc_macro_error] pub fn emit(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut spans = input.into_iter().step_by(2).map(|t| t.span()); emit_error!( spans.next().unwrap(), syn::Error::new(Span::call_site(), "emit!(span, from) test") ); emit_error!( spans.next().unwrap(), "emit!(span, expr1, {}) test", "expr2" ); emit_error!(spans.next().unwrap(), "emit!(span, single_expr) test"); Diagnostic::spanned( spans.next().unwrap().into(), Level::Error, "Diagnostic::emit() test".into(), ) .emit(); emit_call_site_error!("emit_call_site_error!(expr) test"); // NOOP on stable, just checking that the macros themselves compile. emit_warning!(spans.next().unwrap(), "emit_warning! test"); emit_call_site_warning!("emit_call_site_warning! test"); quote!().into() } // Notes #[proc_macro] #[proc_macro_error] pub fn abort_notes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut spans = input.into_iter().map(|s| s.span()); let span = spans.next().unwrap(); let span2 = spans.next().unwrap(); let some_note = Some("Some note"); let none_note: Option<&'static str> = None; abort! { span, "This is {} error", "an"; note = "simple note"; help = "simple help"; hint = "simple hint"; yay = "simple yay"; note = "format {}", "note"; note =? some_note; note =? none_note; note = span2 => "spanned simple note"; note = span2 => "spanned format {}", "note"; note =? span2 => some_note; note =? span2 => none_note; } } #[proc_macro] #[proc_macro_error] pub fn emit_notes(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut spans = input.into_iter().step_by(2).map(|s| s.span()); let span = spans.next().unwrap(); let span2 = spans.next().unwrap(); let some_note = Some("Some note"); let none_note: Option<&'static str> = None; abort! { span, "This is {} error", "an"; note = "simple note"; help = "simple help"; hint = "simple hint"; yay = "simple yay"; note = "format {}", "note"; note =? some_note; note =? none_note; note = span2 => "spanned simple note"; note = span2 => "spanned format {}", "note"; note =? span2 => some_note; note =? span2 => none_note; } } // Extension traits #[proc_macro] #[proc_macro_error] pub fn option_ext(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { let none: Option = None; none.expect_or_abort("Option::expect_or_abort() test"); quote!().into() } #[proc_macro] #[proc_macro_error] pub fn result_unwrap_or_abort(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); let err = Diagnostic::spanned( span.into(), Level::Error, "Result::unwrap_or_abort() test".to_string(), ); let res: Result<(), _> = Err(err); res.unwrap_or_abort(); quote!().into() } #[proc_macro] #[proc_macro_error] pub fn result_expect_or_abort(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); let err = Diagnostic::spanned( span.into(), Level::Error, "Result::expect_or_abort() test".to_string(), ); let res: Result<(), _> = Err(err); res.expect_or_abort("BOOM"); quote!().into() } // Dummy #[proc_macro] #[proc_macro_error] pub fn dummy(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); set_dummy(quote! { impl Default for NeedDefault { fn default() -> Self { NeedDefault::A } } }); abort!(span, "set_dummy test") } #[proc_macro] #[proc_macro_error] pub fn append_dummy(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let span = input.into_iter().next().unwrap().span(); set_dummy(quote! { impl Default for NeedDefault }); proc_macro_error::append_dummy(quote!({ fn default() -> Self { NeedDefault::A } })); abort!(span, "append_dummy test") } // Panic #[proc_macro] #[proc_macro_error] pub fn unrelated_panic(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { panic!("unrelated panic test") } // Success #[proc_macro] #[proc_macro_error] pub fn ok(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = TokenStream::from(input); quote!(fn #input() {}).into() } // Multiple tokens #[proc_macro_attribute] #[proc_macro_error] pub fn multiple_tokens( _: proc_macro::TokenStream, input: proc_macro::TokenStream, ) -> proc_macro::TokenStream { let input = proc_macro2::TokenStream::from(input); abort!(input, "..."); } #[proc_macro] #[proc_macro_error] pub fn to_tokens_span(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ty = parse_macro_input!(input as syn::Type); emit_error!(ty, "whole type"); emit_error!(ty.span(), "explicit .span()"); quote!().into() } #[proc_macro] #[proc_macro_error] pub fn explicit_span_range(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut spans = input.into_iter().step_by(2).map(|s| s.span()); let first = Span::from(spans.next().unwrap()); let last = Span::from(spans.nth(1).unwrap()); abort!(SpanRange { first, last }, "explicit SpanRange") } // Children messages #[proc_macro] #[proc_macro_error] pub fn children_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let mut spans = input.into_iter().step_by(2).map(|s| s.span()); diagnostic!(spans.next().unwrap(), Level::Error, "main macro message") .span_error(spans.next().unwrap().into(), "child message".into()) .emit(); let mut main = syn::Error::new(spans.next().unwrap().into(), "main syn::Error"); let child = syn::Error::new(spans.next().unwrap().into(), "child syn::Error"); main.combine(child); Diagnostic::from(main).abort() }