Try to avoid loading ast for decompiled files (KT-14804)

#KT-14804 Fixed
This commit is contained in:
Nikolay Krasko
2016-11-23 21:59:44 +03:00
parent 68df8c55e7
commit b816c182f0
7 changed files with 113 additions and 16 deletions
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.load.kotlin.JvmBuiltInsSettings
import org.jetbrains.kotlin.psi.KtCallableDeclaration import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtDeclarationContainer
import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.DescriptorRendererModifier import org.jetbrains.kotlin.renderer.DescriptorRendererModifier
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -147,6 +148,24 @@ object ByDescriptorIndexer : DecompiledTextIndexer<String> {
return classOrObject?.getPrimaryConstructor() ?: classOrObject 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 { return file.getDeclaration(this, original.toStringKey()) ?: run {
if (descriptor !is ClassDescriptor) return null if (descriptor !is ClassDescriptor) return null
@@ -23,6 +23,7 @@ import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileFilter import com.intellij.openapi.vfs.VirtualFileFilter
import com.intellij.psi.PsiManager import com.intellij.psi.PsiManager
import com.intellij.psi.impl.PsiManagerImpl import com.intellij.psi.impl.PsiManagerImpl
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.test.InTextDirectivesUtils import org.jetbrains.kotlin.test.InTextDirectivesUtils
import kotlin.test.fail import kotlin.test.fail
@@ -48,15 +49,16 @@ object AstAccessControl {
shouldFail: Boolean, allowedFiles: List<VirtualFile>, shouldFail: Boolean, allowedFiles: List<VirtualFile>,
project: Project, disposable: Disposable, testBody: () -> Unit project: Project, disposable: Disposable, testBody: () -> Unit
) { ) {
setFilter(allowedFiles, disposable, project) val filter = wrapWithDirectiveAllow { file ->
performTest(shouldFail, testBody) file.fileType != KotlinFileType.INSTANCE || file in allowedFiles
}
execute(shouldFail, project, disposable, filter, testBody)
} }
private fun setFilter(allowedFiles: List<VirtualFile>, disposable: Disposable, project: Project) { fun wrapWithDirectiveAllow(allowedFiles: (VirtualFile) -> Boolean): (VirtualFile) -> Boolean {
val manager = (PsiManager.getInstance(project) as PsiManagerImpl) return { file ->
val filter = VirtualFileFilter { if (allowedFiles(file)) {
file ->
if (file!!.fileType != KotlinFileType.INSTANCE || file in allowedFiles) {
false false
} }
else { else {
@@ -64,23 +66,38 @@ object AstAccessControl {
!InTextDirectivesUtils.isDirectiveDefined(text, ALLOW_AST_ACCESS_DIRECTIVE) !InTextDirectivesUtils.isDirectiveDefined(text, ALLOW_AST_ACCESS_DIRECTIVE)
} }
} }
manager.setAssertOnFileLoadingFilter(filter, disposable)
} }
private fun performTest(shouldFail: Boolean, testBody: () -> Unit) { fun <T : Any> execute(shouldFail: Boolean, disposable: Disposable, fixture: CodeInsightTestFixture, testBody: () -> T): T? {
return execute(shouldFail, fixture.project, disposable, { file -> file != fixture.file.virtualFile }, testBody)
}
private fun <T : Any> 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 { try {
testBody() val result = testBody()
if (shouldFail) { if (shouldFail) {
fail("This failure means that that a test that should fail (by triggering ast switch) in fact did not.\n" + 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" + "This could happen for the following reasons:\n" +
"1. This kind of operation no longer trigger ast switch, choose better indicator test case." + "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.") "2. Test is now misconfigured and no longer checks for ast switch, reconfigure the test.")
} }
return result
} }
catch (e: Throwable) { catch (e: Throwable) {
if (!shouldFail) { if (!shouldFail) {
throw e throw e
} }
} }
finally {
manager.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE, disposable)
}
return null
} }
} }
+10
View File
@@ -0,0 +1,10 @@
package test
import dependency.*
fun foo() {
t<caret>est("")
}
// ALLOW_AST_ACCESS
// REF: (dependency).test(kotlin.String)
@@ -0,0 +1,7 @@
package dependency
fun test(a: Int) {
}
fun test(a: String) {
}
@@ -58,7 +58,7 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt
protected fun doSingleResolveTest() { protected fun doSingleResolveTest() {
forEachCaret { index, offset -> forEachCaret { index, offset ->
val expectedResolveData = readResolveData(myFixture.file.text, index, refMarkerText) 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) } checkReferenceResolve(expectedResolveData, offset, psiReference) { checkResolvedTo(it) }
} }
} }
@@ -67,6 +67,9 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt
// do nothing // do nothing
} }
open fun wrapReference(reference: PsiReference?): PsiReference? = reference
open fun wrapReference(reference: PsiPolyVariantReference): PsiPolyVariantReference = reference
protected fun doMultiResolveTest() { protected fun doMultiResolveTest() {
forEachCaret { index, offset -> forEachCaret { index, offset ->
val expectedReferences = getExpectedReferences(myFixture.file.text, index, refMarkerText) val expectedReferences = getExpectedReferences(myFixture.file.text, index, refMarkerText)
@@ -75,7 +78,7 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt
assertTrue(psiReference is PsiPolyVariantReference) assertTrue(psiReference is PsiPolyVariantReference)
psiReference as PsiPolyVariantReference psiReference as PsiPolyVariantReference
val results = psiReference.multiResolve(true) val results = wrapReference(psiReference).multiResolve(true)
val actualResolvedTo = Lists.newArrayList<String>() val actualResolvedTo = Lists.newArrayList<String>()
for (result in results) { for (result in results) {
@@ -140,7 +143,7 @@ abstract class AbstractReferenceResolveTest : KotlinLightPlatformCodeInsightFixt
} }
else { else {
if (!expectedResolveData.shouldBeUnresolved()) { 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)
} }
} }
} }
@@ -16,9 +16,16 @@
package org.jetbrains.kotlin.idea.resolve; 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 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.JdkAndMockLibraryProjectDescriptor;
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase; import org.jetbrains.kotlin.idea.test.PluginTestCaseBase;
import org.jetbrains.kotlin.test.InTextDirectivesUtils;
public abstract class AbstractReferenceResolveWithLibTest extends AbstractReferenceResolveTest { public abstract class AbstractReferenceResolveWithLibTest extends AbstractReferenceResolveTest {
private static final String TEST_DATA_PATH = PluginTestCaseBase.getTestDataPathBase() + "/resolve/referenceWithLib"; 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); 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<PsiElement>() {
@Override
public PsiElement invoke() {
return reference.resolve();
}
});
}
@Override
public String toString() {
return reference.toString();
}
};
}
} }
@@ -78,6 +78,12 @@ public class ReferenceResolveWithLibTestGenerated extends AbstractReferenceResol
doTest(fileName); 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") @TestMetadata("overridingFunctionWithSamAdapter.kt")
public void testOverridingFunctionWithSamAdapter() throws Exception { public void testOverridingFunctionWithSamAdapter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/referenceWithLib/overridingFunctionWithSamAdapter.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/resolve/referenceWithLib/overridingFunctionWithSamAdapter.kt");