Allow to shorten class member references
This commit is contained in:
@@ -16,6 +16,10 @@ import java.util.HashSet;
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.util.containers.HashMap
|
||||
import java.util.ArrayList
|
||||
import org.jetbrains.jet.lang.psi.psiUtil.getParentByType
|
||||
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaPropertyDescriptor
|
||||
import org.jetbrains.jet.lang.resolve.java.lazy.descriptors.LazyPackageFragmentForJavaClass
|
||||
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaMethodDescriptor
|
||||
|
||||
public object ShortenReferences {
|
||||
public fun process(element: JetElement) {
|
||||
@@ -101,16 +105,18 @@ public object ShortenReferences {
|
||||
val referenceExpression = userType.getReferenceExpression()
|
||||
if (referenceExpression == null) return false
|
||||
|
||||
val target = bindingContext(referenceExpression).get(BindingContext.REFERENCE_TARGET, referenceExpression)
|
||||
val target = bindingContext(referenceExpression).get(BindingContext.REFERENCE_TARGET, referenceExpression)?.let { desc ->
|
||||
if (desc is ConstructorDescriptor) desc.getContainingDeclaration() else desc
|
||||
}
|
||||
if (target == null) return false
|
||||
// references to nested classes should be shortened when visiting qualifier
|
||||
if (target.getContainingDeclaration() is ClassDescriptor) return false
|
||||
|
||||
val typeReference = PsiTreeUtil.getParentOfType(userType, javaClass<JetTypeReference>())!!
|
||||
val scope = resolveSession.resolveToElement(typeReference).get(BindingContext.TYPE_RESOLUTION_SCOPE, typeReference)!!
|
||||
val name = target.getName()
|
||||
val targetByName = scope.getClassifier(name)
|
||||
if (targetByName == null) {
|
||||
if (target.getContainingDeclaration() is ClassDescriptor) return false
|
||||
|
||||
addImportIfNeeded(target, file)
|
||||
return true
|
||||
}
|
||||
@@ -147,21 +153,41 @@ public object ShortenReferences {
|
||||
|
||||
private fun processDotQualifiedExpression(qualifiedExpression: JetDotQualifiedExpression): PsiElement {
|
||||
val selectorExpression = qualifiedExpression.getSelectorExpression()
|
||||
|
||||
if (selectorExpression is JetCallExpression) {
|
||||
val calleeExpression = selectorExpression.getCalleeExpression()
|
||||
if (calleeExpression is JetReferenceExpression) {
|
||||
val bindingContext = bindingContext(calleeExpression)
|
||||
|
||||
val targetClass = instantiatedClass(calleeExpression)
|
||||
if (targetClass != null) {
|
||||
return shortenIfPossible(qualifiedExpression, targetClass, bindingContext(calleeExpression))
|
||||
}
|
||||
if (targetClass != null) return shortenIfPossibleByDescriptor(qualifiedExpression, targetClass, bindingContext)
|
||||
|
||||
return shortenIfPossible(qualifiedExpression, calleeExpression, bindingContext)
|
||||
}
|
||||
}
|
||||
else if (selectorExpression is JetReferenceExpression) {
|
||||
val bindingContext = bindingContext(selectorExpression)
|
||||
val target = bindingContext.get(BindingContext.REFERENCE_TARGET, selectorExpression)
|
||||
if (target is ClassDescriptor || target is PackageViewDescriptor) { //TODO: should we ever add imports to real packages?
|
||||
return shortenIfPossible(qualifiedExpression, target, bindingContext)
|
||||
return shortenIfPossible(qualifiedExpression, selectorExpression, bindingContext(selectorExpression))
|
||||
}
|
||||
|
||||
return qualifiedExpression
|
||||
}
|
||||
|
||||
private fun shortenIfPossible(
|
||||
qualifiedExpression: JetDotQualifiedExpression,
|
||||
refExpression: JetReferenceExpression,
|
||||
bindingContext: BindingContext
|
||||
): PsiElement {
|
||||
val receiverExpression = qualifiedExpression.getReceiverExpression()
|
||||
val target = bindingContext.get(BindingContext.REFERENCE_TARGET, refExpression)
|
||||
if (target != null) {
|
||||
if ((target is JavaPropertyDescriptor || target is JavaMethodDescriptor) && receiverExpression is JetDotQualifiedExpression) {
|
||||
val containingDescriptor = target.getContainingDeclaration()
|
||||
if (containingDescriptor is LazyPackageFragmentForJavaClass) {
|
||||
return shortenIfPossibleByDescriptor(receiverExpression, containingDescriptor.getCorrespondingClass(), bindingContext)
|
||||
}
|
||||
}
|
||||
|
||||
return shortenIfPossibleByDescriptor(qualifiedExpression, target, bindingContext)
|
||||
}
|
||||
return qualifiedExpression
|
||||
}
|
||||
@@ -194,9 +220,10 @@ public object ShortenReferences {
|
||||
return null
|
||||
}
|
||||
|
||||
private fun shortenIfPossible(qualifiedExpression: JetDotQualifiedExpression, targetClassOrPackage: DeclarationDescriptor, bindingContext: BindingContext): PsiElement {
|
||||
// references to nested classes should be shortened when visiting qualifier
|
||||
if (targetClassOrPackage.getContainingDeclaration() is ClassDescriptor) return qualifiedExpression
|
||||
private fun shortenIfPossibleByDescriptor(qualifiedExpression: JetDotQualifiedExpression, targetDescriptor: DeclarationDescriptor, bindingContext: BindingContext): PsiElement {
|
||||
val isClassMember = targetDescriptor.getContainingDeclaration() is ClassDescriptor
|
||||
val isUsageInImport = qualifiedExpression.getParentByType(javaClass<JetImportDirective>()) != null
|
||||
val isClassOrPackage = targetDescriptor is ClassDescriptor || targetDescriptor is PackageViewDescriptor
|
||||
|
||||
val referenceExpression = referenceExpression(qualifiedExpression.getSelectorExpression())!!
|
||||
val resolveBefore = resolveState(referenceExpression, bindingContext)
|
||||
@@ -214,7 +241,9 @@ public object ShortenReferences {
|
||||
return newExpression.replace(copy) // revert shortening
|
||||
}
|
||||
|
||||
addImportIfNeeded(targetClassOrPackage, file)
|
||||
if (isUsageInImport || isClassMember || !isClassOrPackage) return newExpression.replace(copy) // revert shortening
|
||||
|
||||
addImportIfNeeded(targetDescriptor, file)
|
||||
return newExpression
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
static class B {
|
||||
static void foo() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar() {
|
||||
<selection>A.B.foo()</selection>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar() {
|
||||
B.foo()
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
public class B {
|
||||
public B(String s) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(s: String) {
|
||||
<selection>val t: A.B = A().B(s)</selection>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(s: String) {
|
||||
val t: A.B = A().B(s)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
public class B {
|
||||
public B(String s) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar(s: String) {
|
||||
<selection>val t: A.B = A().B(s)</selection>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar(s: String) {
|
||||
val t: B = A().B(s)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
public static class B {
|
||||
public B(String s) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(s: String) {
|
||||
<selection>val t: A.B = A.B(s)</selection>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(s: String) {
|
||||
val t: A.B = A.B(s)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
class A {
|
||||
public static class B {
|
||||
public B(String s) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar(s: String) {
|
||||
<selection>val t: A.B = A.B(s)</selection>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar(s: String) {
|
||||
val t: B = B(s)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
public static int X = 10;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar() {
|
||||
<selection>A.X = 100</selection>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar() {
|
||||
A.X = 100
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
class A {
|
||||
public static int X = 10;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar() {
|
||||
<selection>A.X = 100</selection>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar() {
|
||||
X = 100
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
public static void foo(String s) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(s: String) {
|
||||
<selection>A.foo(s)</selection>
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fun bar(s: String) {
|
||||
A.foo(s)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
class A {
|
||||
public static void foo(String s) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar(s: String) {
|
||||
<selection>A.foo(s)</selection>
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import A.*
|
||||
|
||||
fun bar(s: String) {
|
||||
foo(s)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
class A : <selection>java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock</selection>
|
||||
@@ -0,0 +1,3 @@
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||
|
||||
class A : ReentrantReadWriteLock.WriteLock
|
||||
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
open class A {
|
||||
|
||||
}
|
||||
|
||||
class B: <selection>test.A()</selection> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
open class A {
|
||||
|
||||
}
|
||||
|
||||
class B: A() {
|
||||
|
||||
}
|
||||
@@ -36,6 +36,10 @@ abstract class AbstractShortenRefsTest : LightCodeInsightFixtureTestCase() {
|
||||
if (File(dependencyPath).exists()) {
|
||||
fixture.configureByFile(dependencyPath)
|
||||
}
|
||||
val javaDependencyPath = testPath.replace(".kt", ".dependency.java")
|
||||
if (File(javaDependencyPath).exists()) {
|
||||
fixture.configureByFile(javaDependencyPath)
|
||||
}
|
||||
|
||||
fixture.configureByFile(testPath)
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.jetbrains.jet.shortenRefs.AbstractShortenRefsTest;
|
||||
/** This class is generated by {@link org.jetbrains.jet.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */
|
||||
@SuppressWarnings("all")
|
||||
@TestMetadata("idea/testData/shortenRefs")
|
||||
@InnerTestClasses({ShortenRefsTestGenerated.Constructor.class, ShortenRefsTestGenerated.Type.class})
|
||||
@InnerTestClasses({ShortenRefsTestGenerated.Constructor.class, ShortenRefsTestGenerated.Java.class, ShortenRefsTestGenerated.Type.class})
|
||||
public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
public void testAllFilesPresentInShortenRefs() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/shortenRefs"), Pattern.compile("^([^\\.]+)\\.kt$"), true);
|
||||
@@ -88,6 +88,11 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
doTest("idea/testData/shortenRefs/constructor/NestedClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NestedClassWithImport.kt")
|
||||
public void testNestedClassWithImport() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/constructor/NestedClassWithImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoImportNeeded.kt")
|
||||
public void testNoImportNeeded() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/constructor/NoImportNeeded.kt");
|
||||
@@ -105,6 +110,54 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/shortenRefs/java")
|
||||
public static class Java extends AbstractShortenRefsTest {
|
||||
public void testAllFilesPresentInJava() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("idea/testData/shortenRefs/java"), Pattern.compile("^([^\\.]+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("innerClassNoImports.kt")
|
||||
public void testInnerClassNoImports() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/innerClassNoImports.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("innerClassOnDemandImport.kt")
|
||||
public void testInnerClassOnDemandImport() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/innerClassOnDemandImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticClassNoImports.kt")
|
||||
public void testStaticClassNoImports() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/staticClassNoImports.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticClassOnDemandImport.kt")
|
||||
public void testStaticClassOnDemandImport() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/staticClassOnDemandImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticFieldNoImports.kt")
|
||||
public void testStaticFieldNoImports() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/staticFieldNoImports.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticFieldOnDemandImport.kt")
|
||||
public void testStaticFieldOnDemandImport() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/staticFieldOnDemandImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticMethodNoImports.kt")
|
||||
public void testStaticMethodNoImports() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/staticMethodNoImports.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("staticMethodOnDemandImport.kt")
|
||||
public void testStaticMethodOnDemandImport() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/java/staticMethodOnDemandImport.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/shortenRefs/type")
|
||||
public static class Type extends AbstractShortenRefsTest {
|
||||
public void testAllFilesPresentInType() throws Exception {
|
||||
@@ -116,6 +169,11 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
doTest("idea/testData/shortenRefs/type/ClassSameNameAsPackage.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegationSpecifier.kt")
|
||||
public void testDelegationSpecifier() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/type/delegationSpecifier.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("FunctionType.kt")
|
||||
public void testFunctionType() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/type/FunctionType.kt");
|
||||
@@ -146,6 +204,11 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
doTest("idea/testData/shortenRefs/type/NestedClass.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NestedClassRefInImport.kt")
|
||||
public void testNestedClassRefInImport() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/type/NestedClassRefInImport.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoImportNeeded.kt")
|
||||
public void testNoImportNeeded() throws Exception {
|
||||
doTest("idea/testData/shortenRefs/type/NoImportNeeded.kt");
|
||||
@@ -182,6 +245,7 @@ public class ShortenRefsTestGenerated extends AbstractShortenRefsTest {
|
||||
TestSuite suite = new TestSuite("ShortenRefsTestGenerated");
|
||||
suite.addTestSuite(ShortenRefsTestGenerated.class);
|
||||
suite.addTestSuite(Constructor.class);
|
||||
suite.addTestSuite(Java.class);
|
||||
suite.addTestSuite(Type.class);
|
||||
return suite;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user