rust-lang/rust-analyzer · error

bad spacing {other}

Error message

bad spacing {other}

What it means

`PunctRepr::read` decodes punctuation spacing over the legacy proc-macro protocol: tag 0 maps to `tt::Spacing::Alone` and 1 to `tt::Spacing::Joint`. Any other value cannot be represented, so the decoder panics with `bad spacing {other}`.

Source

Thrown at crates/proc-macro-api/src/legacy_protocol/msg/flat.rs:427

    }
    fn read_with_kind([id, text, kind, suffix]: [u32; 4]) -> LiteralRepr {
        LiteralRepr { id: SpanId(id), text, kind: kind as u16, suffix }
    }
}

impl PunctRepr {
    fn write(self) -> [u32; 3] {
        let spacing = match self.spacing {
            tt::Spacing::Alone | tt::Spacing::JointHidden => 0,
            tt::Spacing::Joint => 1,
        };
        [self.id.0, self.char as u32, spacing]
    }
    fn read([id, char, spacing]: [u32; 3]) -> PunctRepr {
        let spacing = match spacing {
            0 => tt::Spacing::Alone,
            1 => tt::Spacing::Joint,
            other => panic!("bad spacing {other}"),
        };
        PunctRepr { id: SpanId(id), char: char.try_into().unwrap(), spacing }
    }
}

impl IdentRepr {
    fn write(self) -> [u32; 2] {
        [self.id.0, self.text]
    }
    fn read(data: [u32; 2]) -> IdentRepr {
        IdentRepr { id: SpanId(data[0]), text: data[1], is_raw: false }
    }
    fn write_with_rawness(self) -> [u32; 3] {
        [self.id.0, self.text, self.is_raw as u32]
    }
    fn read_with_rawness([id, text, is_raw]: [u32; 3]) -> IdentRepr {
        IdentRepr { id: SpanId(id), text, is_raw: is_raw == 1 }
    }

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Align the proc-macro server version with rust-analyzer so both sides use the same spacing encoding
  2. Fix the writer to emit only 0 (Alone) or 1 (Joint)
  3. Validate spacing tags before decoding or upgrade off the legacy protocol

Example fix

// before
let spacing = 2; // invalid legacy encoding
// after
let spacing = match punct.spacing {
    tt::Spacing::Alone => 0,
    tt::Spacing::Joint => 1,
};
Defensive patterns

Strategy: validation

Validate before calling

fn valid_spacing_tag(tag: u32) -> bool { tag <= 1 }

Type guard

fn is_known_spacing(tag: u32) -> bool {
    matches!(tag, 0 | 1)
}

Prevention

When it happens

Trigger: `PunctRepr::read` receives a spacing field other than 0 or 1 from the flat 3-word [id, char, spacing] representation.

Common situations: Mismatched proc-macro server/protocol versions where the spacing encoding changed; corrupted token buffers; a writer bug encoding spacing as a boolean-like value other than 0/1 (e.g. 2 for JointIsolated in other encodings).

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/0e0fc48df89f4e39. Report an issue: GitHub.