diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 2250b797cf9..9d387285287 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -1104,6 +1104,11 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.AddJvmOverloadsIntention
+ Kotlin
+
+
org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention
Kotlin
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddJvmOverloadsIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddJvmOverloadsIntention.kt
new file mode 100644
index 00000000000..644877fccce
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddJvmOverloadsIntention.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.codeInsight.intention.LowPriorityAction
+import com.intellij.openapi.editor.Editor
+import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
+import org.jetbrains.kotlin.idea.util.addAnnotation
+import org.jetbrains.kotlin.idea.util.findAnnotation
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+private val annotationFqName = FqName("kotlin.jvm.JvmOverloads")
+
+class AddJvmOverloadsIntention : SelfTargetingIntention(
+ KtModifierListOwner::class.java, "Add '@JvmOverloads' annotation"
+), LowPriorityAction {
+
+ override fun isApplicableTo(element: KtModifierListOwner, caretOffset: Int): Boolean {
+ val (targetName, parameters) = when (element) {
+ is KtNamedFunction -> {
+ val funKeyword = element.funKeyword ?: return false
+ val valueParameterList = element.valueParameterList ?: return false
+ if (caretOffset !in funKeyword.startOffset..valueParameterList.endOffset) {
+ return false
+ }
+
+ "function '${element.name}'" to valueParameterList.parameters
+ }
+ is KtSecondaryConstructor -> {
+ val constructorKeyword = element.getConstructorKeyword()
+ val valueParameterList = element.valueParameterList ?: return false
+ if (caretOffset !in constructorKeyword.startOffset..valueParameterList.endOffset) {
+ return false
+ }
+
+ "secondary constructor" to valueParameterList.parameters
+ }
+ is KtPrimaryConstructor -> {
+ val parameters = (element.valueParameterList ?: return false).parameters
+
+ // For primary constructors with all default values, a zero-arg constructor is generated anyway. If there's only one
+ // parameter and it has a default value, the bytecode with and without @JvmOverloads is exactly the same.
+ if (parameters.singleOrNull()?.hasDefaultValue() == true) {
+ return false
+ }
+
+ "primary constructor" to parameters
+ }
+ else -> return false
+ }
+
+ text = "Add '@JvmOverloads' annotation to $targetName"
+
+ return !ProjectStructureUtil.isJsKotlinModule(element.getContainingKtFile())
+ && parameters.any { it.hasDefaultValue() }
+ && element.findAnnotation(annotationFqName) == null
+ }
+
+ override fun applyTo(element: KtModifierListOwner, editor: Editor?) {
+ if (element is KtPrimaryConstructor) {
+ if (element.getConstructorKeyword() == null) {
+ element.addBefore(KtPsiFactory(element).createConstructorKeyword(), element.valueParameterList)
+ }
+ element.addAnnotation(annotationFqName, whiteSpaceText = " ")
+ }
+ else {
+ element.addAnnotation(annotationFqName)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/.intention b/idea/testData/intentions/addJvmOverloads/.intention
new file mode 100644
index 00000000000..638ffe104c1
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.AddJvmOverloadsIntention
diff --git a/idea/testData/intentions/addJvmOverloads/alreadyHasAnnotation.kt b/idea/testData/intentions/addJvmOverloads/alreadyHasAnnotation.kt
new file mode 100644
index 00000000000..90f68d38c0e
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/alreadyHasAnnotation.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+@kotlin.jvm.JvmOverloads
+fun foo(a: String = "", b: Int) {
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/method.kt b/idea/testData/intentions/addJvmOverloads/method.kt
new file mode 100644
index 00000000000..7de11fe993e
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/method.kt
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to function 'foo'"
+
+fun foo(a: String = "") {
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/method.kt.after b/idea/testData/intentions/addJvmOverloads/method.kt.after
new file mode 100644
index 00000000000..c10e1bbbc06
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/method.kt.after
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to function 'foo'"
+
+@JvmOverloads
+fun foo(a: String = "") {
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/noDefaultParams.kt b/idea/testData/intentions/addJvmOverloads/noDefaultParams.kt
new file mode 100644
index 00000000000..a559475b452
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/noDefaultParams.kt
@@ -0,0 +1,4 @@
+// IS_APPLICABLE: false
+
+fun foo(a: String, b: Int) {
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/primaryConstructor.kt b/idea/testData/intentions/addJvmOverloads/primaryConstructor.kt
new file mode 100644
index 00000000000..a78b3dff649
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/primaryConstructor.kt
@@ -0,0 +1,4 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to primary constructor"
+
+class A(val a: String = "", b: Int = 0)
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/primaryConstructor.kt.after b/idea/testData/intentions/addJvmOverloads/primaryConstructor.kt.after
new file mode 100644
index 00000000000..b63cb544329
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/primaryConstructor.kt.after
@@ -0,0 +1,4 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to primary constructor"
+
+class A @JvmOverloads constructor(val a: String = "", b: Int = 0)
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/primaryConstructorOneWithDefault.kt b/idea/testData/intentions/addJvmOverloads/primaryConstructorOneWithDefault.kt
new file mode 100644
index 00000000000..f37b8cf10d0
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/primaryConstructorOneWithDefault.kt
@@ -0,0 +1,4 @@
+// IS_APPLICABLE: false
+
+class A(a: String = "") {
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt b/idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt
new file mode 100644
index 00000000000..b5cc78da576
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt
@@ -0,0 +1,4 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to primary constructor"
+
+class A constructor(val a: String = "", b: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt.after b/idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt.after
new file mode 100644
index 00000000000..a4fa5c24912
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt.after
@@ -0,0 +1,4 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to primary constructor"
+
+class A @JvmOverloads constructor(val a: String = "", b: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt b/idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt
new file mode 100644
index 00000000000..4fa3577d782
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to secondary constructor"
+
+class A {
+ constructor(a: String = "", b: Int)
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt.after b/idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt.after
new file mode 100644
index 00000000000..0d0930e804c
--- /dev/null
+++ b/idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+// INTENTION_TEXT: "Add '@JvmOverloads' annotation to secondary constructor"
+
+class A {
+ @JvmOverloads
+ constructor(a: String = "", b: Int)
+}
\ 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 17caa252133..46b5d53b0e1 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
@@ -194,6 +194,57 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
+ @TestMetadata("idea/testData/intentions/addJvmOverloads")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class AddJvmOverloads extends AbstractIntentionTest {
+ public void testAllFilesPresentInAddJvmOverloads() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addJvmOverloads"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
+ }
+
+ @TestMetadata("alreadyHasAnnotation.kt")
+ public void testAlreadyHasAnnotation() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/alreadyHasAnnotation.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("method.kt")
+ public void testMethod() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/method.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("noDefaultParams.kt")
+ public void testNoDefaultParams() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/noDefaultParams.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("primaryConstructor.kt")
+ public void testPrimaryConstructor() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/primaryConstructor.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("primaryConstructorOneWithDefault.kt")
+ public void testPrimaryConstructorOneWithDefault() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/primaryConstructorOneWithDefault.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("primaryConstructorWithConstructorKeyword.kt")
+ public void testPrimaryConstructorWithConstructorKeyword() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/primaryConstructorWithConstructorKeyword.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("secondaryConstructor.kt")
+ public void testSecondaryConstructor() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmOverloads/secondaryConstructor.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/intentions/addNameToArgument")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)