Skip to content

Commit

Permalink
Merge #531 #534
Browse files Browse the repository at this point in the history
531: refactor the `macros` crate r=japaric a=japaric

~~NOT READY FOR REVIEW. I just wanted to signal that I'm working on this~~

534: defmt-test: attribute test progress message to the test in question r=japaric a=jonas-schievink

Sets the span of the tokens making up the `defmt::info!` call to the test function's identifier.

Fixes #425

```
 INFO  (1/11) running `split_filters`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:18
 INFO  (2/11) running `basic_roundtrip`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:43
 INFO  (3/11) running `no_filters_no_frames`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:58
 INFO  (4/11) running `filter_mask32_std`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:68
 INFO  (5/11) running `filter_mask32_ext`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:112
 INFO  (6/11) running `filter_mask16`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:153
 INFO  (7/11) running `filter_list32_std`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:193
 INFO  (8/11) running `filter_list32_ext`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:226
 INFO  (9/11) running `dequeue_lower_priority_frame`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:264
 INFO  (10/11) running `enable_non_blocking`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:317
 INFO  (11/11) running `ext_roundtrip`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:330
```

(this was pretty painful to test, since qemu-run does not print location information, and probe-run doesn't use git defmt out of the box, so I had to edit its Cargo.toml, and then make bxcan use git defmt)

Co-authored-by: Jorge Aparicio <jorge.aparicio@ferrous-systems.com>
Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
  • Loading branch information
3 people authored Jul 16, 2021
3 parents ea22487 + d6806d4 + c8de519 commit cfb71f2
Show file tree
Hide file tree
Showing 38 changed files with 1,703 additions and 1,382 deletions.
13 changes: 8 additions & 5 deletions firmware/defmt-test/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate proc_macro;

use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::{format_ident, quote};
use quote::{format_ident, quote, quote_spanned};
use syn::{parse, spanned::Spanned, Attribute, Item, ItemFn, ItemMod, ReturnType, Type};

#[proc_macro_attribute]
Expand Down Expand Up @@ -202,13 +202,16 @@ fn tests_impl(args: TokenStream, input: TokenStream) -> parse::Result<TokenStrea
};
)
};
let unit_test_running = tests
let unit_test_progress = tests
.iter()
.map(|test| {
format!(
let message = format!(
"({{=usize}}/{{=usize}}) running `{}`...",
test.func.sig.ident
)
);
quote_spanned! {
test.func.sig.ident.span() => defmt::info!(#message, __defmt_test_number, __DEFMT_TEST_COUNT);
}
})
.collect::<Vec<_>>();
Ok(quote!(mod #ident {
Expand All @@ -223,7 +226,7 @@ fn tests_impl(args: TokenStream, input: TokenStream) -> parse::Result<TokenStrea
#(
#(#test_cfgs)*
{
defmt::info!(#unit_test_running, __defmt_test_number, __DEFMT_TEST_COUNT);
#unit_test_progress
#unit_test_calls
__defmt_test_number += 1;
}
Expand Down
1 change: 1 addition & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ unstable-test = []

[dependencies]
defmt-parser = { path = "../parser", features = ["unstable"], version = "=0.2.2" }
proc-macro-error = "1.0.4"
proc-macro2 = "1.0.27"
quote = "1.0.9"
syn = { version = "1.0", features = ["full"] }
4 changes: 4 additions & 0 deletions macros/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
//! Procedural macros that are attributes
pub(crate) mod global_logger;
pub(crate) mod panic_handler;
57 changes: 57 additions & 0 deletions macros/src/attributes/global_logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use proc_macro::TokenStream;
use proc_macro_error::{abort, abort_call_site};
use quote::quote;
use syn::{parse_macro_input, Fields, ItemStruct};

pub(crate) fn expand(args: TokenStream, item: TokenStream) -> TokenStream {
if !args.is_empty() {
abort_call_site!("`#[global_logger]` attribute takes no arguments")
}

let strukt = parse_macro_input!(item as ItemStruct);

validate(&strukt);

codegen(&strukt)
}

fn validate(strukt: &ItemStruct) {
let is_unit_struct = matches!(strukt.fields, Fields::Unit);

if !strukt.generics.params.is_empty()
|| strukt.generics.where_clause.is_some()
|| !is_unit_struct
{
abort!(
strukt,
"struct must be a non-generic unit struct (e.g. `struct S;`)"
);
}
}

fn codegen(strukt: &ItemStruct) -> TokenStream {
let attrs = &strukt.attrs;
let ident = &strukt.ident;
let vis = &strukt.vis;

quote!(
#(#attrs)*
#vis struct #ident;

#[no_mangle]
unsafe fn _defmt_acquire() {
<#ident as defmt::Logger>::acquire()
}

#[no_mangle]
unsafe fn _defmt_release() {
<#ident as defmt::Logger>::release()
}

#[no_mangle]
unsafe fn _defmt_write(bytes: &[u8]) {
<#ident as defmt::Logger>::write(bytes)
}
)
.into()
}
77 changes: 77 additions & 0 deletions macros/src/attributes/panic_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use proc_macro::TokenStream;
use proc_macro_error::{abort, abort_call_site};
use quote::quote;
use syn::{parse_macro_input, Attribute, ItemFn, ReturnType, Type};

pub(crate) fn expand(args: TokenStream, item: TokenStream) -> TokenStream {
if !args.is_empty() {
abort_call_site!("`#[defmt::panic_handler]` attribute takes no arguments");
}

let fun = parse_macro_input!(item as ItemFn);

validate(&fun);

codegen(&fun)
}

fn validate(fun: &ItemFn) {
let is_divergent = match &fun.sig.output {
ReturnType::Default => false,
ReturnType::Type(_, ty) => matches!(&**ty, Type::Never(_)),
};

if fun.sig.constness.is_some()
|| fun.sig.asyncness.is_some()
|| fun.sig.unsafety.is_some()
|| fun.sig.abi.is_some()
|| !fun.sig.generics.params.is_empty()
|| fun.sig.generics.where_clause.is_some()
|| fun.sig.variadic.is_some()
|| !fun.sig.inputs.is_empty()
|| !is_divergent
{
abort!(fun.sig.ident, "function must have signature `fn() -> !`");
}

check_for_attribute_conflicts("panic_handler", &fun.attrs, &["export_name", "no_mangle"]);
}

/// Checks if any attribute in `attrs_to_check` is in `reject_list` and returns a compiler error if there's a match
///
/// The compiler error will indicate that the attribute conflicts with `attr_name`
fn check_for_attribute_conflicts(
attr_name: &str,
attrs_to_check: &[Attribute],
reject_list: &[&str],
) {
for attr in attrs_to_check {
if let Some(ident) = attr.path.get_ident() {
let ident = ident.to_string();

if reject_list.contains(&ident.as_str()) {
abort!(
attr,
"`#[{}]` attribute cannot be used together with `#[{}]`",
attr_name,
ident
)
}
}
}
}

fn codegen(fun: &ItemFn) -> TokenStream {
let attrs = &fun.attrs;
let block = &fun.block;
let ident = &fun.sig.ident;

quote!(
#(#attrs)*
#[export_name = "_defmt_panic"]
fn #ident() -> ! {
#block
}
)
.into()
}
200 changes: 0 additions & 200 deletions macros/src/bitflags.rs

This file was deleted.

5 changes: 5 additions & 0 deletions macros/src/cargo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use std::env;

pub(crate) fn package_name() -> String {
env::var("CARGO_PKG_NAME").unwrap_or_else(|_| "<unknown>".to_string())
}
Loading

0 comments on commit cfb71f2

Please sign in to comment.