{"record":{"id":"ef53e8779575f162","repo":"swc-project/swc","slug":"a-class-descriptor-s-kind-property-must-be-class","errorCode":null,"errorMessage":"A class descriptor's .kind property must be \"class\", but a decorator created a class descriptor with .kind \"${kind}\"","messagePattern":"A class descriptor's \\.kind property must be \"class\", but a decorator created a class descriptor with \\.kind \"(.+?)\"","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"crates/swc_ecma_transforms_base/src/helpers/generated/_decorate.rs","lineNumber":239,"sourceCode":"}\nfunction _toElementFinisherExtras(elementObject) {\n    var element = _toElementDescriptor(elementObject);\n    var finisher = _optionalCallableProperty(elementObject, \"finisher\");\n    var extras = _toElementDescriptors(elementObject.extras);\n\n    return { element: element, finisher: finisher, extras: extras };\n}\nfunction _fromClassDescriptor(elements) {\n    var obj = { kind: \"class\", elements: elements.map(_fromElementDescriptor) };\n    var desc = { value: \"Descriptor\", configurable: true };\n    Object.defineProperty(obj, Symbol.toStringTag, desc);\n\n    return obj;\n}\nfunction _toClassDescriptor(obj) {\n    var kind = String(obj.kind);\n    if (kind !== \"class\") {\n        throw new TypeError(\"A class descriptor's .kind property must be \\\"class\\\", but a decorator\" + \" created a class descriptor with .kind \\\"\" + kind + \"\\\"\");\n    }\n    _disallowProperty(obj, \"key\", \"A class descriptor\");\n    _disallowProperty(obj, \"placement\", \"A class descriptor\");\n    _disallowProperty(obj, \"descriptor\", \"A class descriptor\");\n    _disallowProperty(obj, \"initializer\", \"A class descriptor\");\n    _disallowProperty(obj, \"extras\", \"A class descriptor\");\n    var finisher = _optionalCallableProperty(obj, \"finisher\");\n    var elements = _toElementDescriptors(obj.elements);\n\n    return { elements: elements, finisher: finisher };\n}\nfunction _disallowProperty(obj, name, objectType) {\n    if (obj[name] !== undefined) throw new TypeError(objectType + \" can't have a .\" + name + \" property.\");\n}\nfunction _optionalCallableProperty(obj, name) {\n    var value = obj[name];\n    if (value !== undefined && typeof value !== \"function\") {\n        throw new TypeError(\"Expected '\" + name + \"' to be a function\");","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/swc-project/swc/blob/5176682b65416c6b5de6b47379ae1588ea3ecb3f/crates/swc_ecma_transforms_base/src/helpers/generated/_decorate.rs#L221-L257","documentation":"While expanding #[derive(DeserializeEnum)] (used by swc's #[ast_node] enums), the macro reads each variant's #[tag(..)] attribute and parses its token stream with syn::parse2::<VariantAttr>().expect(\"failed to parse #[tag] attribute\"). VariantAttr only accepts a comma-separated list of literals (Punctuated<Lit, Comma>), so any non-literal token inside #[tag(..)] makes parse2 fail and panics the proc macro at compile time.","triggerScenarios":"Writing #[tag(Name)] (a bare identifier instead of the string literal \"Name\"), #[tag(\"a\" \"b\")] without a comma, function-call syntax like #[tag(tag(\"x\"))], or expressions inside the attribute. Note the separate earlier panic for the wrong meta form (#[tag = \"x\"] gives '#[tag] attribute must be in form of #[tag(..)]'), and the later assert requiring at least one tag per variant.","commonSituations":"First-time authors of swc AST node enums copying serde's #[serde(tag = \"type\")] habits; renaming tags with unquoted identifiers; typos like missing quotes after refactoring; version changes that moved from one accepted syntax to strictly literal lists.","solutions":["Write the attribute as a comma-separated list of string literals: #[tag(\"TagName\")] or #[tag(\"A\", \"B\")].","Quote every tag - identifiers, numbers-as-names, and constants are not accepted, only literals.","Separate multiple tags with commas; a single string \"*\" is the reserved wildcard.","Keep the outer form as a list (#[tag(..)]), never #[tag = ..] or #[tag(..) = ..]."],"exampleFix":"// before: bare identifier -> panic 'failed to parse #[tag] attribute'\n#[ast_node(\"Meta\")]\nenum Node {\n    #[tag(Ident)]\n    Ident(Box<Ident>),\n}\n\n// after: quoted string literal\n#[ast_node(\"Meta\")]\nenum Node {\n    #[tag(\"Ident\")]\n    Ident(Box<Ident>),\n}","handlingStrategy":"validation","validationCode":"// Authoring-time pattern: tags are comma-separated string literals only.\n// OK:   #[tag(\"Ident\")]\n// OK:   #[tag(\"A\", \"B\")]\n// OK:   #[tag(\"*\")]            (wildcard)\n// BAD:  #[tag(Ident)]  #[tag(\"a\" \"b\")]  #[tag = \"x\"]\n// If you generate enums, validate before emitting:\nfn valid_tag(tag: &str) -> bool {\n    tag.chars().all(|c| !c.is_whitespace()) && !tag.is_empty()\n}\nlet attr = format!(\"#[tag({})]\", tags.iter().map(|t| format!(\"{:?}\", t)).collect::<Vec<_>>().join(\", \"));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always quote tag names as string literals; identifiers are rejected by syn::parse2.","Separate multiple tags with commas.","Copy tag examples from existing swc AST crates (e.g. swc_ecma_ast) rather than serde docs."],"tags":["rust","proc-macro","derive","attribute","swc","compile-time"],"backgroundTag":"derive-macro-attribute-syntax","analyzedSha":"5176682b65416c6b5de6b47379ae1588ea3ecb3f","analyzedAt":"2026-08-17T16:16:52.067Z","contentChangedAt":"2026-08-17T16:16:52.067Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}