Create from Usage: Generate secondary constructors by call expressions
This commit is contained in:
@@ -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$);
|
||||
|
||||
+26
-8
@@ -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? {
|
||||
+1
@@ -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)
|
||||
|
||||
+1
@@ -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)
|
||||
|
||||
+13
@@ -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)
|
||||
}
|
||||
+12
@@ -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)
|
||||
}
|
||||
+11
@@ -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)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Create secondary constructor" "true"
|
||||
|
||||
trait T
|
||||
|
||||
class A: T
|
||||
|
||||
fun test() {
|
||||
val t: T = A(<caret>1)
|
||||
}
|
||||
+12
@@ -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)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// "Create secondary constructor" "true"
|
||||
|
||||
class A {
|
||||
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A(<caret>1)
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// "Create secondary constructor" "true"
|
||||
|
||||
class A
|
||||
|
||||
fun test() {
|
||||
val a = A(<caret>1)
|
||||
}
|
||||
+15
@@ -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)
|
||||
}
|
||||
+7
@@ -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)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class G {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
class J {
|
||||
|
||||
public J(int i) {
|
||||
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// "Create secondary constructor" "true"
|
||||
// ERROR: Too many arguments for public/*package*/ constructor J() defined in J
|
||||
|
||||
fun test() = J(1)
|
||||
+4
@@ -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)
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
class J {
|
||||
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
Reference in New Issue
Block a user