No-op macro that "uses" its inputs

Is it possible to define a macro_rules macro that "uses" its input tokens but is otherwise a no-op?

Context: I'm converting some logging mechanisms to be controlled by features, including having no logging at all. In the case of no logging, the macros become no-ops; currently:

macro_rules! debug       ( ($($tt:tt)*) => {{}} );

But then default method bodies that only use arguments to log trigger unused_variables, and imports that are only used in macro calls trigger unused_imports as well.

(cfg_attr isn't an adequate solution as I don't want to ignore actually-unmentioned imports and variables in any configuration.)

1 Like

instead of expanding to "nothing", can you put the code under an if false {} branch? similar to how tracing::level_enabled!() is defined, which could short-circuits to the constant false depending on cargo feature flags.

2 Likes

What code, though? I can't output the code that relies on the non-present feature. I could try to ape every macro I suppose, but I'm hoping for something less onerous (and that wouldn't require ongoing changes to support new upstream invocation patterns).

ponders awhile

But I guess I don't even really want a no-op, and probably will need to ape the macros. Otherwise this works in the no-features case.

fn test() {
    let mut s = String::new();
    let r = &mut s;
    debug!("{s}");
    r;
}

So I need to use things in approximately the same way the featureful macros do, which in turn requires aping the macros.

oh, you have a different design. what I had in mind is the logging functions and types are always available, not feature gated, then the macro expands to a simple if statement.

but apparently, this does not work if the functions and types are conditional compiled.

// this is the implementation, could be generic or type-erased, etc.
// it is always available, not conditional compiled
pub fn _log_event<T: Event>(event: T) { ... }

// this is user-facing API
macro_rules! debug {
  ($($tt:tt)*) => {
    // `is_debug()` can be const,
    // possibly implemented with `cfg!(feature = "foobar")`
    if is_debug() {
      let event = $crate::DebugEvent::new(Instant::now(), file!(), line!(), format!($($tt)*));
      $crate::_log_event(event)
    }
  }
}
1 Like

Yeah, I guess I was unclear -- the macros come from a dependency iff some feature is enabled, and I just wanted to replace those with no-ops. But now I realize that's not really amenable to a robust API anyway.

I've just gone ahead and copied over the relevant macro_rules! patterns from upstream and that will have to do.

Thanks for helping me rubber-duck it.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.