Repository: victools/jsonschema-generator (java)

Issue:
Inheritance support
Hi,
I'm using the library to generate schemas from classes with hierarchy.
As far as we understand,  definitions for parent properties are always duplicated, right? If yes, do you have any plans to support the reference to the parent class (extension)?



**Current behavior:**
For these classes:
```java
@Data
public class Inheritance {

    @NotNull
    private String schemaVersion = "1.0";
    private Inheritance.Parent parent;
    private Inheritance.Child1 child1;

    @Data
    public static class Parent {
        private String parentField;
    }

    @Data
    @EqualsAndHashCode(callSuper = true)
    public static class Child1 extends Parent {

        private String child1NestedField;
    }
}
```
The library will generate the following schema:
```json
{
{
  "$schema" : "https://json-schema.org/draft/2020-12/schema",
  "$ref" : "#/$defs/Inheritance",
  "$defs" : {
    "Child1" : {
      "type" : "object",
      "properties" : {
        "parentField" : {
          "type" : "string"
        },
        "child1NestedField" : {
          "type" : "string"
        }
      }
    },
    "Inheritance" : {
      "type" : "object",
      "properties" : {
        "schemaVersion" : {
          "type" : "string",
          "default" : "1.1"
        },
        "parent" : {
          "$ref" : "#/$defs/Parent"
        },
        "child1" : {
          "$ref" : "#/$defs/Child1"
        }
      },
      "required" : [ "schemaVersion" ]
    },
    "Parent" : {
      "type" : "object",
      "properties" : {
        "parentField" : {
          "type" : "string"
        }
      }
    }
  }
}
```

Notice that the `parentField` is duplicated.

**It would be nice to get something like this:**

```json
"$defs" : {
    "Child1" : {
      "type" : "object",
      "$ref" : "#/$defs/Parent",
      "properties" : {
        "child1NestedField" : {
          "type" : "string"
        }
      }
    },
```


Pull request description:
feat: enable differentiating members declared by super types
Closes #407.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 155e0d0..bd47575 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 #### Changed
 - include new `Option.STANDARD_FORMATS` in `OptionPreset.PLAIN_JSON` by default
+- extended parameters for creation of `FieldScope`/`MethodScope` through the `TypeContext` to include type for which a schema is being generated
 
 #### Fixed
 - when using `Option.FIELDS_DERIVED_FROM_ARGUMENTFREE_METHODS` on a method where the second character of the derived field name is in uppercase, don't capitalise the first character
