diff --git a/annotations/com/intellij/psi/annotations.xml b/annotations/com/intellij/psi/annotations.xml index 32547c9ac4e..6fa70cb6668 100644 --- a/annotations/com/intellij/psi/annotations.xml +++ b/annotations/com/intellij/psi/annotations.xml @@ -1,4 +1,7 @@ + + + diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt b/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt index 189ac974c83..35aca069db8 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/psiUtil/jetPsiUtil.kt @@ -37,6 +37,8 @@ import org.jetbrains.jet.lang.psi.JetPsiFactory import kotlin.test.assertTrue import com.intellij.psi.search.SearchScope import com.intellij.psi.search.PsiSearchScopeUtil +import org.jetbrains.jet.lang.psi.JetClassBody +import org.jetbrains.jet.lang.psi.JetParameterList fun PsiElement.getParentByTypesAndPredicate( strict : Boolean = false, vararg parentClasses : Class, predicate: (T) -> Boolean @@ -155,4 +157,17 @@ fun SearchScope.contains(element: PsiElement): Boolean = PsiSearchScopeUtil.isIn fun JetClass.isInheritable(): Boolean { return isTrait() || hasModifier(JetTokens.OPEN_KEYWORD) +} + +fun JetDeclaration.isOverridable(): Boolean { + val parent = getParent() + if (!(parent is JetClassBody || parent is JetParameterList)) return false + + val klass = parent.getParent() + if (!(klass is JetClass && klass.isInheritable())) return false + + if (hasModifier(JetTokens.FINAL_KEYWORD) || hasModifier(JetTokens.PRIVATE_KEYWORD)) return false + + return klass.isTrait() || + hasModifier(JetTokens.ABSTRACT_KEYWORD) || hasModifier(JetTokens.OPEN_KEYWORD) || hasModifier(JetTokens.OVERRIDE_KEYWORD) } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/search/declarationsSearch/overridersSearch.kt b/idea/src/org/jetbrains/jet/plugin/search/declarationsSearch/overridersSearch.kt new file mode 100644 index 00000000000..1eb43a7d3be --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/search/declarationsSearch/overridersSearch.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2010-2013 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.jet.plugin.search.declarationsSearch + +import com.intellij.psi.PsiMethod +import com.intellij.psi.util.PsiUtil +import org.jetbrains.jet.lang.psi.JetDeclaration +import com.intellij.psi.PsiElement +import com.intellij.util.Processor +import com.intellij.util.Query +import com.intellij.psi.search.GlobalSearchScope +import java.util.HashMap +import org.jetbrains.jet.lang.psi.psiUtil.* +import org.jetbrains.jet.lang.psi.JetNamedFunction +import java.util.Collections +import org.jetbrains.jet.asJava.LightClassUtil +import org.jetbrains.jet.lang.psi.JetProperty +import org.jetbrains.jet.lang.psi.JetPropertyAccessor +import com.intellij.psi.PsiClass +import org.jetbrains.jet.lang.psi.JetParameter +import com.intellij.psi.util.TypeConversionUtil +import com.intellij.psi.PsiSubstitutor +import com.intellij.psi.util.MethodSignatureUtil +import com.intellij.psi.PsiModifier +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.search.searches.DirectClassInheritorsSearch +import com.intellij.util.EmptyQuery +import com.intellij.util.MergeQuery +import com.intellij.util.UniqueResultsQuery +import com.intellij.util.containers.ContainerUtil + +fun PsiElement.isOverridableElement(): Boolean = when (this) { + is PsiMethod -> PsiUtil.canBeOverriden(this) + is JetDeclaration -> isOverridable() + else -> false +} + +public fun HierarchySearchRequest.searchOverriders(): Query { + val psiMethods: List = when (originalElement) { + is JetNamedFunction -> Collections.singletonList(LightClassUtil.getLightClassMethod(originalElement)) + is JetProperty -> LightClassUtil.getLightClassPropertyMethods(originalElement).iterator().toArrayList() + is JetParameter -> LightClassUtil.getLightClassPropertyMethods(originalElement).iterator().toArrayList() + is JetPropertyAccessor -> Collections.singletonList(LightClassUtil.getLightClassAccessorMethod(originalElement)) + is PsiMethod -> Collections.singletonList(originalElement) + else -> return EmptyQuery.getEmptyQuery() + } + return psiMethods + .map { psiMethod -> KotlinPsiMethodOverridersSearch.search(copy(psiMethod)) } + .reduce {(query1, query2) -> MergeQuery(query1, query2)} +} + +public object KotlinPsiMethodOverridersSearch : HierarchySearch(PsiMethodOverridingHierarchyTraverser) { + fun searchDirectOverriders(psiMethod: PsiMethod): Iterable { + fun PsiMethod.isAcceptable(inheritor: PsiClass, baseMethod: PsiMethod, baseClass: PsiClass): Boolean = + when { + hasModifierProperty(PsiModifier.STATIC) -> false + baseMethod.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) -> + JavaPsiFacade.getInstance(getProject()).arePackagesTheSame(baseClass, inheritor) + else -> true + } + + val psiClass = psiMethod.getContainingClass() + if (psiClass == null) return Collections.emptyList() + + val classToMethod = HashMap() + val classTraverser = object : HierarchyTraverser { + override fun nextElements(current: PsiClass): Iterable = + DirectClassInheritorsSearch.search( + aClass = current, + scope = GlobalSearchScope.allScope(current.getProject()), + checkInheritance = true, + includeAnonymous = true + ) + + override fun shouldDescend(element: PsiClass): Boolean = + element.isInheritable() && !classToMethod.containsKey(element) + } + + classTraverser.forEach(psiClass) { inheritor -> + val substitutor = TypeConversionUtil.getSuperClassSubstitutor(psiClass, inheritor, PsiSubstitutor.EMPTY) + val signature = psiMethod.getSignature(substitutor) + val candidate = MethodSignatureUtil.findMethodBySuperSignature(inheritor, signature, false) + if (candidate != null && candidate.isAcceptable(inheritor, psiMethod, psiClass)) { + classToMethod.put(inheritor, candidate) + } + } + + return classToMethod.values() + } + + protected override fun isApplicable(request: HierarchySearchRequest): Boolean = + request.originalElement.isOverridableElement() + + override fun doSearchDirect(request: HierarchySearchRequest, consumer: Processor) { + searchDirectOverriders(request.originalElement).forEach { method -> consumer.process(method) } + } +} + +object PsiMethodOverridingHierarchyTraverser: HierarchyTraverser { + override fun nextElements(current: PsiMethod): Iterable = KotlinPsiMethodOverridersSearch.searchDirectOverriders(current) + override fun shouldDescend(element: PsiMethod): Boolean = PsiUtil.canBeOverriden(element) +} \ No newline at end of file