{"record":{"id":"21cb1e480534defb","repo":"denoland/deno","slug":"cppgc-inheritance-requires-at-least-one-field","errorCode":null,"errorMessage":"cppgc inheritance requires at least one field","messagePattern":"cppgc inheritance requires at least one field","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"libs/ops/cppgc.rs","lineNumber":58,"sourceCode":"    attrs,\n    ..\n  } = input;\n\n  let base = parse_base_attr(&attrs)?;\n  let mut impl_generics = generics.clone();\n  impl_generics.params.push(parse_quote!(__TransitiveBase));\n  let where_clause = impl_generics.make_where_clause();\n  where_clause\n    .predicates\n    .push(parse_quote!(#base: deno_core::cppgc::Inherits<__TransitiveBase>));\n  where_clause\n    .predicates\n    .push(parse_quote!(__TransitiveBase: deno_core::cppgc::Base));\n\n  ensure_repr_c(&attrs, ident.span())?;\n\n  let first_field = first_field(&data).ok_or_else(|| {\n    Error::new(\n      ident.span(),\n      \"cppgc inheritance requires at least one field\",\n    )\n  })?;\n\n  let (field_path, field_ty_span) = match &first_field.field {\n    FieldRef::Named(ident, ty_span) => (quote!(#ident), *ty_span),\n    FieldRef::Unnamed(idx, ty_span) => (quote!(#idx), *ty_span),\n  };\n\n  if !types_equal(&first_field.ty, &base) {\n    return Err(Error::new(\n      field_ty_span,\n      \"first field must be the base type for cppgc inheritance\",\n    ));\n  }\n\n  let (transitive_impl_generics, _, transitive_where_clause) =","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/denoland/deno/blob/9ad36f7a2cce60488e6ec52283efb32efddaf93a/libs/ops/cppgc.rs#L40-L76","documentation":"#[derive(CppgcInherits)] with #[cppgc_inherits_from(Base)] models C++-style inheritance by embedding the base as the first field; the derive grabs that first field to build the transitive-base plumbing (field_path/field_ty_span used for the offset assertions). If the struct has no fields at all, first_field() returns None and this compile error fires on the type's span — an empty struct cannot inherit anything.","triggerScenarios":"Declaring #[derive(CppgcInherits)] #[cppgc_inherits_from(Shape)] struct Square; (or struct Square {}) — a derived cppgc class with zero fields.","commonSituations":"Starting to port a JS-exposed native class and stubbing the struct empty before adding the base field; deleting all fields during a refactor and forgetting the derive now needs a base.","solutions":["Add the base field first, exactly as the derive requires: struct Square { base: Shape } — the base type named in #[cppgc_inherits_from(...)].","If the type is not meant to inherit, remove #[derive(CppgcInherits)] and the #[cppgc_inherits_from] attribute."],"exampleFix":"// before\n#[derive(CppgcInherits)]\n#[cppgc_inherits_from(Shape)]\n#[repr(C)]\npub struct Square; // error: cppgc inheritance requires at least one field\n\n// after\n#[derive(CppgcInherits)]\n#[cppgc_inherits_from(Shape)]\n#[repr(C)]\npub struct Square {\n  base: Shape,\n}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Start every CppgcInherits struct from the template: #[repr(C)] + a first field holding the #[cppgc_inherits_from(...)] base type.","Don't stub derived classes empty — the base field is mandatory, not optional."],"tags":["proc-macro","cppgc","inheritance","garbage-collection","compile-time","deno-core"],"backgroundTag":"cppgc-inheritance-misuse","analyzedSha":"9ad36f7a2cce60488e6ec52283efb32efddaf93a","analyzedAt":"2026-08-20T13:07:44.778Z","contentChangedAt":"2026-08-20T13:07:44.778Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}