diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 227bda213c4..2de48a435ab 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -509,6 +509,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodsWithGenericsSearcher.kt b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodsWithGenericsSearcher.kt new file mode 100644 index 00000000000..c5c87c01c92 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinOverridingMethodsWithGenericsSearcher.kt @@ -0,0 +1,84 @@ +/* + * 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.search.ideaExtensions + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiMethod +import com.intellij.psi.search.searches.ClassInheritorsSearch +import com.intellij.psi.search.searches.OverridingMethodsSearch +import com.intellij.util.Processor +import com.intellij.util.QueryExecutor +import org.jetbrains.kotlin.asJava.KotlinLightClass +import org.jetbrains.kotlin.asJava.KotlinLightMethod +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.util.application.runReadAction +import org.jetbrains.kotlin.psi.JetCallableDeclaration +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.OverrideResolver + +public class KotlinOverridingMethodsWithGenericsSearcher : QueryExecutor { + override fun execute(p: OverridingMethodsSearch.SearchParameters, consumer: Processor): Boolean { + val method = p.method + if (method !is KotlinLightMethod) return true + + val declaration = method.getOrigin() as? JetCallableDeclaration + if (declaration == null) return true + + val callDescriptor = declaration.resolveToDescriptor() + if (callDescriptor !is CallableDescriptor) return true + + // Java overriding method search can't find overloads with primitives types, so + // we do additional search for such methods. + if (!callDescriptor.valueParameters.any { it.type.constructor.declarationDescriptor is TypeParameterDescriptor }) return true + + val parentClass = runReadAction { method.containingClass }!! + + return ClassInheritorsSearch.search(parentClass, p.scope, true).forEach(Processor { inheritor: PsiClass -> + val found = runReadAction { + findOverridingMethod(inheritor, declaration) + } + + found == null || (consumer.process(found) && p.isCheckDeep) + }) + } + + private fun findOverridingMethod(inheritor: PsiClass, callableDeclaration: JetCallableDeclaration): PsiMethod? { + // Leave Java classes search to JavaOverridingMethodsSearcher + if (inheritor !is KotlinLightClass) return null + + val name = callableDeclaration.name + val methodsByName = inheritor.findMethodsByName(name, false) + + for (lightMethodCandidate in methodsByName) { + val candidateDescriptor = (lightMethodCandidate as? KotlinLightMethod)?.getOrigin()?.resolveToDescriptor() ?: continue + if (candidateDescriptor !is CallableMemberDescriptor) continue + + val overriddenDescriptors = OverrideResolver.getDirectlyOverriddenDeclarations(candidateDescriptor) + for (candidateSuper in overriddenDescriptors) { + val candidateDeclaration = DescriptorToSourceUtils.descriptorToDeclaration(candidateSuper) + if (candidateDeclaration == callableDeclaration) { + return lightMethodCandidate + } + } + } + + return null + } +} \ No newline at end of file diff --git a/idea/testData/navigation/gotoSuper/SuperWithNativeToGenericMapping.test b/idea/testData/navigation/gotoSuper/SuperWithNativeToGenericMapping.test new file mode 100644 index 00000000000..571d875cd7a --- /dev/null +++ b/idea/testData/navigation/gotoSuper/SuperWithNativeToGenericMapping.test @@ -0,0 +1,18 @@ +// FILE: b.kt +open class Foo { + open fun bar(t: T) {} +} + +class Bar() : Foo() { + override fun bar(ts: Int) { + } +} +// FILE: a.kt +open class Foo { + open fun bar(t: T) {} +} + +class Bar() : Foo() { + override fun bar(ts: Int) { + } +} \ No newline at end of file diff --git a/idea/testData/navigation/implementations/ImplementGenericWithPrimitives.kt b/idea/testData/navigation/implementations/ImplementGenericWithPrimitives.kt new file mode 100644 index 00000000000..75439f2c22b --- /dev/null +++ b/idea/testData/navigation/implementations/ImplementGenericWithPrimitives.kt @@ -0,0 +1,53 @@ +open class Foo { + open fun bar(t: T, u: U): Z { + return null + } +} + +class AllTypes() : Foo() { + override fun bar(i: Int, d: Double): Int = 42 +} + +class SomeParameters() : Foo() { + override fun bar(d: Double, a: Any): Int = 42 +} + +class ReturnType() : Foo() { + override fun bar(a: Any, s: String): Byte = 12.toByte() +} + +class ParameterStillGeneric() : Foo() { + override fun bar(i: Int, z: Z): String = "" +} + +class ReturnStillGeneric() : Foo() { + override fun bar(i: Int, i2: Int): A = null +} + +open class ThroughProxyAllGenerics: Foo() +class ThroughProxyAllGenericsImpl: ThroughProxyAllGenerics() { + override fun bar(t: Double, i: Int): Boolean = true +} + +open class ThroughProxySomeGenerics: Foo() +class ThroughProxySomeGenericsImpl: ThroughProxySomeGenerics() { + override fun bar(t: Double, i: Int): Boolean = true +} + +class NoPrimitives: Foo() { + override fun bar(t: String, u: Any): String = "" +} + +class NoOverride: Foo() { + // Error: Not override + override fun bar(t: Any, u: String): String = "" +} + +// REF: "(in AllTypes).bar(Int,Double)" +// REF: "(in NoPrimitives).bar(String,Any)" +// REF: "(in ParameterStillGeneric).bar(Int,Z)" +// REF: "(in ReturnStillGeneric).bar(Int,Int)" +// REF: "(in ReturnType).bar(Any,String)" +// REF: "(in SomeParameters).bar(Double,Any)" +// REF: "(in ThroughProxyAllGenericsImpl).bar(Double,Int)" +// REF: "(in ThroughProxySomeGenericsImpl).bar(Double,Int)" diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoSuperTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoSuperTestGenerated.java index 8a7ec6497a9..f0f5a8b1cbf 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoSuperTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/GotoSuperTestGenerated.java @@ -89,6 +89,12 @@ public class GotoSuperTestGenerated extends AbstractGotoSuperTest { doTest(fileName); } + @TestMetadata("SuperWithNativeToGenericMapping.test") + public void testSuperWithNativeToGenericMapping() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoSuper/SuperWithNativeToGenericMapping.test"); + doTest(fileName); + } + @TestMetadata("TraitSimple.test") public void testTraitSimple() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/gotoSuper/TraitSimple.test"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java index dba1b8d349b..c299685f406 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java @@ -71,6 +71,12 @@ public class KotlinGotoImplementationTestGenerated extends AbstractKotlinGotoImp doTest(fileName); } + @TestMetadata("ImplementGenericWithPrimitives.kt") + public void testImplementGenericWithPrimitives() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/implementations/ImplementGenericWithPrimitives.kt"); + doTest(fileName); + } + @TestMetadata("OverridesInEnumEntries.kt") public void testOverridesInEnumEntries() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/navigation/implementations/OverridesInEnumEntries.kt");