Skip to content

Commit

Permalink
KT-8542 Override dialog: only one class from hierarchy is shown
Browse files Browse the repository at this point in the history
 #KT-8542 Fixed
  • Loading branch information
valentinkip committed Jul 28, 2015
1 parent 29379c0 commit 63dc843
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 308 deletions.
1 change: 1 addition & 0 deletions .idea/dictionaries/valentin.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -18,16 +18,18 @@ package org.jetbrains.kotlin.idea.core.overrideImplement

import com.intellij.codeInsight.generation.ClassMemberWithElement
import com.intellij.codeInsight.generation.MemberChooserObject
import com.intellij.codeInsight.generation.MemberChooserObjectBase
import com.intellij.codeInsight.generation.PsiElementMemberChooserObject
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Iconable
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMember
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.psi.JetFile
Expand All @@ -39,10 +41,16 @@ import org.jetbrains.kotlin.renderer.render
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import javax.swing.Icon

public class DescriptorClassMember(
private val psiElement: PsiElement,
class OverrideMemberChooserObject(
project: Project,
val member: CallableMemberDescriptor,
val immediateSuper: CallableMemberDescriptor
) : DescriptorClassMember(DescriptorToSourceUtilsIde.getAnyDeclaration(project, member)!!, member)

public open class DescriptorClassMember(
psiElement: PsiElement,
public val descriptor: DeclarationDescriptor
) : MemberChooserObjectBase(DescriptorClassMember.getText(descriptor), DescriptorClassMember.getIcon(psiElement, descriptor)), ClassMemberWithElement {
) : PsiElementMemberChooserObject(psiElement, DescriptorClassMember.getText(descriptor), DescriptorClassMember.getIcon(psiElement, descriptor)), ClassMemberWithElement {

override fun getParentNodeDelegate(): MemberChooserObject {
val parent = descriptor.containingDeclaration ?: error("No parent for $descriptor")
Expand Down
Expand Up @@ -25,7 +25,10 @@ import org.jetbrains.kotlin.idea.JetBundle
import org.jetbrains.kotlin.resolve.OverrideResolver

public class ImplementMethodsHandler : OverrideImplementMethodsHandler(), IntentionAction {
override fun collectMethodsToGenerate(descriptor: ClassDescriptor) = OverrideResolver.getMissingImplementations(descriptor)
override fun collectMethodsToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
return OverrideResolver.getMissingImplementations(descriptor)
.map { OverrideMemberChooserObject(project, it, it) }
}

override fun getChooserTitle() = "Implement Members"

Expand Down
Expand Up @@ -19,7 +19,6 @@ package org.jetbrains.kotlin.idea.core.overrideImplement
import com.intellij.codeInsight.hint.HintManager
import com.intellij.ide.util.MemberChooser
import com.intellij.lang.LanguageCodeInsightActionHandler
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
Expand All @@ -29,7 +28,6 @@ import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.quickfix.moveCaretIntoGeneratedElement
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.ShortenReferences
Expand All @@ -41,14 +39,14 @@ import java.util.ArrayList

public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActionHandler {

public fun collectMethodsToGenerate(classOrObject: JetClassOrObject): Set<CallableMemberDescriptor> {
public fun collectMethodsToGenerate(classOrObject: JetClassOrObject): Collection<OverrideMemberChooserObject> {
val descriptor = classOrObject.resolveToDescriptor() as? ClassDescriptor ?: return emptySet()
return collectMethodsToGenerate(descriptor)
return collectMethodsToGenerate(descriptor, classOrObject.project)
}

protected abstract fun collectMethodsToGenerate(descriptor: ClassDescriptor): Set<CallableMemberDescriptor>
protected abstract fun collectMethodsToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject>

private fun showOverrideImplementChooser(project: Project, members: Array<DescriptorClassMember>): MemberChooser<DescriptorClassMember>? {
private fun showOverrideImplementChooser(project: Project, members: Array<OverrideMemberChooserObject>): MemberChooser<OverrideMemberChooserObject>? {
val chooser = MemberChooser(members, true, true, project)
chooser.title = getChooserTitle()
chooser.show()
Expand All @@ -71,15 +69,13 @@ public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActio
val elementAtCaret = file.findElementAt(editor.caretModel.offset)
val classOrObject = elementAtCaret?.getNonStrictParentOfType<JetClassOrObject>()!!

val missingImplementations = collectMethodsToGenerate(classOrObject)
if (missingImplementations.isEmpty() && !implementAll) {
val members = collectMethodsToGenerate(classOrObject)
if (members.isEmpty() && !implementAll) {
HintManager.getInstance().showErrorHint(editor, getNoMethodsFoundHint())
return
}

val members = membersFromDescriptors(file as JetFile, missingImplementations)

val selectedElements: List<DescriptorClassMember> = if (implementAll) {
val selectedElements = if (implementAll) {
members
}
else {
Expand Down Expand Up @@ -108,23 +104,7 @@ public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActio
typeNormalizer = IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES
}

private val LOG = Logger.getInstance(javaClass<OverrideImplementMethodsHandler>().canonicalName)

public fun membersFromDescriptors(file: JetFile, missingImplementations: Iterable<CallableMemberDescriptor>): List<DescriptorClassMember> {
val members = ArrayList<DescriptorClassMember>()
for (memberDescriptor in missingImplementations) {
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(file.project, memberDescriptor)
if (declaration == null) {
LOG.error("Can not find declaration for descriptor $memberDescriptor")
}
else {
members.add(DescriptorClassMember(declaration, memberDescriptor))
}
}
return members
}

public fun generateMethods(editor: Editor, classOrObject: JetClassOrObject, selectedElements: List<DescriptorClassMember>) {
public fun generateMethods(editor: Editor, classOrObject: JetClassOrObject, selectedElements: Collection<OverrideMemberChooserObject>) {
runWriteAction {
val body = classOrObject.getOrCreateBody()

Expand Down Expand Up @@ -203,16 +183,15 @@ public abstract class OverrideImplementMethodsHandler : LanguageCodeInsightActio
return whiteSpace
}

private fun generateOverridingMembers(selectedElements: List<DescriptorClassMember>,
private fun generateOverridingMembers(selectedElements: Collection<OverrideMemberChooserObject>,
classOrObject: JetClassOrObject): List<JetElement> {
val overridingMembers = ArrayList<JetElement>()
for (selectedElement in selectedElements) {
val descriptor = selectedElement.descriptor
if (descriptor is SimpleFunctionDescriptor) {
overridingMembers.add(overrideFunction(classOrObject, descriptor))
}
else if (descriptor is PropertyDescriptor) {
overridingMembers.add(overrideProperty(classOrObject, descriptor))
val descriptor = selectedElement.immediateSuper
when (descriptor) {
is SimpleFunctionDescriptor -> overridingMembers.add(overrideFunction(classOrObject, descriptor))
is PropertyDescriptor -> overridingMembers.add(overrideProperty(classOrObject, descriptor))
else -> error("Unknown member to override: $descriptor")
}
}
return overridingMembers
Expand Down
Expand Up @@ -16,53 +16,61 @@

package org.jetbrains.kotlin.idea.core.overrideImplement

import com.google.common.collect.LinkedHashMultimap
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.resolve.OverrideResolver
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE
import org.jetbrains.kotlin.resolve.calls.callResolverUtil.isOrOverridesSynthesized
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import java.util.ArrayList
import java.util.LinkedHashMap

public class OverrideMethodsHandler : OverrideImplementMethodsHandler() {
override fun collectMethodsToGenerate(descriptor: ClassDescriptor): Set<CallableMemberDescriptor> {
val superMethods = collectSuperMethods(descriptor)
for (member in descriptor.defaultType.memberScope.getAllDescriptors()) {
if (member is CallableMemberDescriptor) {
if (member.kind == CallableMemberDescriptor.Kind.DECLARATION) {
superMethods.removeAll(member.overriddenDescriptors)
}
}
}
override fun collectMethodsToGenerate(descriptor: ClassDescriptor, project: Project): Collection<OverrideMemberChooserObject> {
val result = ArrayList<OverrideMemberChooserObject>()
for (member in descriptor.unsubstitutedMemberScope.getAllDescriptors()) {
if (member is CallableMemberDescriptor && !member.isReal()) {
val overridden = member.overriddenDescriptors
if (overridden.any { it.modality == Modality.FINAL }) continue

return superMethods
.filter { it.modality.isOverridable && !isOrOverridesSynthesized(it) }
.toSet()
}
val realSuperToImmediates = LinkedHashMap<CallableMemberDescriptor, MutableCollection<CallableMemberDescriptor>>()
for (immediateSuper in overridden) {
for (realSuper in toRealSupers(immediateSuper)) {
realSuperToImmediates.getOrPut(realSuper) { ArrayList(1) }.add(immediateSuper)
}
}

private fun collectSuperMethods(classDescriptor: ClassDescriptor): MutableSet<CallableMemberDescriptor> {
val inheritedFunctionsSet = classDescriptor.typeConstructor.supertypes
.flatMap { it.memberScope.getAllDescriptors() }
.filterIsInstance<CallableMemberDescriptor>()
.toSet()
val realSupers = realSuperToImmediates.keySet()
val nonAbstractRealSupers = realSupers.filter { it.modality != Modality.ABSTRACT }
val realSupersToUse = if (nonAbstractRealSupers.isNotEmpty()) {
nonAbstractRealSupers
}
else {
listOf(realSupers.first())
}

// Only those actually inherited
val filteredMembers = OverrideResolver.filterOutOverridden(inheritedFunctionsSet)
for (realSuper in realSupersToUse) {
val immediateSupers = realSuperToImmediates[realSuper]!!
assert(immediateSupers.isNotEmpty())

// Group members with "the same" signature
val factoredMembers = LinkedHashMultimap.create<CallableMemberDescriptor, CallableMemberDescriptor>()
for (one in filteredMembers) {
if (factoredMembers.values().contains(one)) continue
for (another in filteredMembers) {
// if (one == another) continue;
factoredMembers.put(one, one)
if (OverridingUtil.DEFAULT.isOverridableBy(one, another).result == OVERRIDABLE || OverridingUtil.DEFAULT.isOverridableBy(another, one).result == OVERRIDABLE) {
factoredMembers.put(one, another)
val immediateSuperToUse = if (immediateSupers.size() == 1) {
immediateSupers.single()
}
else {
immediateSupers.singleOrNull { (it.containingDeclaration as? ClassDescriptor)?.kind == ClassKind.CLASS } ?: immediateSupers.first()
}
result.add(OverrideMemberChooserObject(project, realSuper, immediateSuperToUse))
}
}
}
return result
}

private fun CallableMemberDescriptor.isReal() = kind == CallableMemberDescriptor.Kind.DECLARATION

return factoredMembers.keySet()
private fun toRealSupers(immediateSuper: CallableMemberDescriptor): Collection<CallableMemberDescriptor> {
val overridden = immediateSuper.overriddenDescriptors
if (overridden.isEmpty()) return listOf(immediateSuper)
return overridden.flatMap { toRealSupers(it) }.toSet()
}

override fun getChooserTitle() = "Override Members"
Expand Down
11 changes: 11 additions & 0 deletions idea/testData/codeInsight/overrideImplement/ambiguousSuper.kt
@@ -0,0 +1,11 @@
interface I {
open fun foo(){}
}

open class A {
open fun foo(){}
}

class C : A(), I {
<caret>
}
@@ -0,0 +1,29 @@
interface I {
open fun foo(){}
}

open class A {
open fun foo(){}
}

class C : A(), I {
override fun equals(other: Any?): Boolean {
<selection><caret>return super<A>.equals(other)</selection>
}

override fun foo() {
super<A>.foo()
}

override fun foo() {
super<I>.foo()
}

override fun hashCode(): Int {
return super<A>.hashCode()
}

override fun toString(): String {
return super<A>.toString()
}
}
18 changes: 18 additions & 0 deletions idea/testData/codeInsight/overrideImplement/doNotOverrideFinal.kt
@@ -0,0 +1,18 @@
open class A {
fun a(){}
fun b(){}
}

interface I {
fun b()
}

abstract class B : A() {
open fun f(){}
abstract fun g()
fun h(){}
}

class C : B(), I {
<caret>
}
@@ -0,0 +1,36 @@
open class A {
fun a(){}
fun b(){}
}

interface I {
fun b()
}

abstract class B : A() {
open fun f(){}
abstract fun g()
fun h(){}
}

class C : B(), I {
override fun equals(other: Any?): Boolean {
<selection><caret>return super<B>.equals(other)</selection>
}

override fun f() {
super<B>.f()
}

override fun g() {
throw UnsupportedOperationException()
}

override fun hashCode(): Int {
return super<B>.hashCode()
}

override fun toString(): String {
return super<B>.toString()
}
}
21 changes: 21 additions & 0 deletions idea/testData/codeInsight/overrideImplement/superPreference.kt
@@ -0,0 +1,21 @@
abstract class A : I1 {
open fun a(){}
}

interface I1 {
fun i1()
fun i()
}

interface I2 {
fun i2()
fun a()
}

interface I3 {
fun i()
}

abstract class B : I2, A(), I3 {
<caret>
}

0 comments on commit 63dc843

Please sign in to comment.