From 3bbbfc3b5b8a4c04b56b5c836864e0fec428bffe Mon Sep 17 00:00:00 2001 From: Vyacheslav Gerasimov Date: Fri, 11 Aug 2017 20:46:31 +0200 Subject: [PATCH] UAST: Fix receiverType for JavaUCallExpression Return correct receiver class for a method call with implicit receiver instead of super class, when method defined in one of super classes #IDEA-177417 Fixed --- .../java/expressions/javaUCallExpressions.kt | 50 ++++++---- uast/uast-tests/java/Simple/ReceiverType.java | 93 +++++++++++++++++++ .../uast/test/java/JavaUastApiTest.kt | 33 +++++++ 3 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 uast/uast-tests/java/Simple/ReceiverType.java diff --git a/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt b/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt index 34f1f1d540e8..cd1bcd14c733 100644 --- a/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt +++ b/uast/uast-java/src/org/jetbrains/uast/java/expressions/javaUCallExpressions.kt @@ -16,7 +16,7 @@ package org.jetbrains.uast.java import com.intellij.psi.* -import com.intellij.psi.util.PsiTypesUtil +import com.intellij.psi.util.* import org.jetbrains.uast.* import org.jetbrains.uast.psi.UElementWithLocation @@ -52,9 +52,8 @@ class JavaUCallExpression( override fun resolve() = psi.resolveMethod() - override fun getStartOffset(): Int { - return psi.methodExpression.referenceNameElement?.textOffset ?: psi.methodExpression.textOffset - } + override fun getStartOffset(): Int = + psi.methodExpression.referenceNameElement?.textOffset ?: psi.methodExpression.textOffset override fun getEndOffset() = psi.textRange.endOffset @@ -72,10 +71,27 @@ class JavaUCallExpression( if (qualifierType != null) { return qualifierType } - + val method = resolve() ?: return null if (method.hasModifierProperty(PsiModifier.STATIC)) return null - return method.containingClass?.let { PsiTypesUtil.getClassType(it) } + + val psiManager = psi.manager + val containingClassForMethod = method.containingClass ?: return null + + val containingClass = PsiTreeUtil.getParentOfType(psi, PsiClass::class.java) + val containingClassSequence = generateSequence(containingClass) { + if (it.hasModifierProperty(PsiModifier.STATIC)) + null + else + PsiTreeUtil.getParentOfType(it, PsiClass::class.java) + } + + val receiverClass = containingClassSequence.find { containingClassForExpression -> + psiManager.areElementsEquivalent(containingClassForMethod, containingClassForExpression) || + containingClassForExpression.isInheritor(containingClassForMethod, true) + } + + return receiverClass?.let { PsiTypesUtil.getClassType(it) } } } @@ -109,25 +125,19 @@ class JavaConstructorUCallExpression( override val valueArgumentCount: Int get() { val initializer = psi.arrayInitializer - return if (initializer != null) { - initializer.initializers.size - } else if (psi.arrayDimensions.isNotEmpty()) { - psi.arrayDimensions.size - } else { - psi.argumentList?.expressions?.size ?: 0 + return when { + initializer != null -> initializer.initializers.size + psi.arrayDimensions.isNotEmpty() -> psi.arrayDimensions.size + else -> psi.argumentList?.expressions?.size ?: 0 } } override val valueArguments by lz { val initializer = psi.arrayInitializer - if (initializer != null) { - initializer.initializers.map { JavaConverter.convertOrEmpty(it, this) } - } - else if (psi.arrayDimensions.isNotEmpty()) { - psi.arrayDimensions.map { JavaConverter.convertOrEmpty(it, this) } - } - else { - psi.argumentList?.expressions?.map { JavaConverter.convertOrEmpty(it, this) } ?: emptyList() + when { + initializer != null -> initializer.initializers.map { JavaConverter.convertOrEmpty(it, this) } + psi.arrayDimensions.isNotEmpty() -> psi.arrayDimensions.map { JavaConverter.convertOrEmpty(it, this) } + else -> psi.argumentList?.expressions?.map { JavaConverter.convertOrEmpty(it, this) } ?: emptyList() } } diff --git a/uast/uast-tests/java/Simple/ReceiverType.java b/uast/uast-tests/java/Simple/ReceiverType.java new file mode 100644 index 000000000000..d297e928411b --- /dev/null +++ b/uast/uast-tests/java/Simple/ReceiverType.java @@ -0,0 +1,93 @@ +class TestBaseBase { + void bazBaseBase(int i) { + + } +} + +class TestBase extends TestBaseBase { + void fooBase(int i) { + + } + + void barBase(int i) { + + } +} + +class Test extends TestBase { + Test() { + foo(1); + fooBase(1); + this.barBase(1); + staticMethod(1); + bazBaseBase(1); + } + + void foo(int i) { + + } + + static void staticMethod(int i) { + + } + + + class InnerTest { + InnerTest() { + foo(2); + fooBase(2); + bar(2); + staticMethod(2); + + TestBase t = new TestBase() { + void fooBase(int i) { + bazBaseBase(7); + bar(7); + barBase(7); + } + }; + } + + void bar(int i) { + + } + + class InnerInnerTest { + InnerInnerTest() { + foo(3); + fooBase(3); + bar(3); + baz(3); + staticMethod(3); + } + + void baz(int i) { + + } + } + } + + class InnerTest2 extends TestBase { + InnerTest2() { + foo(4); + fooBase(4); + } + } + + static class StaticTest { + StaticTest() { + bar(5); + staticMethod(5); + } + + void bar(int i) { + + } + + class InnerInStatic { + InnerInStatic() { + bar(6); + } + } + } +} \ No newline at end of file diff --git a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt index 2e41493606f9..1dfaaed7ded4 100644 --- a/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt +++ b/uast/uast-tests/test/org/jetbrains/uast/test/java/JavaUastApiTest.kt @@ -61,4 +61,37 @@ class JavaUastApiTest : AbstractJavaUastTest() { "java.lang.Runnable") } } + + @Test fun testReceiverType() { + doTest("Simple/ReceiverType.java") { name, file -> + assertEquals("Test", file.findElementByText("foo(1)").receiverType?.canonicalText) + assertEquals("Test", file.findElementByText("fooBase(1)").receiverType?.canonicalText) + assertEquals("Test", file.findElementByText("this.barBase(1)").receiverType?.canonicalText) + assertEquals("Test", file.findElementByText("bazBaseBase(1)").receiverType?.canonicalText) + assertNull(file.findElementByText("staticMethod(1)").receiverType) + + assertEquals("Test", file.findElementByText("foo(2)").receiverType?.canonicalText) + assertEquals("Test", file.findElementByText("fooBase(2)").receiverType?.canonicalText) + assertEquals("Test.InnerTest", file.findElementByText("bar(2)").receiverType?.canonicalText) + assertNull(file.findElementByText("staticMethod(2)").receiverType) + + assertEquals("Test", file.findElementByText("foo(3)").receiverType?.canonicalText) + assertEquals("Test", file.findElementByText("fooBase(3)").receiverType?.canonicalText) + assertEquals("Test.InnerTest", file.findElementByText("bar(3)").receiverType?.canonicalText) + assertEquals("Test.InnerTest.InnerInnerTest", file.findElementByText("baz(3)").receiverType?.canonicalText) + assertNull(file.findElementByText("staticMethod(3)").receiverType) + + assertEquals("Test", file.findElementByText("foo(4)").receiverType?.canonicalText) + assertEquals("Test.InnerTest2", file.findElementByText("fooBase(4)").receiverType?.canonicalText) + + assertEquals("Test.StaticTest", file.findElementByText("bar(5)").receiverType?.canonicalText) + assertNull(file.findElementByText("staticMethod(5)").receiverType) + + assertEquals("Test.StaticTest", file.findElementByText("bar(6)").receiverType?.canonicalText) + + assertEquals("TestBase", file.findElementByText("bazBaseBase(7)").receiverType?.canonicalText) + assertEquals("Test.InnerTest", file.findElementByText("bar(7)").receiverType?.canonicalText) + assertEquals("TestBase", file.findElementByText("barBase(7)").receiverType?.canonicalText) + } + } }