// force-host // no-prefer-dynamic #![crate_type = "proc-macro"] #![feature(proc_macro_diagnostic, proc_macro_span)] extern crate proc_macro; use proc_macro::{TokenStream, TokenTree, Span, Diagnostic}; fn parse(input: TokenStream) -> Result<(), Diagnostic> { if let Some(TokenTree::Literal(lit)) = input.into_iter().next() { let mut spans = vec![]; let string = lit.to_string(); for hi in string.matches("hi") { let index = hi.as_ptr() as usize - string.as_ptr() as usize; let subspan = lit.subspan(index..(index + hi.len())).unwrap(); spans.push(subspan); } if !spans.is_empty() { Err(Span::call_site().error("found 'hi's").span_note(spans, "here")) } else { Ok(()) } } else { Err(Span::call_site().error("invalid input: expected string literal")) } } #[proc_macro] pub fn subspan(input: TokenStream) -> TokenStream { if let Err(diag) = parse(input) { diag.emit(); } TokenStream::new() }