Allow to shorten class member references

This commit is contained in:
Alexey Sedunov
2014-02-11 20:16:20 +04:00
parent f244cb05eb
commit d2e6aa2aa5
34 changed files with 259 additions and 15 deletions
@@ -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;
}