-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Description
Summary
rust-analyzer reports E0109 "type arguments are not allowed on this type" for valid macro code that compiles successfully with rustc. The issue stems from how rust-analyzer handles operator precedence in macro expansion, particularly with the as cast operator followed by shift operators.
Environment
- rust-analyzer version: 0.3.2660-standalone (7c810e9 2025-10-27)
- VSCode extension: rust-lang.rust-analyzer-0.3.2660-darwin-arm64
- Rust version: 1.77 (edition 2018)
Minimal Reproducible Example
Repository: https://github.com/arpie-steele/test-e0109
pub trait UnsignedBits<U, const N: usize> {
fn mask() -> U;
}
macro_rules! ub_prim_impl {
($ub:ident; $prim_type:ty, $n:literal; $max_n:literal) => {
impl $ub<$prim_type, $n> for $prim_type {
fn mask() -> $prim_type {
assert!($n <= $max_n);
if $n == $max_n {
return <$prim_type>::MAX;
}
// This compiles with rustc but triggers E0109 in rust-analyzer
(1 as $prim_type << (1 << $n)) - 1
}
}
};
}
// E0109 errors reported by rust-analyzer on these lines:
ub_prim_impl!(UnsignedBits; u8, 0; 3);
ub_prim_impl!(UnsignedBits; u8, 1; 3);
ub_prim_impl!(UnsignedBits; u8, 3; 3);
// Manual expansion (what the macro produces) - compiles fine with rustc:
impl UnsignedBits<u8, 2> for u8 {
fn mask() -> u8 {
assert!(2 <= 3);
if 2 == 3 {
return <u8>::MAX;
}
// Outside the macro, parentheses are required around the cast
((1 as u8) << (1 << 2)) - 1
}
}Root Cause Analysis
The expression 1 as $prim_type << (1 << $n) demonstrates the issue:
-
rustc behavior: Pre-parses the macro body as tokens and correctly applies operator precedence. The
ascast binds tighter than<<, so this is equivalent to(1 as $prim_type) << (1 << $n). -
rust-analyzer behavior: Appears to process macro expansion more like string substitution. When it sees
$prim_typereplaced withu8, followed by<<, it interprets this as attempting to apply type arguments tou8(i.e.,u8<< ...), which triggers E0109. -
Evidence: Outside a macro, the compiler requires explicit parentheses:
((1 as u8) << (1 << 2))to disambiguate.
Expected Behavior
No diagnostic error. The code compiles and runs successfully:
cargo +1.77 check✅cargo +1.77 run✅cargo +1.77 test✅
Actual Behavior
rust-analyzer reports E0109 on macro invocation lines (42-44 in the reproduction).
Workaround
Either:
- Add extra parentheses in the macro:
((1 as $prim_type) << (1 << $n)) - 1 - Suppress the warning:
#[cfg_attr(rust_analyzer, allow(E0109))]
Impact
This affects macros that:
- Generate trait implementations with const generics
- Use type parameters in expressions with operators (especially
asfollowed by<<) - Rely on Rust's standard operator precedence rules
Related Information
- E0109 diagnostic implemented in PR feat: Complete diagnostics in ty lowering groundwork and serve a first diagnostic 🎉 #18541 (merged December 2024)
- Full reproduction: https://github.com/arpie-steele/test-e0109