Light Classes: Implement light elements for 'extends'/'implements' reference lists. Support adding/removing of super type list entries via light elements.
Fix exception on applying "Convert to class" intention to Java interface with Kotlin inheritor(s) #KT-11281 Fixed (cherry picked from commit 4ec4914)
This commit is contained in:
@@ -199,6 +199,7 @@
|
||||
- [`KT-11933`](https://youtrack.jetbrains.com/issue/KT-11933) Entities used only by alias are marked as unused
|
||||
- [`KT-12193`](https://youtrack.jetbrains.com/issue/KT-12193) Convert to block body isn't equivalent for when expressions returning Unit
|
||||
- [`KT-10779`](https://youtrack.jetbrains.com/issue/KT-10779) Simplify 'for' using destructing declaration: intention / inspection quick fix is available only when all variables are used
|
||||
- [`KT-11281`](https://youtrack.jetbrains.com/issue/KT-11281) Fix exception on applying "Convert to class" intention to Java interface with Kotlin inheritor(s)
|
||||
- [`KT-12285`](https://youtrack.jetbrains.com/issue/KT-12285) Fix exception on test class generation
|
||||
- [`KT-12502`](https://youtrack.jetbrains.com/issue/KT-12502) Convert to expression body should be forbidden for non-exhaustive when returning Unit
|
||||
- [`KT-12260`](https://youtrack.jetbrains.com/issue/KT-12260) ISE while replacing an operator with safe call
|
||||
|
||||
@@ -44,6 +44,11 @@ public class KtSuperTypeList extends KtElementImplStub<KotlinPlaceHolderStub<KtS
|
||||
return visitor.visitSuperTypeList(this, data);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public KtSuperTypeListEntry addEntry(@NotNull KtSuperTypeListEntry entry) {
|
||||
return EditCommaSeparatedListHelper.INSTANCE.addItem(this, getEntries(), entry);
|
||||
}
|
||||
|
||||
public void removeEntry(@NotNull KtSuperTypeListEntry entry) {
|
||||
EditCommaSeparatedListHelper.INSTANCE.removeItem(entry);
|
||||
if (getEntries().isEmpty()) {
|
||||
|
||||
+2
-2
@@ -112,8 +112,8 @@ internal open class KtLightClassForAnonymousDeclaration(name: FqName,
|
||||
override fun getQualifiedName(): String? = null
|
||||
override fun getModifierList(): PsiModifierList? = null
|
||||
override fun hasModifierProperty(name: String): Boolean = name == PsiModifier.FINAL
|
||||
override fun getExtendsList(): PsiReferenceList? = null
|
||||
override fun getImplementsList(): PsiReferenceList? = null
|
||||
override fun getExtendsList() = null
|
||||
override fun getImplementsList() = null
|
||||
override fun getContainingClass(): PsiClass? = null
|
||||
override fun isInterface() = false
|
||||
override fun isAnnotationType() = false
|
||||
|
||||
+14
@@ -59,6 +59,16 @@ open class KtLightClassForExplicitDeclaration(
|
||||
: KtWrappingLightClass(classOrObject.manager), KtJavaMirrorMarker, StubBasedPsiElement<KotlinClassOrObjectStub<out KtClassOrObject>> {
|
||||
private val lightIdentifier = KtLightIdentifier(this, classOrObject)
|
||||
|
||||
private val _extendsList by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
val listDelegate = super.getExtendsList() ?: return@lazy null
|
||||
KtLightPsiReferenceList(listDelegate, this)
|
||||
}
|
||||
|
||||
private val _implementsList by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
val listDelegate = super.getImplementsList() ?: return@lazy null
|
||||
KtLightPsiReferenceList(listDelegate, this)
|
||||
}
|
||||
|
||||
private fun getLocalClassParent(): PsiElement? {
|
||||
fun getParentByPsiMethod(method: PsiMethod?, name: String?, forceMethodWrapping: Boolean): PsiElement? {
|
||||
if (method == null || name == null) return null
|
||||
@@ -396,6 +406,10 @@ open class KtLightClassForExplicitDeclaration(
|
||||
|
||||
override fun getNameIdentifier(): KtLightIdentifier? = lightIdentifier
|
||||
|
||||
override fun getExtendsList() = _extendsList
|
||||
|
||||
override fun getImplementsList() = _implementsList
|
||||
|
||||
companion object {
|
||||
private val JAVA_API_STUB = Key.create<CachedValue<WithFileStubAndExtraDiagnostics>>("JAVA_API_STUB")
|
||||
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2010-2016 JetBrains s.r.o.
|
||||
*
|
||||
* 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
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import com.intellij.psi.PsiReferenceList
|
||||
import com.intellij.psi.PsiReferenceList.Role
|
||||
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.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
|
||||
class KtLightPsiReferenceList (
|
||||
override val clsDelegate: PsiReferenceList,
|
||||
private val owner: KtLightClass
|
||||
) : KtLightElement<KtSuperTypeList, PsiReferenceList>, PsiReferenceList by clsDelegate {
|
||||
inner class KtLightSuperTypeReference(
|
||||
override val clsDelegate: PsiJavaCodeReferenceElement
|
||||
) : KtLightElement<KtSuperTypeListEntry, PsiJavaCodeReferenceElement>, PsiJavaCodeReferenceElement by clsDelegate {
|
||||
override fun getName() = null
|
||||
override fun setName(name: String) = throw UnsupportedOperationException()
|
||||
|
||||
override val kotlinOrigin by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
val superTypeList = this@KtLightPsiReferenceList.kotlinOrigin ?: return@lazy null
|
||||
val fqNameToFind = clsDelegate.qualifiedName ?: return@lazy null
|
||||
val context = LightClassGenerationSupport.getInstance(project).analyze(superTypeList)
|
||||
superTypeList.entries.firstOrNull {
|
||||
val referencedType = context[BindingContext.TYPE, it.typeReference]
|
||||
referencedType?.constructor?.declarationDescriptor?.fqNameUnsafe?.asString() == fqNameToFind
|
||||
}
|
||||
}
|
||||
|
||||
override fun getParent() = this@KtLightPsiReferenceList
|
||||
|
||||
override fun delete() {
|
||||
val superTypeList = this@KtLightPsiReferenceList.kotlinOrigin ?: return
|
||||
val entry = kotlinOrigin ?: return
|
||||
superTypeList.removeEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
override val kotlinOrigin: KtSuperTypeList?
|
||||
get() = owner.kotlinOrigin?.getSuperTypeList()
|
||||
|
||||
private val _referenceElements by lazy(LazyThreadSafetyMode.PUBLICATION) {
|
||||
clsDelegate.referenceElements.map { KtLightSuperTypeReference(it) }.toTypedArray()
|
||||
}
|
||||
|
||||
override fun getName() = null
|
||||
override fun setName(name: String) = throw UnsupportedOperationException()
|
||||
|
||||
override fun getParent() = owner
|
||||
|
||||
override fun getReferenceElements() = _referenceElements
|
||||
|
||||
override fun add(element: PsiElement): PsiElement? {
|
||||
if (element !is KtLightSuperTypeReference) throw UnsupportedOperationException("Unexpected element: ${element.getElementTextWithContext()}")
|
||||
|
||||
val superTypeList = 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) {
|
||||
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
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
interface IK
|
||||
|
||||
class C2 : I() {
|
||||
|
||||
}
|
||||
|
||||
class C3: I(), IK {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// "Convert to 'class'" "true"
|
||||
public abstract class <caret>I {
|
||||
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
interface IK
|
||||
|
||||
class C2 : I {
|
||||
|
||||
}
|
||||
|
||||
class C3: I, IK {
|
||||
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Convert to 'class'" "true"
|
||||
public interface <caret>I {
|
||||
|
||||
}
|
||||
@@ -544,6 +544,21 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/convertJavaInterfaceToClass")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ConvertJavaInterfaceToClass extends AbstractQuickFixMultiFileTest {
|
||||
public void testAllFilesPresentInConvertJavaInterfaceToClass() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/convertJavaInterfaceToClass"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlinInheritor.before.Main.java")
|
||||
public void testKotlinInheritor() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/convertJavaInterfaceToClass/kotlinInheritor.before.Main.java");
|
||||
doTestWithExtraFile(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/quickfix/createFromUsage")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user