Copilot commented on code in PR #1812:
URL: https://github.com/apache/commons-lang/pull/1812#discussion_r4212139709


##########
src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java:
##########
@@ -1728,30 +1743,29 @@ private static Type unrollVariables(final 
Map<TypeVariable<?>, Type> typeArgumen
                 if (!visited.add(var)) {
                     return var;
                 }
-                return unrollVariables(typeArguments, typeArguments.get(type), 
visited);
+                final Type unrolled = unrollVariables(typeArguments, 
typeArguments.get(type), visited);
+                // Only guard against cycles: the same variable may 
legitimately occur more than once in a type.
+                visited.remove(var);
+                return unrolled;
             }
             if (type instanceof ParameterizedType) {
                 final ParameterizedType p = (ParameterizedType) type;
-                final Map<TypeVariable<?>, Type> parameterizedTypeArguments;
-                if (p.getOwnerType() == null) {
-                    parameterizedTypeArguments = typeArguments;
-                } else {
-                    parameterizedTypeArguments = new HashMap<>(typeArguments);
-                    parameterizedTypeArguments.putAll(getTypeArguments(p));
-                }
                 final Type[] args = p.getActualTypeArguments().clone();
                 for (int i = 0; i < args.length; i++) {
-                    final Type unrolled = 
unrollVariables(parameterizedTypeArguments, args[i], visited);
+                    final Type unrolled = unrollVariables(typeArguments, 
args[i], visited);
                     if (unrolled != null) {
                         args[i] = unrolled;
                     }
                 }
-                return parameterizeWithOwner(p.getOwnerType(), (Class<?>) 
p.getRawType(), args);
+                // A raw Class owner (for example Map.class for Map.Entry<T, 
Integer>) has nothing to unroll.
+                final Type owner = p.getOwnerType();
+                final Type unrolledOwner = owner != null && !(owner instanceof 
Class<?>) ? unrollVariables(typeArguments, owner, visited) : owner;

Review Comment:
   The `visited` set tracks only type variables, so it cannot stop cycles 
between owner objects. For a custom parameterized type A with no arguments 
whose owner is B<T>, where B's owner is A, `unrollVariables({T -> String}, A)` 
repeatedly follows A → B → A until `StackOverflowError`. Previously, A was 
returned unchanged. A self-owned type with a variable argument has the same 
failure. Track active composite `Type` objects by identity throughout 
unrolling, including wildcard bounds, and add regression tests for owner cycles 
containing variables.



##########
src/main/java/org/apache/commons/lang3/reflect/TypeUtils.java:
##########
@@ -377,36 +377,50 @@ private static <T> String classToString(final Class<T> 
cls) {
      * @since 3.2
      */
     public static boolean containsTypeVariables(final Type type) {
+        return containsTypeVariables(type, new HashSet<>());

Review Comment:
   The new `HashSet` calls the owner's `hashCode()` when checking for cycles. A 
self-owned custom `ParameterizedType` with no arguments and a structural hash 
code that hashes its owner still throws `StackOverflowError` before the guard 
can detect the cycle. The new regression test misses this because its type 
inherits `Object.hashCode()`. Use an identity-backed set, matching `VISITING` 
in this file, and add coverage for an owner with a recursive structural hash 
code.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to