Repository: square/retrofit
Issue: Crash with R8 full mode enabled with generics and Rx

Retrofit: 2.9.0

The crashing retrofit interface contains methods like these:

```
    @FormUrlEncoded
    @POST("token/oauth2")
    fun clientCredentials(
        @Field("client_id") clientId: String,
        @Field("client_secret") clientSecret: String,
        @Field("grant_type") @SupportedGrantType grantType: String = SupportedGrantType.CLIENT_CREDENTIALS
    ): Single<Response<JWTEnvelope>>
```

where JWTEnvelope is my own class:

```
@Keep // androidx.annotation
@JsonClass(generateAdapter = true)
data class JWTEnvelope(
    @Json(name = "access_token")
    val accessToken: String,
    @Json(name = "token_type")
    @SupportedTokenType
    val tokenType: String,
    @Json(name = "expires_in")
    val expiresIn: Int,
    @Json(name = "refresh_token")
    val refreshToken: String? = null
)
```

I'm not sure if this is the same thing that is being discussed here https://github.com/square/retrofit/issues/3588.
But I have the `@Keep` annotation so this should work if the issue is the one described there, isn't it?

I get
```
java.lang.RuntimeException: Unable to start activity ComponentInfo{my.app/my.app.home.HomeActivity}: java.lang.IllegalArgumentException: Unable to create call adapter for class lj.u
[...]
Caused by: java.lang.IllegalArgumentException: Unable to create call adapter for class lj.u
    for method j.a
	at retrofit2.Utils.methodError(Utils.java:54)
	at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:116)
	at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:67)
	at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39)
	at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:202)
	at retrofit2.Retrofit$1.invoke(Retrofit.java:160)
	at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
	at $Proxy5.a(Unknown Source)
	at my.app.auth.oauth2.OAuth2RestAPI$DefaultImpls.clientCredentials$default(OAuth2RestAPI.kt:18)
[...]
Caused by: java.lang.IllegalStateException: Single return type must be parameterized as Single<Foo> or Single<? extends Foo>
	at retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory.get(RxJava2CallAdapterFactory.java:118)
	at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:253)
	at retrofit2.Retrofit.callAdapter(Retrofit.java:237)
	at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:114)
	... 81 more
```

The Single class is the one that can't be mapped apparently: `io.reactivex.Single` -> `lj.u`.

Am I supposed to just keep every Single class? or **is there a better way?**

Thanks

Issue comments:
2.10.0-SNAPSHOT contains the [fix you're looking for](https://github.com/square/retrofit/pull/3596). Working fine for me with R8 full mode.
See https://github.com/square/retrofit/issues/3751#issuecomment-1192043644
Thank you!, both of you :)
UPDATE: The issue seems to be in AGP 7.2.2, not Retrofit. I have tried AGP 7.2 and 7.3 and both seem to be working fine with Retrofit with R8 full mode.

---

Actually, this has started reoccurring in my case after I upgraded AGP from 7.0.4 to 7.2.2. Looks like 2.10.0-SNAPSHOT is missing a rule or two...

