schemagen: object types named Kind, Cat, Frame or SlotInfo are shadowed by RootBuilder's private member types (generated header does not compile)
#77
Reference in new issue
Block a user
Summary
weaseljson_schemagen.pyderives a C++ struct/enum name for every object type from the JSONproperty key (or
$defs/definitionsentry) viacamel(). The generatedRootBuilderclassdeclares four private member types
(
contrib/schemagen/weaseljson_schemagen.py:726,:1006,:1007,:1014), butBuilder._is_reserved()(contrib/schemagen/weaseljson_schemagen.py:271-273) only reserves"Root"and"RootScalar":So when a user type is named
Kind,Cat,SlotInfoorFrame, the generatedRootBuilder::engage()(emitted byEmitter._engage,contrib/schemagen/weaseljson_schemagen.py:781:auto *o = ({name} *)f.dest;) resolves thetype name to the member type instead of the namespace-scope struct, and the header does not
compile. This is reachable from ordinary, supported schemas —
"kind"and"frame"are verycommon JSON property names (e.g. Kubernetes objects all have a
"kind"key).Reproduction
kind.schema.json:Actual result (generation succeeds, compiling the generated header fails):
The generated header contains both a namespace-scope
struct Kind { ... };and, insideclass RootBuilder,enum class Kind : uint8_t { Root1, Kind };, so(Kind *)f.destatgen.h:412 refers to the enum.
Expected result: the header compiles (or the schema is rejected at generation time).
Other names that reproduce it
Same schema, just renaming the property (the value must be an object so that a struct with that
name is generated):
$defsname"kind"(or$defs/Kind)request for member 'x' in '* o', which is of non-class type 'ns::RootBuilder::Kind'"cat"... of non-class type 'ns::RootBuilder::Cat'"frame"'struct ns::RootBuilder::Frame' has no member named 'x'"slotInfo"(also"slot_info",camel()splits on_)'struct ns::RootBuilder::SlotInfo' has no member named 'x'(
$defsnames work the same way, e.g.{"properties":{"k":{"$ref":"#/$defs/Kind"}}}failsidentically.)
Impact
A valid, supported schema produces a header that cannot be compiled, so a whole class of
realistic schemas (any object-typed property named
kind,cat,frame,slotInfo, ...)cannot be used at all. The failure is at C++ compile time and the diagnostics point at the
generated code, not at the schema, so it is hard to diagnose.
Suggested fix
Either extend
_is_reserved()with"Kind","Cat","SlotInfo","Frame"(the same way"Root"/"RootScalar"are reserved), or — more robustly — give the builder's internal helpertypes names that
camel()can never produce.camel()always upper-cases the first character(
p[:1].upper() + p[1:]), so all generated type names start with an upper-case letter; lowercaseinternal names such as
kind_/cat_/slotInfo_/frame_(and already-lowercasekWantKey)are unreachable from user schemas.
Relation to existing issues
This is the same kind of defect as #69, but it is a different set of identifiers with a
different failure mechanism: #69 is about a user type redefining
class RootBuilderat namespacescope, whereas here the user type is shadowed inside
RootBuilderby its private member types.Reserving
"RootBuilder"(the fix for #69) does not fix any of the names above, and"Kind"inparticular is also the enum emitted by
Emitter._kind_enum(), so the four names need separateentries in
_is_reserved().