1 use std::collections::HashSet; 2 use syn::{punctuated::Punctuated, Expr, Ident, LitInt, LitStr, Path, Token}; 3 4 use proc_macro2::TokenStream; 5 use quote::{quote, quote_spanned, ToTokens}; 6 use syn::ext::IdentExt as _; 7 use syn::parse::{Parse, ParseStream}; 8 9 /// Arguments to `#[instrument(err(...))]` and `#[instrument(ret(...))]` which describe how the 10 /// return value event should be emitted. 11 #[derive(Clone, Default, Debug)] 12 pub(crate) struct EventArgs { 13 level: Option<Level>, 14 pub(crate) mode: FormatMode, 15 } 16 17 #[derive(Clone, Default, Debug)] 18 pub(crate) struct InstrumentArgs { 19 level: Option<Level>, 20 pub(crate) name: Option<LitStrOrIdent>, 21 target: Option<LitStrOrIdent>, 22 pub(crate) parent: Option<Expr>, 23 pub(crate) follows_from: Option<Expr>, 24 pub(crate) skips: HashSet<Ident>, 25 pub(crate) skip_all: bool, 26 pub(crate) fields: Option<Fields>, 27 pub(crate) err_args: Option<EventArgs>, 28 pub(crate) ret_args: Option<EventArgs>, 29 /// Errors describing any unrecognized parse inputs that we skipped. 30 parse_warnings: Vec<syn::Error>, 31 } 32 33 impl InstrumentArgs { level(&self) -> Level34 pub(crate) fn level(&self) -> Level { 35 self.level.clone().unwrap_or(Level::Info) 36 } 37 target(&self) -> impl ToTokens38 pub(crate) fn target(&self) -> impl ToTokens { 39 if let Some(ref target) = self.target { 40 quote!(#target) 41 } else { 42 quote!(module_path!()) 43 } 44 } 45 46 /// Generate "deprecation" warnings for any unrecognized attribute inputs 47 /// that we skipped. 48 /// 49 /// For backwards compatibility, we need to emit compiler warnings rather 50 /// than errors for unrecognized inputs. Generating a fake deprecation is 51 /// the only way to do this on stable Rust right now. warnings(&self) -> impl ToTokens52 pub(crate) fn warnings(&self) -> impl ToTokens { 53 let warnings = self.parse_warnings.iter().map(|err| { 54 let msg = format!("found unrecognized input, {}", err); 55 let msg = LitStr::new(&msg, err.span()); 56 // TODO(eliza): This is a bit of a hack, but it's just about the 57 // only way to emit warnings from a proc macro on stable Rust. 58 // Eventually, when the `proc_macro::Diagnostic` API stabilizes, we 59 // should definitely use that instead. 60 quote_spanned! {err.span()=> 61 #[warn(deprecated)] 62 { 63 #[deprecated(since = "not actually deprecated", note = #msg)] 64 const TRACING_INSTRUMENT_WARNING: () = (); 65 let _ = TRACING_INSTRUMENT_WARNING; 66 } 67 } 68 }); 69 quote! { 70 { #(#warnings)* } 71 } 72 } 73 } 74 75 impl Parse for InstrumentArgs { parse(input: ParseStream<'_>) -> syn::Result<Self>76 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 77 let mut args = Self::default(); 78 while !input.is_empty() { 79 let lookahead = input.lookahead1(); 80 if lookahead.peek(kw::name) { 81 if args.name.is_some() { 82 return Err(input.error("expected only a single `name` argument")); 83 } 84 let name = input.parse::<StrArg<kw::name>>()?.value; 85 args.name = Some(name); 86 } else if lookahead.peek(LitStr) { 87 // XXX: apparently we support names as either named args with an 88 // sign, _or_ as unnamed string literals. That's weird, but 89 // changing it is apparently breaking. 90 // This also means that when using idents for name, it must be via 91 // a named arg, i.e. `#[instrument(name = SOME_IDENT)]`. 92 if args.name.is_some() { 93 return Err(input.error("expected only a single `name` argument")); 94 } 95 args.name = Some(input.parse()?); 96 } else if lookahead.peek(kw::target) { 97 if args.target.is_some() { 98 return Err(input.error("expected only a single `target` argument")); 99 } 100 let target = input.parse::<StrArg<kw::target>>()?.value; 101 args.target = Some(target); 102 } else if lookahead.peek(kw::parent) { 103 if args.target.is_some() { 104 return Err(input.error("expected only a single `parent` argument")); 105 } 106 let parent = input.parse::<ExprArg<kw::parent>>()?; 107 args.parent = Some(parent.value); 108 } else if lookahead.peek(kw::follows_from) { 109 if args.target.is_some() { 110 return Err(input.error("expected only a single `follows_from` argument")); 111 } 112 let follows_from = input.parse::<ExprArg<kw::follows_from>>()?; 113 args.follows_from = Some(follows_from.value); 114 } else if lookahead.peek(kw::level) { 115 if args.level.is_some() { 116 return Err(input.error("expected only a single `level` argument")); 117 } 118 args.level = Some(input.parse()?); 119 } else if lookahead.peek(kw::skip) { 120 if !args.skips.is_empty() { 121 return Err(input.error("expected only a single `skip` argument")); 122 } 123 if args.skip_all { 124 return Err(input.error("expected either `skip` or `skip_all` argument")); 125 } 126 let Skips(skips) = input.parse()?; 127 args.skips = skips; 128 } else if lookahead.peek(kw::skip_all) { 129 if args.skip_all { 130 return Err(input.error("expected only a single `skip_all` argument")); 131 } 132 if !args.skips.is_empty() { 133 return Err(input.error("expected either `skip` or `skip_all` argument")); 134 } 135 let _ = input.parse::<kw::skip_all>()?; 136 args.skip_all = true; 137 } else if lookahead.peek(kw::fields) { 138 if args.fields.is_some() { 139 return Err(input.error("expected only a single `fields` argument")); 140 } 141 args.fields = Some(input.parse()?); 142 } else if lookahead.peek(kw::err) { 143 let _ = input.parse::<kw::err>(); 144 let err_args = EventArgs::parse(input)?; 145 args.err_args = Some(err_args); 146 } else if lookahead.peek(kw::ret) { 147 let _ = input.parse::<kw::ret>()?; 148 let ret_args = EventArgs::parse(input)?; 149 args.ret_args = Some(ret_args); 150 } else if lookahead.peek(Token![,]) { 151 let _ = input.parse::<Token![,]>()?; 152 } else { 153 // We found a token that we didn't expect! 154 // We want to emit warnings for these, rather than errors, so 155 // we'll add it to the list of unrecognized inputs we've seen so 156 // far and keep going. 157 args.parse_warnings.push(lookahead.error()); 158 // Parse the unrecognized token tree to advance the parse 159 // stream, and throw it away so we can keep parsing. 160 let _ = input.parse::<proc_macro2::TokenTree>(); 161 } 162 } 163 Ok(args) 164 } 165 } 166 167 impl EventArgs { level(&self, default: Level) -> Level168 pub(crate) fn level(&self, default: Level) -> Level { 169 self.level.clone().unwrap_or(default) 170 } 171 } 172 173 impl Parse for EventArgs { parse(input: ParseStream<'_>) -> syn::Result<Self>174 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 175 if !input.peek(syn::token::Paren) { 176 return Ok(Self::default()); 177 } 178 let content; 179 let _ = syn::parenthesized!(content in input); 180 let mut result = Self::default(); 181 let mut parse_one_arg = 182 || { 183 let lookahead = content.lookahead1(); 184 if lookahead.peek(kw::level) { 185 if result.level.is_some() { 186 return Err(content.error("expected only a single `level` argument")); 187 } 188 result.level = Some(content.parse()?); 189 } else if result.mode != FormatMode::default() { 190 return Err(content.error("expected only a single format argument")); 191 } else if let Some(ident) = content.parse::<Option<Ident>>()? { 192 match ident.to_string().as_str() { 193 "Debug" => result.mode = FormatMode::Debug, 194 "Display" => result.mode = FormatMode::Display, 195 _ => return Err(syn::Error::new( 196 ident.span(), 197 "unknown event formatting mode, expected either `Debug` or `Display`", 198 )), 199 } 200 } 201 Ok(()) 202 }; 203 parse_one_arg()?; 204 if !content.is_empty() { 205 if content.lookahead1().peek(Token![,]) { 206 let _ = content.parse::<Token![,]>()?; 207 parse_one_arg()?; 208 } else { 209 return Err(content.error("expected `,` or `)`")); 210 } 211 } 212 Ok(result) 213 } 214 } 215 216 #[derive(Debug, Clone)] 217 pub(super) enum LitStrOrIdent { 218 LitStr(LitStr), 219 Ident(Ident), 220 } 221 222 impl ToTokens for LitStrOrIdent { to_tokens(&self, tokens: &mut TokenStream)223 fn to_tokens(&self, tokens: &mut TokenStream) { 224 match self { 225 LitStrOrIdent::LitStr(target) => target.to_tokens(tokens), 226 LitStrOrIdent::Ident(ident) => ident.to_tokens(tokens), 227 } 228 } 229 } 230 231 impl Parse for LitStrOrIdent { parse(input: ParseStream<'_>) -> syn::Result<Self>232 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 233 input 234 .parse::<LitStr>() 235 .map(LitStrOrIdent::LitStr) 236 .or_else(|_| input.parse::<Ident>().map(LitStrOrIdent::Ident)) 237 } 238 } 239 240 struct StrArg<T> { 241 value: LitStrOrIdent, 242 _p: std::marker::PhantomData<T>, 243 } 244 245 impl<T: Parse> Parse for StrArg<T> { parse(input: ParseStream<'_>) -> syn::Result<Self>246 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 247 let _ = input.parse::<T>()?; 248 let _ = input.parse::<Token![=]>()?; 249 let value = input.parse()?; 250 Ok(Self { 251 value, 252 _p: std::marker::PhantomData, 253 }) 254 } 255 } 256 257 struct ExprArg<T> { 258 value: Expr, 259 _p: std::marker::PhantomData<T>, 260 } 261 262 impl<T: Parse> Parse for ExprArg<T> { parse(input: ParseStream<'_>) -> syn::Result<Self>263 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 264 let _ = input.parse::<T>()?; 265 let _ = input.parse::<Token![=]>()?; 266 let value = input.parse()?; 267 Ok(Self { 268 value, 269 _p: std::marker::PhantomData, 270 }) 271 } 272 } 273 274 struct Skips(HashSet<Ident>); 275 276 impl Parse for Skips { parse(input: ParseStream<'_>) -> syn::Result<Self>277 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 278 let _ = input.parse::<kw::skip>(); 279 let content; 280 let _ = syn::parenthesized!(content in input); 281 let names = content.parse_terminated(Ident::parse_any, Token![,])?; 282 let mut skips = HashSet::new(); 283 for name in names { 284 if skips.contains(&name) { 285 return Err(syn::Error::new( 286 name.span(), 287 "tried to skip the same field twice", 288 )); 289 } else { 290 skips.insert(name); 291 } 292 } 293 Ok(Self(skips)) 294 } 295 } 296 297 #[derive(Clone, Debug, Hash, PartialEq, Eq, Default)] 298 pub(crate) enum FormatMode { 299 #[default] 300 Default, 301 Display, 302 Debug, 303 } 304 305 #[derive(Clone, Debug)] 306 pub(crate) struct Fields(pub(crate) Punctuated<Field, Token![,]>); 307 308 #[derive(Clone, Debug)] 309 pub(crate) struct Field { 310 pub(crate) name: Punctuated<Ident, Token![.]>, 311 pub(crate) value: Option<Expr>, 312 pub(crate) kind: FieldKind, 313 } 314 315 #[derive(Clone, Debug, Eq, PartialEq)] 316 pub(crate) enum FieldKind { 317 Debug, 318 Display, 319 Value, 320 } 321 322 impl Parse for Fields { parse(input: ParseStream<'_>) -> syn::Result<Self>323 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 324 let _ = input.parse::<kw::fields>(); 325 let content; 326 let _ = syn::parenthesized!(content in input); 327 let fields = content.parse_terminated(Field::parse, Token![,])?; 328 Ok(Self(fields)) 329 } 330 } 331 332 impl ToTokens for Fields { to_tokens(&self, tokens: &mut TokenStream)333 fn to_tokens(&self, tokens: &mut TokenStream) { 334 self.0.to_tokens(tokens) 335 } 336 } 337 338 impl Parse for Field { parse(input: ParseStream<'_>) -> syn::Result<Self>339 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 340 let mut kind = FieldKind::Value; 341 if input.peek(Token![%]) { 342 input.parse::<Token![%]>()?; 343 kind = FieldKind::Display; 344 } else if input.peek(Token![?]) { 345 input.parse::<Token![?]>()?; 346 kind = FieldKind::Debug; 347 }; 348 let name = Punctuated::parse_separated_nonempty_with(input, Ident::parse_any)?; 349 let value = if input.peek(Token![=]) { 350 input.parse::<Token![=]>()?; 351 if input.peek(Token![%]) { 352 input.parse::<Token![%]>()?; 353 kind = FieldKind::Display; 354 } else if input.peek(Token![?]) { 355 input.parse::<Token![?]>()?; 356 kind = FieldKind::Debug; 357 }; 358 Some(input.parse()?) 359 } else { 360 None 361 }; 362 Ok(Self { name, value, kind }) 363 } 364 } 365 366 impl ToTokens for Field { to_tokens(&self, tokens: &mut TokenStream)367 fn to_tokens(&self, tokens: &mut TokenStream) { 368 if let Some(ref value) = self.value { 369 let name = &self.name; 370 let kind = &self.kind; 371 tokens.extend(quote! { 372 #name = #kind #value 373 }) 374 } else if self.kind == FieldKind::Value { 375 // XXX(eliza): I don't like that fields without values produce 376 // empty fields rather than local variable shorthand...but, 377 // we've released a version where field names without values in 378 // `instrument` produce empty field values, so changing it now 379 // is a breaking change. agh. 380 let name = &self.name; 381 tokens.extend(quote!(#name = tracing::field::Empty)) 382 } else { 383 self.kind.to_tokens(tokens); 384 self.name.to_tokens(tokens); 385 } 386 } 387 } 388 389 impl ToTokens for FieldKind { to_tokens(&self, tokens: &mut TokenStream)390 fn to_tokens(&self, tokens: &mut TokenStream) { 391 match self { 392 FieldKind::Debug => tokens.extend(quote! { ? }), 393 FieldKind::Display => tokens.extend(quote! { % }), 394 _ => {} 395 } 396 } 397 } 398 399 #[derive(Clone, Debug)] 400 pub(crate) enum Level { 401 Trace, 402 Debug, 403 Info, 404 Warn, 405 Error, 406 Path(Path), 407 } 408 409 impl Parse for Level { parse(input: ParseStream<'_>) -> syn::Result<Self>410 fn parse(input: ParseStream<'_>) -> syn::Result<Self> { 411 let _ = input.parse::<kw::level>()?; 412 let _ = input.parse::<Token![=]>()?; 413 let lookahead = input.lookahead1(); 414 if lookahead.peek(LitStr) { 415 let str: LitStr = input.parse()?; 416 match str.value() { 417 s if s.eq_ignore_ascii_case("trace") => Ok(Level::Trace), 418 s if s.eq_ignore_ascii_case("debug") => Ok(Level::Debug), 419 s if s.eq_ignore_ascii_case("info") => Ok(Level::Info), 420 s if s.eq_ignore_ascii_case("warn") => Ok(Level::Warn), 421 s if s.eq_ignore_ascii_case("error") => Ok(Level::Error), 422 _ => Err(input.error( 423 "unknown verbosity level, expected one of \"trace\", \ 424 \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5", 425 )), 426 } 427 } else if lookahead.peek(LitInt) { 428 fn is_level(lit: &LitInt, expected: u64) -> bool { 429 match lit.base10_parse::<u64>() { 430 Ok(value) => value == expected, 431 Err(_) => false, 432 } 433 } 434 let int: LitInt = input.parse()?; 435 match &int { 436 i if is_level(i, 1) => Ok(Level::Trace), 437 i if is_level(i, 2) => Ok(Level::Debug), 438 i if is_level(i, 3) => Ok(Level::Info), 439 i if is_level(i, 4) => Ok(Level::Warn), 440 i if is_level(i, 5) => Ok(Level::Error), 441 _ => Err(input.error( 442 "unknown verbosity level, expected one of \"trace\", \ 443 \"debug\", \"info\", \"warn\", or \"error\", or a number 1-5", 444 )), 445 } 446 } else if lookahead.peek(Ident) { 447 Ok(Self::Path(input.parse()?)) 448 } else { 449 Err(lookahead.error()) 450 } 451 } 452 } 453 454 impl ToTokens for Level { to_tokens(&self, tokens: &mut TokenStream)455 fn to_tokens(&self, tokens: &mut TokenStream) { 456 match self { 457 Level::Trace => tokens.extend(quote!(tracing::Level::TRACE)), 458 Level::Debug => tokens.extend(quote!(tracing::Level::DEBUG)), 459 Level::Info => tokens.extend(quote!(tracing::Level::INFO)), 460 Level::Warn => tokens.extend(quote!(tracing::Level::WARN)), 461 Level::Error => tokens.extend(quote!(tracing::Level::ERROR)), 462 Level::Path(ref pat) => tokens.extend(quote!(#pat)), 463 } 464 } 465 } 466 467 mod kw { 468 syn::custom_keyword!(fields); 469 syn::custom_keyword!(skip); 470 syn::custom_keyword!(skip_all); 471 syn::custom_keyword!(level); 472 syn::custom_keyword!(target); 473 syn::custom_keyword!(parent); 474 syn::custom_keyword!(follows_from); 475 syn::custom_keyword!(name); 476 syn::custom_keyword!(err); 477 syn::custom_keyword!(ret); 478 } 479