Correctly processing inherited methods when checking for SAM interface.
This commit is contained in:
+61
-9
@@ -18,10 +18,12 @@ package org.jetbrains.jet.lang.resolve.java.provider;
|
|||||||
|
|
||||||
import com.google.common.collect.HashMultimap;
|
import com.google.common.collect.HashMultimap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
|
import com.intellij.openapi.util.Ref;
|
||||||
import com.intellij.psi.*;
|
import com.intellij.psi.*;
|
||||||
|
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
|
||||||
import com.intellij.psi.util.PsiFormatUtil;
|
import com.intellij.psi.util.PsiFormatUtil;
|
||||||
|
import com.intellij.util.ArrayUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.resolve.java.*;
|
import org.jetbrains.jet.lang.resolve.java.*;
|
||||||
@@ -37,6 +39,7 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import static com.intellij.psi.util.MethodSignatureUtil.areSignaturesErasureEqual;
|
||||||
import static com.intellij.psi.util.PsiFormatUtilBase.*;
|
import static com.intellij.psi.util.PsiFormatUtilBase.*;
|
||||||
|
|
||||||
public final class MembersCache {
|
public final class MembersCache {
|
||||||
@@ -420,17 +423,66 @@ public final class MembersCache {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PsiMethod> methods = Lists.newArrayList();
|
return findOnlyAbstractMethod(psiClass);
|
||||||
for (PsiMethod method : psiClass.getAllMethods()) {
|
}
|
||||||
if (!isObjectMethod(method) && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
|
||||||
methods.add(method);
|
|
||||||
|
|
||||||
if (method.hasTypeParameters()) {
|
@Nullable
|
||||||
return null;
|
private static PsiMethod findOnlyAbstractMethod(@NotNull PsiClass psiClass) {
|
||||||
}
|
Ref<MethodSignatureBackedByPsiMethod> foundRef = Ref.create();
|
||||||
|
if (findOnlyAbstractMethod(JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass), foundRef)) {
|
||||||
|
MethodSignatureBackedByPsiMethod found = foundRef.get();
|
||||||
|
return found == null ? null : found.getMethod();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean findOnlyAbstractMethod(
|
||||||
|
@NotNull PsiClassType classType,
|
||||||
|
@NotNull Ref<MethodSignatureBackedByPsiMethod> foundRef
|
||||||
|
) {
|
||||||
|
PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
|
||||||
|
PsiSubstitutor classSubstitutor = classResolveResult.getSubstitutor();
|
||||||
|
PsiClass psiClass = classResolveResult.getElement();
|
||||||
|
if (psiClass == null) {
|
||||||
|
return false; // can't resolve class -> not a SAM interface
|
||||||
|
}
|
||||||
|
if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (PsiMethod method : psiClass.getMethods()) {
|
||||||
|
if (isObjectMethod(method)) { // e.g., ignore toString() declared in interface
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (method.hasTypeParameters()) {
|
||||||
|
return false; // if interface has generic methods, it is not a SAM interface
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodSignatureBackedByPsiMethod found = foundRef.get();
|
||||||
|
if (found == null) {
|
||||||
|
foundRef.set((MethodSignatureBackedByPsiMethod) method.getSignature(classSubstitutor));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!found.getName().equals(method.getName())) {
|
||||||
|
return false; // optimizing heuristic
|
||||||
|
}
|
||||||
|
MethodSignatureBackedByPsiMethod current = (MethodSignatureBackedByPsiMethod) method.getSignature(classSubstitutor);
|
||||||
|
if (!areSignaturesErasureEqual(current, found) || isVarargMethod(method) != isVarargMethod(found.getMethod())) {
|
||||||
|
return false; // different signatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return methods.size() == 1 ? methods.get(0) : null;
|
|
||||||
|
for (PsiType t : classType.getSuperTypes()) {
|
||||||
|
if (!findOnlyAbstractMethod((PsiClassType) t, foundRef)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isVarargMethod(@NotNull PsiMethod method) {
|
||||||
|
PsiParameter lastParameter = ArrayUtil.getLastElement(method.getParameterList().getParameters());
|
||||||
|
return lastParameter != null && lastParameter.getType() instanceof PsiEllipsisType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static abstract class RunOnce implements Runnable {
|
private static abstract class RunOnce implements Runnable {
|
||||||
|
|||||||
+20
-6
@@ -45,6 +45,7 @@ import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
|
|||||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||||
import org.jetbrains.jet.lang.types.JetType;
|
import org.jetbrains.jet.lang.types.JetType;
|
||||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||||
|
import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -301,15 +302,28 @@ public final class JavaClassResolver {
|
|||||||
return abstractMethod;
|
return abstractMethod;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Set<JetType> supertypes = TypeUtils.getAllSupertypes(samInterface.getDefaultType());
|
return findFunctionWithMostSpecificReturnType(TypeUtils.getAllSupertypes(samInterface.getDefaultType()));
|
||||||
for (JetType supertype : supertypes) {
|
}
|
||||||
List<CallableMemberDescriptor> abstractMembers = SingleAbstractMethodUtils.getAbstractMembers(supertype);
|
}
|
||||||
if (!abstractMembers.isEmpty()) {
|
|
||||||
return (SimpleFunctionDescriptor) abstractMembers.get(0);
|
private static SimpleFunctionDescriptor findFunctionWithMostSpecificReturnType(@NotNull Set<JetType> supertypes) {
|
||||||
}
|
List<SimpleFunctionDescriptor> candidates = Lists.newArrayList();
|
||||||
|
for (JetType supertype : supertypes) {
|
||||||
|
List<CallableMemberDescriptor> abstractMembers = SingleAbstractMethodUtils.getAbstractMembers(supertype);
|
||||||
|
if (!abstractMembers.isEmpty()) {
|
||||||
|
candidates.add((SimpleFunctionDescriptor) abstractMembers.get(0));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (candidates.isEmpty()) {
|
||||||
throw new IllegalStateException("Couldn't find abstract method in supertypes " + supertypes);
|
throw new IllegalStateException("Couldn't find abstract method in supertypes " + supertypes);
|
||||||
}
|
}
|
||||||
|
SimpleFunctionDescriptor currentMostSpecificType = candidates.get(0);
|
||||||
|
for (SimpleFunctionDescriptor candidate : candidates) {
|
||||||
|
if (JetTypeChecker.INSTANCE.isSubtypeOf(candidate.getReturnType(), currentMostSpecificType.getReturnType())) {
|
||||||
|
currentMostSpecificType = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentMostSpecificType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cache(@NotNull FqNameBase fqName, @Nullable ClassDescriptor classDescriptor) {
|
private void cache(@NotNull FqNameBase fqName, @Nullable ClassDescriptor classDescriptor) {
|
||||||
|
|||||||
+14
@@ -0,0 +1,14 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public interface SamSubinterfaceOfTwo {
|
||||||
|
public interface Super1 {
|
||||||
|
CharSequence f();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Super2<T> {
|
||||||
|
T f();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Sub extends Super1, Super2<String> {
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
public trait SamSubinterfaceOfTwo : java.lang.Object {
|
||||||
|
|
||||||
|
public trait Sub : test.SamSubinterfaceOfTwo.Super1, test.SamSubinterfaceOfTwo.Super2<jet.String> {
|
||||||
|
public abstract override /*2*/ /*fake_override*/ fun f(): jet.CharSequence?
|
||||||
|
}
|
||||||
|
|
||||||
|
public trait Super1 : java.lang.Object {
|
||||||
|
public abstract fun f(): jet.CharSequence?
|
||||||
|
}
|
||||||
|
|
||||||
|
public trait Super2</*0*/ T> : java.lang.Object {
|
||||||
|
public abstract fun f(): T?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
package SamSubinterfaceOfTwo {
|
||||||
|
public /*synthesized*/ fun Sub(/*0*/ function: () -> jet.String?): test.SamSubinterfaceOfTwo.Sub
|
||||||
|
public /*synthesized*/ fun Super1(/*0*/ function: () -> jet.CharSequence?): test.SamSubinterfaceOfTwo.Super1
|
||||||
|
public /*synthesized*/ fun </*0*/ T> Super2(/*0*/ function: () -> T?): test.SamSubinterfaceOfTwo.Super2<T>
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package test;
|
||||||
|
|
||||||
|
public interface SamSubinterfaceOverridding extends Runnable {
|
||||||
|
void run();
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
public /*synthesized*/ fun SamSubinterfaceOverridding(/*0*/ function: () -> jet.Unit): test.SamSubinterfaceOverridding
|
||||||
|
|
||||||
|
public trait SamSubinterfaceOverridding : java.lang.Runnable {
|
||||||
|
public abstract override /*1*/ fun run(): jet.Unit
|
||||||
|
}
|
||||||
@@ -1218,6 +1218,16 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest {
|
|||||||
doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java");
|
doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SamSubinterfaceOfTwo.java")
|
||||||
|
public void testSamSubinterfaceOfTwo() throws Exception {
|
||||||
|
doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/SamSubinterfaceOfTwo.java");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("SamSubinterfaceOverridding.java")
|
||||||
|
public void testSamSubinterfaceOverridding() throws Exception {
|
||||||
|
doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/SamSubinterfaceOverridding.java");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("SubstitutedSamInterface.java")
|
@TestMetadata("SubstitutedSamInterface.java")
|
||||||
public void testSubstitutedSamInterface() throws Exception {
|
public void testSubstitutedSamInterface() throws Exception {
|
||||||
doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/SubstitutedSamInterface.java");
|
doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/SubstitutedSamInterface.java");
|
||||||
|
|||||||
Reference in New Issue
Block a user