From b9377c8a8b71454229b08a59b71e9537cd93aefd Mon Sep 17 00:00:00 2001 From: candiduslynx Date: Sun, 8 Oct 2023 11:22:37 +0300 Subject: [PATCH] resolve shadowing --- fixtures/shadowed_clashing_types.json | 71 +++++++++++++++++++++++++++ reflect.go | 17 +++++-- reflect_test.go | 15 ++++++ schema.go | 5 +- 4 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 fixtures/shadowed_clashing_types.json diff --git a/fixtures/shadowed_clashing_types.json b/fixtures/shadowed_clashing_types.json new file mode 100644 index 0000000..be91598 --- /dev/null +++ b/fixtures/shadowed_clashing_types.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/invopop/jsonschema/some-base-type", + "$ref": "#/$defs/SomeBaseType", + "$defs": { + "GrandfatherType": { + "properties": { + "family_name": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "family_name" + ] + }, + "Odd": { + "properties": { + "base": { + "$ref": "#/$defs/SomeBaseType_1" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "base" + ] + }, + "SomeBaseType": { + "properties": { + "odd": { + "$ref": "#/$defs/Odd" + }, + "link": { + "$ref": "#/$defs/SomeBaseType" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "odd", + "link" + ] + }, + "SomeBaseType_1": { + "properties": { + "id": { + "type": "string" + }, + "some_base_property": { + "type": "integer" + }, + "grand": { + "$ref": "#/$defs/GrandfatherType" + }, + "SomeUntaggedBaseProperty": { + "type": "boolean" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "id", + "some_base_property", + "grand", + "SomeUntaggedBaseProperty" + ] + } + } +} \ No newline at end of file diff --git a/reflect.go b/reflect.go index dbe2317..7de3d70 100644 --- a/reflect.go +++ b/reflect.go @@ -574,7 +574,7 @@ func (r *Reflector) lookupComment(t reflect.Type, name string) string { // addDefinition will append the provided schema. If needed, an ID and anchor will also be added. func (r *Reflector) addDefinition(definitions Definitions, t reflect.Type, s *Schema) { // we save both type & pkg info to match against reflected type later - s.sourceType = fullyQualifiedTypeName(t) + s._type = t _, name := r.findDef(definitions, t) if name == "" { @@ -608,7 +608,6 @@ func (r *Reflector) findDef(definitions Definitions, t reflect.Type) (*Schema, s if name == "" { return nil, "" } - sourceType := fullyQualifiedTypeName(t) defName := name for idx := 1; ; idx++ { @@ -616,13 +615,25 @@ func (r *Reflector) findDef(definitions Definitions, t reflect.Type) (*Schema, s if !ok { return nil, defName } - if def.sourceType == sourceType { + if sameReflectTypes(def._type, t) { return def, defName } defName = name + "_" + strconv.Itoa(idx) } } +func sameReflectTypes(a, b reflect.Type) bool { + if a.Kind() != b.Kind() { + return false + } + + if fullyQualifiedTypeName(a) != fullyQualifiedTypeName(b) { + return false + } + + return a.ConvertibleTo(b) && b.ConvertibleTo(a) +} + func (r *Reflector) lookupID(t reflect.Type) ID { if r.Lookup != nil { if t.Kind() == reflect.Ptr { diff --git a/reflect_test.go b/reflect_test.go index b71ae53..48ea9dd 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -668,3 +668,18 @@ func TestClashingTypes(t *testing.T) { r := &Reflector{} compareSchemaOutput(t, "fixtures/clashing_types.json", r, &Odd{}) } + +func TestShadowedClashingTypes(t *testing.T) { + type Odd struct { + Base SomeBaseType `json:"base"` + } + + { + type SomeBaseType struct { + Odd Odd `json:"odd"` + Link *SomeBaseType `json:"link"` + } + r := &Reflector{} + compareSchemaOutput(t, "fixtures/shadowed_clashing_types.json", r, &SomeBaseType{}) + } +} diff --git a/schema.go b/schema.go index 0a7a621..eaba098 100644 --- a/schema.go +++ b/schema.go @@ -2,6 +2,7 @@ package jsonschema import ( "encoding/json" + "reflect" orderedmap "github.com/wk8/go-ordered-map/v2" ) @@ -80,8 +81,8 @@ type Schema struct { // Special boolean representation of the Schema - section 4.3.2 boolean *bool - // sourceType is used to define whether the looked-up definition points to the proper type or not - sourceType string + // _type is used to define whether the looked-up definition points to the proper type or not + _type reflect.Type } var (