RFA-649 · Case file with fixtures · Case 621 of 694 · Compiler evidence
Built-in Attributes Have Exact Argument Grammars
Attributes are structured compiler interfaces, not free-form annotations. Follow each attribute's documented grammar, inspect generated tokens, and measure optimisation hints instead of assuming them.
- Reviewed
- Rust
- Rust 1.98.1, edition 2024
- Targets
- all Rust targets
- Profiles
- dev, release, test
Direct answer
What this Rust failure means
- Why it happens
- A built-in attribute was treated as a free-form annotation or a generator rendered an empty option list with invalid meta-item syntax.
- First discriminating check
- Use the documented word or single-argument form, inspect expanded placement, and retain optimisation hints only with measured evidence.
Rust attributes look compact, but each recognised attribute defines which input forms it accepts. #[inline] is valid, and #[inline(always)] or #[inline(never)] supplies one recognised argument. Empty parentheses are not equivalent to no input. The failing fixture uses them and receives E0805.
Parse the attribute as an interface
The official E0805 explanation shows invalid zero- and two-argument list forms. The Reference documents the inline attribute and its accepted syntax.
The repaired fixture uses bare #[inline], which is a hint to the compiler. Removing parentheses changes the meta-item form rather than passing an empty tuple.
I read the documentation for the exact toolchain. Similar-looking attributes can accept word, list, or name-value forms with different token grammars.
Attribute placement matters as much as input
An attribute can be valid in general but invalid on a particular item. inline applies to functions and closures in documented contexts, while representation attributes apply to types. Built-in, derive-helper, and tool attributes have different registration rules.
I inspect cfg-expanded and macro-expanded code when the visible source seems correct. A generated comma, empty repetition, or moved attribute can produce malformed input only for one feature combination.
The Reference introduces attributes, their inner and outer forms, and meta-item syntax. I avoid assuming a procedural macro receives arbitrary text; it receives structured tokens after Rust lexing.
Inline is a hint with tradeoffs
#[inline] does not guarantee a call is inlined. always strengthens the request but still has documented limitations. Inlining can remove call overhead and expose optimisation, but it can increase code size, instruction-cache pressure, compile time, and duplication across monomorphisations.
I add a hint only after profiling or when a tiny cross-crate abstraction predictably needs visibility. Link-time optimisation and compiler heuristics may already make the right decision.
#[inline(never)] can aid measurement, code layout, panic cold paths, or debugging, but it also needs evidence. Attribute correctness proves syntax, not performance.
Generated attributes need empty-case tests
Macros often build lists from optional arguments. When the list is empty, emitting #[inline()] instead of #[inline] causes E0805. When two mutually exclusive options are both set, the generator may emit too many inputs.
I model accepted variants as an enum rather than a vector of arbitrary tokens. Rendering then has one branch per legal form. Compile tests cover absent, each single valid choice, duplicates, conflicting options, and cfg-driven emptiness.
For custom attributes, diagnostics should point at the offending token and list accepted shapes. A parser which silently ignores extras creates configuration that looks active but does nothing.
Optimisation evidence needs stable measurement
I benchmark the workload, inspect generated assembly only when useful, and record compiler version, target CPU, profile, LTO, and codegen units. A microbenchmark should prevent constant folding and represent realistic call patterns.
Cross-crate generic code and non-generic functions expose different optimisation opportunities. A hint that helps one target may hurt another. I make claims conditional and remove cargo-cult attributes that lack current evidence.
CI usually verifies behaviour rather than exact assembly because compiler output changes. For a small critical primitive, a size or instruction regression test can be justified with tolerances and a pinned environment.
I also keep optimisation attributes out of correctness arguments. Code must remain correct if an inline hint is ignored, and unsafe code cannot depend on two functions becoming one optimisation unit. If correctness appears to change with inlining, I investigate undefined behaviour, timing assumptions, or an optimiser bug with a minimal reproducer rather than strengthening the attribute.
My E0805 checklist
- Which attribute received the wrong number of arguments?
- Does it expect word, list, or name-value syntax?
- Is empty parentheses incorrectly standing in for no input?
- Is the attribute attached to a supported item after expansion?
- Did optional macro arguments create an empty or conflicting list?
- For inline, what profile or cross-crate evidence justifies the hint?
- Have code size, compile time, and target differences been considered?
- Do compile tests cover every legal form and important invalid combination?
The core principle is that attributes are typed configuration surfaces for the compiler and macros. E0805 catches an invalid call shape. I repair the grammar, then verify placement and the actual behavioural or performance reason for keeping the attribute.