diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java index 11cdf6305f98..38eb67cdd892 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/JavaResolveCache.java @@ -27,13 +27,17 @@ import com.intellij.openapi.util.NotNullLazyKey; import com.intellij.psi.*; import com.intellij.psi.impl.AnyPsiChangeListener; import com.intellij.psi.impl.DebugUtil; +import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.source.PsiClassReferenceType; +import com.intellij.psi.impl.source.tree.TreeElement; import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.ConcurrencyUtil; import com.intellij.util.Function; import com.intellij.util.Processor; import com.intellij.util.containers.ConcurrentWeakHashMap; +import com.intellij.util.containers.WeakHashMap; +import com.intellij.util.containers.WeakList; import com.intellij.util.messages.MessageBus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,6 +57,9 @@ public class JavaResolveCache { private final ConcurrentMap myCalculatedTypes = new ConcurrentWeakHashMap(); private final ConcurrentMap myCachedReferencesInPsiTypes = new ConcurrentWeakHashMap(); + // e.g. given FileOutputStream os, os2; + // PsiJavaCodeReferenceElement("FileOutputStream") -> [ PsiReferenceExpression("os"), PsiReferenceExpression("os2") ] + private final Map> myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere = new WeakHashMap>(); private final Map myVarToConstValueMapPhysical; private final Map myVarToConstValueMapNonPhysical; @@ -78,6 +85,7 @@ public class JavaResolveCache { private void clearCaches(boolean isPhysical) { myCalculatedTypes.clear(); myCachedReferencesInPsiTypes.clear(); + myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere.clear(); if (isPhysical) { myVarToConstValueMapPhysical.clear(); } @@ -96,8 +104,15 @@ public class JavaResolveCache { if (type == null) { type = TypeConversionUtil.NULL_TYPE; } - type = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, expr, type); + PsiType stored = ConcurrencyUtil.cacheOrGet(myCalculatedTypes, expr, type); + + if (stored == type && DebugUtil.DO_EXPENSIVE_CHECKS) { + registerDiagnosticsHooks(expr, type); + } + + type = stored; } + if (!type.isValid()) { if (expr.isValid()) { PsiJavaCodeReferenceElement refInside = type instanceof PsiClassReferenceType ? ((PsiClassReferenceType)type).getReference() : null; @@ -109,44 +124,86 @@ public class JavaResolveCache { } } - if (DebugUtil.DO_EXPENSIVE_CHECKS) { - if (type instanceof PsiClassReferenceType) { - PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference(); - ConcurrencyUtil.cacheOrGet(myCachedReferencesInPsiTypes, reference, type); - DebugUtil.trackInvalidation(reference, "Reference inside PsiClassReferenceType was invalidated", new Processor() { - @Override - public boolean process(PsiElement element) { - PsiType cached = myCalculatedTypes.get(element); - if (cached != null) { - LOG.error(element + " (inside ref) is invalid and yet it is still cached: " + cached); - } - PsiType cachedRef = myCachedReferencesInPsiTypes.get(element); - if (cachedRef != null) { - LOG.error(element + " (inside ref) is invalid and yet it is still cached in ref cache: " + cachedRef); - } - return true; - } - }); + return type == TypeConversionUtil.NULL_TYPE ? null : type; + } + private void registerDiagnosticsHooks(T expr, PsiType type) { + if (type instanceof PsiClassReferenceType) { + PsiJavaCodeReferenceElement reference = ((PsiClassReferenceType)type).getReference(); + ConcurrencyUtil.cacheOrGet(myCachedReferencesInPsiTypes, reference, type); + synchronized (myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere) { + WeakList refsTo = myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere.get(reference); + if (refsTo==null) { + refsTo = new WeakList(); + myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere.put(reference, refsTo); + } + refsTo.add(expr); } - DebugUtil.trackInvalidation(expr, "Expression invalidated", new Processor() { + final PsiFile dummyHolder = reference.getContainingFile(); + if (dummyHolder != null && !dummyHolder.isPhysical()) { + PsiElement physicalContext = dummyHolder.getContext(); + PsiFile physicalFile; + if (physicalContext != null && + (physicalFile = physicalContext.getContainingFile()) != null && + physicalFile.getVirtualFile() != null && + !((PsiManagerEx)PsiManager.getInstance(dummyHolder.getProject())).isAssertOnFileLoading(physicalFile.getVirtualFile())) { + DebugUtil.trackInvalidation(physicalContext, "dummy holder was invalidated", new Processor() { + @Override + public boolean process(PsiElement element) { + DebugUtil.onInvalidated((TreeElement)dummyHolder.getNode()); + return true; + } + }); + } + } + + DebugUtil.trackInvalidation(reference, "Reference inside PsiClassReferenceType was invalidated", new Processor() { @Override public boolean process(PsiElement element) { PsiType cached = myCalculatedTypes.get(element); if (cached != null) { - LOG.error(element + " is invalid and yet it is still cached: " + cached); + LOG.error(element + " (inside ref) is invalid and yet it is still cached: " + cached); } - PsiType cachedRef = myCachedReferencesInPsiTypes.get(element); if (cachedRef != null) { - LOG.error(element + " is invalid and yet it is still cached (inside PsiType): " + cachedRef); + LOG.error(element + " (inside ref) is invalid and yet it is still cached in ref cache: " + cachedRef); } + + + synchronized (myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere) { + WeakList refsTo = myCachedReferenceIn_PsiClassReferenceType_To_ListOfReferencesOfThisType_CachedHere.get(element); + if (refsTo != null) { + for (PsiElement ref : refsTo) { + PsiType cachedT = myCalculatedTypes.get(ref); + if (cachedT != null && !cachedT.isValid()) { + LOG.error("During invalidation of " + element + " ("+element.getClass()+")"+ + " cached type " + cachedT + " of the ref "+ref+" ("+ref.getClass()+")"+ + " became invalid and yet it is still cached" + ); + } + } + } + } + return true; } }); } + DebugUtil.trackInvalidation(expr, "Expression invalidated", new Processor() { + @Override + public boolean process(PsiElement element) { + PsiType cached = myCalculatedTypes.get(element); + if (cached != null) { + LOG.error(element + " is invalid and yet it is still cached: " + cached); + } - return type == TypeConversionUtil.NULL_TYPE ? null : type; + PsiType cachedRef = myCachedReferencesInPsiTypes.get(element); + if (cachedRef != null) { + LOG.error(element + " is invalid and yet it is still cached (inside PsiType): " + cachedRef); + } + return true; + } + }); } @Nullable diff --git a/platform/core-impl/src/com/intellij/psi/impl/DebugUtil.java b/platform/core-impl/src/com/intellij/psi/impl/DebugUtil.java index 90aa13fdb388..c8cb5460e197 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DebugUtil.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DebugUtil.java @@ -490,7 +490,7 @@ public class DebugUtil { } public static void onInvalidated(@NotNull TreeElement treeElement) { - treeElement.acceptTree(new RecursiveTreeElementWalkingVisitor() { + treeElement.acceptTree(new RecursiveTreeElementWalkingVisitor(false) { @Override protected void visitNode(TreeElement element) { List>> callbacks = element.getUserData(TRACK_INVALIDATION_KEY); @@ -501,6 +501,7 @@ public class DebugUtil { if (psi != null) callback.process(psi); } } + super.visitNode(element); } }); }