diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt index 7f67b743121..900dd4e1f5c 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt @@ -60,12 +60,7 @@ public class JavaAnnotationCallChecker : CallChecker { resolvedCall: ResolvedCall<*>, context: BasicCallResolutionContext ) { - resolvedCall.getValueArguments().filter { - p -> - p.key.getName() != JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME && - p.value is ExpressionValueArgument && - !((p.value as ExpressionValueArgument).getValueArgument()?.isNamed() ?: true) - }.forEach { + getJavaAnnotationCallValueArgumentsThatShouldBeNamed(resolvedCall).forEach { reportOnValueArgument(context, it, ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION) } } diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/annotationUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/annotationUtil.kt new file mode 100644 index 00000000000..301b50baa56 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/annotationUtil.kt @@ -0,0 +1,32 @@ +/* + * 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.load.kotlin + +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument + +fun getJavaAnnotationCallValueArgumentsThatShouldBeNamed(resolvedCall: ResolvedCall<*>): Map = + resolvedCall.getValueArguments().filter { + p -> + p.key.getName() != JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME && + p.value is ExpressionValueArgument && + !((p.value as ExpressionValueArgument).getValueArgument()?.isNamed() ?: true) + } + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt index bba46649841..e236cfad60c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspection.kt @@ -76,7 +76,8 @@ public class KotlinCleanupInspection(): LocalInspectionTool(), CleanupLocalInspe Errors.UNNECESSARY_NOT_NULL_ASSERTION, Errors.UNNECESSARY_SAFE_CALL, Errors.USELESS_CAST, - Errors.USELESS_ELVIS + Errors.USELESS_ELVIS, + ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION ) private fun Diagnostic.isObsoleteLabel(): Boolean { diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java index 050b2114fed..ea4a6242918 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.java @@ -346,5 +346,7 @@ public class QuickFixRegistrar { QuickFixes.factories.put(DEPRECATED_SYMBOL_WITH_MESSAGE, DeprecatedSymbolUsageFix.Companion); QuickFixes.factories.put(DEPRECATED_SYMBOL_WITH_MESSAGE, DeprecatedSymbolUsageInWholeProjectFix.Companion); + + QuickFixes.factories.put(ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION, ReplaceJavaAnnotationPositionedArgumentsFix.Companion); } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceJavaAnnotationPositionedArgumentsFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceJavaAnnotationPositionedArgumentsFix.kt new file mode 100644 index 00000000000..b05b64938c0 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ReplaceJavaAnnotationPositionedArgumentsFix.kt @@ -0,0 +1,54 @@ +/* + * 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 + +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.quickfix.quickfixUtil.createIntentionForFirstParentOfType +import org.jetbrains.kotlin.load.kotlin.getJavaAnnotationCallValueArgumentsThatShouldBeNamed +import org.jetbrains.kotlin.psi.JetAnnotationEntry +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument + +public class ReplaceJavaAnnotationPositionedArgumentsFix(element: JetAnnotationEntry) +: JetIntentionAction(element), CleanupFix { + override fun getText(): String = "Replace invalid positioned arguments for annotation" + override fun getFamilyName(): String = getText() + + override fun invoke(project: Project, editor: Editor?, file: JetFile) { + val resolvedCall = element.getResolvedCall(element.analyze()) ?: return + val psiFactory = JetPsiFactory(project) + + getJavaAnnotationCallValueArgumentsThatShouldBeNamed(resolvedCall).forEach argumentProcessor@{ + argument -> + val valueArgument = (argument.value as? ExpressionValueArgument)?.getValueArgument() ?: return@argumentProcessor + val expression = valueArgument.getArgumentExpression() ?: return@argumentProcessor + + valueArgument.asElement().replace(psiFactory.createArgument(expression, argument.getKey().getName().asString())) + } + } + + companion object : JetSingleIntentionActionFactory() { + override fun createAction(diagnostic: Diagnostic) = + diagnostic.createIntentionForFirstParentOfType(::ReplaceJavaAnnotationPositionedArgumentsFix) + } +} diff --git a/idea/testData/inspections/cleanup/JavaAnn.java b/idea/testData/inspections/cleanup/JavaAnn.java new file mode 100644 index 00000000000..e23fa2a58b9 --- /dev/null +++ b/idea/testData/inspections/cleanup/JavaAnn.java @@ -0,0 +1,7 @@ +public @interface JavaAnn { + int value(); + String arg1(); + Class[] arg2(); + Class arg3(); + int arg4() default 0; +} diff --git a/idea/testData/inspections/cleanup/cleanup.kt b/idea/testData/inspections/cleanup/cleanup.kt index 9f8535b26c5..12044cae24f 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt +++ b/idea/testData/inspections/cleanup/cleanup.kt @@ -42,3 +42,5 @@ fun unnecessaryExclExcl(x: String) { fun unnecessaryCast(x: String) = x as String fun unnecessaryElvis(x: String) = x ?: "" + +JavaAnn(1, "abc", arrayOf(javaClass>()), javaClass()) class MyClass diff --git a/idea/testData/inspections/cleanup/cleanup.kt.after b/idea/testData/inspections/cleanup/cleanup.kt.after index b1ae42ea09d..e280ee4d6b0 100644 --- a/idea/testData/inspections/cleanup/cleanup.kt.after +++ b/idea/testData/inspections/cleanup/cleanup.kt.after @@ -44,3 +44,5 @@ fun unnecessaryExclExcl(x: String) { fun unnecessaryCast(x: String) = x fun unnecessaryElvis(x: String) = x + +JavaAnn(1, arg1 = "abc", arg2 = arrayOf(Array::class), arg3 = String::class) class MyClass diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.after.kt b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.after.kt new file mode 100644 index 00000000000..0da25dd836f --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.after.kt @@ -0,0 +1,6 @@ +// "Replace invalid positioned arguments for annotation" "true" +// WITH_RUNTIME +// ERROR: Only named arguments are available for Java annotations +// ERROR: Only named arguments are available for Java annotations + +Ann(1, /*abc*/arg1 = "abc", arg2 = arrayOf(Int::class, Array::class), arg3 = String::class) class A diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.Main.kt b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.Main.kt new file mode 100644 index 00000000000..ff1defdbbec --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.Main.kt @@ -0,0 +1,6 @@ +// "Replace invalid positioned arguments for annotation" "true" +// WITH_RUNTIME +// ERROR: Only named arguments are available for Java annotations +// ERROR: Only named arguments are available for Java annotations + +Ann(1, /*abc*/"abc", arrayOf(Int::class, Array::class), arg3 = String::class) class A diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.data.Sample.java b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.data.Sample.java new file mode 100644 index 00000000000..a0c27531313 --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.data.Sample.java @@ -0,0 +1,7 @@ +public @interface Ann { + int value(); + String arg1(); + Class[] arg2(); + Class arg3(); + int arg4() default 0; +} diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.after.kt b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.after.kt new file mode 100644 index 00000000000..f61d35ce744 --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.after.kt @@ -0,0 +1,6 @@ +// "Replace invalid positioned arguments for annotation" "true" +// WITH_RUNTIME +// ERROR: Only named arguments are available for Java annotations +// ERROR: No value passed for parameter arg2 + +Ann(1, arg1 = "abc") class A diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.Main.kt b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.Main.kt new file mode 100644 index 00000000000..a9fd154e246 --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.Main.kt @@ -0,0 +1,6 @@ +// "Replace invalid positioned arguments for annotation" "true" +// WITH_RUNTIME +// ERROR: Only named arguments are available for Java annotations +// ERROR: No value passed for parameter arg2 + +Ann(1, "abc") class A diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.data.Sample.java b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.data.Sample.java new file mode 100644 index 00000000000..7a06c25321d --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.data.Sample.java @@ -0,0 +1,6 @@ +public @interface Ann { + int value(); + String arg1(); + Class arg2(); + int arg3() default 0; +} diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.after.kt b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.after.kt new file mode 100644 index 00000000000..b434a3f8194 --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.after.kt @@ -0,0 +1,6 @@ +// "Replace invalid positioned arguments for annotation" "true" +// WITH_RUNTIME +// ERROR: Only named arguments are available for Java annotations +// ERROR: An integer literal does not conform to the expected type kotlin.String + +Ann(1, arg1 = 2) class A diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.Main.kt b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.Main.kt new file mode 100644 index 00000000000..39265d29b13 --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.Main.kt @@ -0,0 +1,6 @@ +// "Replace invalid positioned arguments for annotation" "true" +// WITH_RUNTIME +// ERROR: Only named arguments are available for Java annotations +// ERROR: An integer literal does not conform to the expected type kotlin.String + +Ann(1, 2) class A diff --git a/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.data.Sample.java b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.data.Sample.java new file mode 100644 index 00000000000..7c83deababf --- /dev/null +++ b/idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.data.Sample.java @@ -0,0 +1,5 @@ +public @interface Ann { + int value(); + String arg1(); + int arg2() default 0; +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt index 0449be6e7a8..bad28259b7d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/KotlinCleanupInspectionTest.kt @@ -33,7 +33,7 @@ class KotlinCleanupInspectionTest(): JetLightCodeInsightFixtureTestCase() { public fun testCleanup() { myFixture.enableInspections(javaClass()) - myFixture.configureByFile("cleanup.kt") + myFixture.configureByFiles("cleanup.kt", "JavaAnn.java") val project = myFixture.getProject() val managerEx = InspectionManager.getInstance(project) diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 835d74729fa..04e5e38214f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -948,6 +948,33 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaAnnotationPositionedArguments extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInJavaAnnotationPositionedArguments() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/javaAnnotationPositionedArguments"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true); + } + + @TestMetadata("basicMultiple.before.Main.kt") + public void testBasicMultiple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments/basicMultiple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("noValueForArgumentMultiple.before.Main.kt") + public void testNoValueForArgumentMultiple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments/noValueForArgumentMultiple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("wrongTypeMultiple.before.Main.kt") + public void testWrongTypeMultiple() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments/wrongTypeMultiple.before.Main.kt"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration/lambdaSyntax") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)