diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java index e34b91be0fc1..b50608d338db 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiReferenceExpressionImpl.java @@ -6,6 +6,8 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.RecursionGuard; +import com.intellij.openapi.util.RecursionManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.pom.java.LanguageLevel; @@ -242,8 +244,11 @@ public class PsiReferenceExpressionImpl extends ExpressionPsiElement implements protected void elementFinished(@NotNull PsiElement element) { if (!(element instanceof PsiReferenceExpressionImpl)) return; PsiReferenceExpressionImpl chainedQualifier = (PsiReferenceExpressionImpl)element; - ourQualifierCache.get() - .put(chainedQualifier, resolveCache.resolveWithCaching(chainedQualifier, INSTANCE, false, false, containingFile)); + RecursionGuard.StackStamp stamp = RecursionManager.markStack(); + ResolveResult[] res = resolveCache.resolveWithCaching(chainedQualifier, INSTANCE, false, false, containingFile); + if (stamp.mayCacheNow()) { + ourQualifierCache.get().put(chainedQualifier, res); + } } // walk only qualifiers, not their argument and other associated stuff diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/ForbidCachingForAllQualifiersWhenDependOnThreadLocalTypes.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/ForbidCachingForAllQualifiersWhenDependOnThreadLocalTypes.java new file mode 100644 index 000000000000..0d5dac9b3687 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/expressions/ForbidCachingForAllQualifiersWhenDependOnThreadLocalTypes.java @@ -0,0 +1,10 @@ +import java.util.*; +import java.util.stream.*; + +class MyTest { + void m(Map directiveInfos){ + Map lowerCaseDirectiveInfos = directiveInfos.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey().toLowerCase(Locale.ENGLISH), + entry -> entry.getValue(), (a, b) -> b)); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/Java8ExpressionsCheckTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/Java8ExpressionsCheckTest.java index a7fc017cfa27..a4b9cf19d2e5 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/Java8ExpressionsCheckTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/Java8ExpressionsCheckTest.java @@ -38,6 +38,25 @@ public class Java8ExpressionsCheckTest extends LightDaemonAnalyzerTestCase { doTestAllMethodCallExpressions(); } + public void testForbidCachingForAllQualifiersWhenDependOnThreadLocalTypes() { + configure(); + PsiMethodCallExpression getKeyCall = + PsiTreeUtil.getParentOfType(getFile().findElementAt(getEditor().getCaretModel().getOffset()), PsiMethodCallExpression.class); + + PsiLambdaExpression l1 = PsiTreeUtil.getParentOfType(getKeyCall, PsiLambdaExpression.class); + PsiLambdaExpression l2 = (PsiLambdaExpression)PsiTreeUtil.skipWhitespacesForward(l1.getNextSibling()); + + //ensure chained method calls inside lambda are resolved + //including entry.getKey() + //these calls depend on ThreadLocalTypes and should not be cached + //note that their types should not be cached as well + l2.getFunctionalInterfaceType(); + + //check that getKey was not cached in the line above + PsiType type = getKeyCall.getType(); + assertEquals(CommonClassNames.JAVA_LANG_STRING, type.getCanonicalText()); + } + public void testLambdaParameterTypeDetection() { configure(); PsiReferenceExpression referenceExpression =