```
 Caused by: java.lang.IllegalArgumentException: Unable to create call adapter for interface retrofit2.Call
     for method HttpBinService.get
     at retrofit2.Utils.methodError(Utils.java:55)
     at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:116)
     at retrofit2.HttpServiceMethod.parseAnnotations(HttpServiceMethod.java:67)
     at retrofit2.ServiceMethod.parseAnnotations(ServiceMethod.java:39)
     at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:208)
     at retrofit2.Retrofit$1.invoke(Retrofit.java:166)
     at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
     at $Proxy3.get(Unknown Source)
     at io.vibin.myapplication.MainActivity.onCreate(MainActivity.kt:45)
     at android.app.Activity.performCreate(Activity.java:8290)
     at android.app.Activity.performCreate(Activity.java:8269)
     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1384)
     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3657)
     ... 12 more
 Caused by: java.lang.IllegalArgumentException: Call return type must be parameterized as Call<Foo> or Call<? extends Foo>
     at retrofit2.DefaultCallAdapterFactory.get(DefaultCallAdapterFactory.java:42)
     at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:259)
     at retrofit2.Retrofit.callAdapter(Retrofit.java:243)
     at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:114)
     ... 23 more
```
I have added [the rules](https://github.com/square/retrofit/blob/master/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro) to proguard and also followed [these](https://github.com/square/retrofit/issues/3751#issuecomment-1192043644), but even with AGP8 and R8 enabled with retrofit 2.9.0:
```
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
```

I get the following:
```
Caused by: java.lang.IllegalStateException: Observable return type must be parameterized as Observable<Foo> or Observable<? extends Foo>
at retrofit2.adapter.rxjava3.RxJava3CallAdapterFactory.get(RxJava3CallAdapterFactory.java:123)
at retrofit2.Retrofit.nextCallAdapter(Retrofit.java:253)
at retrofit2.Retrofit.callAdapter(Retrofit.java:237)
at retrofit2.HttpServiceMethod.createCallAdapter(HttpServiceMethod.java:114)
```

I have been fighting with this issue for quite a while and I would like to re-enable the code shrinking: can anyone help?
https://github.com/square/retrofit/blob/3770704de89233c1240699392baa7198d8b63bff/retrofit-adapters/rxjava3/src/main/resources/META-INF/proguard/retrofit2-rxjava3-adapter.pro#L1-L5
@Goooler Appreciated! Thanks!
See #3886 which fixes this in a general way rather than us playing whack-a-mole in each adapter jar.
> https://github.com/square/retrofit/blob/3770704de89233c1240699392baa7198d8b63bff/retrofit-adapters/rxjava3/src/main/resources/META-INF/proguard/retrofit2-rxjava3-adapter.pro#L1-L5



> -keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Flowable 
>  -keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Maybe 
>  -keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Observable 
>  -keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Single

Thanks, but after adding this I got an error in response: 
"Unable to create instance of class m.b. Registering an InstanceCreator or a TypeAdapter for this type, or adding a no-args constructor may fix this problem."

with minify disabled I have no errors. 
That's Gson, not Retrofit.
> See #3886 which fixes this in a general way rather than us playing whack-a-mole in each adapter jar.

With AGP 8.0.2 this works with `Response<T>` but not with `Observable<Response<T>>`:
```
java.lang.IllegalArgumentException: Unable to create call adapter for io.reactivex.Observable<retrofit2.Response>
Caused by: java.lang.IllegalStateException: Response must be parameterized as Response<Foo> or Response<? extends Foo>
```
@JakeWharton #3910 fixes issue with `Observable<Response<T>>`.
It helped me:
`-keep,allowobfuscation,allowshrinking class io.reactivex.Flowable
-keep,allowobfuscation,allowshrinking class io.reactivex.Maybe
-keep,allowobfuscation,allowshrinking class io.reactivex.Observable
-keep,allowobfuscation,allowshrinking class io.reactivex.Single`

Pull request review comments:

Patch that resolved the issue:
diff --git a/retrofit-adapters/guava/src/main/resources/META-INF/proguard/retrofit2-guava-adapter.pro b/retrofit-adapters/guava/src/main/resources/META-INF/proguard/retrofit2-guava-adapter.pro
deleted file mode 100644
index 603d49f0b4..0000000000
--- a/retrofit-adapters/guava/src/main/resources/META-INF/proguard/retrofit2-guava-adapter.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-# Keep generic signature of ListenableFuture (R8 full mode strips signatures from non-kept items).
--keep,allowobfuscation,allowshrinking class com.google.common.util.concurrent.ListenableFuture
diff --git a/retrofit-adapters/rxjava/src/main/resources/META-INF/proguard/retrofit2-rxjava-adapter.pro b/retrofit-adapters/rxjava/src/main/resources/META-INF/proguard/retrofit2-rxjava-adapter.pro
deleted file mode 100644
index b85ad6ede4..0000000000
--- a/retrofit-adapters/rxjava/src/main/resources/META-INF/proguard/retrofit2-rxjava-adapter.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-# Keep generic signature of RxJava (R8 full mode strips signatures from non-kept items).
--keep,allowobfuscation,allowshrinking class rx.Single
--keep,allowobfuscation,allowshrinking class rx.Observable
diff --git a/retrofit-adapters/rxjava2/src/main/resources/META-INF/proguard/retrofit2-rxjava2-adapter.pro b/retrofit-adapters/rxjava2/src/main/resources/META-INF/proguard/retrofit2-rxjava2-adapter.pro
deleted file mode 100644
index 3880063625..0000000000
--- a/retrofit-adapters/rxjava2/src/main/resources/META-INF/proguard/retrofit2-rxjava2-adapter.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-# Keep generic signature of RxJava2 (R8 full mode strips signatures from non-kept items).
--keep,allowobfuscation,allowshrinking class io.reactivex.Flowable
--keep,allowobfuscation,allowshrinking class io.reactivex.Maybe
--keep,allowobfuscation,allowshrinking class io.reactivex.Observable
--keep,allowobfuscation,allowshrinking class io.reactivex.Single
diff --git a/retrofit-adapters/rxjava3/src/main/resources/META-INF/proguard/retrofit2-rxjava3-adapter.pro b/retrofit-adapters/rxjava3/src/main/resources/META-INF/proguard/retrofit2-rxjava3-adapter.pro
deleted file mode 100644
index ba96f6db81..0000000000
--- a/retrofit-adapters/rxjava3/src/main/resources/META-INF/proguard/retrofit2-rxjava3-adapter.pro
+++ /dev/null
@@ -1,5 +0,0 @@
-# Keep generic signature of RxJava3 (R8 full mode strips signatures from non-kept items).
--keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Flowable
--keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Maybe
--keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Observable
--keep,allowobfuscation,allowshrinking class io.reactivex.rxjava3.core.Single
diff --git a/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro b/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro
index 9599746d30..f5b499fa55 100644
--- a/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro
+++ b/retrofit/src/main/resources/META-INF/proguard/retrofit2.pro
@@ -35,11 +35,11 @@
 -if interface * { @retrofit2.http.* <methods>; }
 -keep,allowobfuscation interface * extends <1>
 
-# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
--keep,allowobfuscation,allowshrinking interface retrofit2.Call
--keep,allowobfuscation,allowshrinking class retrofit2.Response
-
 # With R8 full mode generic signatures are stripped for classes that are not
 # kept. Suspend functions are wrapped in continuations where the type argument
 # is used.
 -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
+
+# R8 full mode strips generic signatures from return types if not kept.
+-if interface * { @retrofit2.http.* public *** *(...); }
+-keep,allowoptimization,allowshrinking,allowobfuscation class <3>


Test patch: