mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-118234 Closure to SAM coercion: infer generic by SAM return type
This commit is contained in:
@@ -55,6 +55,7 @@ public abstract class GroovyConfigUtils extends AbstractConfigUtils {
|
||||
public static final String GROOVY2_0 = "2.0";
|
||||
public static final String GROOVY2_1 = "2.1";
|
||||
public static final String GROOVY2_2 = "2.2";
|
||||
public static final String GROOVY2_2_2 = "2.2.2";
|
||||
public static final String GROOVY2_3 = "2.3";
|
||||
|
||||
private static GroovyConfigUtils myGroovyConfigUtils;
|
||||
|
||||
+24
-16
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -45,18 +45,13 @@ public class ClosureToSamConverter extends GrTypeConverter {
|
||||
@Override
|
||||
public Boolean isConvertible(@NotNull PsiType ltype, @NotNull PsiType rtype, @NotNull final GroovyPsiElement context) {
|
||||
if (rtype instanceof GrClosureType && ltype instanceof PsiClassType && GroovyConfigUtils.getInstance().isVersionAtLeast(context, GroovyConfigUtils.GROOVY2_2)) {
|
||||
PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)ltype).resolveGenerics();
|
||||
final PsiClass resolved = resolveResult.getElement();
|
||||
if (resolved != null) {
|
||||
final MethodSignature signature = findSingleAbstractMethodClass(resolved, resolveResult.getSubstitutor());
|
||||
if (signature != null) {
|
||||
MethodSignature signature = findSAMSignature(ltype);
|
||||
if (signature != null) {
|
||||
final PsiType[] samParameterTypes = signature.getParameterTypes();
|
||||
|
||||
final PsiType[] samParameterTypes = signature.getParameterTypes();
|
||||
|
||||
GrSignature closureSignature = ((GrClosureType)rtype).getSignature();
|
||||
if (GrClosureSignatureUtil.isSignatureApplicable(closureSignature, samParameterTypes, context)) {
|
||||
return true;
|
||||
}
|
||||
GrSignature closureSignature = ((GrClosureType)rtype).getSignature();
|
||||
if (GrClosureSignatureUtil.isSignatureApplicable(closureSignature, samParameterTypes, context)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -65,16 +60,15 @@ public class ClosureToSamConverter extends GrTypeConverter {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static MethodSignature findSingleAbstractMethodClass(@NotNull PsiClass aClass,
|
||||
@NotNull PsiSubstitutor substitutor) {
|
||||
public static MethodSignature findSingleAbstractMethod(@NotNull PsiClass aClass, @NotNull PsiSubstitutor substitutor) {
|
||||
MethodSignature signature;
|
||||
Ref<MethodSignature> cached = SAM_SIGNATURE_LIGHT_CACHE_KEY.getCachedValue(aClass);
|
||||
if (cached != null) {
|
||||
signature = cached.get();
|
||||
}
|
||||
else {
|
||||
cached = Ref.create(doFindSingleAbstractMethodClass(aClass));
|
||||
signature = SAM_SIGNATURE_LIGHT_CACHE_KEY.putCachedValue(aClass, cached).get();
|
||||
Ref<MethodSignature> newCached = Ref.create(doFindSingleAbstractMethodClass(aClass));
|
||||
signature = SAM_SIGNATURE_LIGHT_CACHE_KEY.putCachedValue(aClass, newCached).get();
|
||||
}
|
||||
|
||||
return signature != null ? substitute(signature, substitutor): null;
|
||||
@@ -100,4 +94,18 @@ public class ClosureToSamConverter extends GrTypeConverter {
|
||||
private static MethodSignature substitute(@NotNull MethodSignature signature, @NotNull PsiSubstitutor substitutor) {
|
||||
return MethodSignatureUtil.createMethodSignature(signature.getName(), signature.getParameterTypes(), PsiTypeParameter.EMPTY_ARRAY, substitutor, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MethodSignature findSAMSignature(@Nullable PsiType type) {
|
||||
if (type instanceof PsiClassType) {
|
||||
PsiClassType.ClassResolveResult result = ((PsiClassType)type).resolveGenerics();
|
||||
PsiClass aClass = result.getElement();
|
||||
|
||||
if (aClass != null) {
|
||||
return findSingleAbstractMethod(aClass, result.getSubstitutor());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+15
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,7 +59,7 @@ public class MethodResolverProcessor extends ResolverProcessor implements GrMeth
|
||||
private boolean myStopExecuting = false;
|
||||
|
||||
private final boolean myByShape;
|
||||
|
||||
|
||||
private final SubstitutorComputer mySubstitutorComputer;
|
||||
|
||||
private final boolean myTypedContext;
|
||||
@@ -88,7 +88,7 @@ public class MethodResolverProcessor extends ResolverProcessor implements GrMeth
|
||||
myAllVariants = allVariants;
|
||||
myByShape = byShape;
|
||||
|
||||
mySubstitutorComputer = new SubstitutorComputer(thisType, argumentTypes, typeArguments, allVariants, place, myPlace.getParent());
|
||||
mySubstitutorComputer = new SubstitutorComputer(myThisType, myArgumentTypes, typeArguments, myAllVariants, myPlace, myPlace.getParent());
|
||||
myTypedContext = GppTypeConverter.hasTypedContext(myPlace);
|
||||
}
|
||||
|
||||
@@ -98,15 +98,12 @@ public class MethodResolverProcessor extends ResolverProcessor implements GrMeth
|
||||
if (myStopExecuting) {
|
||||
return false;
|
||||
}
|
||||
PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
|
||||
if (element instanceof PsiMethod) {
|
||||
PsiMethod method = (PsiMethod) element;
|
||||
|
||||
if (method.isConstructor() != myIsConstructor) return true;
|
||||
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
|
||||
if (!myByShape) {
|
||||
substitutor = mySubstitutorComputer.obtainSubstitutor(substitutor, method, state);
|
||||
}
|
||||
|
||||
PsiSubstitutor substitutor = inferSubstitutor(method, state);
|
||||
|
||||
PsiElement resolveContext = state.get(RESOLVE_CONTEXT);
|
||||
final SpreadState spreadState = state.get(SpreadState.SPREAD_STATE);
|
||||
@@ -124,13 +121,20 @@ public class MethodResolverProcessor extends ResolverProcessor implements GrMeth
|
||||
else {
|
||||
myInapplicableCandidates.add(candidate);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private PsiSubstitutor inferSubstitutor(@NotNull PsiMethod method, @NotNull ResolveState state) {
|
||||
PsiSubstitutor substitutor = state.get(PsiSubstitutor.KEY);
|
||||
if (substitutor == null) substitutor = PsiSubstitutor.EMPTY;
|
||||
|
||||
return myByShape ? substitutor
|
||||
: mySubstitutorComputer.obtainSubstitutor(substitutor, method, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public GroovyResolveResult[] getCandidates() {
|
||||
@@ -226,7 +230,7 @@ public class MethodResolverProcessor extends ResolverProcessor implements GrMeth
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
private static boolean isMoreConcreteThan(@NotNull PsiMethod method,
|
||||
@NotNull final PsiSubstitutor substitutor,
|
||||
@NotNull PsiMethod another,
|
||||
|
||||
+53
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,11 +18,12 @@ package org.jetbrains.plugins.groovy.lang.resolve.processors;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
|
||||
import org.jetbrains.plugins.groovy.config.GroovyConfigUtils;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature;
|
||||
@@ -39,7 +40,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMe
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.typeEnhancers.ClosureToSamConverter;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GdkMethodUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
|
||||
|
||||
import java.util.Set;
|
||||
@@ -60,6 +63,7 @@ public class SubstitutorComputer {
|
||||
|
||||
private final GrControlFlowOwner myFlowOwner;
|
||||
private final PsiElement myPlaceToInferContext;
|
||||
private PsiResolveHelper myHelper;
|
||||
|
||||
|
||||
public SubstitutorComputer(PsiType thisType,
|
||||
@@ -81,6 +85,9 @@ public class SubstitutorComputer {
|
||||
else {
|
||||
myFlowOwner = null;
|
||||
}
|
||||
|
||||
myHelper = JavaPsiFacade.getInstance(myPlace.getProject()).getResolveHelper();
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -186,11 +193,10 @@ public class SubstitutorComputer {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
final PsiResolveHelper helper = JavaPsiFacade.getInstance(method.getProject()).getResolveHelper();
|
||||
PsiSubstitutor substitutor = helper.inferTypeArguments(typeParameters, parameterTypes, argumentTypes, LanguageLevel.JDK_1_7);
|
||||
PsiSubstitutor substitutor = myHelper.inferTypeArguments(typeParameters, parameterTypes, argumentTypes, LanguageLevel.JDK_1_7);
|
||||
for (PsiTypeParameter typeParameter : typeParameters) {
|
||||
if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
|
||||
substitutor = inferFromContext(typeParameter, PsiUtil.getSmartReturnType(method), substitutor, helper);
|
||||
substitutor = inferFromContext(typeParameter, PsiUtil.getSmartReturnType(method), substitutor);
|
||||
if (!substitutor.getSubstitutionMap().containsKey(typeParameter)) {
|
||||
substitutor = substitutor.put(typeParameter, null);
|
||||
}
|
||||
@@ -200,7 +206,16 @@ public class SubstitutorComputer {
|
||||
return partialSubstitutor.putAll(substitutor);
|
||||
}
|
||||
|
||||
private PsiType handleConversion(PsiType paramType, PsiType argType) {
|
||||
@Nullable
|
||||
private PsiType handleConversion(@Nullable PsiType paramType, @Nullable PsiType argType) {
|
||||
if (GroovyConfigUtils.getInstance().isVersionAtLeast(myPlace, GroovyConfigUtils.GROOVY2_2_2) &&
|
||||
InheritanceUtil.isInheritor(argType, GroovyCommonClassNames.GROOVY_LANG_CLOSURE)) {
|
||||
PsiType converted = handleConversionOfSAMType(paramType, (PsiClassType)argType);
|
||||
if (converted != null) {
|
||||
return converted;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TypesUtil.isAssignable(TypeConversionUtil.erasure(paramType), argType, myPlace) &&
|
||||
TypesUtil.isAssignableByMethodCallConversion(paramType, argType, myPlace)) {
|
||||
return paramType;
|
||||
@@ -208,13 +223,39 @@ public class SubstitutorComputer {
|
||||
return argType;
|
||||
}
|
||||
|
||||
private PsiSubstitutor inferFromContext(PsiTypeParameter typeParameter,
|
||||
PsiType lType,
|
||||
PsiSubstitutor substitutor,
|
||||
PsiResolveHelper helper) {
|
||||
@Nullable
|
||||
private PsiType handleConversionOfSAMType(@Nullable PsiType samType, @NotNull PsiClassType closure) {
|
||||
if (samType instanceof PsiClassType) {
|
||||
PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)samType).resolveGenerics();
|
||||
PsiClass samClass = resolveResult.getElement();
|
||||
|
||||
if (samClass != null && samClass.getTypeParameters().length != 0) {
|
||||
MethodSignature samSignature = ClosureToSamConverter.findSingleAbstractMethod(samClass, PsiSubstitutor.EMPTY);
|
||||
if (samSignature != null) {
|
||||
|
||||
PsiMethod samMethod = MethodSignatureUtil.findMethodBySignature(samClass, samSignature, true);
|
||||
if (samMethod != null) {
|
||||
PsiSubstitutor substitutor = myHelper.inferTypeArguments(samClass.getTypeParameters(),
|
||||
new PsiType[]{samMethod.getReturnType()},
|
||||
closure.getParameters(),
|
||||
LanguageLevel.JDK_1_7);
|
||||
|
||||
return JavaPsiFacade.getElementFactory(myPlace.getProject()).createType(samClass, substitutor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private PsiSubstitutor inferFromContext(@NotNull PsiTypeParameter typeParameter,
|
||||
@Nullable PsiType lType,
|
||||
@NotNull PsiSubstitutor substitutor) {
|
||||
if (myPlace == null) return substitutor;
|
||||
|
||||
final PsiType inferred = helper.getSubstitutionForTypeParameter(typeParameter, lType, inferContextType(), false, LanguageLevel.JDK_1_7);
|
||||
final PsiType inferred = myHelper.getSubstitutionForTypeParameter(typeParameter, lType, inferContextType(), false, LanguageLevel.JDK_1_7);
|
||||
if (inferred != PsiType.NULL) {
|
||||
return substitutor.put(typeParameter, inferred);
|
||||
}
|
||||
|
||||
+65
@@ -72,4 +72,69 @@ class TestCase {
|
||||
}
|
||||
''', 'java.lang.Integer')
|
||||
}
|
||||
|
||||
void testSAMInference() {
|
||||
doTest('''\
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
interface CustomCallable<T> {
|
||||
T call()
|
||||
}
|
||||
|
||||
class Thing {
|
||||
static <T> T customType(CustomCallable<T> callable) {
|
||||
callable.call()
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static void run() {
|
||||
customType { [] }.ad<caret>d(1) // return type is not inferred - fails compile
|
||||
}
|
||||
}
|
||||
''', "java.lang.Boolean")
|
||||
}
|
||||
|
||||
void testSAMInference2() {
|
||||
doTest('''\
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
interface CustomCallable<T> {
|
||||
List<T> call()
|
||||
}
|
||||
|
||||
class Thing {
|
||||
static <T> T first(CustomCallable<T> callable) {
|
||||
callable.call().iterator().next()
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static void run() {
|
||||
first { [[]] }.ad<caret>d(1) // return type is not inferred - fails compile
|
||||
}
|
||||
}
|
||||
''', "java.lang.Boolean")
|
||||
}
|
||||
|
||||
void testSAMInference3() {
|
||||
doTest('''\
|
||||
import groovy.transform.CompileStatic
|
||||
|
||||
interface CustomCallable<K, V> {
|
||||
Map<K, V> call()
|
||||
}
|
||||
|
||||
class Thing {
|
||||
static <K, V> Map<K, V> customType(CustomCallable<K, V> callable) {
|
||||
callable.call()
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
static void run() {
|
||||
customType { [(1):3] }.pu<caret>t(1, 5) // return type is not inferred - fails compile
|
||||
}
|
||||
}
|
||||
|
||||
''', 'java.lang.Integer')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user