Andrew Dona-Couch 0e852a95e0 attributes: support destructuring in arguments (#397)
This pull request adds support to the `#[instrument]` macro for
destructured arguments.

## Motivation

The `#[instrument]` macro automatically adds function arguments as
fields on the `Span`.  Previously, it would only do so for "plain"
`Ident` argument types, but would ignore destructured arguments.

## Solution

By recursively pattern matching each argument to find all identifiers it
creates, we can construct an authoritative list of all input names.

* attributes: extract param_names extractor

To enable the recursion needed to extract all the various
destructured parameter types.

* attributes: support destructured tuple arguments

* attributes: support destructured ref arguments

* attributes: support destructured tuple struct arguments

* attributes: support destructured struct arguments

* attributes: add crazy destructuring test

This is to test the combination of all the supported function
argument destructuring types.

* attributes: move destructuring tests to their own file

* attributes: apply cargo fmt

* attributes: add helpful comment to pattern pattern match

Since function signatures can only use irrefutable patterns,
we can be confident that no legal code will have any other
variant of `syn::Pat` than the ones we match on.  However, to
prevent anyone else from going down the rabbit hole of
wondering about it, we add a helpful comment.

* attributes: support self argument as span field
2019-10-22 15:24:20 -07:00

203 lines
4.9 KiB
Rust

mod support;
use support::*;
use tracing::subscriber::with_default;
use tracing::Level;
use tracing_attributes::instrument;
#[test]
fn override_everything() {
#[instrument(target = "my_target", level = "debug")]
fn my_fn() {}
#[instrument(level = "debug", target = "my_target")]
fn my_other_fn() {}
let span = span::mock()
.named("my_fn")
.at_level(Level::DEBUG)
.with_target("my_target");
let span2 = span::mock()
.named("my_other_fn")
.at_level(Level::DEBUG)
.with_target("my_target");
let (subscriber, handle) = subscriber::mock()
.new_span(span.clone())
.enter(span.clone())
.exit(span.clone())
.drop_span(span)
.new_span(span2.clone())
.enter(span2.clone())
.exit(span2.clone())
.drop_span(span2)
.done()
.run_with_handle();
with_default(subscriber, || {
my_fn();
my_other_fn();
});
handle.assert_finished();
}
#[test]
fn fields() {
#[instrument(target = "my_target", level = "debug")]
fn my_fn(arg1: usize, arg2: bool) {}
let span = span::mock()
.named("my_fn")
.at_level(Level::DEBUG)
.with_target("my_target");
let span2 = span::mock()
.named("my_fn")
.at_level(Level::DEBUG)
.with_target("my_target");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
field::mock("arg1")
.with_value(&format_args!("2"))
.and(field::mock("arg2").with_value(&format_args!("false")))
.only(),
),
)
.enter(span.clone())
.exit(span.clone())
.drop_span(span)
.new_span(
span2.clone().with_field(
field::mock("arg1")
.with_value(&format_args!("3"))
.and(field::mock("arg2").with_value(&format_args!("true")))
.only(),
),
)
.enter(span2.clone())
.exit(span2.clone())
.drop_span(span2)
.done()
.run_with_handle();
with_default(subscriber, || {
my_fn(2, false);
my_fn(3, true);
});
handle.assert_finished();
}
#[test]
fn skip() {
struct UnDebug(pub u32);
#[instrument(target = "my_target", level = "debug", skip(_arg2, _arg3))]
fn my_fn(arg1: usize, _arg2: UnDebug, _arg3: UnDebug) {}
let span = span::mock()
.named("my_fn")
.at_level(Level::DEBUG)
.with_target("my_target");
let span2 = span::mock()
.named("my_fn")
.at_level(Level::DEBUG)
.with_target("my_target");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone()
.with_field(field::mock("arg1").with_value(&format_args!("2")).only()),
)
.enter(span.clone())
.exit(span.clone())
.drop_span(span)
.new_span(
span2
.clone()
.with_field(field::mock("arg1").with_value(&format_args!("3")).only()),
)
.enter(span2.clone())
.exit(span2.clone())
.drop_span(span2)
.done()
.run_with_handle();
with_default(subscriber, || {
my_fn(2, UnDebug(0), UnDebug(1));
my_fn(3, UnDebug(0), UnDebug(1));
});
handle.assert_finished();
}
#[test]
fn generics() {
#[derive(Debug)]
struct Foo;
#[instrument]
fn my_fn<S, T: std::fmt::Debug>(arg1: S, arg2: T)
where
S: std::fmt::Debug,
{
}
let span = span::mock().named("my_fn");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
field::mock("arg1")
.with_value(&format_args!("Foo"))
.and(field::mock("arg2").with_value(&format_args!("false"))),
),
)
.enter(span.clone())
.exit(span.clone())
.drop_span(span)
.done()
.run_with_handle();
with_default(subscriber, || {
my_fn(Foo, false);
});
handle.assert_finished();
}
#[test]
fn methods() {
#[derive(Debug)]
struct Foo;
impl Foo {
#[instrument]
fn my_fn(&self, arg1: usize) {}
}
let span = span::mock().named("my_fn");
let (subscriber, handle) = subscriber::mock()
.new_span(
span.clone().with_field(
field::mock("self")
.with_value(&format_args!("Foo"))
.and(field::mock("arg1").with_value(&format_args!("42"))),
),
)
.enter(span.clone())
.exit(span.clone())
.drop_span(span)
.done()
.run_with_handle();
with_default(subscriber, || {
let foo = Foo;
foo.my_fn(42);
});
handle.assert_finished();
}