Skip to content

Commit

Permalink
Provide quick fix for migration of single elements in named arguments
Browse files Browse the repository at this point in the history
 See more in KT-20171
  • Loading branch information
zarechenskiy committed Sep 26, 2017
1 parent 8ab7c26 commit 8a545f0
Show file tree
Hide file tree
Showing 14 changed files with 164 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ enum BadNamedArgumentsTarget {
DiagnosticFactory1<KtExpression, KotlinType> MISSING_RECEIVER = DiagnosticFactory1.create(ERROR);
DiagnosticFactory0<KtExpression> NO_RECEIVER_ALLOWED = DiagnosticFactory0.create(ERROR);

DiagnosticFactory0<KtExpression> ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION = DiagnosticFactory0.create(WARNING);
DiagnosticFactory1<KtExpression, KotlinType> ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION = DiagnosticFactory1.create(WARNING);
DiagnosticFactory0<KtExpression> ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION = DiagnosticFactory0.create(WARNING);

// Call resolution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -760,7 +760,7 @@ public static DiagnosticRenderer getRendererForDiagnostic(@NotNull Diagnostic di
MAP.put(NO_VALUE_FOR_PARAMETER, "No value passed for parameter ''{0}''", NAME);
MAP.put(MISSING_RECEIVER, "A receiver of type {0} is required", RENDER_TYPE);
MAP.put(NO_RECEIVER_ALLOWED, "No receiver can be passed to this function or property");
MAP.put(ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION, "Assigning single elements to varargs in named form is deprecated");
MAP.put(ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION, "Assigning single elements to varargs in named form is deprecated", TO_STRING);
MAP.put(ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION, "Assigning single elements to varargs in named form is deprecated");

MAP.put(CREATING_AN_INSTANCE_OF_ABSTRACT_CLASS, "Cannot create an instance of an abstract class");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class AssigningNamedArgumentToVarargChecker : CallChecker {
checkAssignmentOfSingleElementInAnnotation(argument, argumentExpression, context)
}
else {
checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context)
checkAssignmentOfSingleElementInFunction(argument, argumentExpression, context, parameterDescriptor)
}
}

Expand All @@ -75,10 +75,11 @@ class AssigningNamedArgumentToVarargChecker : CallChecker {
private fun checkAssignmentOfSingleElementInFunction(
argument: ValueArgument,
argumentExpression: KtExpression,
context: ResolutionContext<*>
context: ResolutionContext<*>,
parameterDescriptor: ValueParameterDescriptor
) {
if (!argument.hasSpread()) {
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION.on(argumentExpression))
context.trace.report(Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION.on(argumentExpression, parameterDescriptor.type))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,5 +518,6 @@ class QuickFixRegistrar : QuickFixContributor {
ANNOTATION_USED_AS_ANNOTATION_ARGUMENT.registerFactory(RemoveAtFromAnnotationArgument)

ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_ANNOTATION.registerFactory(ReplaceWithArrayCallInAnnotationFix)
ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION.registerFactory(SurroundWithArrayOfWithSpreadOperatorInFunctionFix)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2017 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

import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver.Companion.ARRAY_OF_FUNCTION
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver.Companion.PRIMITIVE_TYPE_TO_ARRAY

class SurroundWithArrayOfWithSpreadOperatorInFunctionFix(
val wrapper: Name,
argument: KtExpression
) : KotlinQuickFixAction<KtExpression>(argument) {
override fun getText() = "Surround with *$wrapper(...)"

override fun getFamilyName() = text

override fun invoke(project: Project, editor: Editor?, file: KtFile) {
val argument = element?.getParentOfType<KtValueArgument>(false) ?: return
val argumentName = argument.getArgumentName()?.asName ?: return
val argumentExpression = argument.getArgumentExpression() ?: return

val factory = KtPsiFactory(argumentExpression)

val surroundedWithArrayOf = factory.createExpressionByPattern("$wrapper($0)", argumentExpression)
val newArgument = factory.createArgument(surroundedWithArrayOf, argumentName, isSpread = true)

argument.replace(newArgument)
}

companion object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtExpression>? {
val actualDiagnostic = Errors.ASSIGNING_SINGLE_ELEMENT_TO_VARARG_IN_NAMED_FORM_FUNCTION.cast(diagnostic)
val parameterType = actualDiagnostic.a

val wrapper = PRIMITIVE_TYPE_TO_ARRAY[KotlinBuiltIns.getPrimitiveArrayElementType(parameterType)] ?: ARRAY_OF_FUNCTION
return SurroundWithArrayOfWithSpreadOperatorInFunctionFix(wrapper, actualDiagnostic.psiElement)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *arrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

fun anyFoo(vararg a: Any) {}

fun test() {
anyFoo(a = intArr<caret>ayOf(1))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *arrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

fun anyFoo(vararg a: Any) {}

fun test() {
anyFoo(a = *arrayOf(intArrayOf(1)))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *intArrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

fun foo(vararg s: Int) {}

fun test() {
foo(s = <caret>1)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *intArrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

fun foo(vararg s: Int) {}

fun test() {
foo(s = <caret>*intArrayOf(1))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *arrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

fun foo(vararg s: String) {}

fun test() {
foo(s = <caret>"value")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *arrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

fun foo(vararg s: String) {}

fun test() {
foo(s = *arrayOf("value"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *arrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

class Foo<T>(vararg val p: T)

fun test() {
Foo(p = 123<caret>)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// "Surround with *arrayOf(...)" "true"
// LANGUAGE_VERSION: 1.2

class Foo<T>(vararg val p: T)

fun test() {
Foo(p = *arrayOf(123))
}
Original file line number Diff line number Diff line change
Expand Up @@ -10428,6 +10428,39 @@ public void testUnusedImports() throws Exception {
}
}

@TestMetadata("idea/testData/quickfix/surroundWithArrayOfForNamedArgumentsToVarargs")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class SurroundWithArrayOfForNamedArgumentsToVarargs extends AbstractQuickFixTest {
public void testAllFilesPresentInSurroundWithArrayOfForNamedArgumentsToVarargs() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/surroundWithArrayOfForNamedArgumentsToVarargs"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
}

@TestMetadata("replaceForVarargOfAny.kt")
public void testReplaceForVarargOfAny() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/surroundWithArrayOfForNamedArgumentsToVarargs/replaceForVarargOfAny.kt");
doTest(fileName);
}

@TestMetadata("replaceToArrayOfPrimitiveTypes.kt")
public void testReplaceToArrayOfPrimitiveTypes() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/surroundWithArrayOfForNamedArgumentsToVarargs/replaceToArrayOfPrimitiveTypes.kt");
doTest(fileName);
}

@TestMetadata("simpleNamedArgumentToVararg.kt")
public void testSimpleNamedArgumentToVararg() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/surroundWithArrayOfForNamedArgumentsToVarargs/simpleNamedArgumentToVararg.kt");
doTest(fileName);
}

@TestMetadata("surroundWithSpreadForConstructorCall.kt")
public void testSurroundWithSpreadForConstructorCall() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/surroundWithArrayOfForNamedArgumentsToVarargs/surroundWithSpreadForConstructorCall.kt");
doTest(fileName);
}
}

@TestMetadata("idea/testData/quickfix/surroundWithNullCheck")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
Expand Down

0 comments on commit 8a545f0

Please sign in to comment.