Intentions: 'Implement abstract member' (Kotlin -> Kotlin)
#KT-8467 In Progress
This commit is contained in:
@@ -22,7 +22,6 @@ import com.intellij.navigation.ItemPresentationProviders
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import com.intellij.psi.impl.CheckUtil
|
||||
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil
|
||||
import com.intellij.psi.stubs.IStubElementType
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
@@ -64,8 +63,6 @@ abstract public class KtClassOrObject :
|
||||
|
||||
public fun getBody(): KtClassBody? = getStubOrPsiChild(KtStubElementTypes.CLASS_BODY)
|
||||
|
||||
public fun getOrCreateBody(): KtClassBody = getBody() ?: add(KtPsiFactory(this).createEmptyClassBody()) as KtClassBody
|
||||
|
||||
public fun addDeclaration(declaration: KtDeclaration): KtDeclaration {
|
||||
val body = getOrCreateBody()
|
||||
val anchor = PsiTreeUtil.skipSiblingsBackward(body.rBrace ?: body.getLastChild()!!, javaClass<PsiWhiteSpace>())
|
||||
@@ -117,3 +114,11 @@ abstract public class KtClassOrObject :
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun KtClassOrObject.getOrCreateBody(): KtClassBody {
|
||||
getBody()?.let { return it }
|
||||
|
||||
val newBody = KtPsiFactory(this).createEmptyClassBody()
|
||||
if (this is KtEnumEntry) return addAfter(newBody, initializerList ?: nameIdentifier) as KtClassBody
|
||||
return add(newBody) as KtClassBody
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
import org.jetbrains.kotlin.psi.psiUtil.siblings
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
import org.jetbrains.kotlin.utils.ifEmpty
|
||||
@@ -156,6 +158,22 @@ public fun <T : KtDeclaration> insertMembersAfter(
|
||||
|
||||
var afterAnchor = anchor ?: findInsertAfterAnchor(editor, body) ?: return@runWriteAction emptyList()
|
||||
val insertedMembers = members.mapTo(SmartList<T>()) {
|
||||
if (classOrObject is KtClass && classOrObject.isEnum()) {
|
||||
val enumEntries = classOrObject.declarations.filterIsInstance<KtEnumEntry>()
|
||||
val bound = (enumEntries.lastOrNull() ?: classOrObject.allChildren.firstOrNull { it.node.elementType == KtTokens.SEMICOLON })
|
||||
if (it !is KtEnumEntry) {
|
||||
if (bound != null && afterAnchor.startOffset <= bound.startOffset) {
|
||||
afterAnchor = bound
|
||||
}
|
||||
}
|
||||
else if (bound == null && body.declarations.isNotEmpty()) {
|
||||
afterAnchor = body.lBrace!!
|
||||
}
|
||||
else if (bound != null && afterAnchor.startOffset >= bound.startOffset) {
|
||||
afterAnchor = bound.prevSibling!!
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
(body.addAfter(it, afterAnchor) as T).apply { afterAnchor = this }
|
||||
}
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ public abstract class OverrideImplementMembersHandler : LanguageCodeInsightActio
|
||||
override fun startInWriteAction(): Boolean = false
|
||||
|
||||
companion object {
|
||||
public fun generateMembers(editor: Editor, classOrObject: KtClassOrObject, selectedElements: Collection<OverrideMemberChooserObject>) {
|
||||
public fun generateMembers(editor: Editor?, classOrObject: KtClassOrObject, selectedElements: Collection<OverrideMemberChooserObject>) {
|
||||
val project = classOrObject.project
|
||||
insertMembersAfter(editor, classOrObject, selectedElements.map { it.generateMember(project) })
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
interface A {
|
||||
fun foo(): Int
|
||||
}
|
||||
|
||||
class B : A {
|
||||
<spot>override fun foo(): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}</spot>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
interface A {
|
||||
<spot>fun foo(): Int</spot>
|
||||
}
|
||||
|
||||
class B : A {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This intention searches for all classes that can implement selected abstract member, and creates default implementation there.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1110,6 +1110,11 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||
displayName="Convert object literal to lambda"
|
||||
groupName="Kotlin"
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.impl.source.codeStyle.PreFormatProcessor
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.allChildren
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.lastIsInstanceOrNull
|
||||
|
||||
class KotlinPreFormatProcessor : PreFormatProcessor {
|
||||
@@ -34,7 +35,8 @@ class KotlinPreFormatProcessor : PreFormatProcessor {
|
||||
if (!klass.isEnum()) return
|
||||
|
||||
val lastEntry = klass.declarations.lastIsInstanceOrNull<KtEnumEntry>()
|
||||
if (KtPsiUtil.skipTrailingWhitespacesAndComments(lastEntry ?: classBody.firstChild)?.node?.elementType == KtTokens.SEMICOLON) return
|
||||
if (lastEntry != null && lastEntry.allChildren.any { it.node.elementType == KtTokens.SEMICOLON }) return
|
||||
if (lastEntry == null && classBody.allChildren.any { it.node.elementType == KtTokens.SEMICOLON }) return
|
||||
|
||||
val semicolon = KtPsiFactory(klass).createSemicolon()
|
||||
classBody.addAfter(semicolon, lastEntry)
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.idea.intentions
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightBundle
|
||||
import com.intellij.codeInsight.FileModificationService
|
||||
import com.intellij.ide.util.PsiClassListCellRenderer
|
||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import org.jetbrains.kotlin.asJava.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.KtLightClassForExplicitDeclaration
|
||||
import org.jetbrains.kotlin.asJava.toLightClass
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideImplementMembersHandler
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.OverrideMemberChooserObject
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass
|
||||
import org.jetbrains.kotlin.idea.refactoring.runSynchronouslyWithProgress
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
|
||||
import org.jetbrains.kotlin.idea.util.application.executeCommand
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.types.TypeSubstitutor
|
||||
import org.jetbrains.kotlin.types.substitutions.getTypeSubstitutor
|
||||
import org.jetbrains.kotlin.util.findCallableMemberBySignature
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonList
|
||||
import java.util.*
|
||||
import javax.swing.ListSelectionModel
|
||||
|
||||
class ImplementAbstractMemberIntention :
|
||||
SelfTargetingRangeIntention<KtNamedDeclaration>(KtNamedDeclaration::class.java, "", "Implement abstract member") {
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance("#${ImplementAbstractMemberIntention::class.java.canonicalName}")
|
||||
}
|
||||
|
||||
private fun isAbstract(element: KtNamedDeclaration): Boolean {
|
||||
if (element.hasModifier(KtTokens.ABSTRACT_KEYWORD)) return true
|
||||
if (!(element.containingClassOrObject?.isInterfaceClass() ?: false)) return false
|
||||
return when (element) {
|
||||
is KtProperty -> element.initializer == null && element.delegate == null && element.accessors.isEmpty()
|
||||
is KtNamedFunction -> !element.hasBody()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun findExistingImplementation(
|
||||
subClass: ClassDescriptor,
|
||||
superMember: CallableMemberDescriptor
|
||||
): CallableMemberDescriptor? {
|
||||
val superClass = superMember.containingDeclaration as? ClassDescriptor ?: return null
|
||||
val substitutor = getTypeSubstitutor(superClass.defaultType, subClass.defaultType) ?: TypeSubstitutor.EMPTY
|
||||
val subMember = subClass.findCallableMemberBySignature(superMember.substitute(substitutor) as CallableMemberDescriptor)
|
||||
if (subMember?.kind?.isReal ?: false) return subMember else return null
|
||||
}
|
||||
|
||||
private fun findClassesToProcess(member: KtNamedDeclaration): Sequence<KtClassOrObject> {
|
||||
val baseClass = member.containingClassOrObject as? KtClass ?: return emptySequence()
|
||||
val memberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: return emptySequence()
|
||||
|
||||
fun acceptSubClass(classOrObject: KtClassOrObject): Boolean {
|
||||
val classDescriptor = classOrObject.resolveToDescriptorIfAny() as? ClassDescriptor ?: return false
|
||||
return classDescriptor.kind != ClassKind.INTERFACE && findExistingImplementation(classDescriptor, memberDescriptor) == null
|
||||
}
|
||||
|
||||
if (baseClass.isEnum()) {
|
||||
return baseClass.declarations
|
||||
.asSequence()
|
||||
.filterIsInstance<KtEnumEntry>()
|
||||
.filter(::acceptSubClass)
|
||||
}
|
||||
|
||||
return HierarchySearchRequest(baseClass, baseClass.useScope, false)
|
||||
.searchInheritors()
|
||||
.asSequence()
|
||||
.mapNotNull { (it as? KtLightClassForExplicitDeclaration)?.getOrigin() }
|
||||
.filter(::acceptSubClass)
|
||||
}
|
||||
|
||||
override fun applicabilityRange(element: KtNamedDeclaration): TextRange? {
|
||||
if (!isAbstract(element)) return null
|
||||
|
||||
text = when(element) {
|
||||
is KtProperty -> "Implement abstract property"
|
||||
is KtNamedFunction -> "Implement abstract function"
|
||||
else -> return null
|
||||
}
|
||||
|
||||
if (!findClassesToProcess(element).any()) return null
|
||||
|
||||
return element.nameIdentifier?.textRange
|
||||
}
|
||||
|
||||
private fun implementInClass(member: KtNamedDeclaration, targetClasses: List<PsiElement>) {
|
||||
val project = member.project
|
||||
project.executeCommand(CodeInsightBundle.message("intention.implement.abstract.method.command.name")) {
|
||||
if (!FileModificationService.getInstance().preparePsiElementsForWrite(targetClasses)) return@executeCommand
|
||||
runWriteAction {
|
||||
for (targetClass in targetClasses) {
|
||||
try {
|
||||
val subClass = (targetClass as? KtLightClass)?.getOrigin() ?: targetClass as? KtClassOrObject ?: continue
|
||||
val subClassDescriptor = subClass.resolveToDescriptorIfAny() as? ClassDescriptor ?: continue
|
||||
val superMemberDescriptor = member.resolveToDescriptorIfAny() as? CallableMemberDescriptor ?: continue
|
||||
val superClassDescriptor = superMemberDescriptor.containingDeclaration as? ClassDescriptor ?: continue
|
||||
val substitutor = getTypeSubstitutor(superClassDescriptor.defaultType, subClassDescriptor.defaultType)
|
||||
?: TypeSubstitutor.EMPTY
|
||||
val descriptorToImplement = superMemberDescriptor.substitute(substitutor) as CallableMemberDescriptor
|
||||
val chooserObject = OverrideMemberChooserObject.create(project,
|
||||
descriptorToImplement,
|
||||
descriptorToImplement,
|
||||
OverrideMemberChooserObject.BodyType.EMPTY)
|
||||
OverrideImplementMembersHandler.generateMembers(null, subClass, chooserObject.singletonList())
|
||||
}
|
||||
catch(e: IncorrectOperationException) {
|
||||
LOG.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ClassRenderer : PsiElementListCellRenderer<PsiElement>() {
|
||||
private val psiClassRenderer = PsiClassListCellRenderer()
|
||||
|
||||
override fun getComparator(): Comparator<PsiElement> {
|
||||
val baseComparator = psiClassRenderer.comparator
|
||||
return Comparator { o1, o2 ->
|
||||
when {
|
||||
o1 is KtEnumEntry && o2 is KtEnumEntry -> o1.name!!.compareTo(o2.name!!)
|
||||
o1 is KtEnumEntry -> -1
|
||||
o2 is KtEnumEntry -> 1
|
||||
o1 is PsiClass && o2 is PsiClass -> baseComparator.compare(o1 as PsiClass, o2 as PsiClass)
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconFlags() = 0
|
||||
|
||||
override fun getElementText(element: PsiElement?): String? {
|
||||
return when (element) {
|
||||
is KtEnumEntry -> element.name
|
||||
is PsiClass -> psiClassRenderer.getElementText(element)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
override fun getContainerText(element: PsiElement?, name: String?): String? {
|
||||
return when (element) {
|
||||
is KtEnumEntry -> element.containingClassOrObject?.fqName?.asString()
|
||||
is PsiClass -> PsiClassListCellRenderer.getContainerTextStatic(element)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtNamedDeclaration, editor: Editor) {
|
||||
val project = element.project
|
||||
|
||||
val classesToProcess = project.runSynchronouslyWithProgress(
|
||||
CodeInsightBundle.message("intention.implement.abstract.method.searching.for.descendants.progress"),
|
||||
true
|
||||
) { findClassesToProcess(element).map { it.toLightClass() ?: it }.toList() } ?: return
|
||||
if (classesToProcess.isEmpty()) return
|
||||
|
||||
classesToProcess.singleOrNull()?.let { return implementInClass(element, it.singletonList()) }
|
||||
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) return implementInClass(element, classesToProcess)
|
||||
|
||||
val renderer = ClassRenderer()
|
||||
val list = JBList(classesToProcess.sortedWith(renderer.comparator)).apply {
|
||||
selectionMode = ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
|
||||
cellRenderer = renderer
|
||||
}
|
||||
val builder = PopupChooserBuilder(list)
|
||||
renderer.installSpeedSearch(builder)
|
||||
builder
|
||||
.setTitle(CodeInsightBundle.message("intention.implement.abstract.method.class.chooser.title"))
|
||||
.setItemChoosenCallback {
|
||||
val index = list.selectedIndex
|
||||
if (index < 0) return@setItemChoosenCallback
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
implementInClass(element, list.selectedValues.toList() as List<KtClassOrObject>)
|
||||
}
|
||||
.createPopup()
|
||||
.showInBestPositionFor(editor)
|
||||
}
|
||||
}
|
||||
@@ -34,10 +34,7 @@ import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||
import org.jetbrains.kotlin.psi.KtClass
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention
|
||||
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
val bar = 1
|
||||
|
||||
fun baz() = 2
|
||||
}
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
val bar = 1
|
||||
|
||||
fun baz() = 2
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E {
|
||||
A, B, C;
|
||||
|
||||
abstract fun <caret>foo(x: Int): Int
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E {
|
||||
A {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}, B {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}, C {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun <caret>foo(x: Int): Int
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E(n: Int) {
|
||||
A(1), B(2), C(3);
|
||||
|
||||
abstract fun <caret>foo(x: Int): Int
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E(n: Int) {
|
||||
A(1) {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}, B(2) {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}, C(3) {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun <caret>foo(x: Int): Int
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
class U : T<String> {
|
||||
|
||||
}
|
||||
|
||||
class V : T<Int> {
|
||||
|
||||
}
|
||||
|
||||
class Z : T<Int> by V() {
|
||||
|
||||
}
|
||||
|
||||
class W : T<Boolean> {
|
||||
override fun foo(x: Boolean): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
fun <caret>foo(x: X): X
|
||||
}
|
||||
|
||||
class U : T<String> {
|
||||
override fun foo(x: String): String {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class V : T<Int> {
|
||||
override fun foo(x: Int): Int {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Z : T<Int> by V() {
|
||||
|
||||
}
|
||||
|
||||
class W : T<Boolean> {
|
||||
override fun foo(x: Boolean): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Abstract function 'foo' in non-abstract class 'A'
|
||||
class A {
|
||||
abstract fun <caret>foo(): Int
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Abstract function 'foo' in non-abstract class 'A'
|
||||
object A {
|
||||
abstract fun <caret>foo(): Int
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Class 'C' must be declared abstract or implement abstract base class member public abstract fun foo(): kotlin.Int defined in B
|
||||
interface A {
|
||||
fun <caret>foo(): Int
|
||||
}
|
||||
|
||||
class X : A {
|
||||
override fun foo() = 1
|
||||
}
|
||||
|
||||
abstract class B : A {
|
||||
abstract override fun foo(): Int
|
||||
}
|
||||
|
||||
class C: B() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
interface A {
|
||||
fun <caret>foo(): Int
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
open class A {
|
||||
fun <caret>foo() = 1
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
interface A {
|
||||
fun <caret>foo() = 1
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Function 'foo' without a body must be abstract
|
||||
open class A {
|
||||
fun <caret>foo(): Int
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.intentions.ImplementAbstractMemberIntention
|
||||
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
}
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
val bar = 1
|
||||
|
||||
fun baz() = 2
|
||||
}
|
||||
Vendored
+16
@@ -0,0 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
enum class E : T<Int> {
|
||||
A, B, C;
|
||||
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
val bar = 1
|
||||
|
||||
fun baz() = 2
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E {
|
||||
A, B, C;
|
||||
|
||||
abstract val <caret>foo: Int
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E {
|
||||
A {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
}, B {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
}, C {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
};
|
||||
|
||||
abstract val <caret>foo: Int
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E(n: Int) {
|
||||
A(1), B(2), C(3);
|
||||
|
||||
abstract val <caret>foo: Int
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// WITH_RUNTIME
|
||||
//DISABLE-ERRORS
|
||||
enum class E(n: Int) {
|
||||
A(1) {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
}, B(2) {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
}, C(3) {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
};
|
||||
|
||||
abstract val <caret>foo: Int
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
class U : T<String> {
|
||||
|
||||
}
|
||||
|
||||
class V : T<Int> {
|
||||
|
||||
}
|
||||
|
||||
class Z : T<Int> by V() {
|
||||
|
||||
}
|
||||
|
||||
class W : T<Boolean> {
|
||||
override val foo: Boolean
|
||||
get() = throw UnsupportedOperationException()
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
// DISABLE-ERRORS
|
||||
interface T<X> {
|
||||
val <caret>foo: X
|
||||
}
|
||||
|
||||
class U : T<String> {
|
||||
override val foo: String
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
}
|
||||
|
||||
class V : T<Int> {
|
||||
override val foo: Int
|
||||
get() = throw UnsupportedOperationException()
|
||||
|
||||
}
|
||||
|
||||
class Z : T<Int> by V() {
|
||||
|
||||
}
|
||||
|
||||
class W : T<Boolean> {
|
||||
override val foo: Boolean
|
||||
get() = throw UnsupportedOperationException()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Abstract property 'foo' in non-abstract class 'A'
|
||||
class A {
|
||||
abstract val <caret>foo: Int
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Abstract property 'foo' in non-abstract class 'A'
|
||||
object A {
|
||||
abstract val <caret>foo: Int
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Class 'C' must be declared abstract or implement abstract base class member public abstract val foo: kotlin.Int defined in B
|
||||
interface A {
|
||||
val <caret>foo: Int
|
||||
}
|
||||
|
||||
class X : A {
|
||||
override val foo = 1
|
||||
}
|
||||
|
||||
abstract class B : A {
|
||||
abstract override val foo: Int
|
||||
}
|
||||
|
||||
class C: B() {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
interface A {
|
||||
val <caret>foo: Int
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
open class A {
|
||||
val <caret>foo = 1
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
// ERROR: Property must be initialized or be abstract
|
||||
open class A {
|
||||
val <caret>foo: Int
|
||||
}
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
// IS_APPLICABLE: false
|
||||
interface A {
|
||||
val <caret>foo: Int
|
||||
get() = 1
|
||||
}
|
||||
@@ -5230,6 +5230,189 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/implementAbstractMember")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ImplementAbstractMember extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInImplementAbstractMember() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/implementAbstractMember"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/implementAbstractMember/function")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Function extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInFunction() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/implementAbstractMember/function"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClass.kt")
|
||||
public void testEnumClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/enumClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClassWithSemicolon.kt")
|
||||
public void testEnumClassWithSemicolon() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/enumClassWithSemicolon.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClassWithSemicolonAndMembers.kt")
|
||||
public void testEnumClassWithSemicolonAndMembers() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/enumClassWithSemicolonAndMembers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntries.kt")
|
||||
public void testEnumEntries() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/enumEntries.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntriesWithArgs.kt")
|
||||
public void testEnumEntriesWithArgs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/enumEntriesWithArgs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("implementAll.kt")
|
||||
public void testImplementAll() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/implementAll.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inFinalClass.kt")
|
||||
public void testInFinalClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/inFinalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inObject.kt")
|
||||
public void testInObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/inObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noDirectOverridesNeeded.kt")
|
||||
public void testNoDirectOverridesNeeded() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/noDirectOverridesNeeded.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noInheritors.kt")
|
||||
public void testNoInheritors() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/noInheritors.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractInClass.kt")
|
||||
public void testNotAbstractInClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/notAbstractInClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractInInterface.kt")
|
||||
public void testNotAbstractInInterface() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/notAbstractInInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractNoBodyInClass.kt")
|
||||
public void testNotAbstractNoBodyInClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/function/notAbstractNoBodyInClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/implementAbstractMember/property")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Property extends AbstractIntentionTest {
|
||||
public void testAllFilesPresentInProperty() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/implementAbstractMember/property"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClass.kt")
|
||||
public void testEnumClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/enumClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClassWithSemicolon.kt")
|
||||
public void testEnumClassWithSemicolon() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/enumClassWithSemicolon.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumClassWithSemicolonAndMembers.kt")
|
||||
public void testEnumClassWithSemicolonAndMembers() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/enumClassWithSemicolonAndMembers.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntries.kt")
|
||||
public void testEnumEntries() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/enumEntries.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("enumEntriesWithArgs.kt")
|
||||
public void testEnumEntriesWithArgs() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/enumEntriesWithArgs.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("implementAll.kt")
|
||||
public void testImplementAll() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/implementAll.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inFinalClass.kt")
|
||||
public void testInFinalClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/inFinalClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inObject.kt")
|
||||
public void testInObject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/inObject.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noDirectOverridesNeeded.kt")
|
||||
public void testNoDirectOverridesNeeded() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/noDirectOverridesNeeded.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noInheritors.kt")
|
||||
public void testNoInheritors() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/noInheritors.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractInClass.kt")
|
||||
public void testNotAbstractInClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/notAbstractInClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractNoBodyInClass.kt")
|
||||
public void testNotAbstractNoBodyInClass() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/notAbstractNoBodyInClass.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("notAbstractWithGetterInInterface.kt")
|
||||
public void testNotAbstractWithGetterInInterface() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/implementAbstractMember/property/notAbstractWithGetterInInterface.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/intentions/importAllMembers")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user