Create From Usage: Create class by annotation entry/super-call delegation specifier

This commit is contained in:
Alexey Sedunov
2014-11-05 14:31:33 +03:00
parent 1b1eb10979
commit afeb03f383
31 changed files with 357 additions and 6 deletions
@@ -42,7 +42,7 @@ public class JetConstructorCalleeExpression extends JetExpressionImplStub<Kotlin
}
@Nullable @IfNotParsed
public JetReferenceExpression getConstructorReferenceExpression() {
public JetSimpleNameExpression getConstructorReferenceExpression() {
JetTypeReference typeReference = getTypeReference();
if (typeReference == null) {
return null;
@@ -18,9 +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.CreateClassFromConstructorCallActionFactory;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.CreateClassFromTypeReferenceActionFactory;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.CreateClassFromReferenceExpressionActionFactory;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass.*;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createFunction.*;
import org.jetbrains.jet.plugin.quickfix.createFromUsage.createVariable.*;
@@ -277,5 +275,6 @@ public class QuickFixRegistrar {
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromTypeReferenceActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromReferenceExpressionActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromCallWithConstructorCalleeActionFactory.INSTANCE$);
}
}
@@ -401,13 +401,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
psiFactory.createEnumEntry("$name ${if (hasParameters) ": ${targetParent.getName()}()" else ""}")
}
else -> {
val openMod = if (open) "open " else ""
val innerMod = if (inner) "inner " else ""
val typeParamList = when (kind) {
ClassKind.PLAIN_CLASS, ClassKind.TRAIT -> "<>"
else -> ""
}
psiFactory.createDeclaration<JetClassOrObject>(
"$innerMod${kind.keyword} $name$typeParamList$paramList$returnTypeString $classBody"
"$openMod$innerMod${kind.keyword} $name$typeParamList$paramList$returnTypeString $classBody"
)
}
}
@@ -0,0 +1,78 @@
package org.jetbrains.jet.plugin.quickfix.createFromUsage.createClass
import org.jetbrains.jet.lang.diagnostics.Diagnostic
import com.intellij.codeInsight.intention.IntentionAction
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.TypeInfo
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.types.Variance
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.plugin.quickfix.createFromUsage.callableBuilder.ParameterInfo
import org.jetbrains.jet.plugin.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.lang.psi.JetAnnotationEntry
import org.jetbrains.jet.lang.psi.JetUserType
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.jet.lang.psi.JetDelegatorToSuperCall
import org.jetbrains.jet.lang.psi.JetDelegatorToThisCall
import org.jetbrains.jet.lang.psi.JetCallElement
import org.jetbrains.jet.lang.psi.psiUtil.isAncestor
import org.jetbrains.jet.lang.psi.JetConstructorCalleeExpression
import org.jetbrains.jet.lang.psi.JetSimpleNameExpression
import java.util.Collections
public object CreateClassFromCallWithConstructorCalleeActionFactory : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val diagElement = diagnostic.getPsiElement()
val callElement = PsiTreeUtil.getParentOfType(
diagElement,
javaClass<JetAnnotationEntry>(),
javaClass<JetDelegatorToSuperCall>()
) as? JetCallElement ?: return null
val isAnnotation = callElement is JetAnnotationEntry
val callee = callElement.getCalleeExpression() as? JetConstructorCalleeExpression ?: return null
val calleeRef = callee.getConstructorReferenceExpression() ?: return null
if (!calleeRef.isAncestor(diagElement)) return null
val file = callElement.getContainingFile() as? JetFile ?: return null
val typeRef = callee.getTypeReference() ?: return null
val userType = typeRef.getTypeElement() as? JetUserType ?: return null
val context = AnalyzerFacadeWithCache.getContextForElement(userType)
val qualifier = userType.getQualifier()?.getReferenceExpression()
val qualifierDescriptor = qualifier?.let { context[BindingContext.REFERENCE_TARGET, it] }
val targetParent = getTargetParentByQualifier(file, qualifier != null, qualifierDescriptor) ?: return null
val anyType = KotlinBuiltIns.getInstance().getNullableAnyType()
val valueArguments = callElement.getValueArguments()
val defaultParamName = if (valueArguments.size == 1) "value" else null
val parameterInfos = valueArguments.map {
ParameterInfo(
it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE),
it.getArgumentName()?.getReferenceExpression()?.getReferencedName() ?: defaultParamName
)
}
val typeArgumentInfos = when {
isAnnotation -> Collections.emptyList<TypeInfo>()
else -> callElement.getTypeArguments().map { TypeInfo(it.getTypeReference(), Variance.INVARIANT) }
}
val classInfo = ClassInfo(
kind = if (isAnnotation) ClassKind.ANNOTATION_CLASS else ClassKind.PLAIN_CLASS,
name = calleeRef.getReferencedName(),
targetParent = targetParent,
expectedTypeInfo = TypeInfo.Empty,
parameterInfos = parameterInfos,
open = !isAnnotation,
typeArguments = typeArgumentInfos
)
return CreateClassFromUsageFix(callElement, classInfo)
}
}
@@ -53,6 +53,7 @@ public class ClassInfo(
val targetParent: PsiElement,
val expectedTypeInfo: TypeInfo,
val inner: Boolean = false,
val open: Boolean = false,
val typeArguments: List<TypeInfo> = Collections.emptyList(),
val parameterInfos: List<ParameterInfo> = Collections.emptyList()
)
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
foo(1, "2") fun test() {
}
annotation class foo(val i: Int, val s: String)
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
[foo] fun test() {
}
annotation class foo
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
[foo()] fun test() {
}
annotation class foo
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
[foo(1, "2")] fun test() {
}
annotation class foo(val i: Int, val s: String)
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
[foo(1, "2")] fun test() {
}
annotation class foo(val i: Int, val s: String)
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
[foo(1)] fun test() {
}
annotation class foo(val value: Int)
@@ -0,0 +1,7 @@
// "Create annotation 'foo'" "true"
[foo(fooBar = 1)] fun test() {
}
annotation class foo(val fooBar: Int)
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
<caret>foo(1, "2") fun test() {
}
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
[<caret>foo] fun test() {
}
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
[<caret>foo()] fun test() {
}
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
[<caret>foo(1, "2")] fun test() {
}
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
[<caret>foo<String>(1, "2")] fun test() {
}
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "false"
// ACTION: Create function 'foo'
// ERROR: Unresolved reference: foo
fun test() = <caret>foo(1, "2")
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
[<caret>foo(1)] fun test() {
}
@@ -0,0 +1,5 @@
// "Create annotation 'foo'" "true"
[<caret>foo(fooBar = 1)] fun test() {
}
@@ -0,0 +1,10 @@
// "Create class 'A'" "true"
package p
class Foo: A(1, "2") {
}
open class A(i: Int, s: String) {
}
@@ -0,0 +1,14 @@
// "Create class 'A'" "true"
package p
class X {
open class A(i: Int, s: String) {
}
}
class Foo: X.A(1, "2") {
}
@@ -0,0 +1,10 @@
// "Create class 'A'" "true"
package p
class Foo: p.A(1, "2") {
}
open class A(i: Int, s: String) {
}
@@ -0,0 +1,14 @@
// "Create class 'A'" "true"
package p
class B {
}
class Foo: A(abc = 1, ghi = "2", def = B()) {
}
open class A(abc: Int, ghi: String, def: B) {
}
@@ -0,0 +1,10 @@
// "Create class 'A'" "true"
package p
class Foo: A<Int, String>(1, "2") {
}
open class A<T, U>(t: T, u: U) {
}
@@ -0,0 +1,6 @@
// "Create class 'A'" "true"
package p
class Foo: <caret>A(1, "2") {
}
@@ -0,0 +1,10 @@
// "Create class 'A'" "true"
package p
class X {
}
class Foo: X.<caret>A(1, "2") {
}
@@ -0,0 +1,6 @@
// "Create class 'A'" "true"
package p
class Foo: p.<caret>A(1, "2") {
}
@@ -0,0 +1,10 @@
// "Create class 'A'" "true"
package p
class B {
}
class Foo: <caret>A(abc = 1, ghi = "2", def = B()) {
}
@@ -0,0 +1,6 @@
// "Create class 'A'" "true"
package p
class Foo: <caret>A<Int, String>(1, "2") {
}
@@ -628,13 +628,70 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@TestMetadata("idea/testData/quickfix/createFromUsage/createClass")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CreateClass.CallExpression.class, CreateClass.DelegationSpecifier.class, CreateClass.ImportDirective.class, CreateClass.ReferenceExpression.class, CreateClass.TypeReference.class})
@InnerTestClasses({CreateClass.AnnotationEntry.class, CreateClass.CallExpression.class, CreateClass.DelegationSpecifier.class, CreateClass.ImportDirective.class, CreateClass.ReferenceExpression.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/annotationEntry")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class AnnotationEntry extends AbstractQuickFixTest {
public void testAllFilesPresentInAnnotationEntry() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createClass/annotationEntry"), Pattern.compile("^before(\\w+)\\.kt$"), true);
}
@TestMetadata("beforeAnnotationNoBrackets.kt")
public void testAnnotationNoBrackets() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeAnnotationNoBrackets.kt");
doTest(fileName);
}
@TestMetadata("beforeAnnotationNoParamList.kt")
public void testAnnotationNoParamList() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeAnnotationNoParamList.kt");
doTest(fileName);
}
@TestMetadata("beforeAnnotationNoParams.kt")
public void testAnnotationNoParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeAnnotationNoParams.kt");
doTest(fileName);
}
@TestMetadata("beforeAnnotationWithParams.kt")
public void testAnnotationWithParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeAnnotationWithParams.kt");
doTest(fileName);
}
@TestMetadata("beforeAnnotationWithTypeParams.kt")
public void testAnnotationWithTypeParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeAnnotationWithTypeParams.kt");
doTest(fileName);
}
@TestMetadata("beforeNotAnnotation.kt")
public void testNotAnnotation() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeNotAnnotation.kt");
doTest(fileName);
}
@TestMetadata("beforeSingleArgAnnotation.kt")
public void testSingleArgAnnotation() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeSingleArgAnnotation.kt");
doTest(fileName);
}
@TestMetadata("beforeSingleNamedArgAnnotation.kt")
public void testSingleNamedArgAnnotation() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/annotationEntry/beforeSingleNamedArgAnnotation.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createClass/callExpression")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({CallExpression.TypeArguments.class})
@@ -914,6 +971,36 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("beforeDelegatorToSupercallNoReceiver.kt")
public void testDelegatorToSupercallNoReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeDelegatorToSupercallNoReceiver.kt");
doTest(fileName);
}
@TestMetadata("beforeDelegatorToSupercallWithClassQualifier.kt")
public void testDelegatorToSupercallWithClassQualifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeDelegatorToSupercallWithClassQualifier.kt");
doTest(fileName);
}
@TestMetadata("beforeDelegatorToSupercallWithPackageQualifier.kt")
public void testDelegatorToSupercallWithPackageQualifier() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeDelegatorToSupercallWithPackageQualifier.kt");
doTest(fileName);
}
@TestMetadata("beforeDelegatorToSupercallWithParamNames.kt")
public void testDelegatorToSupercallWithParamNames() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeDelegatorToSupercallWithParamNames.kt");
doTest(fileName);
}
@TestMetadata("beforeDelegatorToSupercallWithTypeParams.kt")
public void testDelegatorToSupercallWithTypeParams() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeDelegatorToSupercallWithTypeParams.kt");
doTest(fileName);
}
@TestMetadata("beforeTraitDelegatorToSuperclass.kt")
public void testTraitDelegatorToSuperclass() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createClass/delegationSpecifier/beforeTraitDelegatorToSuperclass.kt");