diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/ClsWrapperStubPsiFactory.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/ClsWrapperStubPsiFactory.java index 78166d24883..09770c62217 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/ClsWrapperStubPsiFactory.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/ClsWrapperStubPsiFactory.java @@ -27,19 +27,20 @@ import com.intellij.psi.impl.java.stubs.*; import com.intellij.psi.stubs.StubBase; import com.intellij.psi.stubs.StubElement; import org.jetbrains.annotations.NotNull; -import org.jetbrains.kotlin.psi.KtDeclaration; +import org.jetbrains.annotations.Nullable; class ClsWrapperStubPsiFactory extends StubPsiFactory { - public static final Key ORIGIN_ELEMENT = Key.create("ORIGIN_ELEMENT"); + public static final Key ORIGIN = Key.create("ORIGIN"); private final StubPsiFactory delegate = new ClsStubPsiFactory(); - public static KtDeclaration getOriginalDeclaration(PsiMember member) { + @Nullable + public static LightMemberOrigin getMemberOrigin(@NotNull PsiMember member) { if (member instanceof ClsRepositoryPsiElement) { StubElement stubElement = ((ClsRepositoryPsiElement) member).getStub(); if (stubElement instanceof UserDataHolder) { - PsiElement original = ((UserDataHolder) stubElement).getUserData(ORIGIN_ELEMENT); - if (original instanceof KtDeclaration) { - return (KtDeclaration) original; + LightElementOrigin origin = ((UserDataHolder) stubElement).getUserData(ORIGIN); + if (origin instanceof LightMemberOrigin) { + return (LightMemberOrigin) origin; } } } @@ -48,8 +49,8 @@ class ClsWrapperStubPsiFactory extends StubPsiFactory { } @Override - public PsiClass createClass(PsiClassStub stub) { - final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT); + public PsiClass createClass(@NotNull PsiClassStub stub) { + final PsiElement origin = getOriginalElement(stub); if (origin == null) return delegate.createClass(stub); return new ClsClassImpl(stub) { @@ -61,6 +62,12 @@ class ClsWrapperStubPsiFactory extends StubPsiFactory { }; } + @Nullable + private static PsiElement getOriginalElement(@NotNull StubElement stub) { + LightElementOrigin origin = ((StubBase) stub).getUserData(ORIGIN); + return origin != null ? origin.getOriginalElement() : null; + } + @Override public PsiAnnotation createAnnotation(PsiAnnotationStub stub) { return delegate.createAnnotation(stub); @@ -78,7 +85,7 @@ class ClsWrapperStubPsiFactory extends StubPsiFactory { @Override public PsiField createField(PsiFieldStub stub) { - final PsiElement origin = ((StubBase) stub).getUserData(ORIGIN_ELEMENT); + final PsiElement origin = getOriginalElement(stub); if (origin == null) return delegate.createField(stub); if (stub.isEnumConstant()) { return new ClsEnumConstantImpl(stub) { diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightMethod.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightMethod.kt index 43e3554f3d0..486033a164b 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightMethod.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtLightMethod.kt @@ -25,14 +25,19 @@ import com.intellij.psi.util.* import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.idea.KotlinLanguage import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind -public interface KtLightMethod : PsiMethod, KtLightElement +public interface KtLightMethod : PsiMethod, KtLightElement { + val isDelegated: Boolean +} sealed class KtLightMethodImpl( private val delegate: PsiMethod, - private val origin: KtDeclaration?, + private val lightMethodOrigin: LightMemberOrigin?, containingClass: KtLightClass -): LightMethod(delegate.manager, delegate, containingClass), KtLightMethod { +) : LightMethod(delegate.manager, delegate, containingClass), KtLightMethod { + private val origin = lightMethodOrigin?.originalElement as? KtDeclaration + override fun getContainingClass(): KtLightClass = super.getContainingClass() as KtLightClass private val paramsList: CachedValue by lazy { @@ -70,7 +75,11 @@ sealed class KtLightMethodImpl( override fun getOrigin() = origin override fun getParent(): PsiElement? = containingClass override fun getText() = origin?.text ?: "" - override fun getTextRange() = origin?.textRange ?: TextRange.EMPTY_RANGE + override fun getTextRange() = origin?.textRange ?: TextRange.EMPTY_RANGE + + override val isDelegated: Boolean + get() = lightMethodOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION + || lightMethodOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION_TO_DEFAULT_IMPLS override fun accept(visitor: PsiElementVisitor) { if (visitor is JavaElementVisitor) { @@ -114,7 +123,7 @@ sealed class KtLightMethodImpl( } override fun copy(): PsiElement { - return Factory.create(delegate, origin?.copy() as? KtDeclaration, containingClass) + return Factory.create(delegate, lightMethodOrigin?.copy(), containingClass) } override fun getUseScope() = origin?.useScope ?: super.getUseScope() @@ -143,14 +152,14 @@ sealed class KtLightMethodImpl( override fun hashCode(): Int = ((name.hashCode() * 31 + (origin?.hashCode() ?: 0)) * 31 + containingClass.hashCode()) * 31 + delegate.hashCode() override fun toString(): String = "${this.javaClass.simpleName}:$name" - + private class KtLightMethodForDeclaration( - delegate: PsiMethod, origin: KtDeclaration?, containingClass: KtLightClass + delegate: PsiMethod, origin: LightMemberOrigin?, containingClass: KtLightClass ) : KtLightMethodImpl(delegate, origin, containingClass) private class KtLightAnnotationMethod( delegate: PsiAnnotationMethod, - origin: KtDeclaration?, + origin: LightMemberOrigin?, containingClass: KtLightClass ) : KtLightMethodImpl(delegate, origin, containingClass), PsiAnnotationMethod { override fun getDefaultValue() = getDelegate().defaultValue @@ -159,7 +168,7 @@ sealed class KtLightMethodImpl( companion object Factory { fun create( - delegate: PsiMethod, origin: KtDeclaration?, containingClass: KtLightClass + delegate: PsiMethod, origin: LightMemberOrigin?, containingClass: KtLightClass ): KtLightMethodImpl { return when (delegate) { is PsiAnnotationMethod -> KtLightAnnotationMethod(delegate, origin, containingClass) diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtWrappingLightClass.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtWrappingLightClass.java index 0245c5bbc10..db1f26e2eea 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtWrappingLightClass.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KtWrappingLightClass.java @@ -33,7 +33,10 @@ import kotlin.jvm.functions.Function1; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.KotlinLanguage; -import org.jetbrains.kotlin.psi.*; +import org.jetbrains.kotlin.psi.KtClassOrObject; +import org.jetbrains.kotlin.psi.KtDeclaration; +import org.jetbrains.kotlin.psi.KtProperty; +import org.jetbrains.kotlin.psi.KtPropertyAccessor; import java.util.List; @@ -119,8 +122,8 @@ public abstract class KtWrappingLightClass extends AbstractLightClass implements return ContainerUtil.map(getDelegate().getFields(), new Function() { @Override public PsiField fun(PsiField field) { - KtDeclaration declaration = ClsWrapperStubPsiFactory.getOriginalDeclaration(field); - return KtLightFieldImpl.Factory.create(declaration, field, KtWrappingLightClass.this); + LightMemberOrigin origin = ClsWrapperStubPsiFactory.getMemberOrigin(field); + return KtLightFieldImpl.Factory.create(origin != null ? origin.getOriginalElement() : null, field, KtWrappingLightClass.this); } }); } @@ -131,12 +134,14 @@ public abstract class KtWrappingLightClass extends AbstractLightClass implements return ArraysKt.map(getDelegate().getMethods(), new Function1() { @Override public PsiMethod invoke(PsiMethod method) { - KtDeclaration declaration = ClsWrapperStubPsiFactory.getOriginalDeclaration(method); - if (declaration instanceof KtPropertyAccessor) { - declaration = PsiTreeUtil.getParentOfType(declaration, KtProperty.class); + LightMemberOrigin origin = ClsWrapperStubPsiFactory.getMemberOrigin(method); + KtDeclaration originalElement = origin != null ? origin.getOriginalElement() : null; + if (originalElement instanceof KtPropertyAccessor) { + //noinspection ConstantConditions + origin = origin.copy(PsiTreeUtil.getParentOfType(originalElement, KtProperty.class), origin.getOriginKind()); } - return KtLightMethodImpl.Factory.create(method, declaration, KtWrappingLightClass.this); + return KtLightMethodImpl.Factory.create(method, origin, KtWrappingLightClass.this); } }); } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightElementOrigin.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightElementOrigin.kt new file mode 100644 index 00000000000..eac3ff4072a --- /dev/null +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightElementOrigin.kt @@ -0,0 +1,57 @@ +/* + * 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.asJava + +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind + + +interface LightElementOrigin { + val originalElement: PsiElement? + val originKind: JvmDeclarationOriginKind? + + object None : LightElementOrigin { + override val originalElement: PsiElement? + get() = null + override val originKind: JvmDeclarationOriginKind? + get() = null + + override fun toString() = "NONE" + } +} + +fun JvmDeclarationOrigin.toLightMemberOrigin(): LightElementOrigin { + val originalElement = element + return when (originalElement) { + is KtDeclaration -> LightMemberOrigin(originalElement, originKind) + else -> LightElementOrigin.None + } +} + +data class LightMemberOrigin(override val originalElement: KtDeclaration, override val originKind: JvmDeclarationOriginKind) : LightElementOrigin + +data class LightClassOrigin(override val originalElement: PsiElement?) : LightElementOrigin { + override val originKind: JvmDeclarationOriginKind? get() = null +} + +fun PsiElement?.toLightClassOrigin(): LightElementOrigin { + return if (this != null) LightClassOrigin(this) else LightElementOrigin.None +} + +fun LightMemberOrigin.copy() = LightMemberOrigin(originalElement.copy() as KtDeclaration, originKind) \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/StubClassBuilder.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/StubClassBuilder.java index b8543c12bdf..32ea92e15d6 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/StubClassBuilder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/StubClassBuilder.java @@ -92,7 +92,7 @@ public class StubClassBuilder extends AbstractClassBuilder { parentStack.push(v.getResult()); } - ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin); + ((StubBase) v.getResult()).putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightClassOrigin(origin)); } @NotNull @@ -109,7 +109,7 @@ public class StubClassBuilder extends AbstractClassBuilder { if (internalVisitor != EMPTY_METHOD_VISITOR) { // If stub for method generated - markLastChild(origin.getElement()); + markLastChild(origin); } return internalVisitor; @@ -129,22 +129,24 @@ public class StubClassBuilder extends AbstractClassBuilder { if (internalVisitor != EMPTY_FIELD_VISITOR) { // If stub for field generated - markLastChild(origin.getElement()); + markLastChild(origin); } return internalVisitor; } - private void markLastChild(@Nullable PsiElement origin) { + private void markLastChild(@NotNull JvmDeclarationOrigin origin) { List children = v.getResult().getChildrenStubs(); StubBase last = (StubBase) children.get(children.size() - 1); - PsiElement oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT); + LightElementOrigin oldOrigin = last.getUserData(ClsWrapperStubPsiFactory.ORIGIN); if (oldOrigin != null) { - throw new IllegalStateException("Rewriting origin element: " + oldOrigin.getText() + " for stub " + last.toString()); + PsiElement originalElement = oldOrigin.getOriginalElement(); + throw new IllegalStateException("Rewriting origin element: " + + (originalElement != null ? originalElement.getText() : null) + " for stub " + last.toString()); } - last.putUserData(ClsWrapperStubPsiFactory.ORIGIN_ELEMENT, origin); + last.putUserData(ClsWrapperStubPsiFactory.ORIGIN, LightElementOriginKt.toLightMemberOrigin(origin)); } @Override diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.java b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.java index d2c79d00e25..b0ab06d8c14 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.java +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinDefinitionsSearcher.java @@ -41,6 +41,7 @@ public class KotlinDefinitionsSearcher implements QueryExecutor consumer) { PsiElement element = queryParameters.getElement(); SearchScope scope = queryParameters.getScope(); + consumer = skipDelegatedMethodsConsumer(consumer); if (element instanceof KtClass) { return processClassImplementations((KtClass) element, consumer); @@ -64,6 +65,23 @@ public class KotlinDefinitionsSearcher implements QueryExecutor skipDelegatedMethodsConsumer(@NotNull final Processor baseConsumer) { + return new Processor() { + @Override + public boolean process(PsiElement element) { + if (isDelegated(element)) { + return true; + } + return baseConsumer.process(element); + } + }; + } + + private static boolean isDelegated(@NotNull PsiElement element) { + return element instanceof KtLightMethod && ((KtLightMethod) element).isDelegated(); + } + private static boolean processClassImplementations(final KtClass klass, Processor consumer) { PsiClass psiClass = ApplicationManager.getApplication().runReadAction(new Computable() { @Override @@ -122,6 +140,8 @@ public class KotlinDefinitionsSearcher implements QueryExecutorf() { + + } +} + +class A : I + +class B : I { + override fun f() { + } +} + +class C : I + +interface II: I +interface III: I { + override fun f() { + } +} + +// REF: (in testing.B).f() +// REF: (in testing.III).f() + diff --git a/idea/testData/navigation/implementations/DefaultImplProperty.kt b/idea/testData/navigation/implementations/DefaultImplProperty.kt new file mode 100644 index 00000000000..88363c5df8a --- /dev/null +++ b/idea/testData/navigation/implementations/DefaultImplProperty.kt @@ -0,0 +1,24 @@ +package testing + +interface I { + val p: Int + get() = 0 +} + +class A : I + +class B : I { + override val p = 5 +} + +class C : I + +interface II: I +interface III : I { + override val p: Int get() = 1 +} + +// REF: (in testing.B).p +// REF: (in testing.III).p + + diff --git a/idea/testData/navigation/implementations/DelegatedAndDefaultImplFunction.kt b/idea/testData/navigation/implementations/DelegatedAndDefaultImplFunction.kt new file mode 100644 index 00000000000..a7cdcd598c9 --- /dev/null +++ b/idea/testData/navigation/implementations/DelegatedAndDefaultImplFunction.kt @@ -0,0 +1,36 @@ +package testing + +interface I { + fun f() { + + } +} + +class A : I + +class B : I { + override fun f() { + } +} + +class C : I + +interface II: I +interface III: I { + override fun f() { + } +} + +class A1(i: I) : I by i + +class B1(i: I) : I by i { + override fun f() { + } +} + +class C1(i: I) : I by i + +// REF: (in testing.B).f() +// REF: (in testing.B1).f() +// REF: (in testing.III).f() + diff --git a/idea/testData/navigation/implementations/DelegatedFunction.kt b/idea/testData/navigation/implementations/DelegatedFunction.kt new file mode 100644 index 00000000000..4eb29777ad3 --- /dev/null +++ b/idea/testData/navigation/implementations/DelegatedFunction.kt @@ -0,0 +1,19 @@ +package testing + +interface I { + fun f() { + + } +} + +class A(i: I) : I by i + +class B(i: I) : I by i { + override fun f() { + } +} + +class C(i: I) : I by i + +// REF: (in testing.B).f() + diff --git a/idea/testData/navigation/implementations/DelegatedProperty.kt b/idea/testData/navigation/implementations/DelegatedProperty.kt new file mode 100644 index 00000000000..3247ee76e19 --- /dev/null +++ b/idea/testData/navigation/implementations/DelegatedProperty.kt @@ -0,0 +1,19 @@ +package testing + +interface I { + val p: Int + get() = 0 +} + +class A(i: I) : I by i + +class B(i: I) : I by i { + override val p = 5 +} + +class C(i: I) : I by i + + +// REF: (in testing.B).p + + diff --git a/idea/testData/navigation/implementations/FakeOverride.kt b/idea/testData/navigation/implementations/FakeOverride.kt new file mode 100644 index 00000000000..ae87d4d1338 --- /dev/null +++ b/idea/testData/navigation/implementations/FakeOverride.kt @@ -0,0 +1,16 @@ +package testing + +open class C { + open fun f() { + + } +} + +class A : C() + +class B : C() { + override fun f() { + } +} + +// REF: (in testing.B).f() diff --git a/idea/testData/navigation/implementations/GenericDelegatedAndDefaultImplFunction.kt b/idea/testData/navigation/implementations/GenericDelegatedAndDefaultImplFunction.kt new file mode 100644 index 00000000000..340becc8813 --- /dev/null +++ b/idea/testData/navigation/implementations/GenericDelegatedAndDefaultImplFunction.kt @@ -0,0 +1,36 @@ +package testing + +interface I { + fun f(): T { + + } +} + +class A : I + +class B : I { + override fun f(): T { + } +} + +class C : I + +interface II: I +interface III: I { + override fun f(): T { + } +} + +class A1(i: I) : I by i + +class B1(i: I) : I by i { + override fun f(): T { + } +} + +class C1(i: I) : I by i + +// REF: (in testing.B).f() +// REF: (in testing.B1).f() +// REF: (in testing.III).f() + diff --git a/idea/testData/navigation/implementations/GenericFakeOverride.kt b/idea/testData/navigation/implementations/GenericFakeOverride.kt new file mode 100644 index 00000000000..cd1dbc9bb2c --- /dev/null +++ b/idea/testData/navigation/implementations/GenericFakeOverride.kt @@ -0,0 +1,15 @@ +package testing + +open class C { + open fun f(): T { + + } +} + +class A : C() + +class B : C() { + override fun f() = "A" +} + +// REF: (in testing.B).f() diff --git a/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java index fb87cc7055f..dc7ceb85c44 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/navigation/KotlinGotoImplementationTestGenerated.java @@ -59,18 +59,66 @@ public class KotlinGotoImplementationTestGenerated extends AbstractKotlinGotoImp doTest(fileName); } + @TestMetadata("DefaultImplFunction.kt") + public void testDefaultImplFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/DefaultImplFunction.kt"); + doTest(fileName); + } + + @TestMetadata("DefaultImplProperty.kt") + public void testDefaultImplProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/DefaultImplProperty.kt"); + doTest(fileName); + } + + @TestMetadata("DelegatedAndDefaultImplFunction.kt") + public void testDelegatedAndDefaultImplFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/DelegatedAndDefaultImplFunction.kt"); + doTest(fileName); + } + + @TestMetadata("DelegatedFunction.kt") + public void testDelegatedFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/DelegatedFunction.kt"); + doTest(fileName); + } + + @TestMetadata("DelegatedProperty.kt") + public void testDelegatedProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/DelegatedProperty.kt"); + doTest(fileName); + } + @TestMetadata("EnumEntriesInheritance.kt") public void testEnumEntriesInheritance() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/EnumEntriesInheritance.kt"); doTest(fileName); } + @TestMetadata("FakeOverride.kt") + public void testFakeOverride() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/FakeOverride.kt"); + doTest(fileName); + } + @TestMetadata("FunctionOverrideNavigation.kt") public void testFunctionOverrideNavigation() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/FunctionOverrideNavigation.kt"); doTest(fileName); } + @TestMetadata("GenericDelegatedAndDefaultImplFunction.kt") + public void testGenericDelegatedAndDefaultImplFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/GenericDelegatedAndDefaultImplFunction.kt"); + doTest(fileName); + } + + @TestMetadata("GenericFakeOverride.kt") + public void testGenericFakeOverride() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/GenericFakeOverride.kt"); + doTest(fileName); + } + @TestMetadata("ImplementGenericWithPrimitives.kt") public void testImplementGenericWithPrimitives() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/navigation/implementations/ImplementGenericWithPrimitives.kt");