mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
java 8: resolve conflicts choosing more specific return type (IDEA-98845)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -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);
|
||||
}
|
||||
|
||||
+28
-11
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user