Create From Usage: Create class by type reference

This commit is contained in:
Alexey Sedunov
2014-10-28 17:34:02 +03:00
parent a76607beca
commit 1f14d93412
34 changed files with 458 additions and 2 deletions
@@ -18,6 +18,7 @@ package org.jetbrains.jet.plugin.quickfix;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.plugin.codeInsight.ImplementMethodsHandler;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.CreateClassFromTypeReferenceActionFactory;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction.*;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createVariable.*;
@@ -267,5 +268,7 @@ public class QuickFixRegistrar {
QuickFixes.factories.put(DELEGATE_SPECIAL_FUNCTION_MISSING, CreatePropertyDelegateAccessorsActionFactory.INSTANCE$);
QuickFixes.factories.put(DELEGATE_SPECIAL_FUNCTION_NONE_APPLICABLE, CreatePropertyDelegateAccessorsActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromTypeReferenceActionFactory.INSTANCE$);
}
}
@@ -0,0 +1,99 @@
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass
import org.jetbrains.jet.plugin.quickfix.JetIntentionActionsFactory
import org.jetbrains.jet.lang.diagnostics.Diagnostic
import com.intellij.codeInsight.intention.IntentionAction
import org.jetbrains.jet.plugin.quickfix.QuickFixUtil
import java.util.Collections
import org.jetbrains.jet.lang.psi.JetUserType
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.descriptors.ClassDescriptor
import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor
import org.jetbrains.jet.plugin.caches.resolve.getAnalysisResults
import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil
import com.intellij.psi.JavaPsiFacade
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.psi.PsiDirectory
import org.jetbrains.jet.lang.psi.JetElement
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.jet.lang.types.Variance
import org.jetbrains.jet.lang.psi.JetDelegatorToSuperClass
import org.jetbrains.jet.lang.psi.JetConstructorCalleeExpression
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.plugin.util.ProjectRootsUtil
public object CreateClassFromTypeReferenceActionFactory: JetIntentionActionsFactory() {
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction> {
val userType = QuickFixUtil.getParentElementOfType(diagnostic, javaClass<JetUserType>()) ?: return Collections.emptyList()
val typeArguments = userType.getTypeArgumentsAsTypes()
val project = userType.getProject()
val name = userType.getReferencedName() ?: return Collections.emptyList()
val typeRefParent = userType.getParent()?.getParent()
if (typeRefParent is JetConstructorCalleeExpression) return Collections.emptyList()
val traitExpected = typeRefParent is JetDelegatorToSuperClass
val context = AnalyzerFacadeWithCache.getContextForElement(userType)
val file = userType.getContainingFile() as? JetFile ?: return Collections.emptyList()
val isQualifier = (userType.getParent() as? JetUserType)?.let { it.getQualifier() == userType } ?: false
val qualifier = userType.getQualifier()?.getReferenceExpression()
val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] }
val targetParent =
when {
qualifier == null -> file
qualifierDescriptor is ClassDescriptor -> {
DescriptorToDeclarationUtil.getDeclaration(project, qualifierDescriptor)
}
qualifierDescriptor is PackageViewDescriptor -> {
val currentModule = ModuleUtilCore.findModuleForPsiElement(file)
val targetFqName = qualifierDescriptor.getFqName()
if (targetFqName != file.getPackageFqName()) {
JavaPsiFacade.getInstance(project)
.findPackage(targetFqName.asString())
?.getDirectories()
?.firstOrNull { ModuleUtilCore.findModuleForPsiElement(it) == currentModule }
}
else file
}
else -> null
} ?: return Collections.emptyList()
if (!ProjectRootsUtil.isInProjectOrLibSource(targetParent)) return Collections.emptyList()
if (!(targetParent.isWritable() && (targetParent is PsiDirectory || targetParent is JetElement))) return Collections.emptyList()
val possibleKinds = when {
traitExpected -> Collections.singletonList(ClassKind.TRAIT)
else -> ClassKind.values().filter {
val noTypeArguments = typeArguments.isEmpty()
when (it) {
ClassKind.OBJECT -> noTypeArguments && isQualifier
ClassKind.ANNOTATION_CLASS -> noTypeArguments && !isQualifier
ClassKind.ENUM_ENTRY -> false
ClassKind.ENUM_CLASS -> noTypeArguments
else -> true
}
}
}
return possibleKinds.map {
val classInfo = ClassInfo(
kind = it,
name = name,
targetParent = targetParent,
expectedTypeInfo = TypeInfo.Empty,
typeArguments = typeArguments.map { TypeInfo(it, Variance.INVARIANT) }
)
CreateClassFromUsageFix(userType, classInfo)
}
}
}
@@ -1,4 +1,5 @@
// "class com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFixBase" "false"
// ACTION: Create trait 'SomeTest'
// ERROR: Unresolved reference: SomeTest
package testing
@@ -0,0 +1,10 @@
// "Create trait 'A'" "true"
package p
class Foo: A {
}
trait A {
}
@@ -0,0 +1,8 @@
// "Create class 'A'" "false"
// ACTION: Create trait 'A'
// ERROR: Unresolved reference: A
package p
class Foo: <caret>A {
}
@@ -0,0 +1,6 @@
// "Create trait 'A'" "true"
package p
class Foo: <caret>A {
}
@@ -0,0 +1,6 @@
// "Create annotation 'A'" "true"
package p
fun foo(): A = throw Throwable("")
annotation class A
@@ -0,0 +1,8 @@
// "Create class 'A'" "true"
package p
fun foo(): p.A = throw Throwable("")
class A {
}
@@ -0,0 +1,8 @@
// "Create class 'A'" "true"
package p
fun foo(): A = throw Throwable("")
class A {
}
@@ -0,0 +1,8 @@
// "Create class 'A'" "true"
package p
fun foo(): A<Int, String> = throw Throwable("")
class A<T, U> {
}
@@ -0,0 +1,9 @@
// "Create class 'A'" "true"
// ERROR: Unresolved reference: B
package p
fun foo(): A.B = throw Throwable("")
class A {
}
@@ -0,0 +1,12 @@
// "Create class 'A'" "true"
package p
class T {
class A {
}
}
fun foo(): T.A = throw Throwable("")
@@ -0,0 +1,8 @@
// "Create enum 'A'" "true"
package p
fun foo(): A = throw Throwable("")
enum class A {
}
@@ -0,0 +1,9 @@
// "Create object 'A'" "true"
// ERROR: Unresolved reference: B
package p
fun foo(): A.B = throw Throwable("")
object A {
}
@@ -0,0 +1,8 @@
// "Create trait 'A'" "true"
package p
fun foo(): A = throw Throwable("")
trait A {
}
@@ -0,0 +1,4 @@
// "Create annotation 'A'" "true"
package p
fun foo(): <caret>A = throw Throwable("")
@@ -0,0 +1,8 @@
// "Create annotation 'A'" "false"
// ACTION: Create class 'A'
// ACTION: Create trait 'A'
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): <caret>A<Int, String> = throw Throwable("")
@@ -0,0 +1,10 @@
// "Create annotation 'A'" "false"
// ACTION: Create class 'A'
// ACTION: Create object 'A'
// ACTION: Create trait 'A'
// ACTION: Create enum 'A'
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): <caret>A.B = throw Throwable("")
@@ -0,0 +1,4 @@
// "Create class 'A'" "true"
package p
fun foo(): p.<caret>A = throw Throwable("")
@@ -0,0 +1,6 @@
// "Create class 'A'" "false"
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): Int.<caret>A = throw Throwable("")
@@ -0,0 +1,4 @@
// "Create class 'A'" "true"
package p
fun foo(): <caret>A = throw Throwable("")
@@ -0,0 +1,4 @@
// "Create class 'A'" "true"
package p
fun foo(): <caret>A<Int, String> = throw Throwable("")
@@ -0,0 +1,5 @@
// "Create class 'A'" "true"
// ERROR: Unresolved reference: B
package p
fun foo(): <caret>A.B = throw Throwable("")
@@ -0,0 +1,8 @@
// "Create class 'A'" "true"
package p
class T {
}
fun foo(): T.<caret>A = throw Throwable("")
@@ -0,0 +1,10 @@
// "Create enum constant 'A'" "false"
// ACTION: Create annotation 'A'
// ACTION: Create class 'A'
// ACTION: Create trait 'A'
// ACTION: Create enum 'A'
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): <caret>A = throw Throwable("")
@@ -0,0 +1,4 @@
// "Create enum 'A'" "true"
package p
fun foo(): <caret>A = throw Throwable("")
@@ -0,0 +1,8 @@
// "Create enum 'A'" "false"
// ACTION: Create class 'A'
// ACTION: Create trait 'A'
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): <caret>A<Int, String> = throw Throwable("")
@@ -0,0 +1,10 @@
// "Create object 'A'" "false"
// ACTION: Create annotation 'A'
// ACTION: Create class 'A'
// ACTION: Create trait 'A'
// ACTION: Create enum 'A'
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): <caret>A = throw Throwable("")
@@ -0,0 +1,8 @@
// "Create object 'A'" "false"
// ACTION: Create class 'A'
// ACTION: Create trait 'A'
// ACTION: Convert to block body
// ERROR: Unresolved reference: A
package p
fun foo(): <caret>A<Int, String> = throw Throwable("")
@@ -0,0 +1,5 @@
// "Create object 'A'" "true"
// ERROR: Unresolved reference: B
package p
fun foo(): <caret>A.B = throw Throwable("")
@@ -0,0 +1,4 @@
// "Create trait 'A'" "true"
package p
fun foo(): <caret>A = throw Throwable("")
@@ -1,6 +1,10 @@
// "Change 'foo' function return type to '([ERROR : NoSuchType]) -> Int'" "false"
// ACTION: Disable 'Make Types Implicit In Lambda (May Break Code)'
// ACTION: Edit intention settings
// ACTION: Create annotation 'NoSuchType'
// ACTION: Create class 'NoSuchType'
// ACTION: Create enum 'NoSuchType'
// ACTION: Create trait 'NoSuchType'
// ACTION: Make types implicit in lambda (may break code)
// ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>kotlin.Int</td></tr><tr><td>Found:</td><td>([ERROR : NoSuchType]) &rarr; kotlin.Int</td></tr></table></html>
// ERROR: Unresolved reference: NoSuchType
@@ -221,13 +221,24 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
@TestMetadata("idea/testData/quickfix/createFromUsage")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CreateFromUsage.CreateFunction.class, CreateFromUsage.CreateVariable.class})
@InnerTestClasses({CreateFromUsage.CreateClass.class, CreateFromUsage.CreateFunction.class, CreateFromUsage.CreateVariable.class})
@RunWith(JUnit3RunnerWithInners.class)
public static class CreateFromUsage extends AbstractQuickFixMultiFileTest {
public void testAllFilesPresentInCreateFromUsage() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createClass")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({})
@RunWith(JUnit3RunnerWithInners.class)
public static class CreateClass extends AbstractQuickFixMultiFileTest {
public void testAllFilesPresentInCreateClass() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createClass"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CreateFunction.Call.class})
@@ -619,13 +619,149 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@TestMetadata("idea/testData/quickfix/createFromUsage")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CreateFromUsage.CreateFunction.class, CreateFromUsage.CreateVariable.class})
@InnerTestClasses({CreateFromUsage.CreateClass.class, CreateFromUsage.CreateFunction.class, CreateFromUsage.CreateVariable.class})
@RunWith(JUnit3RunnerWithInners.class)
public static class CreateFromUsage extends AbstractQuickFixTest {
public void testAllFilesPresentInCreateFromUsage() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage"), Pattern.compile("^before(\\w+)\\.kt$"), true);
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createClass")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CreateClass.DelegationSpecifier.class, CreateClass.TypeReference.class})
@RunWith(JUnit3RunnerWithInners.class)
public static class CreateClass extends AbstractQuickFixTest {
public void testAllFilesPresentInCreateClass() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createClass"), Pattern.compile("^before(\\w+)\\.kt$"), true);
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class DelegationSpecifier extends AbstractQuickFixTest {
public void testAllFilesPresentInDelegationSpecifier() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier"), Pattern.compile("^before(\\w+)\\.kt$"), true);
}
@TestMetadata("beforeClassDelegatorToSuperclass.kt")
public void testClassDelegatorToSuperclass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeClassDelegatorToSuperclass.kt");
doTest(fileName);
}
@TestMetadata("beforeTraitDelegatorToSuperclass.kt")
public void testTraitDelegatorToSuperclass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeTraitDelegatorToSuperclass.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class TypeReference extends AbstractQuickFixTest {
public void testAllFilesPresentInTypeReference() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createClass/typeReference"), Pattern.compile("^before(\\w+)\\.kt$"), true);
}
@TestMetadata("beforeAnnotationNotQualifierNoTypeArgs.kt")
public void testAnnotationNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeAnnotationNotQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeAnnotationNotQualifierWithTypeArgs.kt")
public void testAnnotationNotQualifierWithTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeAnnotationNotQualifierWithTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeAnnotationQualifierNoTypeArgs.kt")
public void testAnnotationQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeAnnotationQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeClassCurrentPackageReceiver.kt")
public void testClassCurrentPackageReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassCurrentPackageReceiver.kt");
doTest(fileName);
}
@TestMetadata("beforeClassLibTypeReceiver.kt")
public void testClassLibTypeReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassLibTypeReceiver.kt");
doTest(fileName);
}
@TestMetadata("beforeClassNotQualifierNoTypeArgs.kt")
public void testClassNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassNotQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeClassNotQualifierWithTypeArgs.kt")
public void testClassNotQualifierWithTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassNotQualifierWithTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeClassQualifierNoTypeArgs.kt")
public void testClassQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeClassUserTypeReceiver.kt")
public void testClassUserTypeReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeClassUserTypeReceiver.kt");
doTest(fileName);
}
@TestMetadata("beforeEnumEntryNotQualifierNoTypeArgs.kt")
public void testEnumEntryNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeEnumEntryNotQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeEnumNotQualifierNoTypeArgs.kt")
public void testEnumNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeEnumNotQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeEnumNotQualifierWithTypeArgs.kt")
public void testEnumNotQualifierWithTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeEnumNotQualifierWithTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeObjectNotQualifierNoTypeArgs.kt")
public void testObjectNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeObjectNotQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeObjectNotQualifierWithTypeArgs.kt")
public void testObjectNotQualifierWithTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeObjectNotQualifierWithTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeObjectQualifierNoTypeArgs.kt")
public void testObjectQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeObjectQualifierNoTypeArgs.kt");
doTest(fileName);
}
@TestMetadata("beforeTraitNotQualifierNoTypeArgs.kt")
public void testTraitNotQualifierNoTypeArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/typeReference/beforeTraitNotQualifierNoTypeArgs.kt");
doTest(fileName);
}
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CreateFunction.BinaryOperations.class, CreateFunction.Call.class, CreateFunction.Component.class, CreateFunction.DelegateAccessors.class, CreateFunction.Get.class, CreateFunction.HasNext.class, CreateFunction.Invoke.class, CreateFunction.Iterator.class, CreateFunction.Next.class, CreateFunction.Set.class, CreateFunction.UnaryOperations.class})