rust-lang/rust-analyzer · error · SsrError
Placeholders should either be $name or ${{name:constraints}}
Error message
Placeholders should either be $name or ${{name:constraints}} What it means
SSR placeholders must match $name or ${name:constraints}; if the token after '$' is neither a valid identifier nor an opening '{', parse_placeholder bails with this message. The parse then fails and the whole pattern is rejected.
Source
Thrown at crates/ide-ssr/src/parsing.rs:297
tokens.next().ok_or_else(|| SsrError::new("Unexpected end of placeholder"))?;
if token.kind == SyntaxKind::IDENT {
name = Some(token.text);
}
loop {
let token = tokens
.next()
.ok_or_else(|| SsrError::new("Placeholder is missing closing brace '}'"))?;
match token.kind {
T![:] => {
constraints.push(parse_constraint(tokens)?);
}
T!['}'] => break,
_ => bail!("Unexpected token while parsing placeholder: '{}'", token.text),
}
}
}
_ => {
bail!("Placeholders should either be $name or ${{name:constraints}}");
}
}
}
let name = name.ok_or_else(|| SsrError::new("Placeholder ($) with no name"))?;
Ok(Placeholder::new(name, constraints))
}
fn parse_constraint(tokens: &mut std::vec::IntoIter<Token>) -> Result<Constraint, SsrError> {
let constraint_type = tokens
.next()
.ok_or_else(|| SsrError::new("Found end of placeholder while looking for a constraint"))?
.text
.to_string();
match constraint_type.as_str() {
"kind" => {
expect_token(tokens, "(")?;
let t = tokens.next().ok_or_else(|| {
SsrError::new("Unexpected end of constraint while looking for kind")View on GitHub (pinned to e8f7e90aa3)
Solutions
- Use a bare identifier after $: $name
- Or use the braced form with constraints: ${name:kind(literal)}
- Remove stray characters between '$' and the placeholder name
Example fix
// before let pattern = "foo($1)"; // after let pattern = "foo($arg)";
Defensive patterns
Strategy: validation
Validate before calling
fn has_wellformed_placeholders(p: &str) -> bool {
let bytes = p.as_bytes();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'$' {
match bytes.get(i + 1) {
Some(&b) if b.is_ascii_alphabetic() || b == b'_' => {}
Some(&b'{') => {}
_ => return false, // $ not followed by ident or '{'
}
}
i += 1;
}
true
}
Try / catch
match parse_pattern(pattern) {
Ok(p) => use_pattern(p),
Err(e) if e.message.contains("Placeholders should either be") => {
eprintln!("Fix placeholder: must be $name or ${{name:constraints}}");
}
Err(e) => return Err(e),
} Prevention
- Never place digits or punctuation directly after $
- Name placeholders with identifiers: $arg, not $1
- Complete the ${...} form when opening a brace
- Lint generated patterns for bare '$' characters
When it happens
Trigger: Writing patterns like 'foo($' (dollar at end), '$ kind(literal)', or '$-x' where the character after '$' is not an identifier or '{'.
Common situations: Typing $ followed by a digit or punctuation in the IDE structural search box; scripting pattern generation that concatenates '$' with a non-identifier; confusing SSR placeholders with regex groups.
Related errors
- Unexpected token while parsing placeholder: '{}'
- Failed to parse pattern: {}
- Expected ident, found {:?} while parsing kind constraint
- Unsupported constraint type '{}'
- Expected {} found {}
AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03).
Data as JSON: /api/errors/04063b53e3fb4cec.
Report an issue: GitHub.