new inference: collect additional constraints from lambda return expressions when...
authorAnna Kozlova <anna.kozlova@jetbrains.com>
Thu, 3 Sep 2015 13:10:54 +0000 (16:10 +0300)
committerAnna Kozlova <anna.kozlova@jetbrains.com>
Thu, 3 Sep 2015 14:22:22 +0000 (17:22 +0300)
java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/AdditionalConstraintsBasedOnLambdaResolution.java [new file with mode: 0644]
java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/CacheUnresolvedMethods3.java [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Java8ExpressionsCheckTest.java

index 924224e3e92c45fc31cf402d9809aef39985966d..e028f0b482cda828f676c7c57ca15c2b968660e1 100644 (file)
@@ -345,7 +345,10 @@ public class InferenceSession {
           if (calledMethod != null && PsiPolyExpressionUtil.isMethodCallPolyExpression(arg, calledMethod)) {
             collectAdditionalConstraints(additionalConstraints, (PsiCallExpression)arg);
           }
-        } else if (arg instanceof PsiLambdaExpression && !isProperType(retrieveNonPrimitiveEqualsBounds(myInferenceVariables).substitute(parameterType))) {
+        }
+        else if (arg instanceof PsiLambdaExpression && 
+                 isPertinentToApplicability(arg, parentMethod) && 
+                 !isProperType(retrieveNonPrimitiveEqualsBounds(myInferenceVariables).substitute(parameterType))) {
           collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)arg, parameterType);
         }
       }
