From f3e5fe20951768cbd03f055bf69ecc17fda3c769 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 7 Jul 2017 07:56:30 +0200 Subject: [PATCH] let psi file become invalid when its original file is invalid because otherwise clients call getOriginalFile and end up with invalid PSI --- .../com/intellij/java/psi/ClsPsiTest.java | 20 +++++++++++++++++++ .../psi/PsiInvalidElementAccessException.java | 19 ++++++++++++++---- .../intellij/psi/impl/source/PsiFileImpl.java | 3 +++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/java/java-tests/testSrc/com/intellij/java/psi/ClsPsiTest.java b/java/java-tests/testSrc/com/intellij/java/psi/ClsPsiTest.java index 5f89c9fc4b04..cba3d4acbf3e 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/ClsPsiTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/ClsPsiTest.java @@ -16,6 +16,7 @@ package com.intellij.java.psi; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.IoTestUtil; @@ -461,4 +462,23 @@ public class ClsPsiTest extends LightIdeaTestCase { LeakHunter.checkLeak(dbl, ClassElement.class, element -> element.getPsi().getUserData(ClsElementImpl.COMPILED_ELEMENT) == dbl); assertFalse(hash1 == ((ClsClassImpl)dbl).getMirror().hashCode()); } + + public void testMirrorBecomesInvalidTogetherWithCls() throws IOException { + File testFile = IoTestUtil.createTestFile("TestClass.class"); + File file1 = new File(PathManagerEx.getTestDataPath() + TEST_DATA_PATH + "/1_TestClass.class"); + FileUtil.copy(file1, testFile); + VirtualFile copyVFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(testFile); + + ClsFileImpl clsFile = (ClsFileImpl)PsiManager.getInstance(getProject()).findFile(copyVFile); + PsiElement mirror = clsFile.getMirror(); + + assertTrue(clsFile.isValid()); + assertTrue(mirror.isValid()); + + WriteAction.run(() -> copyVFile.delete(this)); + assertFalse(clsFile.isValid()); + assertFalse(mirror.isValid()); + assertTrue(PsiInvalidElementAccessException.findOutInvalidationReason(mirror) + .contains(PsiInvalidElementAccessException.findOutInvalidationReason(clsFile))); + } } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/psi/PsiInvalidElementAccessException.java b/platform/core-api/src/com/intellij/psi/PsiInvalidElementAccessException.java index 01c9ac0c96c0..ae07b491eeed 100644 --- a/platform/core-api/src/com/intellij/psi/PsiInvalidElementAccessException.java +++ b/platform/core-api/src/com/intellij/psi/PsiInvalidElementAccessException.java @@ -107,7 +107,12 @@ public class PsiInvalidElementAccessException extends RuntimeException implement @Nullable private static Object getPsiInvalidationTrace(@NotNull PsiElement element) { Object trace = getInvalidationTrace(element); - return trace != null || element instanceof PsiFile ? trace : findInvalidationTrace(element.getNode()); + if (trace != null) return trace; + + if (element instanceof PsiFile) { + return getInvalidationTrace(((PsiFile)element).getOriginalFile()); + } + return findInvalidationTrace(element.getNode()); } private static String getMessageWithReason(@NotNull PsiElement element, @@ -120,7 +125,7 @@ public class PsiInvalidElementAccessException extends RuntimeException implement trace != null ? "see attachment" : "no info"; try { - reason += " because: " + reason(element); + reason += " because: " + findOutInvalidationReason(element); } catch (PsiInvalidElementAccessException ignore) { } @@ -161,7 +166,7 @@ public class PsiInvalidElementAccessException extends RuntimeException implement @NonNls @NotNull - private static String reason(@NotNull PsiElement root) { + public static String findOutInvalidationReason(@NotNull PsiElement root) { if (root == PsiUtilCore.NULL_PSI_ELEMENT) return "NULL_PSI_ELEMENT"; PsiElement element = root instanceof PsiFile ? root : root.getParent(); @@ -170,6 +175,7 @@ public class PsiInvalidElementAccessException extends RuntimeException implement if (root instanceof StubBasedPsiElement) { StubElement stub = ((StubBasedPsiElement)root).getStub(); while (stub != null) { + //noinspection StringConcatenationInLoop m += "\n each stub=" + stub; if (stub instanceof PsiFileStub) { m += "; fileStub.psi=" + stub.getPsi() + "; reason=" + ((PsiFileStub)stub).getInvalidationReason(); @@ -190,10 +196,15 @@ public class PsiInvalidElementAccessException extends RuntimeException implement if (!provider.isPhysical()) { PsiElement context = file.getContext(); if (context != null && !context.isValid()) { - return "invalid context: " + reason(context); + return "invalid context: " + findOutInvalidationReason(context); } } + PsiFile original = file.getOriginalFile(); + if (original != file && !original.isValid()) { + return "invalid original: " + findOutInvalidationReason(original); + } + PsiManager manager = file.getManager(); if (manager.getProject().isDisposed()) return "project is disposed"; diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index d792fe199102..80407284793f 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -162,6 +162,9 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF // but some VFS listeners receive the same events before that and ask PsiFile.isValid return false; } + if (myOriginalFile != null && !myOriginalFile.isValid()) { + return false; + } return !myInvalidated; }