inference: propagate variable renames through the call hierarchy; calculate tooltips in tests (IDEA-151948)

This commit is contained in:
Anna Kozlova
2016-02-22 17:31:52 +01:00
parent 792d40fc9f
commit 930df16f98
14 changed files with 73 additions and 18 deletions

View File

@@ -24,7 +24,6 @@ import com.intellij.codeInsight.daemon.impl.quickfix.*;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.codeInsight.intention.QuickFixFactory;
import com.intellij.codeInspection.LocalQuickFixOnPsiElementAsIntentionAdapter;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.IndexNotReadyException;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.util.Comparing;
@@ -398,7 +397,7 @@ public class HighlightMethodUtil {
String description = JavaErrorMessages.message("wrong.method.arguments", methodName, containerName, argTypes);
final Ref<PsiElement> elementToHighlight = new Ref<PsiElement>(list);
String toolTip;
if (parent instanceof PsiClass && !ApplicationManager.getApplication().isUnitTestMode()) {
if (parent instanceof PsiClass) {
toolTip = buildOneLineMismatchDescription(list, candidateInfo, elementToHighlight);
if (toolTip == null) {
toolTip = createMismatchedArgumentsHtmlTooltip(candidateInfo, list);

View File

@@ -1175,6 +1175,13 @@ public class InferenceSession {
}
public void registerIncompatibleErrorMessage(Collection<InferenceVariable> variables, String incompatibleTypesMessage) {
variables = new ArrayList<InferenceVariable>(variables);
Collections.sort((ArrayList<InferenceVariable>)variables, new Comparator<InferenceVariable>() {
@Override
public int compare(InferenceVariable v1, InferenceVariable v2) {
return Comparing.compare(v1.getName(), v2.getName());
}
});
final String variablesEnumeration = StringUtil.join(variables, new Function<InferenceVariable, String>() {
@Override
public String fun(InferenceVariable variable) {
@@ -1814,8 +1821,9 @@ public class InferenceSession {
return myContext;
}
public void propagateVariables(Collection<InferenceVariable> variables) {
public void propagateVariables(Collection<InferenceVariable> variables, PsiSubstitutor substitution) {
myInferenceVariables.addAll(variables);
myRestoreNameSubstitution = myRestoreNameSubstitution.putAll(substitution);
}
public PsiType substituteWithInferenceVariables(PsiType type) {
@@ -1826,6 +1834,10 @@ public class InferenceSession {
return myInferenceSubstitution;
}
public PsiSubstitutor getRestoreNameSubstitution() {
return myRestoreNameSubstitution;
}
public InferenceSessionContainer getInferenceSessionContainer() {
return myInferenceSessionContainer;
}

View File

@@ -99,7 +99,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
}
if (callSession != session) {
session.getInferenceSessionContainer().registerNestedSession(callSession);
session.propagateVariables(callSession.getInferenceVariables());
session.propagateVariables(callSession.getInferenceVariables(), callSession.getRestoreNameSubstitution());
if (callSession.isErased()) {
session.setErased();
}
@@ -149,7 +149,7 @@ public class ExpressionCompatibilityConstraint extends InputOutputConstraintForm
if (typeParams != null) {
PsiSubstitutor siteSubstitutor = InferenceSession.chooseSiteSubstitutor(candidateProperties, resolveResult, method);
final InferenceSession callSession = new InferenceSession(typeParams, siteSubstitutor, expression.getManager(), expression);
callSession.propagateVariables(session.getInferenceVariables());
callSession.propagateVariables(session.getInferenceVariables(), session.getRestoreNameSubstitution());
if (method != null) {
final PsiExpression[] args = argumentList.getExpressions();
final PsiParameter[] parameters = method.getParameterList().getParameters();

View File

@@ -2,6 +2,6 @@ class A {{
String.valueOf(<error descr="Cannot resolve symbol 'chars'">chars</error>, 0, 10); // all arguments are highlighted when only chars has a problemij
new String(<error descr="Cannot resolve symbol 'chars'">chars</error>, 0, 10); // highlighting is good here.
String.valueOf<error descr="'valueOf(char[], int, int)' in 'java.lang.String' cannot be applied to '(int, int, int)'">(0, 0, 10)</error>;
String.valueOf(<error descr="'valueOf(char[], int, int)' in 'java.lang.String' cannot be applied to '(int, int, int)'">0</error>, 0, 10);
new String<error descr="Cannot resolve constructor 'String(int, int, int)'">(0, 0, 10)</error>;
}}

View File

@@ -3,7 +3,7 @@ import java.util.List;
class IdeaBugTest {
public void foo(List<Base> base) {
MyCollection.fun<error descr="'fun(java.util.List<? extends Base>, java.util.Comparator<? super Base>)' in 'MyCollection' cannot be applied to '(java.util.List<Base>, SubComparator)'">(base, new SubComparator())</error>;
MyCollection.fun(base, <error descr="'fun(java.util.List<? extends Base>, java.util.Comparator<? super Base>)' in 'MyCollection' cannot be applied to '(java.util.List<Base>, SubComparator)'">new SubComparator()</error>);
}
}

View File

@@ -3,7 +3,7 @@ import java.util.Set;
class Test {
public static void test(Set foo, Matcher<Iterable<String>> matcher) {
assertThat<error descr="'assertThat(Test.Matcher<? super java.util.Set>, java.util.Set)' in 'Test' cannot be applied to '(Test.Matcher<java.lang.Iterable<java.lang.String>>, java.util.Set)'">(matcher, foo)</error>;
assertThat(<error descr="'assertThat(Test.Matcher<? super java.util.Set>, java.util.Set)' in 'Test' cannot be applied to '(Test.Matcher<java.lang.Iterable<java.lang.String>>, java.util.Set)'">matcher</error>, foo);
Matcher<Iterable<String>> b = null;
<error descr="Incompatible types. Found: 'Test.Matcher<java.lang.Iterable<java.lang.String>>', required: 'Test.Matcher<? super java.util.Set>'">Matcher<? super Set> a = b;</error>

View File

@@ -5,6 +5,6 @@ abstract class X {
abstract <T> void copy(List<T> dest, List<? extends T> src);
void foo(List<?> x, List<?> y){
copy<error descr="'copy(java.util.List<capture<?>>, java.util.List<? extends capture<?>>)' in 'X' cannot be applied to '(java.util.List<capture<?>>, java.util.List<capture<?>>)'">(x, y)</error>;
copy(x, <error descr="'copy(java.util.List<capture<?>>, java.util.List<? extends capture<?>>)' in 'X' cannot be applied to '(java.util.List<capture<?>>, java.util.List<capture<?>>)'">y</error>);
}
}

View File

@@ -2,7 +2,7 @@ class Test {
public void doesNotCompile() {
Container<String> container = new Container<String>();
assertThat<error descr="'assertThat(Test.Container<java.lang.String>, Test.Matcher<? super Test.Container<java.lang.String>>)' in 'Test' cannot be applied to '(Test.Container<java.lang.String>, Test.Matcher<Test.Container<capture<? super java.lang.String>>>)'">(container, hasSomething(is("foo")))</error>;
assertThat(container, <error descr="'assertThat(Test.Container<java.lang.String>, Test.Matcher<? super Test.Container<java.lang.String>>)' in 'Test' cannot be applied to '(Test.Container<java.lang.String>, Test.Matcher<Test.Container<capture<? super java.lang.String>>>)'">hasSomething(is("foo"))</error>);
}
public static class Container<T> {}

View File

@@ -1,6 +1,6 @@
class D<T> {
void foo(D<?> x){
bar<error descr="'bar(D<?>, D<? super java.lang.Object>)' in 'D' cannot be applied to '(D<capture<?>>, D<capture<?>>)'">(x,x)</error>;
bar(x,<error descr="'bar(D<?>, D<? super java.lang.Object>)' in 'D' cannot be applied to '(D<capture<?>>, D<capture<?>>)'">x</error>);
}
<T> void bar(D<? extends T> x, D<? super T> y){}
}

View File

@@ -7,7 +7,7 @@ class NestedGenericGoodCodeIsRed {
Number num = null;
satisfiesAllOf(isPositive(), isEqualTo(num));
this.<Number>satisfiesAllOf<error descr="'satisfiesAllOf(NestedGenericGoodCodeIsRed.Predicate<? super java.lang.Number>, NestedGenericGoodCodeIsRed.Predicate<? super java.lang.Number>)' in 'NestedGenericGoodCodeIsRed' cannot be applied to '(NestedGenericGoodCodeIsRed.Predicate<java.lang.Number>, NestedGenericGoodCodeIsRed.Predicate<java.lang.Integer>)'">(isPositive(), isEqualTo(10))</error>;
this.<Number>satisfiesAllOf(isPositive(), <error descr="'satisfiesAllOf(NestedGenericGoodCodeIsRed.Predicate<? super java.lang.Number>, NestedGenericGoodCodeIsRed.Predicate<? super java.lang.Number>)' in 'NestedGenericGoodCodeIsRed' cannot be applied to '(NestedGenericGoodCodeIsRed.Predicate<java.lang.Number>, NestedGenericGoodCodeIsRed.Predicate<java.lang.Integer>)'">isEqualTo(10)</error>);
}

View File

@@ -61,7 +61,7 @@ class VarianceTesting {
class SuperTester <U> {
void go(Acceptor<? super U> acceptor, U u) {
acceptor.accept<error descr="'accept(SuperTester<capture<? super U>>, capture<? super U>)' in 'SuperTester.Acceptor' cannot be applied to '(SuperTester<U>, U)'">(this, u)</error>;
acceptor.accept(<error descr="'accept(SuperTester<capture<? super U>>, capture<? super U>)' in 'SuperTester.Acceptor' cannot be applied to '(SuperTester<U>, U)'">this</error>, u);
}
static class Acceptor <V> {
@@ -142,7 +142,7 @@ class S1 {
}
void bar(List<? extends S1> k) {
f<error descr="'f(java.util.List<capture<? extends S1>>, capture<? extends S1>)' in 'S1' cannot be applied to '(java.util.List<capture<? extends S1>>, S1)'">(k, k.get(0))</error>;
f(k, <error descr="'f(java.util.List<capture<? extends S1>>, capture<? extends S1>)' in 'S1' cannot be applied to '(java.util.List<capture<? extends S1>>, S1)'">k.get(0)</error>);
}
}
@@ -152,7 +152,7 @@ class S2 {
}
void bar(List<? extends S2> k) {
f<error descr="'f(java.util.List<capture<? extends S2>>, java.util.List<capture<? extends S2>>)' in 'S2' cannot be applied to '(java.util.List<capture<? extends S2>>, java.util.List<capture<? extends S2>>)'">(k, k)</error>;
f(k, <error descr="'f(java.util.List<capture<? extends S2>>, java.util.List<capture<? extends S2>>)' in 'S2' cannot be applied to '(java.util.List<capture<? extends S2>>, java.util.List<capture<? extends S2>>)'">k</error>);
}
}
@@ -214,7 +214,7 @@ public static void foo(List<? extends Foo> foos) {
class OtherBug1 {
public static void foo(List<? super Foo> foos) {
final Comparator<Foo> comparator = createComparator();
Collections.sort<error descr="'sort(java.util.List<capture<? super OtherBug1.Foo>>, java.util.Comparator<? super capture<? super OtherBug1.Foo>>)' in 'java.util.Collections' cannot be applied to '(java.util.List<capture<? super OtherBug1.Foo>>, java.util.Comparator<OtherBug1.Foo>)'">(foos, comparator)</error>;
Collections.sort(foos, <error descr="'sort(java.util.List<capture<? super OtherBug1.Foo>>, java.util.Comparator<? super capture<? super OtherBug1.Foo>>)' in 'java.util.Collections' cannot be applied to '(java.util.List<capture<? super OtherBug1.Foo>>, java.util.Comparator<OtherBug1.Foo>)'">comparator</error>);
}
private static Comparator<Foo> createComparator() {

View File

@@ -61,7 +61,7 @@ class VarianceTesting {
class SuperTester <U> {
void go(Acceptor<? super U> acceptor, U u) {
acceptor.accept<error descr="'accept(SuperTester<capture<? super U>>, capture<? super U>)' in 'SuperTester.Acceptor' cannot be applied to '(SuperTester<U>, U)'">(this, u)</error>;
acceptor.accept(<error descr="'accept(SuperTester<capture<? super U>>, capture<? super U>)' in 'SuperTester.Acceptor' cannot be applied to '(SuperTester<U>, U)'">this</error>, u);
}
static class Acceptor <V> {
@@ -87,7 +87,7 @@ class CaptureTest {
}
void foo (Class<? extends Emum<CaptureTest>> clazz) {
Emum.valueOf<error descr="'valueOf(java.lang.Class<T>, java.lang.String)' in 'CaptureTest.Emum' cannot be applied to '(java.lang.Class<capture<? extends CaptureTest.Emum<CaptureTest>>>, java.lang.String)'">(clazz, "CCC")</error>;
Emum.valueOf(<error descr="'valueOf(java.lang.Class<T>, java.lang.String)' in 'CaptureTest.Emum' cannot be applied to '(java.lang.Class<capture<? extends CaptureTest.Emum<CaptureTest>>>, java.lang.String)'">clazz</error>, "CCC");
}
}

View File

@@ -0,0 +1,10 @@
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
class BigNumber {
public <U, V> void toTypeMap(Stream<U> p) {
Function<U, V> map2 = p.collect(Collectors.toMap(Function.identity(<caret>), Function.identity()));
}
}

View File

@@ -16,11 +16,17 @@
package com.intellij.codeInsight.daemon.lambda;
import com.intellij.codeInsight.daemon.LightDaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.testFramework.IdeaTestUtil;
import org.jetbrains.annotations.NonNls;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase {
@NonNls static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/lambda/graphInference";
@@ -399,6 +405,34 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase
doTest();
}
public void testVariableNamesOfNestedCalls() throws Exception {
IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_8, getModule(), getTestRootDisposable());
String filePath = BASE_PATH + "/" + getTestName(false) + ".java";
configureByFile(filePath);
Collection<HighlightInfo> infos = doHighlighting();
List<String> tooltips = new ArrayList<>();
for (HighlightInfo info : infos) {
if (info.getSeverity() == HighlightSeverity.ERROR) {
tooltips.add(info.getToolTip());
}
}
assertTrue(tooltips.contains("<html><body><table border=0><tr><td>" +
"<b>identity(&nbsp;)&nbsp;</b></td><td colspan=1>in <b>Function</b>&nbsp;cannot be applied</td></tr><tr><td>to</td><td><b>()</b>&nbsp;" +
"</td></tr></table><br/>" +
"reason: no instance(s) of type variable(s) K, U exist so that Map&lt;K, U&gt; conforms to Function&lt;U, V&gt;" +
"</body></html>"));
assertTrue(tooltips.contains(
"<html><body><table border=0><tr><td>" +
"<b>identity(&nbsp;)&nbsp;</b></td><td colspan=1>in <b>Function</b>&nbsp;cannot be applied</td></tr><tr><td>to</td><td><b>()</b>&nbsp;" +
"</td></tr></table><br/>" +
"reason: no instance(s) of type variable(s) K, U exist so that Map&lt;K, U&gt; conforms to Function&lt;U, V&gt;" +
"</body></html>"));
}
private void doTest() throws Exception {
doTest(false);
}