diff --git a/translator/src/main/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslator.java b/translator/src/main/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslator.java index 471e96551e..f9c4a934c6 100644 --- a/translator/src/main/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslator.java +++ b/translator/src/main/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslator.java @@ -625,8 +625,64 @@ boolean matches(String methodName) { return methodName.startsWith(prefix); } + /** + * Checks if the adapter method's type matches the target candidate type. + * First attempts an exact match. If the method declares type parameters (e.g., {@code }), + * it performs a looser match that treats type variables as matching any type. + */ boolean matchesType(ExecutableElement method, TypeMirror candidate, TypeUtil typeUtil) { - return typeUtil.isSameType(getType(method), candidate); + TypeMirror methodType = getType(method); + if (typeUtil.isSameType(methodType, candidate)) { + return true; + } + // If the adapter method has generic type parameters, allow type variables to act as + // wildcards. + if (!method.getTypeParameters().isEmpty()) { + return isTypeMatchIgnoringTypeVariables(methodType, candidate, typeUtil); + } + return false; + } + + /** + * Recursively checks if {@code methodType} matches {@code candidate}, treating any type + * variable in {@code methodType} as a match-all wildcard. For example, this allows + * {@code ImmutableList} to match {@code ImmutableList}. + */ + private boolean isTypeMatchIgnoringTypeVariables( + TypeMirror methodType, TypeMirror candidate, TypeUtil typeUtil) { + // A type variable (like 'T') matches any type in the candidate. + if (TypeUtil.isTypeVariable(methodType)) { + return true; + } + + // For declared types (like classes or interfaces), ensure the base elements are the same, + // then recursively check their type arguments. + if (TypeUtil.isDeclaredType(methodType) && TypeUtil.isDeclaredType(candidate)) { + DeclaredType mDeclared = (DeclaredType) methodType; + DeclaredType cDeclared = (DeclaredType) candidate; + + // Check if the base type (e.g., ImmutableList) matches. + if (!mDeclared.asElement().equals(cDeclared.asElement())) { + return false; + } + + List mArgs = mDeclared.getTypeArguments(); + List cArgs = cDeclared.getTypeArguments(); + if (mArgs.size() != cArgs.size()) { + return false; + } + + // Recursively compare each type argument. + for (int i = 0; i < mArgs.size(); i++) { + if (!isTypeMatchIgnoringTypeVariables(mArgs.get(i), cArgs.get(i), typeUtil)) { + return false; + } + } + return true; + } + + // Fallback to strict equality for other types (e.g., primitives, arrays). + return typeUtil.isSameType(methodType, candidate); } abstract TypeMirror getType(ExecutableElement method); diff --git a/translator/src/test/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslatorTest.java b/translator/src/test/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslatorTest.java index 918abf4b17..2e86487837 100644 --- a/translator/src/test/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslatorTest.java +++ b/translator/src/test/java/com/google/devtools/j2objc/translate/ObjectiveCKmpMethodTranslatorTest.java @@ -937,11 +937,10 @@ public class CustomClass { addSourceFile( """ import com.google.common.collect.ImmutableList; - import my.pkg.CustomClass; public class ImmutableListAdapter { - public static native Object fromImmutableList(ImmutableList list) /*-[ return nil; ]-*/; - public static native ImmutableList toImmutableList(Object list) /*-[ return nil; ]-*/; + public static native Object fromImmutableList(ImmutableList list) /*-[ return nil; ]-*/; + public static native ImmutableList toImmutableList(Object list) /*-[ return nil; ]-*/; } """, "ImmutableListAdapter.java"); @@ -988,7 +987,7 @@ public ImmutableList getItems() { abstractImpl, """ - (void)setItems:(NSArray *)items { - [self setItemsWithComGoogleCommonCollectImmutableList:[ImmutableListAdapter toImmutableListWithId:items]]; + [self setItemsWithComGoogleCommonCollectImmutableList:(ComGoogleCommonCollectImmutableList *) [ImmutableListAdapter toImmutableListWithId:items]]; } """); assertInTranslation(