Create from Usage: Implement "Create function" on callable references

#KT-10283 Fixed
This commit is contained in:
Alexey Sedunov
2015-12-07 20:21:15 +03:00
parent 87aebd2cdf
commit f50059a11a
15 changed files with 186 additions and 0 deletions
@@ -252,6 +252,7 @@ public class QuickFixRegistrar : QuickFixContributor {
UNRESOLVED_REFERENCE_WRONG_RECEIVER.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
UNRESOLVED_REFERENCE.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
UNRESOLVED_REFERENCE.registerFactory(CreateFunctionFromCallableReferenceActionFactory)
NO_VALUE_FOR_PARAMETER.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
TOO_MANY_ARGUMENTS.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
EXPRESSION_EXPECTED_PACKAGE_FOUND.registerFactory(*CreateCallableFromCallActionFactory.INSTANCES)
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
import com.intellij.util.SmartList
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.core.refactoring.getExtractionContainers
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.ifEmpty
object CreateFunctionFromCallableReferenceActionFactory : CreateCallableMemberFromUsageFactory<KtCallableReferenceExpression>() {
override fun getElementOfInterest(diagnostic: Diagnostic): KtCallableReferenceExpression? {
return diagnostic.psiElement.getStrictParentOfType<KtCallableReferenceExpression>()
}
override fun extractFixData(element: KtCallableReferenceExpression, diagnostic: Diagnostic): List<CallableInfo> {
val name = element.callableReference.getReferencedName()
val resolutionFacade = element.getResolutionFacade()
val context = resolutionFacade.analyze(element, BodyResolveMode.PARTIAL)
return element
.guessTypes(context, resolutionFacade.moduleDescriptor)
.filter { KotlinBuiltIns.isExactFunctionType(it) || KotlinBuiltIns.isExactExtensionFunctionType(it) }
.mapNotNull {
val receiverTypeInfo = element.typeReference?.let { TypeInfo(it, Variance.IN_VARIANCE) } ?: TypeInfo.Empty
val returnTypeInfo = TypeInfo(KotlinBuiltIns.getReturnTypeFromFunctionType(it), Variance.OUT_VARIANCE)
val containers = element.getExtractionContainers(includeAll = true).ifEmpty { return@mapNotNull null }
val parameterInfos = SmartList<ParameterInfo>().apply {
if (element.typeReference == null) {
KotlinBuiltIns.getReceiverType(it)?.let { add(ParameterInfo(TypeInfo(it, Variance.IN_VARIANCE))) }
}
KotlinBuiltIns
.getParameterTypeProjectionsFromFunctionType(it)
.mapTo(this) { ParameterInfo(TypeInfo(it.type, it.projectionKind)) }
}
FunctionInfo(name, receiverTypeInfo, returnTypeInfo, containers, parameterInfos)
}
}
}
@@ -0,0 +1,8 @@
// "Create function 'foo'" "true"
fun <T, U> T.map(f: T.() -> U) = f()
fun consume(s: String) {}
fun test() {
consume(1.map(::<caret>foo))
}
@@ -0,0 +1,11 @@
// "Create function 'foo'" "true"
fun <T, U> T.map(f: T.() -> U) = f()
fun consume(s: String) {}
fun test() {
fun foo(i: Int): String {
<selection>throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
consume(1.map(::foo))
}
@@ -0,0 +1,8 @@
// "Create extension function 'foo'" "true"
fun <T, U> T.map(f: T.() -> U) = f()
fun consume(s: String) {}
fun test() {
consume(1.map(Int::<caret>foo))
}
@@ -0,0 +1,12 @@
// "Create extension function 'foo'" "true"
fun <T, U> T.map(f: T.() -> U) = f()
fun consume(s: String) {}
fun test() {
consume(1.map(Int::foo))
}
fun Int.foo(): String {
<selection>throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
@@ -0,0 +1,6 @@
// "Create function 'foo'" "true"
fun <T, U> T.map(f: (T) -> U) = f(this)
fun test() {
1.map(::<caret>foo)
}
@@ -0,0 +1,9 @@
// "Create function 'foo'" "true"
fun <T, U> T.map(f: (T) -> U) = f(this)
fun test() {
fun foo(i: Int): Any {
<selection>throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
1.map(::foo)
}
@@ -0,0 +1,11 @@
// "Create function 'foo'" "false"
// ACTION: Convert to expression body
// ACTION: Rename reference
// ERROR: Unresolved reference: foo
fun bar(n: Int) = "$n"
fun consume(s: String) {}
fun test() {
consume(bar(::<caret>foo))
}
@@ -0,0 +1,8 @@
// "Create function 'foo'" "true"
fun <T, U> T.map(f: (T) -> U) = f(this)
fun consume(s: String) {}
fun test() {
consume(1.map(::<caret>foo))
}
@@ -0,0 +1,11 @@
// "Create function 'foo'" "true"
fun <T, U> T.map(f: (T) -> U) = f(this)
fun consume(s: String) {}
fun test() {
fun foo(i: Int): String {
<selection>throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.</selection>
}
consume(1.map(::foo))
}
@@ -1,6 +1,7 @@
// "Create local variable 'foo'" "false"
// ACTION: Rename reference
// ACTION: Add 'f =' to argument
// ACTION: Create function 'foo'
// ERROR: Unresolved reference: foo
fun test(f: (Int) -> Int) {}
@@ -1,6 +1,7 @@
// "Create parameter 'foo'" "false"
// ACTION: Rename reference
// ACTION: Add 'f =' to argument
// ACTION: Create function 'foo'
// ERROR: Unresolved reference: foo
fun test(f: (Int) -> Int) {}
@@ -1,6 +1,7 @@
// "Create property 'foo'" "false"
// ACTION: Rename reference
// ACTION: Add 'f =' to argument
// ACTION: Create function 'foo'
// ERROR: Unresolved reference: foo
fun test(f: (Int) -> Int) {}
@@ -2157,6 +2157,45 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/callableReferences")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CallableReferences extends AbstractQuickFixTest {
public void testAllFilesPresentInCallableReferences() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/callableReferences"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("extensionNoReceiverInCallableRef.kt")
public void testExtensionNoReceiverInCallableRef() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/callableReferences/extensionNoReceiverInCallableRef.kt");
doTest(fileName);
}
@TestMetadata("extensionWithReceiverInCallableRef.kt")
public void testExtensionWithReceiverInCallableRef() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/callableReferences/extensionWithReceiverInCallableRef.kt");
doTest(fileName);
}
@TestMetadata("noExpectedReturnType.kt")
public void testNoExpectedReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/callableReferences/noExpectedReturnType.kt");
doTest(fileName);
}
@TestMetadata("noFunctionalType.kt")
public void testNoFunctionalType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/callableReferences/noFunctionalType.kt");
doTest(fileName);
}
@TestMetadata("withExpectedReturnType.kt")
public void testWithExpectedReturnType() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/callableReferences/withExpectedReturnType.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/component")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)