Repository: raphw/byte-buddy (language: java)

Issue:
TypeDescription.isGenerified does not work with anonymous subclasses using the enclosing methods type parameter
> Edit:
I added #1271 with a suggested fix. I locally tried it also with my mockito tests and using my fixed version in mockito and the test described here passes.  

Hi,
I encountered a problem while using `mockito`, that I traced back to Byte Buddy.
I have generic class or interface:
```Java
private static class GenericSample<T> {

}
```
Now I want to return an anonymous subclass of my generic class:
```Java
private <T> GenericSample<T> getAnonymousGenericSample() {
    return new GenericSample<T>() {
        /* empty */
    };
}
```
I would expect (and otherwise it leads to errors down the road), that `isGenerified()` of the class of the returned object returns `true`:
```Java
assertThat(describe(getAnonymousGenericSample().getClass()).isGenerified(), is(true));
```
However this is currently not the case.

I encountered this problem in `mockito` with the following problem:

```Java
private static Runnable getRunnableSpy() {
    return spy(new Runnable() {
        @Override
        public void run() {

        }
    });
}

private static <T> Callable<T> getCallableSpy(T result) {
    return spy(new Callable<T>() {
        @Override
        public T call() throws Exception {
            return result;
        }
    });
}

@Test
public void testRunnable() {
    Runnable spy = getRunnableSpy();
}

@Test
public void testCallable() {
    Callable<String> spy2 = getCallableSpy("Result");
}
```

Test 1 runs without a problem, Test 2 fails with following stack trace:
```
Underlying exception : java.lang.IllegalArgumentException: Cannot resolve T from class org.mockitousage.stubbing.StubbingReturnsSelfTest$2$MockitoMock$tTEkhXEk
	at app//org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.prettifyFailure(SubclassByteBuddyMockMaker.java:114)
	at app//org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMockType(SubclassByteBuddyMockMaker.java:85)
	at app//org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:43)
	at app//org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:42)
	at app//org.mockito.internal.util.MockUtil.lambda$createMock$0(MockUtil.java:48)
	at java.base@11.0.10/java.util.Optional.orElseGet(Optional.java:369)
	at app//org.mockito.internal.util.MockUtil.createMock(MockUtil.java:46)
	at app//org.mockito.internal.MockitoCore.mock(MockitoCore.java:96)
	at app//org.mockito.Mockito.spy(Mockito.java:2064)
	at app//org.mockitousage.stubbing.StubbingReturnsSelfTest.getCallableSpy(StubbingReturnsSelfTest.java:180)
	at app//org.mockitousage.stubbing.StubbingReturnsSelfTest.testCallable(StubbingReturnsSelfTest.java:128)
	at java.base@11.0.10/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.10/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.10/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.10/java.lang.reflect.Method.invoke(Method.java:566)
	at app//org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at app//org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at app//org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at app//org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at app//org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
	at app//org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
	at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
	at app//org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
	at app//org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
	at app//org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
	at app//org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
	at app//org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
	at app//org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
	at app//org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at app//org.junit.runners.ParentRunner.run(ParentRunner.java:413)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
	at java.base@11.0.10/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base@11.0.10/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base@11.0.10/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base@11.0.10/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at com.sun.proxy.$Proxy5.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker$2.run(TestWorker.java:176)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
	at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
	at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
	at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
```






Discussion / hints:

Fix patch:
diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/description/TypeVariableSource.java b/byte-buddy-dep/src/main/java/net/bytebuddy/description/TypeVariableSource.java
index 7cbc255f5e3..0f63e308b81 100644
--- a/byte-buddy-dep/src/main/java/net/bytebuddy/description/TypeVariableSource.java
+++ b/byte-buddy-dep/src/main/java/net/bytebuddy/description/TypeVariableSource.java
@@ -88,6 +88,7 @@ public interface TypeVariableSource extends ModifierReviewable.OfAbstraction {
      * <ul>
      * <li>A type declares type variables or is an inner class of a type with a generic declaration.</li>
      * <li>A method declares at least one type variable.</li>
+     * <li>A type is an inner anonymous class of a method with a generic declaration</li>
      * </ul>
      *
      * @return {@code true} if this type code element has a generic declaration.
diff --git a/byte-buddy-dep/src/main/java/net/bytebuddy/description/type/TypeDescription.java b/byte-buddy-dep/src/main/java/net/bytebuddy/description/type/TypeDescription.java
index 2285a75f6e3..b031c1fce88 100644
--- a/byte-buddy-dep/src/main/java/net/bytebuddy/description/type/TypeDescription.java
+++ b/byte-buddy-dep/src/main/java/net/bytebuddy/description/type/TypeDescription.java
@@ -8060,7 +8060,11 @@ public boolean isGenerified() {
                 return false;
             }
             TypeDescription declaringType = getDeclaringType();
-            return declaringType != null && declaringType.isGenerified();
+            if (declaringType != null && declaringType.isGenerified()) {
+                return true;
+            }
+            MethodDescription.InDefinedShape enclosingMethod = getEnclosingMethod();
+            return enclosingMethod != null && enclosingMethod.isGenerified();
         }
 
         /**


Test patch:
diff --git a/byte-buddy-dep/src/test/java/net/bytebuddy/description/type/AbstractTypeDescriptionTest.java b/byte-buddy-dep/src/test/java/net/bytebuddy/description/type/AbstractTypeDescriptionTest.java
index 6b39873c6f5..12a1ed6bc61 100644
--- a/byte-buddy-dep/src/test/java/net/bytebuddy/description/type/AbstractTypeDescriptionTest.java
+++ b/byte-buddy-dep/src/test/java/net/bytebuddy/description/type/AbstractTypeDescriptionTest.java
@@ -666,6 +666,7 @@ public void testIsGenerified() throws Exception {
         assertThat(describe(GenericSample.Nested.class).isGenerified(), is(false));
         assertThat(describe(GenericSample.NestedInterface.class).isGenerified(), is(false));
         assertThat(describe(Object.class).isGenerified(), is(false));
+        assertThat(describe(getAnonymousGenericSample().getClass()).isGenerified(), is(true));
     }
 
     @Test
@@ -1034,6 +1035,12 @@ interface NestedInterface {
         }
     }
 
+    private <T> GenericSample<T> getAnonymousGenericSample() {
+        return new GenericSample<T>() {
+            /* empty */
+        };
+    }
+
     private static class Type$With$Dollar {
         /* */
     }