diff --git a/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/InheritanceRefExample.java b/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/InheritanceRefExample.java
new file mode 100644
index 0000000..395dfab
--- /dev/null
+++ b/jsonschema-examples/src/main/java/com/github/victools/jsonschema/examples/InheritanceRefExample.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2023 VicTools.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.github.victools.jsonschema.examples;
+
+import com.fasterxml.classmate.ResolvedType;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.github.victools.jsonschema.generator.CustomDefinition;
+import com.github.victools.jsonschema.generator.CustomDefinitionProviderV2;
+import com.github.victools.jsonschema.generator.Option;
+import com.github.victools.jsonschema.generator.OptionPreset;
+import com.github.victools.jsonschema.generator.SchemaGenerationContext;
+import com.github.victools.jsonschema.generator.SchemaGenerator;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
+import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
+import com.github.victools.jsonschema.generator.SchemaKeyword;
+import com.github.victools.jsonschema.generator.SchemaVersion;
+import java.time.LocalDate;
+
+/**
+ * Example created in response to <a href="https://github.com/victools/jsonschema-generator/issues/407">#407</a>.
+ * <br>
+ * Reference separate definitions for common super types. Show-casing capabilities; but not recommended due to risk of infinite loop.
+ */
+public class InheritanceRefExample implements SchemaGenerationExampleInterface {
+
+    @Override
+    public ObjectNode generateSchema() {
+        SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_2020_12, OptionPreset.PLAIN_JSON)
+                .with(Option.DEFINITIONS_FOR_ALL_OBJECTS);
+        // ignore all fields declared in super types
+        configBuilder.forFields()
+                .withIgnoreCheck(field -> !field.getDeclaringType().equals(field.getDeclarationDetails().getSchemaTargetType()));
+        // reference super types explicitly
+        configBuilder.forTypesInGeneral()
+                // including SubtypeResolver would result in a StackOverflowError as the super reference is again replaced with it subtypes
+                //.withSubtypeResolver(new SubtypeLookUpExample.ClassGraphSubtypeResolver())
+                .withCustomDefinitionProvider(new ParentReferenceDefinitionProvider());
+        SchemaGeneratorConfig config = configBuilder.build();
+        return new SchemaGenerator(config).generateSchema(TestType.class);
+    }
+
+    static class ParentReferenceDefinitionProvider implements CustomDefinitionProviderV2 {
+        @Override
+        public CustomDefinition provideCustomSchemaDefinition(ResolvedType javaType, SchemaGenerationContext context) {
+            if (javaType.getErasedType().getSuperclass() == null || javaType.getErasedType().getSuperclass() == Object.class) {
+                return null;
+            }
+            ResolvedType superType = context.getTypeContext().resolve(javaType.getErasedType().getSuperclass());
+            // two options here:
+            // 1. providing "null" as second parameter = multiple hierarchy levels are respected
+            // 2. providing "this" as second parameter = only one hierarchy level is respected
+            ObjectNode superTypeReference = context.createStandardDefinitionReference(superType, null);
+            ObjectNode definition = context.createStandardDefinition(javaType, this);
+            definition.withArray(context.getKeyword(SchemaKeyword.TAG_ALLOF))
+                    .add(superTypeReference);
+            return new CustomDefinition(definition, CustomDefinition.DefinitionType.STANDARD, CustomDefinition.AttributeInclusion.YES);
+        }
+    }
+
+    static class TestType {
+        public Book favoriteBook;
+        public Poster favoritePoster;
+    }
+
+    static class Book extends PrintPublication {
+        public int pageCount;
+    }
+
+    static class Poster extends PrintPublication {
+        public boolean forMovie;
+    }
+
+    static class PrintPublication extends Publication {
+        public String author;
+        public String publisher;
+    }
+
+    static class Publication {
+        public LocalDate published;
+        public String title;
+    }
+}
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java
index ff5aa88..d67212f 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/FieldScope.java
@@ -40,38 +40,24 @@ public class FieldScope extends MemberScope<ResolvedField, Field> {
      * Constructor.
      *
      * @param field targeted field
-     * @param declaringTypeMembers collection of the declaring type's (other) fields and methods
+     * @param declarationDetails basic details regarding the declaration context
+     * @param overrideDetails augmenting details (e.g., overridden type, name, or container item index)
      * @param context the overall type resolution context
      */
-    protected FieldScope(ResolvedField field, ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
-        this(field, null, null, declaringTypeMembers, null, context);
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param field targeted field
-     * @param overriddenType alternative type for this field
-     * @param overriddenName alternative name for this field
-     * @param declaringTypeMembers collection of the declaring type's (other) fields and methods
-     * @param fakeContainerItemIndex index of the container item on the generic field/method scope's declared type (e.g., in case of a List, it is 0)
-     * @param context the overall type resolution context
-     */
-    protected FieldScope(ResolvedField field, ResolvedType overriddenType, String overriddenName,
-            ResolvedTypeWithMembers declaringTypeMembers, Integer fakeContainerItemIndex, TypeContext context) {
-        super(field, overriddenType, overriddenName, declaringTypeMembers, fakeContainerItemIndex, context);
+    protected FieldScope(ResolvedField field, DeclarationDetails declarationDetails, OverrideDetails overrideDetails, TypeContext context) {
+        super(field, declarationDetails, overrideDetails, context);
     }
 
     @Override
     public FieldScope withOverriddenType(ResolvedType overriddenType) {
-        return new FieldScope(this.getMember(), overriddenType, this.getOverriddenName(), this.getDeclaringTypeMembers(),
-                this.getFakeContainerItemIndex(), this.getContext());
+        OverrideDetails overrideDetails = new OverrideDetails(overriddenType, this.getOverriddenName(), this.getFakeContainerItemIndex());
+        return new FieldScope(this.getMember(), this.getDeclarationDetails(), overrideDetails, this.getContext());
     }
 
     @Override
     public FieldScope withOverriddenName(String overriddenName) {
-        return new FieldScope(this.getMember(), this.getOverriddenType(), overriddenName, this.getDeclaringTypeMembers(),
-                this.getFakeContainerItemIndex(), this.getContext());
+        OverrideDetails overrideDetails = new OverrideDetails(this.getOverriddenType(), overriddenName, this.getFakeContainerItemIndex());
+        return new FieldScope(this.getMember(), this.getDeclarationDetails(), overrideDetails, this.getContext());
     }
 
     @Override
@@ -135,7 +121,7 @@ public class FieldScope extends MemberScope<ResolvedField, Field> {
                 .filter(method -> method.isPublic() && method.getRawMember().getParameterCount() == 0)
                 .filter(method -> possibleGetterNames.contains(method.getName()))
                 .findFirst()
-                .map(method -> this.getContext().createMethodScope(method, this.getDeclaringTypeMembers()))
+                .map(method -> this.getContext().createMethodScope(method, this.getDeclarationDetails()))
                 .orElse(null);
     }
 
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
index 77b80a0..6a037e5 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MemberScope.java
@@ -37,9 +37,9 @@ import java.util.function.Predicate;
 public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member> extends TypeScope {
 
     private final M member;
+    private final DeclarationDetails declarationDetails;
     private final ResolvedType overriddenType;
     private final String overriddenName;
-    private final ResolvedTypeWithMembers declaringTypeMembers;
     private Integer fakeContainerItemIndex;
     private final LazyValue<String> schemaPropertyName = new LazyValue<>(this::doGetSchemaPropertyName);
 
@@ -47,20 +47,17 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
      * Constructor.
      *
      * @param member targeted field or method
-     * @param overriddenType alternative type for this field or method's return value
-     * @param overriddenName alternative name for this field or method
-     * @param declaringTypeMembers collection of the declaring type's (other) fields and methods
-     * @param fakeContainerItemIndex index of the container item on the generic field/method scope's declared type (e.g., in case of a List, it is 0)
+     * @param declarationDetails basic details regarding the declaration context
+     * @param overrideDetails augmenting details (e.g., overridden type, name, or container item index)
      * @param context the overall type resolution context
      */
-    protected MemberScope(M member, ResolvedType overriddenType, String overriddenName,
-            ResolvedTypeWithMembers declaringTypeMembers, Integer fakeContainerItemIndex, TypeContext context) {
-        super(Optional.ofNullable(overriddenType).orElseGet(member::getType), context);
+    protected MemberScope(M member, DeclarationDetails declarationDetails, OverrideDetails overrideDetails, TypeContext context) {
+        super(Optional.ofNullable(OverrideDetails.getOverriddenType(overrideDetails)).orElseGet(member::getType), context);
         this.member = member;
-        this.overriddenType = overriddenType;
-        this.overriddenName = overriddenName;
-        this.declaringTypeMembers = declaringTypeMembers;
-        this.fakeContainerItemIndex = fakeContainerItemIndex;
+        this.declarationDetails = declarationDetails;
+        this.overriddenType = OverrideDetails.getOverriddenType(overrideDetails);
+        this.overriddenName = OverrideDetails.getOverriddenName(overrideDetails);
+        this.fakeContainerItemIndex = OverrideDetails.getFakeContainerItemIndex(overrideDetails);
     }
 
     /**
@@ -135,13 +132,22 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
         return this.member;
     }
 
+    /**
+     * Getter for additional declaration context information.
+     *
+     * @return wrapper for the schema target type and declaring type's field and methods
+     */
+    public DeclarationDetails getDeclarationDetails() {
+        return this.declarationDetails;
+    }
+
     /**
      * Getter for the collection of the member's declaring type's (other) fields and methods.
      *
      * @return declaring type's fields and methods
      */
     public ResolvedTypeWithMembers getDeclaringTypeMembers() {
-        return this.declaringTypeMembers;
+        return this.declarationDetails.getDeclaringTypeMembers();
     }
 
     /**
@@ -475,4 +481,64 @@ public abstract class MemberScope<M extends ResolvedMember<T>, T extends Member>
     public String toString() {
         return this.getSimpleTypeDescription() + " " + this.getSchemaPropertyName();
     }
+
+    public static class DeclarationDetails {
+        private final ResolvedType schemaTargetType;
+        private final ResolvedTypeWithMembers declaringTypeMembers;
+
+        public DeclarationDetails(ResolvedType schemaTargetType, ResolvedTypeWithMembers declaringTypeMembers) {
+            this.schemaTargetType = schemaTargetType;
+            this.declaringTypeMembers = declaringTypeMembers;
+        }
+
+        /**
+         * Getter for the specific type for which a schema is being generated, that includes this field/method. This can differ from the wrapped
+         * member's declaring type, if that declaring type is an implemented interface or super type of the targeted one.
+         *
+         * @return target type for which a schema is being generated including this field/method
+         */
+        public ResolvedType getSchemaTargetType() {
+            return this.schemaTargetType;
+        }
+
+        /**
+         * Getter for the collection of the member's declaring type's (other) fields and methods.
+         *
+         * @return declaring type's fields and methods
+         */
+        public ResolvedTypeWithMembers getDeclaringTypeMembers() {
+            return this.declaringTypeMembers;
+        }
+    }
+
+    static class OverrideDetails {
+        private final ResolvedType overriddenType;
+        private final String overriddenName;
+        private final Integer fakeContainerItemIndex;
+
+        /**
+         * Constructor.
+         *
+         * @param overriddenType alternative type for this field or method's return value
+         * @param overriddenName alternative name for this field or method
+         * @param fakeContainerItemIndex index of the container item on the generic field/method's declared type (e.g., in case of a List, it is 0)
+         */
+        OverrideDetails(ResolvedType overriddenType, String overriddenName, Integer fakeContainerItemIndex) {
+            this.overriddenType = overriddenType;
+            this.overriddenName = overriddenName;
+            this.fakeContainerItemIndex = fakeContainerItemIndex;
+        }
+
+        private static ResolvedType getOverriddenType(OverrideDetails details) {
+            return details == null ? null : details.overriddenType;
+        }
+
+        private static String getOverriddenName(OverrideDetails details) {
+            return details == null ? null : details.overriddenName;
+        }
+
+        private static Integer getFakeContainerItemIndex(OverrideDetails details) {
+            return details == null ? null : details.fakeContainerItemIndex;
+        }
+    }
 }
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java
index 434f602..2631dae 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/MethodScope.java
@@ -17,7 +17,6 @@
 package com.github.victools.jsonschema.generator;
 
 import com.fasterxml.classmate.ResolvedType;
-import com.fasterxml.classmate.ResolvedTypeWithMembers;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.github.victools.jsonschema.generator.impl.LazyValue;
 import java.lang.annotation.Annotation;
@@ -43,38 +42,24 @@ public class MethodScope extends MemberScope<ResolvedMethod, Method> {
      * Constructor.
      *
      * @param method targeted method
-     * @param declaringTypeMembers collection of the declaring type's fields and (other) methods
+     * @param declarationDetails basic details regarding the declaration context
+     * @param overrideDetails augmenting details (e.g., overridden type, name, or container item index)
      * @param context the overall type resolution context
      */
-    protected MethodScope(ResolvedMethod method, ResolvedTypeWithMembers declaringTypeMembers, TypeContext context) {
-        this(method, null, null, declaringTypeMembers, null, context);
-    }
-
-    /**
-     * Constructor.
-     *
-     * @param method targeted method
-     * @param overriddenType alternative type for this method's return value
-     * @param overriddenName alternative name for this method
-     * @param declaringTypeMembers collection of the declaring type's fields and (other) methods
-     * @param fakeContainerItemIndex index of the container item on the generic field/method scope's declared type (e.g., in case of a List, it is 0)
-     * @param context the overall type resolution context
-     */
-    protected MethodScope(ResolvedMethod method, ResolvedType overriddenType, String overriddenName,
-            ResolvedTypeWithMembers declaringTypeMembers, Integer fakeContainerItemIndex, TypeContext context) {
-        super(method, overriddenType, overriddenName, declaringTypeMembers, fakeContainerItemIndex, context);
+    protected MethodScope(ResolvedMethod method, DeclarationDetails declarationDetails, OverrideDetails overrideDetails, TypeContext context) {
+        super(method, declarationDetails, overrideDetails, context);
     }
 
     @Override
     public MethodScope withOverriddenType(ResolvedType overriddenType) {
-        return new MethodScope(this.getMember(), overriddenType, this.getOverriddenName(), this.getDeclaringTypeMembers(),
-                this.getFakeContainerItemIndex(), this.getContext());
+        OverrideDetails overrideDetails = new OverrideDetails(overriddenType, this.getOverriddenName(), this.getFakeContainerItemIndex());
+        return new MethodScope(this.getMember(), this.getDeclarationDetails(), overrideDetails, this.getContext());
     }
 
     @Override
     public MethodScope withOverriddenName(String overriddenName) {
-        return new MethodScope(this.getMember(), this.getOverriddenType(), overriddenName, this.getDeclaringTypeMembers(),
-                this.getFakeContainerItemIndex(), this.getContext());
+        OverrideDetails overrideDetails = new OverrideDetails(this.getOverriddenType(), overriddenName, this.getFakeContainerItemIndex());
+        return new MethodScope(this.getMember(), this.getDeclarationDetails(), overrideDetails, this.getContext());
     }
 
     @Override
@@ -147,7 +132,7 @@ public class MethodScope extends MemberScope<ResolvedMethod, Method> {
         return Stream.of(this.getDeclaringTypeMembers().getMemberFields())
                 .filter(memberField -> possibleFieldNames.contains(memberField.getName()))
                 .findFirst()
-                .map(field -> this.getContext().createFieldScope(field, this.getDeclaringTypeMembers()))
+                .map(field -> this.getContext().createFieldScope(field, this.getDeclarationDetails()))
                 .orElse(null);
     }
 
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
index 291d66e..5ffc95c 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/TypeContext.java
@@ -152,9 +152,24 @@ public class TypeContext {
      * @param field targeted field
      * @param declaringTypeMembers collection of the declaring type's (other) fields and methods
      * @return created {@link FieldScope} instance
+     * @deprecated use {@link #createFieldScope(ResolvedField, MemberScope.DeclarationDetails)} instead
      */
+    @Deprecated
     public FieldScope createFieldScope(ResolvedField field, ResolvedTypeWithMembers declaringTypeMembers) {
-        return new FieldScope(field, declaringTypeMembers, this);
+        return this.createFieldScope(field, new MemberScope.DeclarationDetails(field.getDeclaringType(), declaringTypeMembers));
+    }
+
+    /**
+     * Construct a {@link FieldScope} instance for the given field.
+     *
+     * @param field targeted field
+     * @param declarationDetails context of declaration
+     * @return created {@link FieldScope} instance
+     *
+     * @since 4.33.0
+     */
+    public FieldScope createFieldScope(ResolvedField field, MemberScope.DeclarationDetails declarationDetails) {
+        return new FieldScope(field, declarationDetails, null, this);
     }
 
     /**
@@ -163,9 +178,24 @@ public class TypeContext {
      * @param method targeted method
      * @param declaringTypeMembers collection of the declaring type's fields and (other) methods
      * @return created {@link MethodScope} instance
+     * @deprecated use {@link #createMethodScope(ResolvedMethod, MemberScope.DeclarationDetails)} instead
      */
+    @Deprecated
     public MethodScope createMethodScope(ResolvedMethod method, ResolvedTypeWithMembers declaringTypeMembers) {
-        return new MethodScope(method, declaringTypeMembers, this);
+        return this.createMethodScope(method, new MemberScope.DeclarationDetails(method.getDeclaringType(), declaringTypeMembers));
+    }
+
+    /**
+     * Construct a {@link MethodScope} instance for the given method.
+     *
+     * @param method targeted method
+     * @param declarationDetails context of declaration
+     * @return created {@link MethodScope} instance
+     *
+     * @since 4.33.0
+     */
+    public MethodScope createMethodScope(ResolvedMethod method, MemberScope.DeclarationDetails declarationDetails) {
+        return new MethodScope(method, declarationDetails, null, this);
     }
 
     /**
diff --git a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/MemberCollectionContextImpl.java b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/MemberCollectionContextImpl.java
index af40433..19fbfbb 100644
--- a/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/MemberCollectionContextImpl.java
+++ b/jsonschema-generator/src/main/java/com/github/victools/jsonschema/generator/impl/MemberCollectionContextImpl.java
@@ -91,9 +91,10 @@ class MemberCollectionContextImpl {
     public void collectProperties() {
         logger.debug("collecting non-static fields and methods from {}", this.schemaTargetType);
         ResolvedTypeWithMembers typeToCollectMembersFrom = this.typeContext.resolveWithMembers(this.schemaTargetType);
+        MemberScope.DeclarationDetails declarationDetails = new MemberScope.DeclarationDetails(this.schemaTargetType, typeToCollectMembersFrom);
         // member fields and methods are being collected from the targeted type as well as its super types
-        this.collectFields(typeToCollectMembersFrom.getMemberFields(), typeToCollectMembersFrom);
-        this.collectMethods(typeToCollectMembersFrom.getMemberMethods(), typeToCollectMembersFrom);
+        this.collectFields(typeToCollectMembersFrom.getMemberFields(), declarationDetails);
+        this.collectMethods(typeToCollectMembersFrom.getMemberMethods(), declarationDetails);
 
         if (this.generatorConfig.shouldIncludeStaticFields() || this.generatorConfig.shouldIncludeStaticMethods()) {
             // static fields and methods are being collected only for the targeted type itself, i.e. need to iterate over super types specifically
@@ -108,23 +109,24 @@ class MemberCollectionContextImpl {
         logger.debug("collecting static fields and methods from {}", hierarchyType);
         // static members need to be looked up from the declaring type directly as they are not inherited as such
         ResolvedTypeWithMembers hierarchyTypeWithMembers = this.typeContext.resolveWithMembers(hierarchyType);
+        MemberScope.DeclarationDetails declarationDetails = new MemberScope.DeclarationDetails(this.schemaTargetType, hierarchyTypeWithMembers);
         if (this.generatorConfig.shouldIncludeStaticFields()) {
-            this.collectFields(hierarchyTypeWithMembers.getStaticFields(), hierarchyTypeWithMembers);
+            this.collectFields(hierarchyTypeWithMembers.getStaticFields(), declarationDetails);
         }
         if (this.generatorConfig.shouldIncludeStaticMethods()) {
-            this.collectMethods(hierarchyTypeWithMembers.getStaticMethods(), hierarchyTypeWithMembers);
+            this.collectMethods(hierarchyTypeWithMembers.getStaticMethods(), declarationDetails);
         }
     }
 
     /**
      * Preparation Step: collect the designated fields.
      *
-     * @param fields targeted fields from {@code typeToCollectMembersFrom}
-     * @param typeToCollectMembersFrom specific container of given fields (possibly a supertype or interface of the targeted type)
+     * @param fields targeted fields
+     * @param declarationDetails common declaration context for all given fields
      */
-    private void collectFields(ResolvedField[] fields, ResolvedTypeWithMembers typeToCollectMembersFrom) {
+    private void collectFields(ResolvedField[] fields, MemberScope.DeclarationDetails declarationDetails) {
         Stream.of(fields)
-                .map(declaredField -> this.typeContext.createFieldScope(declaredField, typeToCollectMembersFrom))
+                .map(declaredField -> this.typeContext.createFieldScope(declaredField, declarationDetails))
                 .filter(fieldScope -> !this.generatorConfig.shouldIgnore(fieldScope))
                 .forEach(this::collect);
     }
@@ -132,12 +134,12 @@ class MemberCollectionContextImpl {
     /**
      * Preparation Step: collect the designated methods.
      *
-     * @param methods targeted methods from {@code typeToCollectMembersFrom}
-     * @param typeToCollectMembersFrom specific container of given fields (possibly a supertype or interface of the targeted type)
+     * @param methods targeted methods
+     * @param declarationDetails common declaration context for all given methods
      */
-    private void collectMethods(ResolvedMethod[] methods, ResolvedTypeWithMembers typeToCollectMembersFrom) {
+    private void collectMethods(ResolvedMethod[] methods, MemberScope.DeclarationDetails declarationDetails) {
         Stream.of(methods)
-                .map(declaredMethod -> this.typeContext.createMethodScope(declaredMethod, typeToCollectMembersFrom))
+                .map(declaredMethod -> this.typeContext.createMethodScope(declaredMethod, declarationDetails))
                 .filter(methodScope -> !this.generatorConfig.shouldIgnore(methodScope))
                 .forEach(this::collect);
     }


Test patch:
diff --git a/jsonschema-examples/src/test/java/com/github/victools/jsonschema/examples/ExampleTest.java b/jsonschema-examples/src/test/java/com/github/victools/jsonschema/examples/ExampleTest.java
index 92a4782..ea49a00 100644
--- a/jsonschema-examples/src/test/java/com/github/victools/jsonschema/examples/ExampleTest.java
+++ b/jsonschema-examples/src/test/java/com/github/victools/jsonschema/examples/ExampleTest.java
@@ -36,6 +36,7 @@ public class ExampleTest {
             ExternalRefAnnotationExample.class,
             ExternalRefPackageExample.class,
             IfThenElseExample.class,
+            InheritanceRefExample.class,
             JacksonDescriptionAsTitleExample.class,
             JacksonSubtypeDefinitionExample.class,
             StrictTypeInfoExample.class,
diff --git a/jsonschema-examples/src/test/resources/com/github/victools/jsonschema/examples/InheritanceRefExample-result.json b/jsonschema-examples/src/test/resources/com/github/victools/jsonschema/examples/InheritanceRefExample-result.json
new file mode 100644
index 0000000..7a7dcb1
--- /dev/null
+++ b/jsonschema-examples/src/test/resources/com/github/victools/jsonschema/examples/InheritanceRefExample-result.json
@@ -0,0 +1,56 @@
+{
+    "$schema" : "https://json-schema.org/draft/2020-12/schema",
+    "$defs" : {
+        "Book" : {
+            "type" : "object",
+            "properties" : {
+                "pageCount" : {
+                    "type" : "integer"
+                }
+            },
+            "$ref" : "#/$defs/PrintPublication"
+        },
+        "Poster" : {
+            "type" : "object",
+            "properties" : {
+                "forMovie" : {
+                    "type" : "boolean"
+                }
+            },
+            "$ref" : "#/$defs/PrintPublication"
+        },
+        "PrintPublication" : {
+            "type" : "object",
+            "properties" : {
+                "author" : {
+                    "type" : "string"
+                },
+                "publisher" : {
+                    "type" : "string"
+                }
+            },
+            "$ref" : "#/$defs/Publication"
+        },
+        "Publication" : {
+            "type" : "object",
+            "properties" : {
+                "published" : {
+                    "type" : "string",
+                    "format" : "date"
+                },
+                "title" : {
+                    "type" : "string"
+                }
+            }
+        }
+    },
+    "type" : "object",
+    "properties" : {
+        "favoriteBook" : {
+            "$ref" : "#/$defs/Book"
+        },
+        "favoritePoster" : {
+            "$ref" : "#/$defs/Poster"
+        }
+    }
+}
\ No newline at end of file
diff --git a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java
index 71d9f18..6766280 100644
--- a/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java
+++ b/jsonschema-generator/src/test/java/com/github/victools/jsonschema/generator/AbstractTypeAwareTest.java
@@ -33,7 +33,7 @@ public class AbstractTypeAwareTest {
 
     private final Class<?> testClass;
     private SchemaGenerationContext context;
-    private ResolvedTypeWithMembers testClassMembers;
+    private MemberScope.DeclarationDetails declarationDetails;
 
     protected AbstractTypeAwareTest(Class<?> testClass) {
         this.testClass = testClass;
@@ -47,7 +47,7 @@ public class AbstractTypeAwareTest {
     protected void prepareContextForVersion(SchemaVersion schemaVersion) {
         TypeContext typeContext = Mockito.spy(TypeContextFactory.createDefaultTypeContext());
         ResolvedType resolvedTestClass = typeContext.resolve(this.testClass);
-        this.testClassMembers = typeContext.resolveWithMembers(resolvedTestClass);
+        this.declarationDetails = new MemberScope.DeclarationDetails(resolvedTestClass, typeContext.resolveWithMembers(resolvedTestClass));
         this.context = Mockito.mock(SchemaGenerationContext.class, Mockito.RETURNS_DEEP_STUBS);
         Mockito.when(this.context.getTypeContext()).thenReturn(typeContext);
         ObjectMapper objectMapper = new ObjectMapper();
@@ -66,26 +66,26 @@ public class AbstractTypeAwareTest {
     }
 
     protected FieldScope getTestClassField(String fieldName) {
-        ResolvedField resolvedField = Stream.of(this.testClassMembers.getMemberFields())
+        ResolvedField resolvedField = Stream.of(this.declarationDetails.getDeclaringTypeMembers().getMemberFields())
                 .filter(field -> fieldName.equals(field.getName()))
                 .findAny()
                 .orElseGet(
-                        () -> Stream.of(this.testClassMembers.getStaticFields())
+                        () -> Stream.of(this.declarationDetails.getDeclaringTypeMembers().getStaticFields())
                                 .filter(field -> fieldName.equals(field.getName()))
                                 .findAny()
                                 .get());
-        return this.context.getTypeContext().createFieldScope(resolvedField, this.testClassMembers);
+        return this.context.getTypeContext().createFieldScope(resolvedField, this.declarationDetails);
     }
 
     protected MethodScope getTestClassMethod(String methodName) {
-        ResolvedMethod resolvedMethod = Stream.of(this.testClassMembers.getMemberMethods())
+        ResolvedMethod resolvedMethod = Stream.of(this.declarationDetails.getDeclaringTypeMembers().getMemberMethods())
                 .filter(method -> methodName.equals(method.getName()))
                 .findAny()
                 .orElseGet(
-                        () -> Stream.of(this.testClassMembers.getStaticMethods())
+                        () -> Stream.of(this.declarationDetails.getDeclaringTypeMembers().getStaticMethods())
                                 .filter(method -> methodName.equals(method.getName()))
                                 .findAny()
                                 .get());
-        return this.context.getTypeContext().createMethodScope(resolvedMethod, this.testClassMembers);
+        return this.context.getTypeContext().createMethodScope(resolvedMethod, this.declarationDetails);
     }
 }
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java
index 50ce687..3ad0acc 100644
--- a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/AbstractTypeAwareTest.java
@@ -22,6 +22,7 @@ import com.fasterxml.classmate.members.ResolvedField;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.SchemaGenerationContext;
 import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
@@ -40,7 +41,7 @@ public class AbstractTypeAwareTest {
 
     private final Class<?> testClass;
     private SchemaGenerationContext context;
-    private ResolvedTypeWithMembers testClassMembers;
+    private MemberScope.DeclarationDetails declarationDetails;
 
     protected AbstractTypeAwareTest(Class<?> testClass) {
         this.testClass = testClass;
@@ -54,7 +55,7 @@ public class AbstractTypeAwareTest {
     protected void prepareContextForVersion(SchemaVersion schemaVersion) {
         TypeContext typeContext = TypeContextFactory.createDefaultTypeContext();
         ResolvedType resolvedTestClass = typeContext.resolve(this.testClass);
-        this.testClassMembers = typeContext.resolveWithMembers(resolvedTestClass);
+        this.declarationDetails = new MemberScope.DeclarationDetails(resolvedTestClass, typeContext.resolveWithMembers(resolvedTestClass));
         this.context = Mockito.mock(SchemaGenerationContext.class, Mockito.RETURNS_DEEP_STUBS);
         Mockito.when(this.context.getTypeContext()).thenReturn(typeContext);
 
@@ -94,26 +95,26 @@ public class AbstractTypeAwareTest {
     }
 
     protected FieldScope getTestClassField(String fieldName) {
-        ResolvedField resolvedField = Stream.of(this.testClassMembers.getMemberFields())
+        ResolvedField resolvedField = Stream.of(this.declarationDetails.getDeclaringTypeMembers().getMemberFields())
                 .filter(field -> fieldName.equals(field.getName()))
                 .findAny()
                 .orElseGet(
-                        () -> Stream.of(this.testClassMembers.getStaticFields())
+                        () -> Stream.of(this.declarationDetails.getDeclaringTypeMembers().getStaticFields())
                                 .filter(field -> fieldName.equals(field.getName()))
                                 .findAny()
                                 .get());
-        return this.context.getTypeContext().createFieldScope(resolvedField, this.testClassMembers);
+        return this.context.getTypeContext().createFieldScope(resolvedField, this.declarationDetails);
     }
 
     protected MethodScope getTestClassMethod(String methodName) {
-        ResolvedMethod resolvedMethod = Stream.of(this.testClassMembers.getMemberMethods())
+        ResolvedMethod resolvedMethod = Stream.of(this.declarationDetails.getDeclaringTypeMembers().getMemberMethods())
                 .filter(method -> methodName.equals(method.getName()))
                 .findAny()
                 .orElseGet(
-                        () -> Stream.of(this.testClassMembers.getStaticMethods())
+                        () -> Stream.of(this.declarationDetails.getDeclaringTypeMembers().getStaticMethods())
                                 .filter(method -> methodName.equals(method.getName()))
                                 .findAny()
                                 .get());
-        return this.context.getTypeContext().createMethodScope(resolvedMethod, this.testClassMembers);
+        return this.context.getTypeContext().createMethodScope(resolvedMethod, this.declarationDetails);
     }
 }
diff --git a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/TestType.java b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/TestType.java
index 2017588..aee7067 100644
--- a/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/TestType.java
+++ b/jsonschema-module-jackson/src/test/java/com/github/victools/jsonschema/module/jackson/TestType.java
@@ -21,6 +21,7 @@ import com.fasterxml.classmate.ResolvedTypeWithMembers;
 import com.fasterxml.classmate.members.ResolvedField;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.TypeContext;
 import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
@@ -32,44 +33,43 @@ import java.util.stream.Stream;
 public class TestType {
 
     private final TypeContext context;
-    private final ResolvedType resolvedTestClass;
-    private final ResolvedTypeWithMembers testClassMembers;
+    private final MemberScope.DeclarationDetails declarationDetails;
 
     public TestType(Class<?> testClass) {
         this.context = TypeContextFactory.createDefaultTypeContext();
-        this.resolvedTestClass = this.context.resolve(testClass);
-        this.testClassMembers = this.context.resolveWithMembers(this.resolvedTestClass);
+        ResolvedType resolvedTestClass = this.context.resolve(testClass);
+        this.declarationDetails = new MemberScope.DeclarationDetails(resolvedTestClass, this.context.resolveWithMembers(resolvedTestClass));
     }
 
     public FieldScope getMemberField(String fieldName) {
-        return this.getField(this.testClassMembers.getMemberFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getMemberFields(), fieldName);
     }
 
     public FieldScope getStaticField(String fieldName) {
-        return this.getField(this.testClassMembers.getStaticFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getStaticFields(), fieldName);
     }
 
     private FieldScope getField(ResolvedField[] fields, String fieldName) {
         return Stream.of(fields)
                 .filter(field -> fieldName.equals(field.getName()))
                 .findAny()
-                .map(field -> this.context.createFieldScope(field, this.testClassMembers))
+                .map(field -> this.context.createFieldScope(field, this.declarationDetails))
                 .get();
     }
 
     public MethodScope getMemberMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getMemberMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getMemberMethods(), methodName);
     }
 
     public MethodScope getStaticMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getStaticMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getStaticMethods(), methodName);
     }
 
     private MethodScope getMethod(ResolvedMethod[] methods, String methodName) {
         return Stream.of(methods)
                 .filter(method -> methodName.equals(method.getName()))
                 .findAny()
-                .map(method -> this.context.createMethodScope(method, this.testClassMembers))
+                .map(method -> this.context.createMethodScope(method, this.declarationDetails))
                 .get();
     }
 }
diff --git a/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/TestType.java b/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/TestType.java
index 423c956..d642664 100644
--- a/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/TestType.java
+++ b/jsonschema-module-jakarta-validation/src/test/java/com/github/victools/jsonschema/module/jakarta/validation/TestType.java
@@ -21,6 +21,7 @@ import com.fasterxml.classmate.ResolvedTypeWithMembers;
 import com.fasterxml.classmate.members.ResolvedField;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.TypeContext;
 import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
@@ -32,44 +33,43 @@ import java.util.stream.Stream;
 public class TestType {
 
     private final TypeContext context;
-    private final ResolvedType resolvedTestClass;
-    private final ResolvedTypeWithMembers testClassMembers;
+    private final MemberScope.DeclarationDetails declarationDetails;
 
     public TestType(Class<?> testClass) {
         this.context = TypeContextFactory.createDefaultTypeContext();
-        this.resolvedTestClass = this.context.resolve(testClass);
-        this.testClassMembers = this.context.resolveWithMembers(this.resolvedTestClass);
+        ResolvedType resolvedTestClass = this.context.resolve(testClass);
+        this.declarationDetails = new MemberScope.DeclarationDetails(resolvedTestClass, this.context.resolveWithMembers(resolvedTestClass));
     }
 
     public FieldScope getMemberField(String fieldName) {
-        return this.getField(this.testClassMembers.getMemberFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getMemberFields(), fieldName);
     }
 
     public FieldScope getStaticField(String fieldName) {
-        return this.getField(this.testClassMembers.getStaticFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getStaticFields(), fieldName);
     }
 
     private FieldScope getField(ResolvedField[] fields, String fieldName) {
         return Stream.of(fields)
                 .filter(field -> fieldName.equals(field.getName()))
                 .findAny()
-                .map(field -> this.context.createFieldScope(field, this.testClassMembers))
+                .map(field -> this.context.createFieldScope(field, this.declarationDetails))
                 .get();
     }
 
     public MethodScope getMemberMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getMemberMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getMemberMethods(), methodName);
     }
 
     public MethodScope getStaticMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getStaticMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getStaticMethods(), methodName);
     }
 
     private MethodScope getMethod(ResolvedMethod[] methods, String methodName) {
         return Stream.of(methods)
                 .filter(method -> methodName.equals(method.getName()))
                 .findAny()
-                .map(method -> this.context.createMethodScope(method, this.testClassMembers))
+                .map(method -> this.context.createMethodScope(method, this.declarationDetails))
                 .get();
     }
 }
diff --git a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/TestType.java b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/TestType.java
index a2ef8a9..e55748d 100644
--- a/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/TestType.java
+++ b/jsonschema-module-javax-validation/src/test/java/com/github/victools/jsonschema/module/javax/validation/TestType.java
@@ -21,6 +21,7 @@ import com.fasterxml.classmate.ResolvedTypeWithMembers;
 import com.fasterxml.classmate.members.ResolvedField;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.TypeContext;
 import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
@@ -32,44 +33,43 @@ import java.util.stream.Stream;
 public class TestType {
 
     private final TypeContext context;
-    private final ResolvedType resolvedTestClass;
-    private final ResolvedTypeWithMembers testClassMembers;
+    private final MemberScope.DeclarationDetails declarationDetails;
 
     public TestType(Class<?> testClass) {
         this.context = TypeContextFactory.createDefaultTypeContext();
-        this.resolvedTestClass = this.context.resolve(testClass);
-        this.testClassMembers = this.context.resolveWithMembers(this.resolvedTestClass);
+        ResolvedType resolvedTestClass = this.context.resolve(testClass);
+        this.declarationDetails = new MemberScope.DeclarationDetails(resolvedTestClass, this.context.resolveWithMembers(resolvedTestClass));
     }
 
     public FieldScope getMemberField(String fieldName) {
-        return this.getField(this.testClassMembers.getMemberFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getMemberFields(), fieldName);
     }
 
     public FieldScope getStaticField(String fieldName) {
-        return this.getField(this.testClassMembers.getStaticFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getStaticFields(), fieldName);
     }
 
     private FieldScope getField(ResolvedField[] fields, String fieldName) {
         return Stream.of(fields)
                 .filter(field -> fieldName.equals(field.getName()))
                 .findAny()
-                .map(field -> this.context.createFieldScope(field, this.testClassMembers))
+                .map(field -> this.context.createFieldScope(field, this.declarationDetails))
                 .get();
     }
 
     public MethodScope getMemberMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getMemberMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getMemberMethods(), methodName);
     }
 
     public MethodScope getStaticMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getStaticMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getStaticMethods(), methodName);
     }
 
     private MethodScope getMethod(ResolvedMethod[] methods, String methodName) {
         return Stream.of(methods)
                 .filter(method -> methodName.equals(method.getName()))
                 .findAny()
-                .map(method -> this.context.createMethodScope(method, this.testClassMembers))
+                .map(method -> this.context.createMethodScope(method, this.declarationDetails))
                 .get();
     }
 }
diff --git a/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/TestType.java b/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/TestType.java
index 7bd47fc..80cefa1 100644
--- a/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/TestType.java
+++ b/jsonschema-module-swagger-1.5/src/test/java/com/github/victools/jsonschema/module/swagger15/TestType.java
@@ -21,6 +21,7 @@ import com.fasterxml.classmate.ResolvedTypeWithMembers;
 import com.fasterxml.classmate.members.ResolvedField;
 import com.fasterxml.classmate.members.ResolvedMethod;
 import com.github.victools.jsonschema.generator.FieldScope;
+import com.github.victools.jsonschema.generator.MemberScope;
 import com.github.victools.jsonschema.generator.MethodScope;
 import com.github.victools.jsonschema.generator.TypeContext;
 import com.github.victools.jsonschema.generator.impl.TypeContextFactory;
@@ -32,44 +33,43 @@ import java.util.stream.Stream;
 public class TestType {
 
     private final TypeContext context;
-    private final ResolvedType resolvedTestClass;
-    private final ResolvedTypeWithMembers testClassMembers;
+    private final MemberScope.DeclarationDetails declarationDetails;
 
     public TestType(Class<?> testClass) {
         this.context = TypeContextFactory.createDefaultTypeContext();
-        this.resolvedTestClass = this.context.resolve(testClass);
-        this.testClassMembers = this.context.resolveWithMembers(this.resolvedTestClass);
+        ResolvedType resolvedTestClass = this.context.resolve(testClass);
+        this.declarationDetails = new MemberScope.DeclarationDetails(resolvedTestClass, this.context.resolveWithMembers(resolvedTestClass));
     }
 
     public FieldScope getMemberField(String fieldName) {
-        return this.getField(this.testClassMembers.getMemberFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getMemberFields(), fieldName);
     }
 
     public FieldScope getStaticField(String fieldName) {
-        return this.getField(this.testClassMembers.getStaticFields(), fieldName);
+        return this.getField(this.declarationDetails.getDeclaringTypeMembers().getStaticFields(), fieldName);
     }
 
     private FieldScope getField(ResolvedField[] fields, String fieldName) {
         return Stream.of(fields)
                 .filter(field -> fieldName.equals(field.getName()))
                 .findAny()
-                .map(field -> this.context.createFieldScope(field, this.testClassMembers))
+                .map(field -> this.context.createFieldScope(field, this.declarationDetails))
                 .get();
     }
 
     public MethodScope getMemberMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getMemberMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getMemberMethods(), methodName);
     }
 
     public MethodScope getStaticMethod(String methodName) {
-        return this.getMethod(this.testClassMembers.getStaticMethods(), methodName);
+        return this.getMethod(this.declarationDetails.getDeclaringTypeMembers().getStaticMethods(), methodName);
     }
 
     private MethodScope getMethod(ResolvedMethod[] methods, String methodName) {
         return Stream.of(methods)
                 .filter(method -> methodName.equals(method.getName()))
                 .findAny()
-                .map(method -> this.context.createMethodScope(method, this.testClassMembers))
+                .map(method -> this.context.createMethodScope(method, this.declarationDetails))
                 .get();
     }
 }