Bug Fix: KT-4613

Fix replace with infix function intention to be inapplicable in the
case of package function calls, such as “kotlin.io.println(“”).”
This commit is contained in:
Pradyoth Kukkapalli
2014-04-24 12:42:02 -04:00
parent 33fd82cf45
commit 65e670ef15
7 changed files with 88 additions and 12 deletions
@@ -276,6 +276,8 @@ replace.with.dot.qualified.method.call.intention=Replace with simple method call
replace.with.dot.qualified.method.call.intention.family=Replace with simple method call
replace.with.infix.function.call.intention=Replace with infix function call
replace.with.infix.function.call.intention.family=Replace with infix function call
replace.with.infix.function.call.intention.error.resolution.failed=The element cannot be resolved
replace.with.infix.function.call.intention.error.package.call=Cannot be applied with a package as the receiver
replace.explicit.function.literal.param.with.it=Replace explicit parameter ''{0}'' with ''it''
replace.explicit.function.literal.param.with.it.family=Replace Explicit Parameter With 'it'
move.lambda.inside.parentheses=Move lambda function into parentheses
@@ -19,14 +19,18 @@ package org.jetbrains.jet.plugin.intentions
import org.jetbrains.jet.lang.psi.JetCallExpression
import com.intellij.openapi.editor.Editor
import org.jetbrains.jet.lang.psi.JetDotQualifiedExpression
import org.jetbrains.jet.lang.psi.JetValueArgument
import org.jetbrains.jet.lang.psi.JetPsiFactory
import org.jetbrains.jet.lang.psi.JetPsiUnparsingUtils
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.psi.JetFile
import org.jetbrains.jet.plugin.caches.resolve.getBindingContext
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.plugin.JetBundle
import com.intellij.openapi.ui.popup.JBPopupFactory
import org.jetbrains.jet.lang.psi.JetValueArgument
import org.jetbrains.jet.plugin.project.AnalyzerFacadeWithCache
import org.jetbrains.jet.analyzer.computeTypeInfoInContext
import org.jetbrains.jet.lang.types.PackageType
import org.jetbrains.jet.lang.psi.JetPsiUnparsingUtils
import org.jetbrains.jet.lang.psi.JetPsiFactory
public class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<JetCallExpression>("replace.with.infix.function.call.intention", javaClass()) {
public open class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<JetCallExpression>("replace.with.infix.function.call.intention", javaClass()) {
override fun isApplicableTo(element: JetCallExpression): Boolean {
throw IllegalStateException("isApplicableTo(JetExpressionImpl, Editor) should be called instead")
}
@@ -54,11 +58,12 @@ public class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<J
if (typeArguments?.getArguments()?.size() ?: 0 == 0 &&
numOfTotalValueArguments == 1 &&
callee != null) {
if (valueArguments?.getArguments()?.size() == 1 && valueArguments?.getArguments()?.first()?.isNamed() ?: false) {
val file: JetFile = element.getContainingJetFile()
val file = element.getContainingJetFile()
val bindingContext = file.getBindingContext()
val descriptor = bindingContext.get(BindingContext.RESOLVED_CALL, callee)
val valueArgumentsMap = descriptor?.getValueArguments()
val resolvedCallDescriptor = bindingContext[BindingContext.RESOLVED_CALL, callee]
val valueArgumentsMap = resolvedCallDescriptor?.getValueArguments()
val firstArgument = valueArguments?.getArguments()?.first()
return valueArgumentsMap?.keySet()?.any { it.getName().asString() == firstArgument?.getArgumentName()?.getText() && it.getIndex() == 0 } ?: false
@@ -73,13 +78,34 @@ public class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<J
}
}
open fun intentionFailed(editor: Editor, messageID: String) {
JBPopupFactory.getInstance()!!.createMessage("Intention failed: ${JetBundle.message("replace.with.infix.function.call.intention.error.$messageID")}").showInBestPositionFor(editor)
}
override fun applyTo(element: JetCallExpression, editor: Editor) {
val parent = element.getParent() as JetDotQualifiedExpression
val receiver = parent.getReceiverExpression()
val leftHandText = parent.getReceiverExpression().getText()
val rightHandTextStringBuilder = StringBuilder()
val operatorText = element.getCalleeExpression()!!.getText()
val valueArguments = element.getValueArgumentList()?.getArguments() ?: listOf<JetValueArgument>()
val functionLiteralArguments = element.getFunctionLiteralArguments()
val bindingContext = AnalyzerFacadeWithCache.getContextForElement(parent)
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, parent]
when {
scope == null -> {
intentionFailed(editor, "resolution.failed")
return
}
else ->
when (receiver.computeTypeInfoInContext(scope).getType()) {
is PackageType -> {
intentionFailed(editor, "package.call")
return
}
}
}
rightHandTextStringBuilder.append(
if (valueArguments.size() > 0)
@@ -92,5 +118,4 @@ public class ReplaceWithInfixFunctionCallIntention : JetSelfTargetingIntention<J
parent.replace(replacement)
}
}
}
@@ -0,0 +1,5 @@
// WITH_RUNTIME
// SHOULD_FAIL_WITH: package.call
fun main() {
kotlin.io.<caret>println("")
}
@@ -0,0 +1,9 @@
// WITH_RUNTIME
// IS_APPLICABLE: false
package demo
fun foo(str: String) = kotlin.io.println(str)
fun main() {
<caret>demo.foo("")
}
@@ -179,7 +179,7 @@ public abstract class AbstractCodeTransformationTest extends LightCodeInsightTes
}
public void doTestReplaceWithInfixFunctionCall(@NotNull String path) throws Exception {
doTestIntention(path, new ReplaceWithInfixFunctionCallIntention());
doTestIntention(path, new TestableReplaceWithInfixFunctionCallIntention());
}
public void doTestReplaceExplicitFunctionLiteralParamWithIt(@NotNull String path) throws Exception {
@@ -2067,6 +2067,11 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
doTestReplaceWithInfixFunctionCall("idea/testData/intentions/replaceWithInfixFunctionCall/nullAssertedReceiver.kt");
}
@TestMetadata("packageFunctionCall.kt")
public void testPackageFunctionCall() throws Exception {
doTestReplaceWithInfixFunctionCall("idea/testData/intentions/replaceWithInfixFunctionCall/packageFunctionCall.kt");
}
@TestMetadata("propertyAccess.kt")
public void testPropertyAccess() throws Exception {
doTestReplaceWithInfixFunctionCall("idea/testData/intentions/replaceWithInfixFunctionCall/propertyAccess.kt");
@@ -2087,6 +2092,11 @@ public class CodeTransformationTestGenerated extends AbstractCodeTransformationT
doTestReplaceWithInfixFunctionCall("idea/testData/intentions/replaceWithInfixFunctionCall/simpleMethodCall.kt");
}
@TestMetadata("singlePackageFunctionCall.kt")
public void testSinglePackageFunctionCall() throws Exception {
doTestReplaceWithInfixFunctionCall("idea/testData/intentions/replaceWithInfixFunctionCall/singlePackageFunctionCall.kt");
}
@TestMetadata("zeroArguments.kt")
public void testZeroArguments() throws Exception {
doTestReplaceWithInfixFunctionCall("idea/testData/intentions/replaceWithInfixFunctionCall/zeroArguments.kt");
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2014 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.jet.plugin.intentions
import com.intellij.openapi.editor.Editor
public class TestableReplaceWithInfixFunctionCallIntention : ReplaceWithInfixFunctionCallIntention() {
override fun intentionFailed(editor: Editor, messageID: String) {
throw IntentionTestException(messageID)
}
}