Fix of ConvertJavaInterfaceToClass test
Platform is trying to modify the class inheritance list during intension (see the test name above) action process but it cause a cross-language changes via PSI. To make this possible LightClasses are supports this behaviour with a little support of it (inheritance list Add and inheritance list element Delete are supported). It need's to be done for UL-classes in same way.
This commit is contained in:
+57
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.asJava.classes
|
||||||
|
|
||||||
|
import com.intellij.lang.Language
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||||
|
import com.intellij.psi.PsiManager
|
||||||
|
import com.intellij.psi.PsiReferenceList
|
||||||
|
import org.jetbrains.kotlin.psi.KtSuperTypeList
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
|
|
||||||
|
class KotlinSuperTypeListBuilder(kotlinOrigin: KtSuperTypeList?, manager: PsiManager, language: Language, role: PsiReferenceList.Role) :
|
||||||
|
KotlinLightReferenceListBuilder(
|
||||||
|
manager,
|
||||||
|
language,
|
||||||
|
role
|
||||||
|
) {
|
||||||
|
|
||||||
|
private val myKotlinOrigin: KtSuperTypeList? = kotlinOrigin
|
||||||
|
|
||||||
|
inner class KotlinSuperTypeReference(private val element: PsiJavaCodeReferenceElement) : PsiJavaCodeReferenceElement by element {
|
||||||
|
|
||||||
|
override fun getParent() = this@KotlinSuperTypeListBuilder
|
||||||
|
|
||||||
|
val kotlinOrigin by lazyPub {
|
||||||
|
element.qualifiedName?.let { this@KotlinSuperTypeListBuilder.myKotlinOrigin?.findEntry(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete() {
|
||||||
|
val superTypeList = this@KotlinSuperTypeListBuilder.myKotlinOrigin ?: return
|
||||||
|
val entry = kotlinOrigin ?: return
|
||||||
|
superTypeList.removeEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val referenceElementsCache by lazyPub {
|
||||||
|
super.getReferenceElements().map { KotlinSuperTypeReference(it) }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getReferenceElements() = referenceElementsCache
|
||||||
|
|
||||||
|
override fun add(element: PsiElement): PsiElement {
|
||||||
|
|
||||||
|
if (element !is KotlinSuperTypeReference) throw UnsupportedOperationException("Unexpected element: ${element.getElementTextWithContext()}")
|
||||||
|
|
||||||
|
val superTypeList = myKotlinOrigin ?: return element
|
||||||
|
val entry = element.kotlinOrigin ?: return element
|
||||||
|
|
||||||
|
this.addSuperTypeEntry(superTypeList, entry, element)
|
||||||
|
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,52 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
*
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.asJava.classes
|
package org.jetbrains.kotlin.asJava.classes
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||||
|
import com.intellij.psi.PsiReferenceList
|
||||||
import com.intellij.psi.impl.light.LightElement
|
import com.intellij.psi.impl.light.LightElement
|
||||||
import com.intellij.util.IncorrectOperationException
|
import com.intellij.util.IncorrectOperationException
|
||||||
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||||
|
import org.jetbrains.kotlin.psi.KtSuperTypeList
|
||||||
|
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
|
||||||
// NOTE: avoid using blocking lazy in light classes, it leads to deadlocks
|
// NOTE: avoid using blocking lazy in light classes, it leads to deadlocks
|
||||||
fun <T> lazyPub(initializer: () -> T) = lazy(LazyThreadSafetyMode.PUBLICATION, initializer)
|
fun <T> lazyPub(initializer: () -> T) = lazy(LazyThreadSafetyMode.PUBLICATION, initializer)
|
||||||
|
|
||||||
fun LightElement.cannotModify(): Nothing {
|
fun LightElement.cannotModify(): Nothing {
|
||||||
throw IncorrectOperationException("Modification not implemented.")
|
throw IncorrectOperationException("Modification not implemented.")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtSuperTypeList.findEntry(fqNameToFind: String): KtSuperTypeListEntry? {
|
||||||
|
val context = LightClassGenerationSupport.getInstance(project).analyzeWithContent(parent as KtClassOrObject)
|
||||||
|
return entries.firstOrNull {
|
||||||
|
val referencedType = context[BindingContext.TYPE, it.typeReference]
|
||||||
|
referencedType?.constructor?.declarationDescriptor?.fqNameUnsafe?.asString() == fqNameToFind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PsiReferenceList.addSuperTypeEntry(
|
||||||
|
superTypeList: KtSuperTypeList,
|
||||||
|
entry: KtSuperTypeListEntry,
|
||||||
|
reference: PsiJavaCodeReferenceElement
|
||||||
|
) {
|
||||||
|
// Only classes may be mentioned in 'extends' list, thus create super call instead simple type reference
|
||||||
|
val entryToAdd =
|
||||||
|
if ((reference.parent as? PsiReferenceList)?.role == PsiReferenceList.Role.IMPLEMENTS_LIST && role == PsiReferenceList.Role.EXTENDS_LIST) {
|
||||||
|
KtPsiFactory(this).createSuperTypeCallEntry("${entry.text}()")
|
||||||
|
} else entry
|
||||||
|
// TODO: implement KtSuperListEntry qualification/shortening when inserting reference from another context
|
||||||
|
if (entry.parent != superTypeList) {
|
||||||
|
superTypeList.addEntry(entryToAdd)
|
||||||
|
} else {
|
||||||
|
// Preserve original entry order
|
||||||
|
entry.replace(entryToAdd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
|
|||||||
|
|
||||||
override fun createExtendsList(): PsiReferenceList? =
|
override fun createExtendsList(): PsiReferenceList? =
|
||||||
if (tooComplex) super.createExtendsList()
|
if (tooComplex) super.createExtendsList()
|
||||||
else KotlinLightReferenceListBuilder(
|
else KotlinSuperTypeListBuilder(
|
||||||
|
kotlinOrigin.getSuperTypeList(),
|
||||||
manager,
|
manager,
|
||||||
language,
|
language,
|
||||||
PsiReferenceList.Role.EXTENDS_LIST
|
PsiReferenceList.Role.EXTENDS_LIST
|
||||||
@@ -109,7 +110,8 @@ open class KtUltraLightClass(classOrObject: KtClassOrObject, internal val suppor
|
|||||||
|
|
||||||
override fun createImplementsList(): PsiReferenceList? =
|
override fun createImplementsList(): PsiReferenceList? =
|
||||||
if (tooComplex) super.createImplementsList()
|
if (tooComplex) super.createImplementsList()
|
||||||
else KotlinLightReferenceListBuilder(
|
else KotlinSuperTypeListBuilder(
|
||||||
|
kotlinOrigin.getSuperTypeList(),
|
||||||
manager,
|
manager,
|
||||||
language,
|
language,
|
||||||
PsiReferenceList.Role.IMPLEMENTS_LIST
|
PsiReferenceList.Role.IMPLEMENTS_LIST
|
||||||
|
|||||||
+11
-28
@@ -23,31 +23,24 @@ import com.intellij.psi.PsiReferenceList
|
|||||||
import com.intellij.psi.PsiReferenceList.Role
|
import com.intellij.psi.PsiReferenceList.Role
|
||||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.addSuperTypeEntry
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.findEntry
|
||||||
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
import org.jetbrains.kotlin.asJava.classes.lazyPub
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
|
||||||
import org.jetbrains.kotlin.psi.KtSuperTypeList
|
|
||||||
import org.jetbrains.kotlin.psi.KtSuperTypeListEntry
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
|
||||||
class KtLightPsiReferenceList (
|
class KtLightPsiReferenceList(
|
||||||
override val clsDelegate: PsiReferenceList,
|
override val clsDelegate: PsiReferenceList,
|
||||||
private val owner: KtLightClass
|
private val owner: KtLightClass
|
||||||
) : KtLightElement<KtSuperTypeList, PsiReferenceList>, PsiReferenceList by clsDelegate {
|
) : KtLightElement<KtSuperTypeList, PsiReferenceList>, PsiReferenceList by clsDelegate {
|
||||||
inner class KtLightSuperTypeReference(
|
inner class KtLightSuperTypeReference(
|
||||||
override val clsDelegate: PsiJavaCodeReferenceElement
|
override val clsDelegate: PsiJavaCodeReferenceElement
|
||||||
) : KtLightElement<KtSuperTypeListEntry, PsiJavaCodeReferenceElement>, PsiJavaCodeReferenceElement by clsDelegate {
|
) : KtLightElement<KtSuperTypeListEntry, PsiJavaCodeReferenceElement>, PsiJavaCodeReferenceElement by clsDelegate {
|
||||||
|
|
||||||
override val kotlinOrigin by lazyPub {
|
override val kotlinOrigin by lazyPub {
|
||||||
val superTypeList = this@KtLightPsiReferenceList.kotlinOrigin ?: return@lazyPub null
|
clsDelegate.qualifiedName?.let { this@KtLightPsiReferenceList.kotlinOrigin?.findEntry(it) }
|
||||||
val fqNameToFind = clsDelegate.qualifiedName ?: return@lazyPub null
|
|
||||||
val context = LightClassGenerationSupport.getInstance(project).analyzeWithContent(superTypeList.parent as KtClassOrObject)
|
|
||||||
superTypeList.entries.firstOrNull {
|
|
||||||
val referencedType = context[BindingContext.TYPE, it.typeReference]
|
|
||||||
referencedType?.constructor?.declarationDescriptor?.fqNameUnsafe?.asString() == fqNameToFind
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getParent() = this@KtLightPsiReferenceList
|
override fun getParent() = this@KtLightPsiReferenceList
|
||||||
@@ -77,19 +70,9 @@ class KtLightPsiReferenceList (
|
|||||||
|
|
||||||
val superTypeList = kotlinOrigin ?: return element
|
val superTypeList = kotlinOrigin ?: return element
|
||||||
val entry = element.kotlinOrigin ?: return element
|
val entry = element.kotlinOrigin ?: return element
|
||||||
// Only classes may be mentioned in 'extends' list, thus create super call instead simple type reference
|
|
||||||
val entryToAdd = if ((element.parent as? PsiReferenceList)?.role == Role.IMPLEMENTS_LIST && role == Role.EXTENDS_LIST) {
|
this.addSuperTypeEntry(superTypeList, entry, element)
|
||||||
KtPsiFactory(this).createSuperTypeCallEntry("${entry.text}()")
|
|
||||||
}
|
|
||||||
else entry
|
|
||||||
// TODO: implement KtSuperListEntry qualification/shortening when inserting reference from another context
|
|
||||||
if (entry.parent != superTypeList) {
|
|
||||||
superTypeList.addEntry(entryToAdd)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Preserve original entry order
|
|
||||||
entry.replace(entryToAdd)
|
|
||||||
}
|
|
||||||
return element
|
return element
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -1395,7 +1395,6 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("kotlinInheritor.before.Main.java")
|
@TestMetadata("kotlinInheritor.before.Main.java")
|
||||||
@Ignore("UltraLight classes does not support modification yet")
|
|
||||||
public void testKotlinInheritor() throws Exception {
|
public void testKotlinInheritor() throws Exception {
|
||||||
runTest("idea/testData/quickfix/convertJavaInterfaceToClass/kotlinInheritor.before.Main.java");
|
runTest("idea/testData/quickfix/convertJavaInterfaceToClass/kotlinInheritor.before.Main.java");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user