{"record":{"id":"d9b163ab2dd29542","repo":"astral-sh/ruff","slug":"must-be-at-the-end-rule","errorCode":null,"errorMessage":"`*` must be at the end: {rule}","messagePattern":"`\\*` must be at the end: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"crates/ruff_python_ast/generate.py","lineNumber":246,"sourceCode":"    inner_type = rust_type_str[open_bracket_index + 1 : close_bracket_index].strip()\n    inner_type = inner_type.replace(\"crate::\", \"\")\n    return inner_type\n\n\nclass SequenceKind(Enum):\n    VEC = \"vec\"\n    BOXED_SLICE = \"boxed_slice\"\n    THIN_VEC = \"thin_vec\"\n\n\ndef split_sequence_type(rule: str) -> tuple[SequenceKind | None, str]:\n    if \"&\" in rule:\n        raise ValueError(f\"`&T*` is unsupported; use `Box<[T]>`: {rule}\")\n\n    if \"*\" in rule:\n        if rule.endswith(\"*\") and rule.count(\"*\") == 1:\n            return SequenceKind.VEC, rule[:-1]\n        raise ValueError(f\"`*` must be at the end: {rule}\")\n\n    for prefix, suffix, sequence_kind in (\n        (\"Vec<\", \">\", SequenceKind.VEC),\n        (\"ThinVec<\", \">\", SequenceKind.THIN_VEC),\n        (\"Box<[\", \"]>\", SequenceKind.BOXED_SLICE),\n    ):\n        if rule.startswith(prefix):\n            if not rule.endswith(suffix):\n                raise ValueError(f\"Unclosed collection type: {rule}\")\n            return sequence_kind, rule[len(prefix) : -len(suffix)]\n\n    return None, rule\n\n\n@dataclass\nclass FieldType:\n    rule: str\n    name: str","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/astral-sh/ruff/blob/d1087a4b9e03d253a88703f34e0869ee4b805456/crates/ruff_python_ast/generate.py#L228-L264","documentation":"'T*' is the AST DSL shorthand for Vec<T>. split_sequence_type() accepts the star only as a trailing marker appearing exactly once; any other placement ('*Name', 'Name**', 'Vec<*T>') raises this ValueError with the offending rule.","triggerScenarios":"Adding or editing a field rule where the '*' is not the final character or appears more than once, then running generate.py.","commonSituations":"Typos when writing sequence shorthand; pasting Rust pointer syntax (e.g. '*const T') into the DSL.","solutions":["Move the single '*' to the very end of the rule: 'Name*'.","Remove any extra '*' characters.","Re-run the generator to confirm the rule parses."],"exampleFix":"# before\nnames: *Name\n\n# after\nnames: Name*","handlingStrategy":"validation","validationCode":"def rule_ok(rule: str) -> bool:\n    return '*' not in rule or (rule.endswith('*') and rule.count('*') == 1)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Remember the DSL places sequence markers at the end: 'Name*', never '*Name'.","Run the generator as part of pre-commit for AST definition changes."],"tags":["codegen","rust-ast","syntax","developer-tooling"],"backgroundTag":"codegen-dsl-parse-error","analyzedSha":"d1087a4b9e03d253a88703f34e0869ee4b805456","analyzedAt":"2026-08-20T16:33:49.445Z","contentChangedAt":"2026-08-20T16:33:49.445Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}