Skip to content

Commit 9c28e64

Browse files
authored
macros: Remove 'r#' prefix from raw identifiers in field names (#3130)
* macros: Add test involving raw identifier * macros: Remove 'r#' prefix from raw identifiers in field names
1 parent 28561e0 commit 9c28e64

File tree

3 files changed

+80
-5
lines changed

3 files changed

+80
-5
lines changed

tracing/src/lib.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,8 +1002,8 @@ pub mod span;
10021002
pub mod __macro_support {
10031003
pub use crate::callsite::{Callsite, Registration};
10041004
use crate::{collect::Interest, Metadata};
1005-
use core::fmt;
10061005
use core::sync::atomic::Ordering;
1006+
use core::{fmt, str};
10071007

10081008
#[cfg(feature = "portable-atomic")]
10091009
use portable_atomic::AtomicU8;
@@ -1014,7 +1014,7 @@ pub mod __macro_support {
10141014
// Re-export the `core` functions that are used in macros. This allows
10151015
// a crate to be named `core` and avoid name clashes.
10161016
// See here: https://github.com/tokio-rs/tracing/issues/2761
1017-
pub use core::{concat, file, format_args, iter::Iterator, line, option::Option};
1017+
pub use core::{concat, file, format_args, iter::Iterator, line, option::Option, stringify};
10181018

10191019
/// Callsite implementation used by macro-generated code.
10201020
///
@@ -1202,6 +1202,66 @@ pub mod __macro_support {
12021202
.finish()
12031203
}
12041204
}
1205+
1206+
/// Implementation detail used for constructing FieldSet names from raw
1207+
/// identifiers. In `info!(..., r#type = "...")` the macro would end up
1208+
/// constructing a name equivalent to `FieldName(*b"type")`.
1209+
pub struct FieldName<const N: usize>([u8; N]);
1210+
1211+
impl<const N: usize> FieldName<N> {
1212+
/// Convert `"prefix.r#keyword.suffix"` to `b"prefix.keyword.suffix"`.
1213+
pub const fn new(input: &str) -> Self {
1214+
let input = input.as_bytes();
1215+
let mut output = [0u8; N];
1216+
let mut read = 0;
1217+
let mut write = 0;
1218+
while read < input.len() {
1219+
if read + 1 < input.len() && input[read] == b'r' && input[read + 1] == b'#' {
1220+
read += 2;
1221+
}
1222+
output[write] = input[read];
1223+
read += 1;
1224+
write += 1;
1225+
}
1226+
assert!(write == N);
1227+
Self(output)
1228+
}
1229+
1230+
pub const fn as_str(&self) -> &str {
1231+
// SAFETY: Because of the private visibility of self.0, it must have
1232+
// been computed by Self::new. So these bytes are all of the bytes
1233+
// of some original valid UTF-8 string, but with "r#" substrings
1234+
// removed, which cannot have produced invalid UTF-8.
1235+
unsafe { str::from_utf8_unchecked(self.0.as_slice()) }
1236+
}
1237+
}
1238+
1239+
impl FieldName<0> {
1240+
/// For `"prefix.r#keyword.suffix"` compute `"prefix.keyword.suffix".len()`.
1241+
pub const fn len(input: &str) -> usize {
1242+
// Count occurrences of "r#"
1243+
let mut raw = 0;
1244+
1245+
let mut i = 0;
1246+
while i < input.len() {
1247+
if input.as_bytes()[i] == b'#' {
1248+
raw += 1;
1249+
}
1250+
i += 1;
1251+
}
1252+
1253+
input.len() - 2 * raw
1254+
}
1255+
}
1256+
1257+
impl<const N: usize> fmt::Debug for FieldName<N> {
1258+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
1259+
formatter
1260+
.debug_tuple("FieldName")
1261+
.field(&self.as_str())
1262+
.finish()
1263+
}
1264+
}
12051265
}
12061266

12071267
#[cfg(feature = "log")]

tracing/src/macros.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3099,9 +3099,12 @@ macro_rules! level_to_log {
30993099
#[doc(hidden)]
31003100
#[macro_export]
31013101
macro_rules! __tracing_stringify {
3102-
($($t:tt)*) => {
3103-
stringify!($($t)*)
3104-
};
3102+
($($k:ident).+) => {{
3103+
const NAME: $crate::__macro_support::FieldName<{
3104+
$crate::__macro_support::FieldName::len($crate::__macro_support::stringify!($($k).+))
3105+
}> = $crate::__macro_support::FieldName::new($crate::__macro_support::stringify!($($k).+));
3106+
NAME.as_str()
3107+
}};
31053108
}
31063109

31073110
#[cfg(not(feature = "log"))]

tracing/tests/event.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,3 +509,15 @@ fn keyword_ident_in_field_name() {
509509
with_default(collector, || error!(crate = "tracing", "message"));
510510
handle.assert_finished();
511511
}
512+
513+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
514+
#[test]
515+
fn raw_ident_in_field_name() {
516+
let (collector, handle) = collector::mock()
517+
.event(expect::event().with_fields(expect::field("this.type").with_value(&"Value")))
518+
.only()
519+
.run_with_handle();
520+
521+
with_default(collector, || error!(this.r#type = "Value"));
522+
handle.assert_finished();
523+
}

0 commit comments

Comments
 (0)