From b816c182f012a584638fca3f731c564cd2fd7d65 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 23 Nov 2016 21:59:44 +0300 Subject: [PATCH] Try to avoid loading ast for decompiled files (KT-14804) #KT-14804 Fixed --- .../navigation/findDecompiledDeclaration.kt | 19 ++++++++ .../kotlin/idea/test/AstAccessControl.kt | 43 +++++++++++++------ .../resolve/referenceWithLib/overloadFun.kt | 10 +++++ .../referenceWithLib/overloadFunSrc/lib.kt | 7 +++ .../resolve/AbstractReferenceResolveTest.kt | 9 ++-- .../AbstractReferenceResolveWithLibTest.java | 35 +++++++++++++++ .../ReferenceResolveWithLibTestGenerated.java | 6 +++ 7 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 idea/testData/resolve/referenceWithLib/overloadFun.kt create mode 100644 idea/testData/resolve/referenceWithLib/overloadFunSrc/lib.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt index 597d3f30603..642ec7e28c7 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/navigation/findDecompiledDeclaration.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.kotlin.JvmBuiltInsSettings import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.KtDeclarationContainer import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRendererModifier import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -147,6 +148,24 @@ object ByDescriptorIndexer : DecompiledTextIndexer { return classOrObject?.getPrimaryConstructor() ?: classOrObject } + if (!file.isContentsLoaded) { + if (original is MemberDescriptor) { + val declarationContainer: KtDeclarationContainer? = when { + DescriptorUtils.isTopLevelDeclaration(original) -> file + original.containingDeclaration is ClassDescriptor -> + getDeclarationForDescriptor(original.containingDeclaration as ClassDescriptor, file) as? KtClassOrObject + else -> null + } + + if (declarationContainer != null) { + val descriptorName = original.name.asString() + val singleOrNull = declarationContainer.declarations.singleOrNull { it.name == descriptorName } + if (singleOrNull != null) { + return singleOrNull + } + } + } + } return file.getDeclaration(this, original.toStringKey()) ?: run { if (descriptor !is ClassDescriptor) return null diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/AstAccessControl.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/AstAccessControl.kt index 44b5b81021f..7b980b9fd04 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/AstAccessControl.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/AstAccessControl.kt @@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileFilter import com.intellij.psi.PsiManager import com.intellij.psi.impl.PsiManagerImpl +import com.intellij.testFramework.fixtures.CodeInsightTestFixture import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.test.InTextDirectivesUtils import kotlin.test.fail @@ -48,15 +49,16 @@ object AstAccessControl { shouldFail: Boolean, allowedFiles: List, project: Project, disposable: Disposable, testBody: () -> Unit ) { - setFilter(allowedFiles, disposable, project) - performTest(shouldFail, testBody) + val filter = wrapWithDirectiveAllow { file -> + file.fileType != KotlinFileType.INSTANCE || file in allowedFiles + } + + execute(shouldFail, project, disposable, filter, testBody) } - private fun setFilter(allowedFiles: List, disposable: Disposable, project: Project) { - val manager = (PsiManager.getInstance(project) as PsiManagerImpl) - val filter = VirtualFileFilter { - file -> - if (file!!.fileType != KotlinFileType.INSTANCE || file in allowedFiles) { + fun wrapWithDirectiveAllow(allowedFiles: (VirtualFile) -> Boolean): (VirtualFile) -> Boolean { + return { file -> + if (allowedFiles(file)) { false } else { @@ -64,23 +66,38 @@ object AstAccessControl { !InTextDirectivesUtils.isDirectiveDefined(text, ALLOW_AST_ACCESS_DIRECTIVE) } } - manager.setAssertOnFileLoadingFilter(filter, disposable) } - private fun performTest(shouldFail: Boolean, testBody: () -> Unit) { + fun execute(shouldFail: Boolean, disposable: Disposable, fixture: CodeInsightTestFixture, testBody: () -> T): T? { + return execute(shouldFail, fixture.project, disposable, { file -> file != fixture.file.virtualFile }, testBody) + } + + private fun execute(shouldFail: Boolean, project: Project, disposable: Disposable, + forbidAstAccessFilter: (VirtualFile) -> Boolean, testBody: () -> T): T? { + val manager = (PsiManager.getInstance(project) as PsiManagerImpl) + + manager.setAssertOnFileLoadingFilter(VirtualFileFilter { file -> forbidAstAccessFilter(file) }, disposable) + try { - testBody() + val result = testBody() if (shouldFail) { fail("This failure means that that a test that should fail (by triggering ast switch) in fact did not.\n" + - "This could happen for the following reasons:\n" + - "1. This kind of operation no longer trigger ast switch, choose better indicator test case." + - "2. Test is now misconfigured and no longer checks for ast switch, reconfigure the test.") + "This could happen for the following reasons:\n" + + "1. This kind of operation no longer trigger ast switch, choose better indicator test case." + + "2. Test is now misconfigured and no longer checks for ast switch, reconfigure the test.") } + + return result } catch (e: Throwable) { if (!shouldFail) { throw e } } + finally { + manager.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE, disposable) + } + + return null } } \ No newline at end of file diff --git a/idea/testData/resolve/referenceWithLib/overloadFun.kt b/idea/testData/resolve/referenceWithLib/overloadFun.kt new file mode 100644 index 00000000000..c5fe9fb46d4 --- /dev/null +++ b/idea/testData/resolve/referenceWithLib/overloadFun.kt @@ -0,0 +1,10 @@ +package test + +import dependency.* + +fun foo() { + test("") +} + +// ALLOW_AST_ACCESS +// REF: (dependency).test(kotlin.String) \ No newline at end of file diff --git a/idea/testData/resolve/referenceWithLib/overloadFunSrc/lib.kt b/idea/testData/resolve/referenceWithLib/overloadFunSrc/lib.kt new file mode 100644 index 00000000000..ac5185600f2 --- /dev/null +++ b/idea/testData/resolve/referenceWithLib/overloadFunSrc/lib.kt @@ -0,0 +1,7 @@ +package dependency + +fun test(a: Int) { +} + +fun test(a: String) { +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveTest.kt index 3ea48948b26..25a50394c8a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveTest.kt @@ -58,7 +58,7 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt protected fun doSingleResolveTest() { forEachCaret { index, offset -> val expectedResolveData = readResolveData(myFixture.file.text, index, refMarkerText) - val psiReference = myFixture.file.findReferenceAt(offset) + val psiReference = wrapReference(myFixture.file.findReferenceAt(offset)) checkReferenceResolve(expectedResolveData, offset, psiReference) { checkResolvedTo(it) } } } @@ -67,6 +67,9 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt // do nothing } + open fun wrapReference(reference: PsiReference?): PsiReference? = reference + open fun wrapReference(reference: PsiPolyVariantReference): PsiPolyVariantReference = reference + protected fun doMultiResolveTest() { forEachCaret { index, offset -> val expectedReferences = getExpectedReferences(myFixture.file.text, index, refMarkerText) @@ -75,7 +78,7 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt assertTrue(psiReference is PsiPolyVariantReference) psiReference as PsiPolyVariantReference - val results = psiReference.multiResolve(true) + val results = wrapReference(psiReference).multiResolve(true) val actualResolvedTo = Lists.newArrayList() for (result in results) { @@ -140,7 +143,7 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt } else { if (!expectedResolveData.shouldBeUnresolved()) { - assertNull("Element $psiReference wasn't resolved to anything, but $expectedString was expected", expectedString) + assertNull("Element $psiReference (${psiReference.element.text}) wasn't resolved to anything, but $expectedString was expected", expectedString) } } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveWithLibTest.java b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveWithLibTest.java index 98d62bcaf43..d03f531d2ac 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveWithLibTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/resolve/AbstractReferenceResolveWithLibTest.java @@ -16,9 +16,16 @@ package org.jetbrains.kotlin.idea.resolve; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiReference; +import com.intellij.psi.impl.source.resolve.reference.impl.PsiDelegateReference; import com.intellij.testFramework.LightProjectDescriptor; +import kotlin.jvm.functions.Function0; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.idea.test.AstAccessControl; import org.jetbrains.kotlin.idea.test.JdkAndMockLibraryProjectDescriptor; import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; +import org.jetbrains.kotlin.test.InTextDirectivesUtils; public abstract class AbstractReferenceResolveWithLibTest extends AbstractReferenceResolveTest { private static final String TEST_DATA_PATH = PluginTestCaseBase.getTestDataPathBase() + "/resolve/referenceWithLib"; @@ -30,4 +37,32 @@ public abstract class AbstractReferenceResolveWithLibTest extends AbstractRefere } return new JdkAndMockLibraryProjectDescriptor(TEST_DATA_PATH + "/" + getTestName(true) + "Src", false, true, false, false); } + + @Nullable + @Override + public PsiReference wrapReference(@Nullable final PsiReference reference) { + if (reference == null) return null; + + if (InTextDirectivesUtils.isDirectiveDefined(myFixture.getFile().getText(), AstAccessControl.INSTANCE.getALLOW_AST_ACCESS_DIRECTIVE())) { + return reference; + } + + return new PsiDelegateReference(reference) { + @Nullable + @Override + public PsiElement resolve() { + return AstAccessControl.INSTANCE.execute(false, myTestRootDisposable, myFixture, new Function0() { + @Override + public PsiElement invoke() { + return reference.resolve(); + } + }); + } + + @Override + public String toString() { + return reference.toString(); + } + }; + } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/resolve/ReferenceResolveWithLibTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/resolve/ReferenceResolveWithLibTestGenerated.java index 143466ba594..03541b0ac74 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/resolve/ReferenceResolveWithLibTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/resolve/ReferenceResolveWithLibTestGenerated.java @@ -78,6 +78,12 @@ public class ReferenceResolveWithLibTestGenerated extends AbstractReferenceResol doTest(fileName); } + @TestMetadata("overloadFun.kt") + public void testOverloadFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/referenceWithLib/overloadFun.kt"); + doTest(fileName); + } + @TestMetadata("overridingFunctionWithSamAdapter.kt") public void testOverridingFunctionWithSamAdapter() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/referenceWithLib/overridingFunctionWithSamAdapter.kt");