Try to avoid loading ast for decompiled files (KT-14804)
#KT-14804 Fixed
This commit is contained in:
+19
@@ -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<String> {
|
||||
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
|
||||
|
||||
@@ -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<VirtualFile>,
|
||||
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<VirtualFile>, 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 <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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
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<String>()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<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);
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
Reference in New Issue
Block a user