{"record":{"id":"d3d79825d83e0b60","repo":"rust-lang/rust-analyzer","slug":"replacement-placeholders-cannot-have-constraints","errorCode":null,"errorMessage":"Replacement placeholders cannot have constraints","messagePattern":"Replacement placeholders cannot have constraints","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/ide-ssr/src/parsing.rs","lineNumber":247,"sourceCode":"}\n\n/// Checks for errors in a rule. e.g. the replace pattern referencing placeholders that the search\n/// pattern didn't define.\nfn validate_rule(rule: &SsrRule) -> Result<(), SsrError> {\n    let mut defined_placeholders = FxHashSet::default();\n    for p in &rule.pattern.tokens {\n        if let PatternElement::Placeholder(placeholder) = p {\n            defined_placeholders.insert(&placeholder.ident);\n        }\n    }\n    let mut undefined = Vec::new();\n    for p in &rule.template.tokens {\n        if let PatternElement::Placeholder(placeholder) = p {\n            if !defined_placeholders.contains(&placeholder.ident) {\n                undefined.push(placeholder.ident.to_string());\n            }\n            if !placeholder.constraints.is_empty() {\n                bail!(\"Replacement placeholders cannot have constraints\");\n            }\n        }\n    }\n    if !undefined.is_empty() {\n        bail!(\"Replacement contains undefined placeholders: {}\", undefined.join(\", \"));\n    }\n    Ok(())\n}\n\nfn tokenize(source: &str) -> Result<Vec<Token>, SsrError> {\n    let lexed = parser::LexedStr::new(parser::Edition::CURRENT, source);\n    if let Some((_, first_error)) = lexed.errors().next() {\n        bail!(\"Failed to parse pattern: {}\", first_error);\n    }\n    let mut tokens: Vec<Token> = Vec::new();\n    for i in 0..lexed.len() {\n        tokens.push(Token { kind: lexed.kind(i), text: lexed.text(i).into() });\n    }","sourceCodeStart":229,"sourceCodeEnd":265,"githubUrl":"https://github.com/rust-lang/rust-analyzer/blob/e8f7e90aa3e7b26aa9a000200f606c1078da99ec/crates/ide-ssr/src/parsing.rs#L229-L265","documentation":"Placeholder constraints (e.g. kind/type constraints) are only meaningful on *search-pattern* placeholders, where they filter what matches. The replacement template must be a plain construction; if a placeholder in the replacement template carries constraints, `validate_rule` rejects the rule.","triggerScenarios":"`SsrPattern::from_str` where the text after `==>>` contains a placeholder with constraint syntax, e.g. `foo($x) ==>> bar($x:not(expr))` — constraints parsed into the template's placeholders trigger this bail.","commonSituations":"Users symmetrically applying constraint syntax they used in the search part to the replacement part; copy-pasting a rule where `:`-constraints leaked into the replacement; misunderstanding that constraints select inputs, not shape outputs.","solutions":["Remove the constraint from the placeholder in the replacement template, keeping only the bare name: `bar($x)`.","Move any filtering logic into the search pattern's placeholder constraints instead.","If you wanted a literal `:` in the replacement, escape or restructure so it is not parsed as constraint syntax."],"exampleFix":"// before\n\"foo($x) ==>> bar($x:not(expr))\"\n// after\n\"foo($x:not(expr)) ==>> bar($x)\"","handlingStrategy":"validation","validationCode":"fn replacement_side(rule: &str) -> &str {\n    rule.split_once(\"==>>\").map(|(_, r)| r).unwrap_or(\"\")\n}\nanyhow::ensure!(\n    !replacement_side(rule).contains(\":not(\") && !replacement_side(rule).contains(\":\"),\n    \"constraints are only allowed in the search pattern\"\n);","typeGuard":"fn replacement_has_constraints(replacement: &str) -> bool {\n    replacement.contains('$') && replacement[replacement.find('$').unwrap()..]\n        .contains(':')\n}","tryCatchPattern":"match SsrPattern::from_str(rule) {\n    Ok(p) => p,\n    Err(e) if e.to_string().contains(\"cannot have constraints\") => {\n        eprintln!(\"Strip constraints from the ==>> replacement side of the rule\");\n        return Ok(());\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Keep the search side and replacement side symmetric in syntax: constraints only on search placeholders","When copy-pasting rules, re-check both sides of ==>> independently","Remember constraints filter inputs; outputs must be plain captures or literals"],"tags":["rust","ide-ssr","placeholder","constraints","rule-validation"],"backgroundTag":"invalid-placeholder-constraint","analyzedSha":"e8f7e90aa3e7b26aa9a000200f606c1078da99ec","analyzedAt":"2026-09-03T21:08:06.959Z","contentChangedAt":"2026-09-03T21:08:06.959Z","schemaVersion":2},"datasetVersion":"2026-09-11T07:07:21.782Z"}