Create from Usage: Generate secondary constructors by call expressions

This commit is contained in:
Alexey Sedunov
2015-03-25 13:16:56 +03:00
parent 15dddf362e
commit 9c0bcee9a3
20 changed files with 210 additions and 13 deletions
@@ -252,11 +252,11 @@ public class QuickFixRegistrar {
QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateBinaryOperationActionFactory.INSTANCE$);
QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateBinaryOperationActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, CreateFunctionOrPropertyFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateFunctionOrPropertyFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateFunctionOrPropertyFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateFunctionOrPropertyFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(EXPRESSION_EXPECTED_PACKAGE_FOUND, CreateFunctionOrPropertyFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, CreateCallableFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateCallableFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(NO_VALUE_FOR_PARAMETER, CreateCallableFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(TOO_MANY_ARGUMENTS, CreateCallableFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(EXPRESSION_EXPECTED_PACKAGE_FOUND, CreateCallableFromCallActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE_WRONG_RECEIVER, CreateClassFromConstructorCallActionFactory.INSTANCE$);
QuickFixes.factories.put(UNRESOLVED_REFERENCE, CreateClassFromConstructorCallActionFactory.INSTANCE$);
@@ -37,10 +37,17 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import com.intellij.psi.PsiClass
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyzeAndGetResult
import org.jetbrains.kotlin.idea.refactoring.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
import java.util.ArrayList
object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactory() {
object CreateCallableFromCallActionFactory : JetIntentionActionsFactory() {
override fun doCreateActions(diagnostic: Diagnostic): List<IntentionAction>? {
val diagElement = diagnostic.getPsiElement()
if (PsiTreeUtil.getParentOfType(
@@ -87,9 +94,12 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
}
else Collections.emptyList()
val callableInfo = when (callExpr) {
val name = calleeExpr.getReferencedName()
val anyType = KotlinBuiltIns.getInstance().getNullableAnyType()
val callableInfos = ArrayList<CallableInfo>(2)
when (callExpr) {
is JetCallExpression -> {
val anyType = KotlinBuiltIns.getInstance().getNullableAnyType()
val parameters = callExpr.getValueArguments().map {
ParameterInfo(
it.getArgumentExpression()?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo(anyType, Variance.IN_VARIANCE),
@@ -98,7 +108,17 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
}
val typeParameters = callExpr.getTypeInfoForTypeArguments()
val returnType = TypeInfo(fullCallExpr, Variance.OUT_VARIANCE)
FunctionInfo(calleeExpr.getReferencedName(), receiverType, returnType, possibleContainers, parameters, typeParameters)
callableInfos.add(FunctionInfo(name, receiverType, returnType, possibleContainers, parameters, typeParameters))
val expectedType = context[BindingContext.EXPECTED_EXPRESSION_TYPE, fullCallExpr] ?: anyType
val constructorDescriptor = callExpr.getResolvedCall(context)?.getResultingDescriptor() as? ConstructorDescriptor
val classDescriptor = constructorDescriptor?.getContainingDeclaration() as? ClassDescriptor
val klass = classDescriptor?.let { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
if ((klass is JetClass || klass is PsiClass) && klass.canRefactor()
&& typeParameters.isEmpty()
&& classDescriptor!!.getDefaultType().isSubtypeOf(expectedType)) {
callableInfos.add(SecondaryConstructorInfo(parameters, klass))
}
}
is JetSimpleNameExpression -> {
@@ -107,13 +127,11 @@ object CreateFunctionOrPropertyFromCallActionFactory : JetIntentionActionsFactor
fullCallExpr.getExpressionForTypeGuess(),
if (varExpected) Variance.INVARIANT else Variance.OUT_VARIANCE
)
PropertyInfo(calleeExpr.getReferencedName(), receiverType, returnType, varExpected, possibleContainers)
callableInfos.add(PropertyInfo(name, receiverType, returnType, varExpected, possibleContainers))
}
else -> return null
}
return CreateCallableFromUsageFixes(callExpr, callableInfo)
return callableInfos.flatMap{ CreateCallableFromUsageFixes(callExpr, it) }
}
private fun getReceiverTypeInfo(context: BindingContext, project: Project, receiver: ReceiverValue): TypeInfo? {
@@ -2,6 +2,7 @@
// ACTION: Create function 'Foo'
// ACTION: Add parameter to constructor 'Foo'
// ACTION: Split property declaration
// ACTION: Create secondary constructor
// ERROR: Too many arguments for public constructor Foo(a: kotlin.Int) defined in Foo
class Foo(a: Int)
@@ -2,6 +2,7 @@
// ACTION: Create function 'Foo'
// ACTION: Remove parameter 's'
// ACTION: Split property declaration
// ACTION: Create secondary constructor
// ERROR: No value passed for parameter s
class Foo(i: Int, s: String)
@@ -0,0 +1,13 @@
// "Create secondary constructor" "true"
trait T
class A: T {
constructor(i: Int) {
//To change body of created constructors use File | Settings | File Templates.
}
}
fun test() {
val t: T = A(1)
}
@@ -0,0 +1,12 @@
// "Create secondary constructor" "true"
class A {
constructor(i: Int) {
//To change body of created constructors use File | Settings | File Templates.
}
}
fun test() {
val a = A(1)
}
@@ -0,0 +1,11 @@
// "Create secondary constructor" "true"
class A {
constructor(i: Int) {
//To change body of created constructors use File | Settings | File Templates.
}
}
fun test() {
val a = A(1)
}
@@ -0,0 +1,9 @@
// "Create secondary constructor" "true"
trait T
class A: T
fun test() {
val t: T = A(<caret>1)
}
@@ -0,0 +1,12 @@
// "Create secondary constructor" "false"
// ACTION: Add parameter to constructor 'A'
// ACTION: Create function 'A'
// ACTION: Split property declaration
// ERROR: No type arguments expected
// ERROR: Too many arguments for public constructor A() defined in A
class A
fun test() {
val a = A<Int>(<caret>1)
}
@@ -0,0 +1,9 @@
// "Create secondary constructor" "true"
class A {
}
fun test() {
val a = A(<caret>1)
}
@@ -0,0 +1,7 @@
// "Create secondary constructor" "true"
class A
fun test() {
val a = A(<caret>1)
}
@@ -0,0 +1,15 @@
// "Create secondary constructor" "false"
// ACTION: Add parameter to constructor 'A'
// ACTION: Change 'b' type to 'A'
// ACTION: Create function 'A'
// ACTION: Split property declaration
// ERROR: <html>Type mismatch.<table><tr><td>Required:</td><td>B</td></tr><tr><td>Found:</td><td>A</td></tr></table></html>
// ERROR: Too many arguments for public constructor A() defined in A
class A
class B
fun test() {
val b: B = A(<caret>1)
}
@@ -0,0 +1,7 @@
// "Create secondary constructor" "false"
// ERROR: Too many arguments for public constructor G() defined in G
// ACTION: Add parameter to constructor 'G'
// ACTION: Convert to block body
// ACTION: Create function 'G'
fun test() = G(<caret>1)
@@ -0,0 +1,6 @@
class J {
public J(int i) {
}
}
@@ -0,0 +1,4 @@
// "Create secondary constructor" "true"
// ERROR: Too many arguments for public/*package*/ constructor J() defined in J
fun test() = J(1)
@@ -0,0 +1,4 @@
// "Create secondary constructor" "true"
// ERROR: Too many arguments for public/*package*/ constructor J() defined in J
fun test() = J(<caret>1)
@@ -289,6 +289,7 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
@InnerTestClasses({
CreateFromUsage.CreateClass.class,
CreateFromUsage.CreateFunction.class,
CreateFromUsage.CreateSecondaryConstructor.class,
CreateFromUsage.CreateVariable.class,
})
@RunWith(JUnit3RunnerWithInners.class)
@@ -756,6 +757,27 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CreateSecondaryConstructor extends AbstractQuickFixMultiFileTest {
public void testAllFilesPresentInCreateSecondaryConstructor() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createSecondaryConstructor"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
@TestMetadata("groovyConstructor.before.Main.kt")
public void testGroovyConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/groovyConstructor.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("javaConstructor.before.Main.kt")
public void testJavaConstructor() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/javaConstructor.before.Main.kt");
doTestWithExtraFile(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createVariable")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({
@@ -698,6 +698,7 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
@InnerTestClasses({
CreateFromUsage.CreateClass.class,
CreateFromUsage.CreateFunction.class,
CreateFromUsage.CreateSecondaryConstructor.class,
CreateFromUsage.CreateVariable.class,
})
@RunWith(JUnit3RunnerWithInners.class)
@@ -2266,6 +2267,45 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CreateSecondaryConstructor extends AbstractQuickFixTest {
public void testAllFilesPresentInCreateSecondaryConstructor() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createSecondaryConstructor"), Pattern.compile("^before(\\w+)\\.kt$"), true);
}
@TestMetadata("beforeCallWithExpectedType.kt")
public void testCallWithExpectedType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeCallWithExpectedType.kt");
doTest(fileName);
}
@TestMetadata("beforeCallWithTypeArguments.kt")
public void testCallWithTypeArguments() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeCallWithTypeArguments.kt");
doTest(fileName);
}
@TestMetadata("beforeClassWithBody.kt")
public void testClassWithBody() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeClassWithBody.kt");
doTest(fileName);
}
@TestMetadata("beforeClassWithoutBody.kt")
public void testClassWithoutBody() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeClassWithoutBody.kt");
doTest(fileName);
}
@TestMetadata("beforeWrongExpectedType.kt")
public void testWrongExpectedType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor/beforeWrongExpectedType.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createVariable")
@TestDataPath("$PROJECT_ROOT")
@InnerTestClasses({