calc types by applicability constraints if inference fails

include all inference variables for resolve and ignore additional constraints checks if the inference is going to failure; this way if (expr()) would get type of the 'standalone' expression instead of expression with boolean as expected type
This commit is contained in:
Anna.Kozlova
2017-12-07 15:51:06 +01:00
parent c9a3fba58c
commit 1fc76eb779
17 changed files with 69 additions and 66 deletions
@@ -523,7 +523,7 @@ public class HighlightMethodUtil {
PsiType rType = methodCall.getType();
if (rType != null && !variable.getType().isAssignableFrom(rType)) {
PsiType expectedTypeByApplicabilityConstraints = resolveResult.getSubstitutor(false).substitute(resolved.getReturnType());
if (expectedTypeByApplicabilityConstraints != null && !expectedTypeByApplicabilityConstraints.equals(rType)) {
if (expectedTypeByApplicabilityConstraints != null && !variable.getType().isAssignableFrom(expectedTypeByApplicabilityConstraints)) {
HighlightFixUtil.registerChangeVariableTypeFixes(variable, expectedTypeByApplicabilityConstraints, methodCall, highlightInfo);
}
}
@@ -378,11 +378,9 @@ public class InferenceSession {
if (expectedActualErrorMessage != null && myErrorMessages != null) {
myErrorMessages.add(0, expectedActualErrorMessage);
}
resolveBounds(getInputInferenceVariablesFromTopLevelFunctionalExpressions(args, properties), initialSubstitutor);
return;
}
if (parameters != null && args != null && !isOverloadCheck()) {
//proceed to B3 constraints
else if (parameters != null && args != null && !isOverloadCheck()) {
final Set<ConstraintFormula> additionalConstraints = new LinkedHashSet<>();
final HashSet<ConstraintFormula> ignoredConstraints = new HashSet<>();
if (parameters.length > 0) {
@@ -390,41 +388,13 @@ public class InferenceSession {
ignoredConstraints, properties.isVarargs(), initialSubstitutor);
}
if (!additionalConstraints.isEmpty() && !proceedWithAdditionalConstraints(additionalConstraints, ignoredConstraints)) {
resolveBounds(getInputInferenceVariablesFromTopLevelFunctionalExpressions(args, properties), initialSubstitutor);
return;
}
proceedWithAdditionalConstraints(additionalConstraints, ignoredConstraints);
}
}
resolveBounds(myInferenceVariables, initialSubstitutor);
}
private Collection<InferenceVariable> getInputInferenceVariablesFromTopLevelFunctionalExpressions(PsiExpression[] args, MethodCandidateInfo.CurrentCandidateProperties properties) {
if (args == null) return Collections.emptyList();
final PsiMethod method = properties.getMethod();
final PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length == 0) return Collections.emptyList();
final HashSet<InferenceVariable> dependencies = new HashSet<>();
for (int i = 0; i < args.length; i++) {
PsiExpression arg = args[i];
if (arg instanceof PsiLambdaExpression && !((PsiLambdaExpression)arg).hasFormalParameterTypes() ||
arg instanceof PsiMethodReferenceExpression && !((PsiMethodReferenceExpression)arg).isExact()) {
final PsiSubstitutor nestedSubstitutor = myInferenceSessionContainer.findNestedSubstitutor(arg, myInferenceSubstitution);
final PsiType parameterType = nestedSubstitutor.substitute(getParameterType(parameters, i, mySiteSubstitutor, properties.isVarargs()));
final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(parameterType);
final PsiMethod interfaceMethod = LambdaUtil.getFunctionalInterfaceMethod(result);
if (interfaceMethod != null) {
final PsiSubstitutor substitutor = LambdaUtil.getSubstitutor(interfaceMethod, result);
for (PsiParameter parameter : interfaceMethod.getParameterList().getParameters()) {
collectDependencies(substitutor.substitute(parameter.getType()), dependencies);
}
}
}
}
return dependencies;
}
private boolean isOverloadCheck() {
if (myContext != null) {
for (Object o : MethodCandidateInfo.ourOverloadGuard.currentStack()) {
@@ -1089,7 +1059,7 @@ public class InferenceSession {
}
if (!myIncorporationPhase.hasCaptureConstraints(unresolved)) {
PsiSubstitutor firstSubstitutor = resolveSubset(vars, substitutor);
if (hasBoundProblems(vars, firstSubstitutor)) {
if (myErrorMessages == null && hasBoundProblems(vars, firstSubstitutor)) {
firstSubstitutor = null;
unresolved = vars;
}
@@ -1243,7 +1213,7 @@ public class InferenceSession {
type = var.getBounds(InferenceBound.UPPER).size() == 1 ? myPolicy.getInferredTypeWithNoConstraint(myManager, upperBound).first : upperBound;
}
if (type instanceof PsiIntersectionType) {
if (myErrorMessages == null && type instanceof PsiIntersectionType) {
String conflictingConjunctsMessage = ((PsiIntersectionType)type).getConflictingConjunctsMessage();
if (conflictingConjunctsMessage == null) {
if (findParameterizationOfTheSameGenericClass(var.getBounds(InferenceBound.UPPER),
@@ -1265,7 +1235,7 @@ public class InferenceSession {
}
else {
for (PsiType upperType : var.getBounds(InferenceBound.UPPER)) {
if (isProperType(upperType) ) {
if (myErrorMessages == null && isProperType(upperType)) {
String incompatibleBoundsMessage = null;
if (type != lowerBound && !TypeConversionUtil.isAssignable(upperType, type)) {
incompatibleBoundsMessage = incompatibleBoundsMessage(var, substitutor, InferenceBound.EQ, EQUALITY_CONSTRAINTS_PRESENTATION, InferenceBound.UPPER, UPPER_BOUNDS_PRESENTATION);
@@ -20,7 +20,6 @@ import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiTypesUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
@@ -67,9 +66,6 @@ public class RefactoringChangeUtil {
if (refClass instanceof PsiAnonymousClass) {
type = ((PsiAnonymousClass)refClass).getBaseClassType();
}
if (refClass instanceof PsiTypeParameter && !PsiTypesUtil.isAccessibleAt((PsiTypeParameter)refClass, expr)) {
type = ((PsiExpression)expr.copy()).getType();
}
return GenericsUtil.getVariableTypeByExpressionType(type);
}
@@ -8,9 +8,6 @@ abstract class Test {
private void call(){
String s = <error descr="Incompatible types. Required String but 'test' was inferred to T:
no instance(s) of type variable(s) exist so that String[] conforms to String
inference variable T has incompatible bounds:
equality constraints: String[]
upper bounds: Object, String">test(String[].class);</error>
no instance(s) of type variable(s) exist so that String[] conforms to String">test(String[].class);</error>
}
}
@@ -7,10 +7,7 @@ class Test {
final Class<? extends ClassB> bClass = null;
ClassB b = factory.create(bClass);
String str = <error descr="Incompatible types. Required String but 'create' was inferred to T:
no instance(s) of type variable(s) exist so that capture of ? extends ClassB conforms to String
inference variable T has incompatible bounds:
equality constraints: capture of ? extends ClassB
upper bounds: ClassA<I>, Object, String">factory.create(bClass);</error>
no instance(s) of type variable(s) exist so that capture of ? extends ClassB conforms to String">factory.create(bClass);</error>
}
public static class Factory {
@@ -15,13 +15,13 @@ class NoInferenceResult {
<T> void m1(T t) { }
void test() {
m(<error descr="Object is not a functional interface">(String s1) -> (String s2) -> s1 + s2</error>);
m(<error descr="Object is not a functional interface">(String s1) -> {return (String s2) -> s1 + s2;}</error>);
m((String s1) -> <error descr="Target type of a lambda conversion must be an interface">(String s2) -> s1 + s2</error>);
m((String s1) -> {return <error descr="Target type of a lambda conversion must be an interface">(String s2) -> s1 + s2</error>;});
m((String s1) -> s1.length());
m((String s1) -> s1);
m1(<error descr="Object is not a functional interface">() -> { }</error>);
m1(<error descr="Target type of a lambda conversion must be an interface">() -> { }</error>);
Foo<String> foo = new Foo<String>();
foo.map(v -> null);
@@ -14,7 +14,7 @@ class NoLambda {
static <T> T id(T i2) {return i2;}
{
id(<error descr="Object is not a functional interface">() -> {System.out.println("hi");}</error>);
id(<error descr="Target type of a lambda conversion must be an interface">() -> {System.out.println("hi");}</error>);
NoLambda.<Runnable>id(() -> {System.out.println("hi");});
}
}
@@ -5,7 +5,7 @@ import java.util.function.Consumer;
class Test {
public static void main(String[] args) {
Iterable<Consumer<Reader>> i = Arrays.asList(<error descr="Unhandled exception: IOException">(r) -> r.read()</error>);
Iterable<Consumer<Reader>> i = Arrays.asList((r) -> r.<error descr="Unhandled exception: java.io.IOException">read()</error>);
Iterable<Consumer<Reader>> i1 = Arrays.<Consumer<Reader>>asList((r) -> r.<error descr="Unhandled exception: java.io.IOException">read()</error>);
}
}
@@ -10,10 +10,7 @@ class Test {
<R> SuperFoo<R> foo(I<R> ax) { return null; }
SuperFoo<String> ls = foo(<error descr="Incompatible types. Required SuperFoo<String> but 'foo' was inferred to SuperFoo<R>:
no instance(s) of type variable(s) exist so that String conforms to Number
inference variable R has incompatible bounds:
equality constraints: String
upper bounds: Object, Number">() -> new Foo<>()</error>);
no instance(s) of type variable(s) exist so that String conforms to Number">() -> new Foo<>()</error>);
SuperFoo<Integer> li = foo(() -> new Foo<>());
SuperFoo<?> lw = foo(() -> new Foo<>());
}
@@ -26,10 +26,7 @@ abstract class NoFormalParamTypeInferenceNeeded {
{
map(a -> zip(text -> text));
zip(a -> zip(text -> text));
Integer zip = zip(<error descr="no instance(s) of type variable(s) exist so that Object conforms to Integer
inference variable R has incompatible bounds:
lower bounds: Object
upper bounds: Object, Integer">a -> zip(text -> text)</error>);
Integer zip = zip(<error descr="no instance(s) of type variable(s) exist so that Object conforms to Integer">a -> zip(text -> text)</error>);
}
}
@@ -7,7 +7,7 @@ class Test {
<T extends Runnable> void call1(T t) {}
{
call(<error descr="Object is not a functional interface">() -> {}</error>);
call(<error descr="Target type of a lambda conversion must be an interface">() -> {}</error>);
call1(() -> {});
}
}
@@ -1,7 +1,7 @@
class Test {
{
asList(<error descr="Integer is not a functional interface">o -> {}</error>, 1, 2, 3);
asList(<error descr="T is not a functional interface">Test::foo</error>, 1, 2, 3);
asList(<error descr="Target type of a lambda conversion must be an interface">o -> {}</error>, 1, 2, 3);
asList(<error descr="Integer is not a functional interface">Test::foo</error>, 1, 2, 3);
}
void foo() {}
@@ -0,0 +1,10 @@
// "Replace with 'java.util.TreeSet' constructor" "true"
import java.util.*;
import java.util.stream.*;
class Test {
public static void test(List<String> s) {
new TreeSet<? extends String>(s).contains("abc");
}
}
@@ -1,4 +1,4 @@
// "Replace with 'java.util.TreeSet' constructor" "false"
// "Replace with 'java.util.TreeSet' constructor" "true"
import java.util.*;
import java.util.stream.*;
@@ -0,0 +1,19 @@
import java.util.List;
class GenericTypeMismatch {
interface Key<T> {}
static final Key<String> KEY = new Key<String>() {};
<T> java.util.List<T> getByKey(Key<T> key) {
return null;
}
void test() {
final List<String> i = getByKey(KEY);
if(i)
{
}
}
}
@@ -0,0 +1,16 @@
class GenericTypeMismatch {
interface Key<T> {}
static final Key<String> KEY = new Key<String>() {};
<T> java.util.List<T> getByKey(Key<T> key) {
return null;
}
void test() {
if(ge<caret>tByKey(KEY))
{
}
}
}
@@ -238,6 +238,10 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase {
doTest(new MockIntroduceVariableHandler("i", true, true, false, "java.lang.String"));
}
public void testGenericTypeMismatch1() {
doTest(new MockIntroduceVariableHandler("i", true, true, false, "java.util.List<java.lang.String>"));
}
public void testThisQualifier() {
doTest(new MockIntroduceVariableHandler("count", true, true, false, "int"));
}