@@ -354,21 +357,7 @@ public class InferenceSession {
 
   private static PsiMethod getCalledMethod(PsiCallExpression arg) {
     final PsiExpressionList argumentList = arg.getArgumentList();
-    if (argumentList == null || argumentList.getExpressions().length == 0) {
-      return null;
-    }
-
-    boolean found = false;
-    for (PsiExpression expression : argumentList.getExpressions()) {
-      expression = PsiUtil.skipParenthesizedExprDown(expression);
-      if (expression instanceof PsiConditionalExpression ||
-          expression instanceof PsiCallExpression ||
-          expression instanceof PsiFunctionalExpression) {
-        found = true;
-        break;
-      }
-    }
-    if (!found) {
+    if (argumentList == null) {
       return null;
     }
 
@@ -1121,7 +1110,7 @@ public class InferenceSession {
       }
 
       for (ConstraintFormula formula : subset) {
-        if (!processOneConstraint(formula, siteSubstitutor, varsToResolve)) return false;
+        if (!processOneConstraint(formula, siteSubstitutor, varsToResolve, additionalConstraints)) return false;
       }
     }
     return true;
@@ -1137,7 +1126,10 @@ public class InferenceSession {
     }
   }
 
-  private boolean processOneConstraint(ConstraintFormula formula, PsiSubstitutor siteSubstitutor, Set<InferenceVariable> varsToResolve) {
+  private boolean processOneConstraint(ConstraintFormula formula,
+                                       PsiSubstitutor siteSubstitutor,
+                                       Set<InferenceVariable> varsToResolve,
+                                       Set<ConstraintFormula> additionalConstraints) {
     if (formula instanceof ExpressionCompatibilityConstraint) {
       final PsiExpression expression = ((ExpressionCompatibilityConstraint)formula).getExpression();
       final PsiCallExpression callExpression = PsiTreeUtil.getParentOfType(expression, PsiCallExpression.class, false);
@@ -1169,6 +1161,16 @@ public class InferenceSession {
       if (!repeatInferencePhases(true)) {
         return false;
       }
+
+      if (formula instanceof ExpressionCompatibilityConstraint) {
+        PsiExpression expression = ((ExpressionCompatibilityConstraint)formula).getExpression();
+        if (expression instanceof PsiLambdaExpression) {
+          PsiType parameterType = ((ExpressionCompatibilityConstraint)formula).getT();
+          if (!isProperType(parameterType)) {
+            collectLambdaReturnExpression(additionalConstraints, (PsiLambdaExpression)expression, parameterType);
+          }
+        }
+      }
     }
     finally {
       LambdaUtil.ourFunctionTypes.set(null);
@@ -1178,7 +1180,7 @@ public class InferenceSession {
 
   private Set<ConstraintFormula> buildSubset(final Set<ConstraintFormula> additionalConstraints) {
 
-    final Set<ConstraintFormula> subset = new LinkedHashSet<ConstraintFormula>();
+    Set<ConstraintFormula> subset = new LinkedHashSet<ConstraintFormula>();
     final Set<InferenceVariable> outputVariables = new HashSet<InferenceVariable>();
     for (ConstraintFormula constraint : additionalConstraints) {
       if (constraint instanceof InputOutputConstraintFormula) {
@@ -1190,6 +1192,7 @@ public class InferenceSession {
       }
     }
 
+    Set<ConstraintFormula> noInputVariables = new LinkedHashSet<ConstraintFormula>();
     for (ConstraintFormula constraint : additionalConstraints) {
       if (constraint instanceof InputOutputConstraintFormula) {
         final Set<InferenceVariable> inputVariables = ((InputOutputConstraintFormula)constraint).getInputVariables(this);
@@ -1217,10 +1220,15 @@ public class InferenceSession {
           }
           if (!dependsOnOutput) {
             subset.add(constraint);
+
+            if (inputVariables.isEmpty()) {
+              noInputVariables.add(constraint);
+            }
           }
         }
         else {
           subset.add(constraint);
+          noInputVariables.add(constraint);
         }
       }
       else {
@@ -1230,6 +1238,10 @@ public class InferenceSession {
     if (subset.isEmpty()) {
       subset.add(additionalConstraints.iterator().next()); //todo choose one constraint
     }
+    
+    if (!noInputVariables.isEmpty()) {
+      subset = noInputVariables;
+    }
 
     additionalConstraints.removeAll(subset);
     return subset;
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/AdditionalConstraintsBasedOnLambdaResolution.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/AdditionalConstraintsBasedOnLambdaResolution.java
new file mode 100644 (file)
index 0000000..b6ab78a
--- /dev/null
@@ -0,0 +1,23 @@
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+class Test {
+  public void testIt()
+  {
+    Map<Integer, String> innerMap = new HashMap<>();
+    innerMap.put(2, "abc");
+    Map<Long, Map<Integer, String>> outerMap = new HashMap<>();
+    outerMap.put(1L, innerMap);
+    Map<Long, Map<Integer, String>> transformedMap = outerMap.entrySet().stream()
+      .collect(Collectors.toMap(
+        Map.Entry::getKey,
+        m -> m.getValue().entrySet().stream()
+          .collect(Collectors.toMap(
+            Map.Entry::getKey,
+            v -> v.getValue().toUpperCase()))));
+  }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/CacheUnresolvedMethods3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/CacheUnresolvedMethods3.java
new file mode 100644 (file)
index 0000000..a3607e6
--- /dev/null
@@ -0,0 +1,22 @@
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+
+import java.util.Map;
+import java.util.stream.Collector;
+
+class FooBar {
+
+  void p(Map<String, String> m) {
+    m.entrySet().stream()
+      .collect(Collector.of(() -> new HashMap<>(),
+                            (a, e) -> a.put(e.getValue(), e.getKey()),
+                            (l, r) -> {
+                              l.put<caret>All(r);
+                              return l;
+                            }));
+  }
+}
index 60de9b9b87a3db8ecfee35b8dc21bda0700beb08..07be9f2b553f129ee088f20a8701757dd44a7898 100644 (file)
@@ -54,6 +54,10 @@ public class Java8ExpressionsCheckTest extends LightDaemonAnalyzerTestCase {
   public void testCacheUnresolvedMethods2() throws Exception {
     doTestCachedUnresolved();
   }
+  
+  public void testCacheUnresolvedMethods3() throws Exception {
+    doTestCachedUnresolved();
+  }
 
   private void doTestCachedUnresolved() {
     configureByFile(BASE_PATH + "/" + getTestName(false) + ".java");
@@ -80,6 +84,10 @@ public class Java8ExpressionsCheckTest extends LightDaemonAnalyzerTestCase {
     }
   }
 
+  public void testAdditionalConstraintsBasedOnLambdaResolution() throws Exception {
+    doTestAllMethodCallExpressions();
+  }
+
   private void doTestAllMethodCallExpressions() {
     configureByFile(BASE_PATH + "/" + getTestName(false) + ".java");
     final Collection<PsiCallExpression> methodCallExpressions = PsiTreeUtil.findChildrenOfType(getFile(), PsiCallExpression.class);