diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt index 633baa92014..59734969cca 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt @@ -262,6 +262,8 @@ class KtPsiFactory(private val project: Project) { return createDeclaration(funDecl) } + fun createCallableReferenceExpression(text: String) = createExpression(text) as? KtCallableReferenceExpression + fun createSecondaryConstructor(decl: String): KtSecondaryConstructor { return createClass("class Foo {\n $decl \n}").getSecondaryConstructors().first() } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt index fb897ba5ad6..c101bf7ce10 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/createByPattern.kt @@ -33,6 +33,9 @@ import java.util.* fun KtPsiFactory.createExpressionByPattern(pattern: String, vararg args: Any): KtExpression = createByPattern(pattern, *args) { createExpression(it) } +fun KtPsiFactory.createValueArgumentListByPattern(pattern: String, vararg args: Any): KtValueArgumentList + = createByPattern(pattern, *args) { createCallArguments(it) } + fun KtPsiFactory.createDeclarationByPattern(pattern: String, vararg args: Any): TDeclaration = createByPattern(pattern, *args) { createDeclaration(it) } @@ -314,6 +317,10 @@ fun KtPsiFactory.buildExpression(build: BuilderByPattern.() -> Uni return buildByPattern({ pattern, args -> this.createExpressionByPattern(pattern, *args) }, build) } +fun KtPsiFactory.buildValueArgumentList(build: BuilderByPattern.() -> Unit): KtValueArgumentList { + return buildByPattern({ pattern, args -> this.createValueArgumentListByPattern(pattern, *args) }, build) +} + fun KtPsiFactory.buildDeclaration(build: BuilderByPattern.() -> Unit): KtDeclaration { return buildByPattern({ pattern, args -> this.createDeclarationByPattern(pattern, *args) }, build) } diff --git a/idea/resources/inspectionDescriptions/ConvertLambdaToReference.html b/idea/resources/inspectionDescriptions/ConvertLambdaToReference.html new file mode 100644 index 00000000000..72b89c3299b --- /dev/null +++ b/idea/resources/inspectionDescriptions/ConvertLambdaToReference.html @@ -0,0 +1,5 @@ + + +This inspection reports a lambda (function literal) expression that can be replaced with function reference expression + + diff --git a/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/after.kt.template b/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/after.kt.template new file mode 100644 index 00000000000..758471000da --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/after.kt.template @@ -0,0 +1,3 @@ +class Person(val name: String) + +val persons = listOf("Jack", "Tom").map(::Person) diff --git a/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/before.kt.template b/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/before.kt.template new file mode 100644 index 00000000000..db1c5a834b5 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/before.kt.template @@ -0,0 +1,3 @@ +class Person(val name: String) + +val persons = listOf("Jack", "Tom").map { Person(it) } diff --git a/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/description.html b/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/description.html new file mode 100644 index 00000000000..ad5671cfd08 --- /dev/null +++ b/idea/resources/intentionDescriptions/ConvertLambdaToReferenceIntention/description.html @@ -0,0 +1,5 @@ + + +This intention converts a lambda (function literal) expression calling a single function to the function reference + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 4e33e1213ec..fc16cff2510 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1260,6 +1260,11 @@ Kotlin + + org.jetbrains.kotlin.idea.intentions.ConvertLambdaToReferenceIntention + Kotlin + + + + diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt new file mode 100644 index 00000000000..c58ed53652f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertLambdaToReferenceIntention.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2010-2016 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.intentions + +import com.intellij.openapi.editor.Editor +import org.jetbrains.kotlin.builtins.getReturnTypeFromFunctionType +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade +import org.jetbrains.kotlin.idea.core.isVisible +import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingContext.REFERENCE_TARGET +import org.jetbrains.kotlin.resolve.descriptorUtil.hasDefaultValue +import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor +import org.jetbrains.kotlin.types.isDynamic +import org.jetbrains.kotlin.types.typeUtil.isTypeParameter +import org.jetbrains.kotlin.types.typeUtil.isUnit + +class ConvertLambdaToReferenceInspection : IntentionBasedInspection(ConvertLambdaToReferenceIntention()) + +class ConvertLambdaToReferenceIntention : SelfTargetingOffsetIndependentIntention( + KtLambdaExpression::class.java, "Convert lambda to reference" +) { + override fun isApplicableTo(element: KtLambdaExpression): Boolean { + val body = element.bodyExpression ?: return false + val statement = body.statements.singleOrNull() ?: return false + val lambdaParent = element.parent + val context: BindingContext + var lambdaMustReturnUnit = false + if (lambdaParent is KtLambdaArgument) { + val outerCallExpression = lambdaParent.parent as? KtCallExpression ?: return false + context = outerCallExpression.analyze() + val outerCallee = outerCallExpression.calleeExpression as? KtReferenceExpression ?: return false + val outerCalleeDescriptor = context[REFERENCE_TARGET, outerCallee] as? FunctionDescriptor ?: return false + // No function parameter predecessors with default value + if (outerCalleeDescriptor.valueParameters.any { it.hasDefaultValue() }) return false + val lambdaParameterType = outerCalleeDescriptor.valueParameters.lastOrNull()?.type + if (lambdaParameterType != null && lambdaParameterType.isFunctionType) { + // Special Unit case (non-Unit returning lambda is accepted here, but non-Unit returning reference is not) + lambdaMustReturnUnit = getReturnTypeFromFunctionType(lambdaParameterType).isUnit() + } + } + else { + context = statement.analyze() + } + + fun isConvertableCallInLambda( + callableExpression: KtExpression, + explicitReceiver: KtExpression? = null, + lambdaExpression: KtLambdaExpression + ): Boolean { + val calleeReferenceExpression = when (callableExpression) { + is KtCallExpression -> callableExpression.calleeExpression as? KtNameReferenceExpression ?: return false + is KtNameReferenceExpression -> callableExpression + else -> return false + } + val calleeDescriptor = context[REFERENCE_TARGET, calleeReferenceExpression] as? CallableMemberDescriptor ?: return false + // No references with type parameters + if (calleeDescriptor.typeParameters.isNotEmpty()) return false + // No references to Java synthetic properties + if (calleeDescriptor is SyntheticJavaPropertyDescriptor) return false + val descriptorHasReceiver = with (calleeDescriptor) { + // No references to both member / extension + if (dispatchReceiverParameter != null && extensionReceiverParameter != null) return false + dispatchReceiverParameter != null || extensionReceiverParameter != null + } + val callHasReceiver = explicitReceiver != null + if (descriptorHasReceiver != callHasReceiver) return false + val callableArgumentsCount = if (callableExpression is KtCallExpression) callableExpression.valueArguments.size else 0 + if (calleeDescriptor.valueParameters.size != callableArgumentsCount) return false + if (lambdaMustReturnUnit) { + calleeDescriptor.returnType.let { + // If Unit required, no references to non-Unit callables + if (it == null || !it.isUnit()) return false + } + } + + val hasSpecification = lambdaExpression.functionLiteral.hasParameterSpecification() + val receiverShift = if (callHasReceiver) 1 else 0 + val parametersCount = if (hasSpecification) lambdaExpression.valueParameters.size else 1 + if (parametersCount != callableArgumentsCount + receiverShift) return false + if (explicitReceiver != null) { + if (explicitReceiver !is KtNameReferenceExpression) return false + val callReceiverDescriptor = context[REFERENCE_TARGET, explicitReceiver] as? ParameterDescriptor ?: return false + val receiverType = callReceiverDescriptor.type + // No exotic receiver types + if (receiverType.isTypeParameter() || receiverType.isError || receiverType.isDynamic() || + receiverType.isFlexibleRecursive() || !receiverType.constructor.isDenotable || receiverType.isFunctionType) return false + val receiverDeclarationDescriptor = receiverType.constructor.declarationDescriptor + if (receiverDeclarationDescriptor is ClassDescriptor) { + // No references to object members + if (receiverDeclarationDescriptor.kind == ClassKind.OBJECT) return false + // No invisible receiver types + if (!receiverDeclarationDescriptor.isVisible( + explicitReceiver, null, context, explicitReceiver.getResolutionFacade() + )) return false + } + + val parameterName = if (hasSpecification) lambdaExpression.valueParameters[0].name else "it" + if (explicitReceiver.getReferencedName() != parameterName) return false + } + // Same lambda / references function parameter order + if (callableExpression is KtCallExpression) { + callableExpression.valueArguments.forEachIndexed { i, argument -> + val argumentExpression = argument.getArgumentExpression() as? KtNameReferenceExpression ?: return false + val parameterName = if (hasSpecification) lambdaExpression.valueParameters[i + receiverShift].name else "it" + if (argumentExpression.getReferencedName() != parameterName) return false + } + } + return true + } + + return when (statement) { + is KtCallExpression -> { + isConvertableCallInLambda(callableExpression = statement, lambdaExpression = element) + } + is KtNameReferenceExpression -> false // Global property reference is not possible (?!) + is KtDotQualifiedExpression -> { + val selector = statement.selectorExpression ?: return false + isConvertableCallInLambda(callableExpression = selector, explicitReceiver = statement.receiverExpression, + lambdaExpression = element) + } + else -> false + } + } + + private fun KtCallExpression.getCallReferencedName() = (calleeExpression as? KtNameReferenceExpression)?.getReferencedName() + + private fun buildReferenceText(expression: KtExpression): String? { + return when (expression) { + is KtCallExpression -> "::${expression.getCallReferencedName()}" + is KtDotQualifiedExpression -> { + val selector = expression.selectorExpression + val selectorReferenceName = when (selector) { + is KtCallExpression -> selector.getCallReferencedName() ?: return null + is KtNameReferenceExpression -> selector.getReferencedName() + else -> return null + } + val receiver = expression.receiverExpression as? KtNameReferenceExpression ?: return null + val context = receiver.analyze() + val receiverDescriptor = context[REFERENCE_TARGET, receiver] as? ParameterDescriptor ?: return null + val receiverType = receiverDescriptor.type + "$receiverType::$selectorReferenceName" + } + else -> null + } + } + + override fun applyTo(element: KtLambdaExpression, editor: Editor?) { + val body = element.bodyExpression ?: return + val referenceName = buildReferenceText(body.statements.singleOrNull() ?: return) ?: return + val factory = KtPsiFactory(editor?.project) + val lambdaArgument = element.parent as? KtLambdaArgument + if (lambdaArgument == null) { + // Without lambda argument syntax, just replace lambda with reference + val callableReferenceExpr = factory.createCallableReferenceExpression(referenceName) ?: return + element.replace(callableReferenceExpr) + } + else { + // Otherwise, replace the whole argument list for lambda argument-using call + val outerCallExpression = lambdaArgument.parent as? KtCallExpression ?: return + val arguments = outerCallExpression.valueArguments.filter { it !is KtLambdaArgument } + val newArgumentList = factory.buildValueArgumentList { + appendFixedText("(") + for (argument in arguments) { + appendExpression(argument.getArgumentExpression()) + appendFixedText(", ") + } + appendFixedText(referenceName) + appendFixedText(")") + } + val argumentList = outerCallExpression.valueArgumentList + if (argumentList == null) { + lambdaArgument.replace(newArgumentList) + } + else { + argumentList.replace(newArgumentList) + lambdaArgument.delete() + } + } + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt index 5ebafb150e4..00d70858a91 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt @@ -49,11 +49,6 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention(KtCallableDeclaration::class.java, "Specify type explicitly"), LowPriorityAction { - private fun KotlinType.isFlexibleRecursive(): Boolean { - if (isFlexible()) return true - return arguments.any { !it.isStarProjection && it.type.isFlexibleRecursive() } - } - fun dangerousFlexibleTypeOrNull(declaration: KtCallableDeclaration, publicAPIOnly: Boolean): KotlinType? { when (declaration) { is KtFunction -> if (declaration.isLocal || declaration.hasDeclaredReturnType()) return null diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt index dbba0e53b75..ee81e359438 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/Utils.kt @@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isFlexible fun KtCallableDeclaration.setType(type: KotlinType, shortenReferences: Boolean = true) { if (type.isError) return @@ -203,3 +204,10 @@ private fun getNegatedOperatorText(token: IElementType): String { else -> throw IllegalArgumentException("The token $token does not have a negated equivalent.") } } + +internal fun KotlinType.isFlexibleRecursive(): Boolean { + if (isFlexible()) return true + return arguments.any { !it.isStarProjection && it.type.isFlexibleRecursive() } +} + + diff --git a/idea/testData/intentions/convertLambdaToReference/.intention b/idea/testData/intentions/convertLambdaToReference/.intention new file mode 100644 index 00000000000..30062ef5ac3 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/.intention @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.intentions.ConvertLambdaToReferenceIntention diff --git a/idea/testData/intentions/convertLambdaToReference/constructor.kt b/idea/testData/intentions/convertLambdaToReference/constructor.kt new file mode 100644 index 00000000000..e62f32e0e22 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/constructor.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +class Person(val name: String) + +val x = listOf("Jack", "Tom").map { Person(it) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/constructor.kt.after b/idea/testData/intentions/convertLambdaToReference/constructor.kt.after new file mode 100644 index 00000000000..89b00988656 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/constructor.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +class Person(val name: String) + +val x = listOf("Jack", "Tom").map(::Person) \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/conversion.kt b/idea/testData/intentions/convertLambdaToReference/conversion.kt new file mode 100644 index 00000000000..b81603ae624 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/conversion.kt @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf("123", "4567").map { it.toInt() } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/conversion.kt.after b/idea/testData/intentions/convertLambdaToReference/conversion.kt.after new file mode 100644 index 00000000000..a3f454d09ea --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/conversion.kt.after @@ -0,0 +1,3 @@ +// WITH_RUNTIME + +val x = listOf("123", "4567").map(String::toInt) \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/defaultArgument.kt b/idea/testData/intentions/convertLambdaToReference/defaultArgument.kt new file mode 100644 index 00000000000..30171e0d752 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/defaultArgument.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +fun foo(z: Int, y: Int = 0) = y + z + +val x = { arg: Int -> foo(arg) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/defaultBeforeLambda.kt b/idea/testData/intentions/convertLambdaToReference/defaultBeforeLambda.kt new file mode 100644 index 00000000000..1e52ab15929 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/defaultBeforeLambda.kt @@ -0,0 +1,8 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +fun List.transform(x: Int = 0, f: (Int) -> Int) = map { f(it + x) } + +fun bar(x: Int) = x * x + +val y = listOf(1, 2, 3).transform { bar(it) } diff --git a/idea/testData/intentions/convertLambdaToReference/defaultOverridden.kt b/idea/testData/intentions/convertLambdaToReference/defaultOverridden.kt new file mode 100644 index 00000000000..885dd6919a9 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/defaultOverridden.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +interface Transformer { + fun transform(x: Int = 0, f: (Int) -> Int) = f(x) +} + +class TransformerImpl : Transformer + +fun bar(x: Int) = x * x + +val y = TransformerImpl().transform { bar(it) } diff --git a/idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt b/idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt new file mode 100644 index 00000000000..4226a88b83b --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun bar(s: String) = s.length + +val x = listOf("Jack", "Tom").map() { w -> bar(w) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt.after b/idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt.after new file mode 100644 index 00000000000..bd3507959c5 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun bar(s: String) = s.length + +val x = listOf("Jack", "Tom").map(::bar) \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/erasedReceiver.kt b/idea/testData/intentions/convertLambdaToReference/erasedReceiver.kt new file mode 100644 index 00000000000..11bc55317c7 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/erasedReceiver.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +class Generic { + val y = { arg: T -> arg.hashCode() } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/extension.kt b/idea/testData/intentions/convertLambdaToReference/extension.kt new file mode 100644 index 00000000000..8e0595daa28 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/extension.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +class Owner(val z: Int) { + // Possible only in 1.1 with bound references (this::foo) + val x = { arg: Int -> foo(arg) } +} + +fun Owner.foo(y: Int) = y + z + diff --git a/idea/testData/intentions/convertLambdaToReference/extensionProperty.kt b/idea/testData/intentions/convertLambdaToReference/extensionProperty.kt new file mode 100644 index 00000000000..3867fa8487f --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/extensionProperty.kt @@ -0,0 +1,3 @@ +val Any.name: String get() = toString() + +val converted = { x: Any -> x.name } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/extensionProperty.kt.after b/idea/testData/intentions/convertLambdaToReference/extensionProperty.kt.after new file mode 100644 index 00000000000..13e2417abec --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/extensionProperty.kt.after @@ -0,0 +1,3 @@ +val Any.name: String get() = toString() + +val converted = Any::name \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/generic.kt b/idea/testData/intentions/convertLambdaToReference/generic.kt new file mode 100644 index 00000000000..e92396426ea --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/generic.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +fun id(y: T) = y + +val x = { arg: Int -> id(arg) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/globalProperty.kt b/idea/testData/intentions/convertLambdaToReference/globalProperty.kt new file mode 100644 index 00000000000..47f4895462f --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/globalProperty.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false + +val name = "Kotlin" +val x = { obj: Any -> name } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/globalPropertyNoArgs.kt b/idea/testData/intentions/convertLambdaToReference/globalPropertyNoArgs.kt new file mode 100644 index 00000000000..35b07c86db1 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/globalPropertyNoArgs.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false + +val name = "Kotlin" +val x = { -> name } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/inner.kt b/idea/testData/intentions/convertLambdaToReference/inner.kt new file mode 100644 index 00000000000..74d62be4ddb --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/inner.kt @@ -0,0 +1,5 @@ +class Owner { + inner class Inner + + val x = { o: Owner -> o.Inner() } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/inner.kt.after b/idea/testData/intentions/convertLambdaToReference/inner.kt.after new file mode 100644 index 00000000000..27789391093 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/inner.kt.after @@ -0,0 +1,5 @@ +class Owner { + inner class Inner + + val x = Owner::Inner +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/mapTo.kt b/idea/testData/intentions/convertLambdaToReference/mapTo.kt new file mode 100644 index 00000000000..0d2a0fcce14 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/mapTo.kt @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun bar(s: String) = s.length + +val x = listOf("Jack", "Tom").mapTo(hashSetOf()) { w -> bar(w) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/mapTo.kt.after b/idea/testData/intentions/convertLambdaToReference/mapTo.kt.after new file mode 100644 index 00000000000..fdbd2b5b6ea --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/mapTo.kt.after @@ -0,0 +1,5 @@ +// WITH_RUNTIME + +fun bar(s: String) = s.length + +val x = listOf("Jack", "Tom").mapTo(hashSetOf(), ::bar) \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/member.kt b/idea/testData/intentions/convertLambdaToReference/member.kt new file mode 100644 index 00000000000..218e7f5afcc --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/member.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +class Owner(val z: Int) { + fun foo(y: Int) = y + z + // Possible only in 1.1 with bound references (this::foo) + val x = { arg: Int -> foo(arg) } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/memberExtension.kt b/idea/testData/intentions/convertLambdaToReference/memberExtension.kt new file mode 100644 index 00000000000..1462a9ea245 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/memberExtension.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +class Their { + fun Int.foo() = "x" + + val x = { arg: Int -> arg.foo() } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/name.kt b/idea/testData/intentions/convertLambdaToReference/name.kt new file mode 100644 index 00000000000..b717e9fe48b --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/name.kt @@ -0,0 +1,3 @@ +class Person(val name: String) + +val reader = { p: Person -> p.name } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/name.kt.after b/idea/testData/intentions/convertLambdaToReference/name.kt.after new file mode 100644 index 00000000000..10d0a018ef1 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/name.kt.after @@ -0,0 +1,3 @@ +class Person(val name: String) + +val reader = Person::name \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/nullable.kt b/idea/testData/intentions/convertLambdaToReference/nullable.kt new file mode 100644 index 00000000000..b524a026d8b --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/nullable.kt @@ -0,0 +1,3 @@ +fun Int?.foo() = this?.hashCode() ?: 0 + +val x = { arg: Int? -> arg.foo() } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/nullable.kt.after b/idea/testData/intentions/convertLambdaToReference/nullable.kt.after new file mode 100644 index 00000000000..ceae051bff0 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/nullable.kt.after @@ -0,0 +1,3 @@ +fun Int?.foo() = this?.hashCode() ?: 0 + +val x = Int?::foo \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/object.kt b/idea/testData/intentions/convertLambdaToReference/object.kt new file mode 100644 index 00000000000..565134be492 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/object.kt @@ -0,0 +1,7 @@ +// IS_APPLICABLE: false + +object Object { + fun foo() = 42 +} + +val x = { o: Object -> o.foo() } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/receiverParameter.kt b/idea/testData/intentions/convertLambdaToReference/receiverParameter.kt new file mode 100644 index 00000000000..7d58d671c2e --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/receiverParameter.kt @@ -0,0 +1,3 @@ +fun Int.foo(x: Int) = this - x + +val x = { a: Int, b: Int -> a.foo(b) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/receiverParameter.kt.after b/idea/testData/intentions/convertLambdaToReference/receiverParameter.kt.after new file mode 100644 index 00000000000..e13615a299b --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/receiverParameter.kt.after @@ -0,0 +1,3 @@ +fun Int.foo(x: Int) = this - x + +val x = Int::foo \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/receiverParameterReversed.kt b/idea/testData/intentions/convertLambdaToReference/receiverParameterReversed.kt new file mode 100644 index 00000000000..2d93d9d1998 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/receiverParameterReversed.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +fun Int.foo(x: Int) = this - x + +val x = { a: Int, b: Int -> b.foo(a) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/simple.kt b/idea/testData/intentions/convertLambdaToReference/simple.kt new file mode 100644 index 00000000000..e08b1e34723 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/simple.kt @@ -0,0 +1,3 @@ +fun foo(y: Int) = y + +val x = { arg: Int -> foo(arg) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/simple.kt.after b/idea/testData/intentions/convertLambdaToReference/simple.kt.after new file mode 100644 index 00000000000..273e0ef4c33 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/simple.kt.after @@ -0,0 +1,3 @@ +fun foo(y: Int) = y + +val x = ::foo \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/syntheticProperty.kt b/idea/testData/intentions/convertLambdaToReference/syntheticProperty.kt new file mode 100644 index 00000000000..1e80b0ddf1d --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/syntheticProperty.kt @@ -0,0 +1,4 @@ +// IS_APPLICABLE: false +// WITH_RUNTIME + +val x = listOf(java.lang.String("")).map { it.bytes } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/threeParameters.kt b/idea/testData/intentions/convertLambdaToReference/threeParameters.kt new file mode 100644 index 00000000000..73e76e4681d --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/threeParameters.kt @@ -0,0 +1,3 @@ +fun foo(x: Int, y: Int, z: Int) = x - y / z + +val x = { a: Int, b: Int, c: Int -> foo(a, b, c) } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/threeParameters.kt.after b/idea/testData/intentions/convertLambdaToReference/threeParameters.kt.after new file mode 100644 index 00000000000..18c1110c732 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/threeParameters.kt.after @@ -0,0 +1,3 @@ +fun foo(x: Int, y: Int, z: Int) = x - y / z + +val x = ::foo \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/toString.kt b/idea/testData/intentions/convertLambdaToReference/toString.kt new file mode 100644 index 00000000000..1b4daa9e1c7 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/toString.kt @@ -0,0 +1 @@ +val x = { arg: Int -> arg.toString() } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/toString.kt.after b/idea/testData/intentions/convertLambdaToReference/toString.kt.after new file mode 100644 index 00000000000..cad4c5c2973 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/toString.kt.after @@ -0,0 +1 @@ +val x = Int::toString \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/unit.kt b/idea/testData/intentions/convertLambdaToReference/unit.kt new file mode 100644 index 00000000000..2a2b3e6daec --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/unit.kt @@ -0,0 +1,9 @@ +// IS_APPLICABLE: false + +fun Int.exec(f: (Int) -> Unit) = f(this) + +fun bar(x: Int) = x + +fun foo() { + 2.exec { bar(it) } +} diff --git a/idea/testData/intentions/convertLambdaToReference/unwrap.kt b/idea/testData/intentions/convertLambdaToReference/unwrap.kt new file mode 100644 index 00000000000..28978f6dca7 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/unwrap.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +class Wrapper(private val x: T) { + fun unwrap() = x +} + +val unwrapped = listOf(Wrapper(1), Wrapper("B")).map { it.unwrap() } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/unwrap.kt.after b/idea/testData/intentions/convertLambdaToReference/unwrap.kt.after new file mode 100644 index 00000000000..977122fc3cc --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/unwrap.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +class Wrapper(private val x: T) { + fun unwrap() = x +} + +val unwrapped = listOf(Wrapper(1), Wrapper("B")).map(Wrapper::unwrap) \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/wrongParameterNumber.kt b/idea/testData/intentions/convertLambdaToReference/wrongParameterNumber.kt new file mode 100644 index 00000000000..f0eb3b0dc2a --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/wrongParameterNumber.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +fun foo() = 42 + +val x = { arg: Int -> foo() } \ No newline at end of file diff --git a/idea/testData/intentions/convertLambdaToReference/wrongParameterOrder.kt b/idea/testData/intentions/convertLambdaToReference/wrongParameterOrder.kt new file mode 100644 index 00000000000..7c2173b3034 --- /dev/null +++ b/idea/testData/intentions/convertLambdaToReference/wrongParameterOrder.kt @@ -0,0 +1,5 @@ +// IS_APPLICABLE: false + +fun foo(y: Int, z: Int) = y - z + +val x = { second: Int, first: Int -> foo(first, second) } \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 4d68d2d2911..0fe37fe6981 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -3713,6 +3713,189 @@ public class IntentionTestGenerated extends AbstractIntentionTest { } } + @TestMetadata("idea/testData/intentions/convertLambdaToReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ConvertLambdaToReference extends AbstractIntentionTest { + public void testAllFilesPresentInConvertLambdaToReference() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/convertLambdaToReference"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); + } + + @TestMetadata("constructor.kt") + public void testConstructor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/constructor.kt"); + doTest(fileName); + } + + @TestMetadata("conversion.kt") + public void testConversion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/conversion.kt"); + doTest(fileName); + } + + @TestMetadata("defaultArgument.kt") + public void testDefaultArgument() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/defaultArgument.kt"); + doTest(fileName); + } + + @TestMetadata("defaultBeforeLambda.kt") + public void testDefaultBeforeLambda() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/defaultBeforeLambda.kt"); + doTest(fileName); + } + + @TestMetadata("defaultOverridden.kt") + public void testDefaultOverridden() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/defaultOverridden.kt"); + doTest(fileName); + } + + @TestMetadata("emptyBrackets.kt") + public void testEmptyBrackets() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/emptyBrackets.kt"); + doTest(fileName); + } + + @TestMetadata("erasedReceiver.kt") + public void testErasedReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/erasedReceiver.kt"); + doTest(fileName); + } + + @TestMetadata("extension.kt") + public void testExtension() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/extension.kt"); + doTest(fileName); + } + + @TestMetadata("extensionProperty.kt") + public void testExtensionProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/extensionProperty.kt"); + doTest(fileName); + } + + @TestMetadata("generic.kt") + public void testGeneric() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/generic.kt"); + doTest(fileName); + } + + @TestMetadata("globalProperty.kt") + public void testGlobalProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/globalProperty.kt"); + doTest(fileName); + } + + @TestMetadata("globalPropertyNoArgs.kt") + public void testGlobalPropertyNoArgs() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/globalPropertyNoArgs.kt"); + doTest(fileName); + } + + @TestMetadata("inner.kt") + public void testInner() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/inner.kt"); + doTest(fileName); + } + + @TestMetadata("mapTo.kt") + public void testMapTo() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/mapTo.kt"); + doTest(fileName); + } + + @TestMetadata("member.kt") + public void testMember() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/member.kt"); + doTest(fileName); + } + + @TestMetadata("memberExtension.kt") + public void testMemberExtension() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/memberExtension.kt"); + doTest(fileName); + } + + @TestMetadata("name.kt") + public void testName() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/name.kt"); + doTest(fileName); + } + + @TestMetadata("nullable.kt") + public void testNullable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/nullable.kt"); + doTest(fileName); + } + + @TestMetadata("object.kt") + public void testObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/object.kt"); + doTest(fileName); + } + + @TestMetadata("receiverParameter.kt") + public void testReceiverParameter() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/receiverParameter.kt"); + doTest(fileName); + } + + @TestMetadata("receiverParameterReversed.kt") + public void testReceiverParameterReversed() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/receiverParameterReversed.kt"); + doTest(fileName); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/simple.kt"); + doTest(fileName); + } + + @TestMetadata("syntheticProperty.kt") + public void testSyntheticProperty() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/syntheticProperty.kt"); + doTest(fileName); + } + + @TestMetadata("threeParameters.kt") + public void testThreeParameters() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/threeParameters.kt"); + doTest(fileName); + } + + @TestMetadata("toString.kt") + public void testToString() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/toString.kt"); + doTest(fileName); + } + + @TestMetadata("unit.kt") + public void testUnit() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/unit.kt"); + doTest(fileName); + } + + @TestMetadata("unwrap.kt") + public void testUnwrap() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/unwrap.kt"); + doTest(fileName); + } + + @TestMetadata("wrongParameterNumber.kt") + public void testWrongParameterNumber() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/wrongParameterNumber.kt"); + doTest(fileName); + } + + @TestMetadata("wrongParameterOrder.kt") + public void testWrongParameterOrder() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertLambdaToReference/wrongParameterOrder.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/intentions/convertNegatedBooleanSequence") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)