Find Usages: Support primary constructors

#KT-7881 Fixed
This commit is contained in:
Alexey Sedunov
2015-06-08 18:42:13 +03:00
parent 1ea2c24562
commit e2d758463f
18 changed files with 188 additions and 9 deletions
@@ -79,7 +79,7 @@ public fun PsiReference.matchesTarget(candidateTarget: PsiElement): Boolean {
} }
} }
// TODO: Workaround for Kotlin constructor search in Java code. To be removed after refactoring of the search API // TODO: Workaround for Kotlin constructor search in Java code. To be removed after refactoring of the search API
else if (this is PsiJavaCodeReferenceElement && unwrappedCandidate is JetSecondaryConstructor) { else if (this is PsiJavaCodeReferenceElement && unwrappedCandidate is JetConstructor<*>) {
var parent = getElement().getParent() var parent = getElement().getParent()
if (parent is PsiAnonymousClass) { if (parent is PsiAnonymousClass) {
parent = parent.getParent() parent = parent.getParent()
@@ -104,7 +104,7 @@ private fun PsiElement.isConstructorOf(unwrappedCandidate: PsiElement) =
// call to Java constructor // call to Java constructor
(this is PsiMethod && isConstructor() && getContainingClass() == unwrappedCandidate) || (this is PsiMethod && isConstructor() && getContainingClass() == unwrappedCandidate) ||
// call to Kotlin constructor // call to Kotlin constructor
(this is JetSecondaryConstructor && getStrictParentOfType<JetClass>() == unwrappedCandidate) (this is JetConstructor<*> && getClassOrObject() == unwrappedCandidate)
fun AbstractJetReference<out JetExpression>.renameImplicitConventionalCall(newName: String?): JetExpression { fun AbstractJetReference<out JetExpression>.renameImplicitConventionalCall(newName: String?): JetExpression {
if (newName == null) return expression if (newName == null) return expression
@@ -86,7 +86,8 @@ fun PsiReference.isConstructorUsage(jetClassOrObject: JetClassOrObject): Boolean
val descriptor = getConstructorCallDescriptor() val descriptor = getConstructorCallDescriptor()
if (descriptor !is ConstructorDescriptor) return false if (descriptor !is ConstructorDescriptor) return false
return DescriptorToSourceUtils.descriptorToDeclaration(descriptor.getContainingDeclaration()) == jetClassOrObject val declaration = DescriptorToSourceUtils.descriptorToDeclaration(descriptor.getContainingDeclaration())
return declaration == jetClassOrObject || (declaration is JetConstructor<*> && declaration.getClassOrObject() == jetClassOrObject)
} }
checkJavaUsage() || checkKotlinUsage() checkJavaUsage() || checkKotlinUsage()
@@ -118,7 +119,7 @@ public fun PsiElement.processDelegationCallConstructorUsages(scope: SearchScope,
private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (JetConstructorDelegationCall) -> Unit) { private fun PsiElement.processDelegationCallKotlinConstructorUsages(scope: SearchScope, process: (JetConstructorDelegationCall) -> Unit) {
val element = unwrapped val element = unwrapped
val klass = when (element) { val klass = when (element) {
is JetSecondaryConstructor -> element.getClassOrObject() is JetConstructor<*> -> element.getClassOrObject()
is JetClass -> element is JetClass -> element
else -> return else -> return
} }
@@ -46,14 +46,14 @@ public class KotlinFindUsagesHandlerFactory(project: Project) : FindUsagesHandle
element is JetProperty || element is JetProperty ||
element is JetParameter || element is JetParameter ||
element is JetTypeParameter || element is JetTypeParameter ||
element is JetSecondaryConstructor element is JetConstructor<*>
public override fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean): FindUsagesHandler? { public override fun createFindUsagesHandler(element: PsiElement, forHighlightUsages: Boolean): FindUsagesHandler? {
val handler = when (element) { val handler = when (element) {
is JetClassOrObject -> is JetClassOrObject ->
KotlinFindClassUsagesHandler(element, this) KotlinFindClassUsagesHandler(element, this)
is JetNamedFunction, is JetProperty, is JetParameter, is JetSecondaryConstructor -> { is JetNamedFunction, is JetProperty, is JetParameter, is JetConstructor<*> -> {
val declaration = element as JetNamedDeclaration val declaration = element as JetNamedDeclaration
if (forHighlightUsages) return KotlinFindMemberUsagesHandler.getInstance(declaration, this) if (forHighlightUsages) return KotlinFindMemberUsagesHandler.getInstance(declaration, this)
@@ -123,7 +123,7 @@ public abstract class KotlinFindMemberUsagesHandler<T extends JetNamedDeclaratio
protected abstract UsagesSearchHelper<T> getSearchHelper(KotlinCallableFindUsagesOptions options); protected abstract UsagesSearchHelper<T> getSearchHelper(KotlinCallableFindUsagesOptions options);
private static Iterable<PsiMethod> getLightMethods(JetNamedDeclaration element) { private static Iterable<PsiMethod> getLightMethods(JetNamedDeclaration element) {
if (element instanceof JetNamedFunction || element instanceof JetSecondaryConstructor) { if (element instanceof JetFunction) {
PsiMethod method = LightClassUtil.getLightClassMethod((JetFunction) element); PsiMethod method = LightClassUtil.getLightClassMethod((JetFunction) element);
return method != null ? Collections.singletonList(method) : Collections.<PsiMethod>emptyList(); return method != null ? Collections.singletonList(method) : Collections.<PsiMethod>emptyList();
} }
@@ -163,7 +163,7 @@ public abstract class KotlinFindMemberUsagesHandler<T extends JetNamedDeclaratio
PsiMethod psiMethod = PsiMethod psiMethod =
element instanceof PsiMethod element instanceof PsiMethod
? (PsiMethod) element ? (PsiMethod) element
: element instanceof JetSecondaryConstructor : element instanceof JetConstructor
? LightClassUtil.getLightClassMethod((JetFunction) element) ? LightClassUtil.getLightClassMethod((JetFunction) element)
: null; : null;
if (psiMethod != null) { if (psiMethod != null) {
@@ -17,12 +17,22 @@
package org.jetbrains.kotlin.idea.search.ideaExtensions package org.jetbrains.kotlin.idea.search.ideaExtensions
import com.intellij.codeInsight.TargetElementEvaluator import com.intellij.codeInsight.TargetElementEvaluator
import com.intellij.codeInsight.TargetElementEvaluatorEx
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.psi.psiUtil.isAbstract import org.jetbrains.kotlin.psi.psiUtil.isAbstract
import com.intellij.psi.PsiReference import com.intellij.psi.PsiReference
import org.jetbrains.kotlin.psi.JetClass import org.jetbrains.kotlin.psi.JetClass
import org.jetbrains.kotlin.psi.JetPrimaryConstructor
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
public class JetTargetElementEvaluator : TargetElementEvaluator { public class JetTargetElementEvaluator : TargetElementEvaluatorEx {
override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is JetClass && element.isAbstract()) override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is JetClass && element.isAbstract())
override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? = null override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? = null
override fun isIdentifierPart(file: PsiFile, text: CharSequence?, offset: Int): Boolean {
// '(' is considered identifier part if it belongs to primary constructor without 'constructor' keyword
return file.findElementAt(offset)?.getNonStrictParentOfType<JetPrimaryConstructor>()?.getTextOffset() == offset
}
} }
@@ -0,0 +1,14 @@
// PSI_ELEMENT: com.intellij.psi.PsiMethod
// OPTIONS: usages
// FIND_BY_REF
// WITH_FILE_NAME
class J extends A {
constructor(n: Int) {
super(n);
}
static void test() {
new A();
new <caret>A(1);
}
}
@@ -0,0 +1,14 @@
open class A (n: Int) {
constructor(): this(1)
}
class B: A {
constructor(n: Int): super(n)
}
class C(): A(1)
fun test() {
A()
A(1)
}
@@ -0,0 +1,6 @@
[primaryConstructorByJavaRef.0.java] New instance creation (12: 13) new A(1);
[primaryConstructorByJavaRef.0.java] Unclassified usage (7: 9) super(n);
[primaryConstructorByJavaRef.1.kt] New instance creation (13: 5) A(1)
[primaryConstructorByJavaRef.1.kt] Supertype (9: 12) class C(): A(1)
[primaryConstructorByJavaRef.1.kt] Unclassified usage (2: 20) constructor(): this(1)
[primaryConstructorByJavaRef.1.kt] Unclassified usage (6: 26) constructor(n: Int): super(n)
@@ -0,0 +1,14 @@
// PSI_ELEMENT: com.intellij.psi.PsiMethod
// OPTIONS: usages
// FIND_BY_REF
// WITH_FILE_NAME
class J extends A {
constructor(n: Int) {
<caret>super(n);
}
static void test() {
new A();
new A(1);
}
}
@@ -0,0 +1,14 @@
open class A (n: Int) {
constructor(): this(1)
}
class B: A {
constructor(n: Int): super(n)
}
class C(): A(1)
fun test() {
A()
A(1)
}
@@ -0,0 +1,6 @@
[primaryConstructorByJavaSuperRef.0.java] New instance creation (12: 13) new A(1);
[primaryConstructorByJavaSuperRef.0.java] Unclassified usage (7: 9) super(n);
[primaryConstructorByJavaSuperRef.1.kt] New instance creation (13: 5) A(1)
[primaryConstructorByJavaSuperRef.1.kt] Supertype (9: 12) class C(): A(1)
[primaryConstructorByJavaSuperRef.1.kt] Unclassified usage (2: 20) constructor(): this(1)
[primaryConstructorByJavaSuperRef.1.kt] Unclassified usage (6: 26) constructor(n: Int): super(n)
@@ -0,0 +1,17 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.JetPrimaryConstructor
// OPTIONS: usages
// FIND_BY_REF
open class A (n: Int) {
constructor(): this(1)
}
class B: A {
constructor(n: Int): super(n)
}
class C(): A(1)
fun test() {
A()
<caret>A(1)
}
@@ -0,0 +1,4 @@
New instance creation (16: 5) A(1)
Supertype (12: 12) class C(): A(1)
Unclassified usage (5: 20) constructor(): this(1)
Unclassified usage (9: 26) constructor(n: Int): super(n)
@@ -0,0 +1,16 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.JetPrimaryConstructor
// OPTIONS: usages
open class A <caret>(n: Int) {
constructor(): this(1)
}
class B: A {
constructor(n: Int): super(n)
}
class C(): A(1)
fun test() {
A()
A(1)
}
@@ -0,0 +1,4 @@
New instance creation (15: 5) A(1)
Supertype (11: 12) class C(): A(1)
Unclassified usage (4: 20) constructor(): this(1)
Unclassified usage (8: 26) constructor(n: Int): super(n)
@@ -0,0 +1,16 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.JetPrimaryConstructor
// OPTIONS: usages
open class A <caret>constructor(n: Int) {
constructor(): this(1)
}
class B: A {
constructor(n: Int): super(n)
}
class C(): A(1)
fun test() {
A()
A(1)
}
@@ -0,0 +1,4 @@
New instance creation (15: 5) A(1)
Supertype (11: 12) class C(): A(1)
Unclassified usage (4: 20) constructor(): this(1)
Unclassified usage (8: 26) constructor(n: Int): super(n)
@@ -736,6 +736,33 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
} }
} }
@TestMetadata("idea/testData/findUsages/kotlin/findPrimaryConstructorUsages")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class FindPrimaryConstructorUsages extends AbstractJetFindUsagesTest {
public void testAllFilesPresentInFindPrimaryConstructorUsages() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/findUsages/kotlin/findPrimaryConstructorUsages"), Pattern.compile("^(.+)\\.0\\.kt$"), true);
}
@TestMetadata("primaryConstructorByRef.0.kt")
public void testPrimaryConstructorByRef() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findPrimaryConstructorUsages/primaryConstructorByRef.0.kt");
doTest(fileName);
}
@TestMetadata("primaryConstructorNoKeyword.0.kt")
public void testPrimaryConstructorNoKeyword() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findPrimaryConstructorUsages/primaryConstructorNoKeyword.0.kt");
doTest(fileName);
}
@TestMetadata("primaryConstructorWithKeyword.0.kt")
public void testPrimaryConstructorWithKeyword() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findPrimaryConstructorUsages/primaryConstructorWithKeyword.0.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/findUsages/kotlin/findPropertyUsages") @TestMetadata("idea/testData/findUsages/kotlin/findPropertyUsages")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -1056,6 +1083,18 @@ public class JetFindUsagesTestGenerated extends AbstractJetFindUsagesTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("primaryConstructorByJavaRef.0.java")
public void testPrimaryConstructorByJavaRef() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/java/findConstructorUsages/primaryConstructorByJavaRef.0.java");
doTest(fileName);
}
@TestMetadata("primaryConstructorByJavaSuperRef.0.java")
public void testPrimaryConstructorByJavaSuperRef() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/java/findConstructorUsages/primaryConstructorByJavaSuperRef.0.java");
doTest(fileName);
}
@TestMetadata("secondaryConstructorByJavaNewExpression.0.java") @TestMetadata("secondaryConstructorByJavaNewExpression.0.java")
public void testSecondaryConstructorByJavaNewExpression() throws Exception { public void testSecondaryConstructorByJavaNewExpression() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/java/findConstructorUsages/secondaryConstructorByJavaNewExpression.0.java"); String fileName = JetTestUtils.navigationMetadata("idea/testData/findUsages/java/findConstructorUsages/secondaryConstructorByJavaNewExpression.0.java");