java 8: resolve conflicts choosing more specific return type (IDEA-98845)

This commit is contained in:
anna
2013-01-11 18:55:34 +01:00
parent 7f67356440
commit c892ea7f32
5 changed files with 140 additions and 87 deletions
@@ -17,10 +17,12 @@ package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Pair;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.util.*;
import org.jetbrains.annotations.NotNull;
@@ -579,6 +581,84 @@ public class LambdaUtil {
return result;
}
public static void checkMoreSpecificReturnType(List<CandidateInfo> conflicts, int functionalInterfaceIdx) {
final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
for (int i = 1; i < newConflictsArray.length; i++) {
final CandidateInfo method = newConflictsArray[i];
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
for (int j = 0; j < i; j++) {
final CandidateInfo conflict = newConflictsArray[j];
assert conflict != method;
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
int moreSpecific = isMoreSpecific(interfaceReturnType, interfaceReturnType1);
if (moreSpecific > 0) {
conflicts.remove(method);
break;
}
else if (moreSpecific < 0) {
conflicts.remove(conflict);
}
}
}
}
}
private static int isMoreSpecific(PsiType returnType, PsiType returnType1) {
if (returnType instanceof PsiPrimitiveType && returnType != PsiType.VOID && !(returnType1 instanceof PsiPrimitiveType)) {
return -1;
}
if (returnType1 instanceof PsiPrimitiveType && returnType1 != PsiType.VOID && !(returnType instanceof PsiPrimitiveType)) {
return 1;
}
final PsiClassType.ClassResolveResult r = PsiUtil.resolveGenericsClassInType(returnType);
final PsiClass rClass = r.getElement();
final PsiClassType.ClassResolveResult r1 = PsiUtil.resolveGenericsClassInType(returnType1);
final PsiClass rClass1 = r1.getElement();
if (rClass != null && rClass1 != null) {
if (rClass == rClass1) {
int moreSpecific = 0;
for (PsiTypeParameter parameter : rClass.getTypeParameters()) {
final PsiType t = r.getSubstitutor().substituteWithBoundsPromotion(parameter);
final PsiType t1 = r1.getSubstitutor().substituteWithBoundsPromotion(parameter);
if (t == null || t1 == null) continue;
if (t1.isAssignableFrom(t) && !GenericsUtil.eliminateWildcards(t1).equals(t)) {
if (moreSpecific == 1) {
return 0;
}
moreSpecific = -1;
}
else if (t.isAssignableFrom(t1) && !GenericsUtil.eliminateWildcards(t).equals(t1)) {
if (moreSpecific == -1) {
return 0;
}
moreSpecific = 1;
}
else {
return 0;
}
}
return moreSpecific;
}
else if (rClass1.isInheritor(rClass, true)) {
return 1;
}
else if (rClass.isInheritor(rClass1, true)) {
return -1;
}
}
return 0;
}
@Nullable
private static PsiType getReturnType(int functionalTypeIdx, CandidateInfo method) {
final PsiParameter[] methodParameters = ((PsiMethod)method.getElement()).getParameterList().getParameters();
if (methodParameters.length == 0) return null;
final PsiParameter param = functionalTypeIdx < methodParameters.length ? methodParameters[functionalTypeIdx] : methodParameters[methodParameters.length - 1];
final PsiType functionalInterfaceType = method.getSubstitutor().substitute(param.getType());
return getFunctionalInterfaceReturnType(functionalInterfaceType);
}
static class TypeParamsChecker extends PsiTypeVisitor<Boolean> {
private PsiMethod myMethod;
private final PsiClass myClass;
@@ -16,9 +16,7 @@
package com.intellij.psi;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.infos.CandidateInfo;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.PsiUtil;
@@ -26,7 +24,6 @@ import com.intellij.psi.util.TypeConversionUtil;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@@ -272,76 +269,4 @@ public class PsiMethodReferenceUtil {
methodReferenceExpression);
return (PsiLocalVariable)((PsiDeclarationStatement)assignmentFromText).getDeclaredElements()[0];
}
public static void processMethodReferenceReturnType(List<CandidateInfo> conflicts, int functionalInterfaceIdx) {
final CandidateInfo[] newConflictsArray = conflicts.toArray(new CandidateInfo[conflicts.size()]);
for (int i = 1; i < newConflictsArray.length; i++) {
final CandidateInfo method = newConflictsArray[i];
final PsiType interfaceReturnType = getReturnType(functionalInterfaceIdx, method);
for (int j = 0; j < i; j++) {
final CandidateInfo conflict = newConflictsArray[j];
assert conflict != method;
final PsiType interfaceReturnType1 = getReturnType(functionalInterfaceIdx, conflict);
if (interfaceReturnType != null && interfaceReturnType1 != null && !Comparing.equal(interfaceReturnType, interfaceReturnType1)) {
int moreSpecific = isMoreSpecific(interfaceReturnType, interfaceReturnType1);
if (moreSpecific > 0) {
conflicts.remove(method);
break;
}
else if (moreSpecific < 0) {
conflicts.remove(conflict);
}
}
}
}
}
private static int isMoreSpecific(PsiType returnType, PsiType returnType1) {
final PsiClassType.ClassResolveResult r = PsiUtil.resolveGenericsClassInType(returnType);
final PsiClass rClass = r.getElement();
final PsiClassType.ClassResolveResult r1 = PsiUtil.resolveGenericsClassInType(returnType1);
final PsiClass rClass1 = r1.getElement();
if (rClass != null && rClass1 != null) {
if (rClass == rClass1) {
int moreSpecific = 0;
for (PsiTypeParameter parameter : rClass.getTypeParameters()) {
final PsiType t = r.getSubstitutor().substituteWithBoundsPromotion(parameter);
final PsiType t1 = r1.getSubstitutor().substituteWithBoundsPromotion(parameter);
if (t == null || t1 == null) continue;
if (t1.isAssignableFrom(t) && !GenericsUtil.eliminateWildcards(t1).equals(t)) {
if (moreSpecific == 1) {
return 0;
}
moreSpecific = -1;
}
else if (t.isAssignableFrom(t1) && !GenericsUtil.eliminateWildcards(t).equals(t1)) {
if (moreSpecific == -1) {
return 0;
}
moreSpecific = 1;
}
else {
return 0;
}
}
return moreSpecific;
}
else if (rClass1.isInheritor(rClass, true)) {
return 1;
}
else if (rClass.isInheritor(rClass1, true)) {
return -1;
}
}
return 0;
}
@Nullable
private static PsiType getReturnType(int functionalTypeIdx, CandidateInfo method) {
final PsiParameter[] methodParameters = ((PsiMethod)method.getElement()).getParameterList().getParameters();
if (methodParameters.length == 0) return null;
final PsiParameter param = functionalTypeIdx < methodParameters.length ? methodParameters[functionalTypeIdx] : methodParameters[methodParameters.length - 1];
final PsiType functionalInterfaceType = method.getSubstitutor().substitute(param.getType());
return LambdaUtil.getFunctionalInterfaceReturnType(functionalInterfaceType);
}
}
@@ -123,9 +123,10 @@ public class JavaMethodsConflictResolver implements PsiConflictResolver{
}
}
}
LambdaUtil.checkMoreSpecificReturnType(conflicts, i);
}
else if (parameterType instanceof PsiMethodReferenceType) {
PsiMethodReferenceUtil.processMethodReferenceReturnType(conflicts, i);
LambdaUtil.checkMoreSpecificReturnType(conflicts, i);
}
}
}
@@ -0,0 +1,30 @@
class IntStream {
private void foo(IntStream s) {
s.map(i -> 1 << i);
s.map(i -> 1);
s.map(i -> i);
}
public static void main(String[] args) {
new IntStream().foo(null);
}
private IntStream map(IntUnaryOperator mapper) {
System.out.println(mapper);
return null;
}
private <T> IntStream <warning descr="Private method 'map(ObjIntFunction<T>)' is never used">map</warning>(ObjIntFunction<T> mapper) {
System.out.println(mapper);
return null;
}
}
interface IntUnaryOperator {
public int applyAsInt(int operand);
}
interface ObjIntFunction<T> {
public T apply(int i);
}
@@ -16,11 +16,20 @@
package com.intellij.codeInsight.daemon.lambda;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection;
import org.jetbrains.annotations.NonNls;
public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/highlighting";
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new UnusedSymbolLocalInspection(),
};
}
public void testStaticAccess() throws Exception {
doTest();
}
@@ -28,11 +37,11 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testEffectiveFinal() throws Exception {
doTest();
}
public void testReassignUsedVars() throws Exception {
doTest();
}
public void testLambdaContext() throws Exception {
doTest();
}
@@ -40,7 +49,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testReturnTypeCompatibility() throws Exception {
doTest();
}
public void testTypeArgsConsistency() throws Exception {
doTest();
}
@@ -55,7 +64,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testTypeArgsConsistencyWithoutParams() throws Exception {
doTest();
}
}
public void testIncompatibleReturnTypes() throws Exception {
doTest();
@@ -72,7 +81,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testInferFromTypeArgs() throws Exception {
doTest();
}
public void testAmbiguity1() throws Exception {
doTest();
}
@@ -80,7 +89,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void _testAmbiguity2() throws Exception {
doTest();
}
public void testAmbiguityVarargs() throws Exception {
doTest();
}
@@ -88,7 +97,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testAmbiguityRawGenerics() throws Exception {
doTest();
}
public void testDefaultMethod() throws Exception {
doTest();
}
@@ -104,7 +113,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testReturnTypeCompatibility1() throws Exception {
doTest();
}
public void testNoInferenceResult() throws Exception {
doTest();
}
@@ -128,7 +137,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testConditionalExpr() throws Exception {
doTest();
}
public void testIncompleteSubst() throws Exception {
doTest();
}
@@ -144,7 +153,7 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
public void testReturnValue() throws Exception {
doTest();
}
public void testAlreadyUsedParamName() throws Exception {
doTest();
}
@@ -181,7 +190,15 @@ public class LambdaHighlightingTest extends LightDaemonAnalyzerTestCase {
doTest();
}
public void testAmbiguitySpecificReturn() throws Exception {
doTest(true);
}
private void doTest() throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false);
doTest(false);
}
private void doTest(final boolean checkWarnings) throws Exception {
doTest(BASE_PATH + "/" + getTestName(false) + ".java", checkWarnings, false);
}
}