For clippy’s disallowed_macros lint, an #[allow(clippy::disallowed_macros)] placed at or around the call site (the macro invocation) is silently ineffective on newer clippy — the lint resolves at the lexical site where the banned macro tokens live, so the only robust placement is a module-root inner attribute #![allow(...)] at the file that defines the wrapper macro.
Core finding
mando bans
tracing::error!andlog::error!viaclippy.toml(disallowed-macros) to force all error logging through themando_core::error!wrapper macro. That wrapper must itself calltracing::error!, so it needs to suppress the lint for its own expansion. A per-invocation#[allow(clippy::disallowed_macros)]— on thetracing::error!call, on a wrapping#[allow] { tracing::error!(...) }block, or on the enclosing fn / match arm /letstatement — is silently INEFFECTIVE on a newer clippy (probe reported clippy1.95). The lint still fires, and clippy additionally warns the attribute is “unused, since it’s applied to a macro invocation”. The only placement that reliably works is a crate/module-root inner attribute#![allow(clippy::disallowed_macros)]at the top of the module that DEFINES the macro — i.e.mando-core/src/error.rs.
Why it behaves this way
disallowed_macros resolves the lint level at the lexical site where the banned macro tokens (tracing::error!) physically appear — which is the macro definition, not any call site. Consequences:
- An
#[allow]at the call site is the wrong scope entirely; the banned tokens aren’t lexically there, they arrive via expansion. Hence “unused attribute, applied to a macro invocation”. - One module-root
#![allow(clippy::disallowed_macros)]inmando-core/src/error.rs(wheretracing::error!is actually written, inside theerror!wrapper) covers all cross-crate callers —mando-lib,mando-bess, etc. — because they expand the wrapper but never lexically containtracing::error!themselves.
General lesson
For clippy lints that fire on macro expansions (disallowed_macros is the canonical example), #[allow] attributes attached at or around the expansion/call site frequently do not take effect. Prefer a module-root #![allow(...)] at the macro’s definition site. This is the version-independent, single-point placement.
For Agents
Rule of thumb: if a clippy lint fires because of what a macro expands into, put the
#![allow]where the macro is defined, not where it’s called. Attaching#[allow]to a macro invocation (or its enclosing statement/fn/block) is a no-op for expansion-resolved lints and clippy will flag the attribute itself as unused.
Caveat — do not overstate
Toolchain-version dependence
The workspace pins toolchain
1.88.0(rust-toolchain.toml). The wrapper macro currently ondevelopuses the block-level#[allow]form and presumably passes CI on 1.88 — so the block form may still work under the pinned clippy. The probe that exposed the ineffectiveness used a newer clippy (1.95). Takeaway: the block / per-invocation form is fragile across clippy versions; the module-root#![allow]is the robust placement that can’t be worse on 1.88 and is confirmed to work on newer clippy. Anyone touching this macro should verify clippy-clean under the pinned toolchain before relying on either form.
Placement summary
| Placement | Effective? | Notes |
|---|---|---|
#[allow] on the tracing::error! invocation | No (on 1.95) | Clippy warns: unused attribute on a macro invocation |
#[allow] { tracing::error!(...) } wrapping block | No (on 1.95) | Same — lint resolves at definition site |
#[allow] on enclosing fn / match arm / let | No (on 1.95) | Same |
#![allow(clippy::disallowed_macros)] at top of the defining module (mando-core/src/error.rs) | Yes | Covers all cross-crate callers; version-independent |
Where this lives in mando
- Ban is declared in mando’s
clippy.toml(disallowed-macros) — targetstracing::error!andlog::error!. - The sanctioned wrapper is
mando_core::error!, defined inmando-core/src/error.rs. That file is where the#![allow(clippy::disallowed_macros)]belongs. - Related macro plumbing for the error path is documented in BE-3117 Per-Flow Error Context Store (the
record_error!macro falls back tomando_core::error!out of flow scope).
Related
- mando-lib-macro — proc-macro crate (
#[derive(ErrorCode)]); the wider macro/error-code tooling surface. - BE-3117 Per-Flow Error Context Store — error-handling redesign during which this gotcha was found;
record_error!callsmando_core::error!as its out-of-scope fallback. - Agent Context — dense agent reference for the Mando workspace (error handling =
thiserror+#[derive(ErrorCode)]). - LOG — activity log.