Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -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 <T>}),
* 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<T>} to match {@code ImmutableList<CustomClass>}.
*/
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<? extends TypeMirror> mArgs = mDeclared.getTypeArguments();
List<? extends TypeMirror> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<CustomClass> list) /*-[ return nil; ]-*/;
public static native ImmutableList<CustomClass> toImmutableList(Object list) /*-[ return nil; ]-*/;
public static native <T> Object fromImmutableList(ImmutableList<T> list) /*-[ return nil; ]-*/;
public static native <T> ImmutableList<T> toImmutableList(Object list) /*-[ return nil; ]-*/;
}
""",
"ImmutableListAdapter.java");
Expand Down Expand Up @@ -988,7 +987,7 @@ public ImmutableList<CustomClass> getItems() {
abstractImpl,
"""
- (void)setItems:(NSArray<MyPkgCustomClass *> *)items {
[self setItemsWithComGoogleCommonCollectImmutableList:[ImmutableListAdapter toImmutableListWithId:items]];
[self setItemsWithComGoogleCommonCollectImmutableList:(ComGoogleCommonCollectImmutableList *) [ImmutableListAdapter toImmutableListWithId:items]];
}
""");
assertInTranslation(
Expand Down