Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions VERSION.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ Release notes:
- Array types are cached by `TypeResolver` using element type as part of the key
- Types with `TypePlaceHolder`s (from `resolveSubtype()`) or self-references
(`ResolvedRecursiveType`) in their type parameters (at any level of nesting,
including array element types) are no longer cached (but see #128 for
self-references via supertypes)
including array element types) are no longer cached
#127: `resolveSubtype()` does not verify or resolve array element types
- `resolveSubtype()` now rejects type variable bound to conflicting types
(like `E` in `Dup<E> extends Pair<E[], E>`; except that `Object`, which may
Expand All @@ -36,6 +35,24 @@ Release notes:
`IllegalArgumentException` if any of type parameters is a primitive type
(like `resolve(List.class, int.class)`)
(fix by @cowtowncoder w/ Claude code)
#128: Self-references via supertypes still cached; self-references not equal
to fully resolved types
- Types containing self-references to types still being resolved (like `B` in
`B extends Base<A>` when resolving `A extends Base<B>`) are no longer cached,
and are re-resolved when used later (like for member types)
- Self-references (`ResolvedRecursiveType`) nested in type parameters or array
element types are equal to fully resolved types with same type bindings
(so f.ex raw `Enum` is now equal to `Enum<Enum<Object>>`)
- Raw self-references have type parameters resolved to bounds, like other raw types
(except ones created while resolving bounds of the type itself, like `T` in
`RawBound<T extends RawBound>`; types containing those may also be shared)
- Self-references with bindings different from referenced type (like raw `Mid`
within `Mid<String>`, or `N<N<T>>` within `N<T>`) now represent type with own
bindings: see new `ResolvedRecursiveType.getActualType()` (also used for
`getParentClass()`, members and `resolveSubtype()`)
(fix by @cowtowncoder w/ Claude code)
#132: `MemberResolver` misses members inherited via self-referential interface
(fix by @cowtowncoder w/ Claude code)

1.7.3 (02-Jan-2026)

Expand Down
14 changes: 13 additions & 1 deletion src/main/java/com/fasterxml/classmate/MemberResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.*;

import com.fasterxml.classmate.members.*;
import com.fasterxml.classmate.types.ResolvedRecursiveType;
import com.fasterxml.classmate.util.ClassKey;

/**
Expand Down Expand Up @@ -113,10 +114,21 @@ public MemberResolver setConstructorFilter(Filter<RawConstructor> f) {
* @param annotationConfig Configuration of annotation types; which ones to include, how to inherit
* @param annotationOverrides Definitions of annotation overrides to use, if any (may be null)
*/
public ResolvedTypeWithMembers resolve(final ResolvedType mainType,
public ResolvedTypeWithMembers resolve(ResolvedType mainType,
AnnotationConfiguration annotationConfig,
AnnotationOverrides annotationOverrides)
{
// [classmate#128]: type may be one only valid within its original resolution context
// (like one obtained via type parameters of another type), with self-references in
// its supertypes: if so, need to resolve it as stand-alone type
// (and self-reference itself has no supertypes, so need to use type it represents)
if (mainType instanceof ResolvedRecursiveType) {
ResolvedType actual = ((ResolvedRecursiveType) mainType).getActualType();
if (actual != null) {
mainType = actual;
}
}
mainType = _typeResolver.resolve(mainType);
List<ResolvedType> types;
HashSet<ClassKey> seenTypes = new HashSet<ClassKey>();

Expand Down
53 changes: 50 additions & 3 deletions src/main/java/com/fasterxml/classmate/ResolvedType.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.*;

import com.fasterxml.classmate.members.*;
import com.fasterxml.classmate.types.ResolvedRecursiveType;

public abstract class ResolvedType
implements Type
Expand All @@ -25,19 +26,46 @@ public abstract class ResolvedType
* constructors) of this type
*/
protected final TypeBindings _typeBindings;


/**
* Whether this type contains (via type parameters, array element type or supertypes)
* self-references to types it does not itself contain (like {@code B} in
* {@code B extends Base<A>}, when resolved as part of {@code A extends Base<B>});
* or is such a self-reference.
* Such types are only valid within the resolution of the type that contains them,
* and are never cached by {@link TypeResolver} (nor are types containing them).
* Set by {@link TypeResolver} when type is constructed, before it is exposed.
*
* @since 1.8
*/
private volatile boolean _incomplete;

/*
/**********************************************************************
/* Life cycle
/**********************************************************************
*/

protected ResolvedType(Class<?> cls, TypeBindings bindings)
{
_erasedType = cls;
_typeBindings = (bindings == null) ? TypeBindings.emptyBindings() : bindings;
}


/**
* @since 1.8
*/
void _markIncomplete() {
_incomplete = true;
}

/**
* @since 1.8
*/
boolean _isIncomplete() {
return _incomplete;
}

/**
* Method that can be used to check if call to {@link TypeResolver#resolveSubtype(ResolvedType, Class)}
* may ever succeed; if false, it will fail with an exception, if true, it may succeed.
Expand Down Expand Up @@ -280,6 +308,25 @@ public String getBriefDescription() {
// and type bindings must match as well
return _typeBindings.equals(other._typeBindings);
}

/**
* Helper method for comparing types contained in other types (as type parameters
* or array element types): differs from {@link #equals} in that a self-reference
* ({@link ResolvedRecursiveType}) matches a fully resolved type with the same erased
* type and type bindings.
*
* @since 1.8
*/
protected static boolean _equalTypes(ResolvedType t1, ResolvedType t2)
{
if (t1 == t2) return true;
if ((t1 == null) || (t2 == null)) return false;
if ((t1 instanceof ResolvedRecursiveType) != (t2 instanceof ResolvedRecursiveType)) {
return (t1._erasedType == t2._erasedType)
&& t1._typeBindings.equals(t2._typeBindings);
}
return t1.equals(t2);
}

/*
/**********************************************************************
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/fasterxml/classmate/TypeBindings.java
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ public boolean hasUnbound(String name) {
}
ResolvedType[] otherTypes = other._types;
for (int i = 0; i < len; ++i) {
if (!otherTypes[i].equals(_types[i])) {
// [classmate#128]: self-references must match fully resolved types
if (!ResolvedType._equalTypes(otherTypes[i], _types[i])) {
return false;
}
}
Expand Down
Loading
Loading