Skip to content

Commit 4a7e866

Browse files
authored
attributes: downgrade unrecognized input errors to warnings (#786)
## Motivation In PR #672, the `#[instrument]` attribute was rewritten to use a custom `syn` parser rather than using `syn::AttributeArgs`. This was primarily to allow the use of arbitrary expressions as fields, but it had the side benefit of also allowing more sophisticated error reporting. In particular, we were able to replace the previous behavior of silently ignoring unknown input with a compiler error, which has been requested a few times (see #772 and #727). However, emitting a compiler error for inputs that we previously allowed (even if they did nothing) is technically a breaking change --- see #773 (comment) and following comments. This means that the other new features added in #672 cannot be released until we release `tracing-subscriber` 0.2. ## Solution This branch avoids the breaking change by downgrading these errors to warnings. However, it isn't currently possible to emit a warning from a proc-macro on stable --- the `proc_macro::Diagnostic` API which would allow this requires a nightly feature. This branch hacks around this by generating a fake deprecated item with for each unrecognized token in the input, emitting the skipped item's parse error as the deprecation's `note`, and "using" the deprecated item. The deprecated warning is used here rather than other code that will emit compiler warnings, because it gives us a way to customize the warning output (via the `note` field). I've tried to make it fairly obvious that this is not a "real" deprecation. Sample output looks like this: ![Screenshot_20200707_153151](https://user-images.githubusercontent.com/2796466/86850948-fce0a780-c066-11ea-9b1c-5085c0fd43e0.png) Fixes #727 Fixes #772 Signed-off-by: Eliza Weisman <[email protected]>
1 parent 7f60c92 commit 4a7e866

File tree

1 file changed

+40
-1
lines changed

1 file changed

+40
-1
lines changed

tracing-attributes/src/lib.rs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,7 @@ fn gen_body(
274274
} = sig;
275275

276276
let err = args.err;
277+
let warnings = args.warnings();
277278

278279
// generate the span's name
279280
let span_name = args
@@ -430,6 +431,7 @@ fn gen_body(
430431
#vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #return_type
431432
#where_clause
432433
{
434+
#warnings
433435
#body
434436
}
435437
)
@@ -443,6 +445,8 @@ struct InstrumentArgs {
443445
skips: HashSet<Ident>,
444446
fields: Option<Fields>,
445447
err: bool,
448+
/// Errors describing any unrecognized parse inputs that we skipped.
449+
parse_warnings: Vec<syn::Error>,
446450
}
447451

448452
impl InstrumentArgs {
@@ -493,6 +497,34 @@ impl InstrumentArgs {
493497
quote!(module_path!())
494498
}
495499
}
500+
501+
/// Generate "deprecation" warnings for any unrecognized attribute inputs
502+
/// that we skipped.
503+
///
504+
/// For backwards compatibility, we need to emit compiler warnings rather
505+
/// than errors for unrecognized inputs. Generating a fake deprecation is
506+
/// the only way to do this on stable Rust right now.
507+
fn warnings(&self) -> impl ToTokens {
508+
let warnings = self.parse_warnings.iter().map(|err| {
509+
let msg = format!("found unrecognized input, {}", err);
510+
let msg = LitStr::new(&msg, err.span());
511+
// TODO(eliza): This is a bit of a hack, but it's just about the
512+
// only way to emit warnings from a proc macro on stable Rust.
513+
// Eventually, when the `proc_macro::Diagnostic` API stabilizes, we
514+
// should definitely use that instead.
515+
quote_spanned! {err.span()=>
516+
#[warn(deprecated)]
517+
{
518+
#[deprecated(since = "not actually deprecated", note = #msg)]
519+
const TRACING_INSTRUMENT_WARNING: () = ();
520+
let _ = TRACING_INSTRUMENT_WARNING;
521+
}
522+
}
523+
});
524+
quote! {
525+
{ #(#warnings)* }
526+
}
527+
}
496528
}
497529

498530
impl Parse for InstrumentArgs {
@@ -542,7 +574,14 @@ impl Parse for InstrumentArgs {
542574
} else if lookahead.peek(Token![,]) {
543575
let _ = input.parse::<Token![,]>()?;
544576
} else {
545-
return Err(lookahead.error());
577+
// We found a token that we didn't expect!
578+
// We want to emit warnings for these, rather than errors, so
579+
// we'll add it to the list of unrecognized inputs we've seen so
580+
// far and keep going.
581+
args.parse_warnings.push(lookahead.error());
582+
// Parse the unrecognized token tree to advance the parse
583+
// stream, and throw it away so we can keep parsing.
584+
let _ = input.parse::<proc_macro2::TokenTree>();
546585
}
547586
}
548587
Ok(args)

0 commit comments

Comments
 (0)