diff --git a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt
index e0a0e251fda..87e36868a6a 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt
@@ -46,7 +46,8 @@ class MainFunctionDetector {
return findMainFunction(declarations) != null
}
- fun isMain(function: KtNamedFunction): Boolean {
+ @JvmOverloads
+ fun isMain(function: KtNamedFunction, checkJvmStaticAnnotation: Boolean = true): Boolean {
if (function.isLocal) {
return false
}
@@ -61,11 +62,11 @@ class MainFunctionDetector {
}
/* Psi only check for kotlin.jvm.jvmStatic annotation */
- if (!function.isTopLevel && !hasAnnotationWithExactNumberOfArguments(function, 0)) {
+ if (checkJvmStaticAnnotation && !function.isTopLevel && !hasAnnotationWithExactNumberOfArguments(function, 0)) {
return false
}
- return isMain(getFunctionDescriptor(function))
+ return isMain(getFunctionDescriptor(function), checkJvmStaticAnnotation)
}
fun getMainFunction(module: ModuleDescriptor): FunctionDescriptor? = getMainFunction(module, module.getPackage(FqName.ROOT))
@@ -90,7 +91,7 @@ class MainFunctionDetector {
companion object {
- fun isMain(descriptor: DeclarationDescriptor): Boolean {
+ fun isMain(descriptor: DeclarationDescriptor, checkJvmStaticAnnotation: Boolean = true): Boolean {
if (descriptor !is FunctionDescriptor) return false
if (getJVMFunctionName(descriptor) != "main") {
@@ -123,7 +124,7 @@ class MainFunctionDetector {
val containingDeclaration = descriptor.containingDeclaration
return containingDeclaration is ClassDescriptor
&& containingDeclaration.kind.isSingleton
- && descriptor.hasJvmStaticAnnotation()
+ && (descriptor.hasJvmStaticAnnotation() || !checkJvmStaticAnnotation)
}
private fun getJVMFunctionName(functionDescriptor: FunctionDescriptor): String {
diff --git a/idea/resources/intentionDescriptions/AddJvmStaticIntention/after.kt.template b/idea/resources/intentionDescriptions/AddJvmStaticIntention/after.kt.template
new file mode 100644
index 00000000000..9fe19d07997
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddJvmStaticIntention/after.kt.template
@@ -0,0 +1,5 @@
+object Test {
+ @JvmStatic
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddJvmStaticIntention/before.kt.template b/idea/resources/intentionDescriptions/AddJvmStaticIntention/before.kt.template
new file mode 100644
index 00000000000..238796eef0e
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddJvmStaticIntention/before.kt.template
@@ -0,0 +1,4 @@
+object Test {
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/AddJvmStaticIntention/description.html b/idea/resources/intentionDescriptions/AddJvmStaticIntention/description.html
new file mode 100644
index 00000000000..837b11d23a8
--- /dev/null
+++ b/idea/resources/intentionDescriptions/AddJvmStaticIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention adds @JvmStatic annotation to main() method in an object.
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index ffb28e9e267..7744a836b5b 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -1288,6 +1288,11 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.AddJvmStaticIntention
+ Kotlin
+
+
org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention
Kotlin
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/AddJvmStaticIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/AddJvmStaticIntention.kt
new file mode 100644
index 00000000000..95b3de7552f
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/AddJvmStaticIntention.kt
@@ -0,0 +1,48 @@
+/*
+ * 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.intentions
+
+import com.intellij.codeInsight.intention.LowPriorityAction
+import com.intellij.openapi.editor.Editor
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.idea.MainFunctionDetector
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
+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.KtNamedFunction
+
+class AddJvmStaticIntention : SelfTargetingIntention(
+ KtNamedFunction::class.java,
+ "Add '@JvmStatic' annotation"
+), LowPriorityAction {
+
+ private val annotationFqName = FqName("kotlin.jvm.JvmStatic")
+
+ override fun isApplicableTo(element: KtNamedFunction, caretOffset: Int): Boolean {
+ if (element.findAnnotation(annotationFqName) != null) return false
+ if (element.isTopLevel) return false
+ val detector = MainFunctionDetector { function ->
+ function.resolveToDescriptor() as FunctionDescriptor
+ }
+ return detector.isMain(element, false)
+ }
+
+ override fun applyTo(element: KtNamedFunction, editor: Editor?) {
+ element.addAnnotation(annotationFqName)
+ }
+}
diff --git a/idea/testData/intentions/addJvmStatic/.intention b/idea/testData/intentions/addJvmStatic/.intention
new file mode 100644
index 00000000000..dad9d3eacc4
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.AddJvmStaticIntention
diff --git a/idea/testData/intentions/addJvmStatic/hasJvmStatic.kt b/idea/testData/intentions/addJvmStatic/hasJvmStatic.kt
new file mode 100644
index 00000000000..f537bb8e504
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/hasJvmStatic.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+object Test {
+ @JvmStatic
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inAnonymousObject.kt b/idea/testData/intentions/addJvmStatic/inAnonymousObject.kt
new file mode 100644
index 00000000000..916b5952659
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inAnonymousObject.kt
@@ -0,0 +1,6 @@
+// IS_APPLICABLE: false
+
+private fun test() = object {
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inClass.kt b/idea/testData/intentions/addJvmStatic/inClass.kt
new file mode 100644
index 00000000000..226a56497b8
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inClass.kt
@@ -0,0 +1,6 @@
+// IS_APPLICABLE: false
+
+class Test {
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inCompanionObject.kt b/idea/testData/intentions/addJvmStatic/inCompanionObject.kt
new file mode 100644
index 00000000000..81ddf128729
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inCompanionObject.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+class Test {
+ companion object {
+ fun main(args: Array) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inCompanionObject.kt.after b/idea/testData/intentions/addJvmStatic/inCompanionObject.kt.after
new file mode 100644
index 00000000000..7b14edcab2d
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inCompanionObject.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+class Test {
+ companion object {
+ @JvmStatic
+ fun main(args: Array) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inObjedct.kt b/idea/testData/intentions/addJvmStatic/inObjedct.kt
new file mode 100644
index 00000000000..a0b87f27b85
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inObjedct.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+
+object Test {
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inObjedct.kt.after b/idea/testData/intentions/addJvmStatic/inObjedct.kt.after
new file mode 100644
index 00000000000..41077952c1f
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inObjedct.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+
+object Test {
+ @JvmStatic
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/inTopLevel.kt b/idea/testData/intentions/addJvmStatic/inTopLevel.kt
new file mode 100644
index 00000000000..df6ec2b953a
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/inTopLevel.kt
@@ -0,0 +1,4 @@
+// IS_APPLICABLE: false
+
+fun main(args: Array) {
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/mainJvmName.kt b/idea/testData/intentions/addJvmStatic/mainJvmName.kt
new file mode 100644
index 00000000000..ebd68374e7d
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/mainJvmName.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+
+object Test {
+ @JvmName("main")
+ fun test(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/mainJvmName.kt.after b/idea/testData/intentions/addJvmStatic/mainJvmName.kt.after
new file mode 100644
index 00000000000..06e0126fbfd
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/mainJvmName.kt.after
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+object Test {
+ @JvmStatic
+ @JvmName("main")
+ fun test(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/notMainJvmName.kt b/idea/testData/intentions/addJvmStatic/notMainJvmName.kt
new file mode 100644
index 00000000000..f2b31b2ad06
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/notMainJvmName.kt
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+// IS_APPLICABLE: false
+
+object Test {
+ @JvmName("test")
+ fun main(args: Array) {
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/addJvmStatic/notMainMethod.kt b/idea/testData/intentions/addJvmStatic/notMainMethod.kt
new file mode 100644
index 00000000000..f118e5af40b
--- /dev/null
+++ b/idea/testData/intentions/addJvmStatic/notMainMethod.kt
@@ -0,0 +1,6 @@
+// IS_APPLICABLE: false
+
+object Test {
+ fun test(args: Array) {
+ }
+}
\ 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 1751978403f..713262a2170 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
@@ -288,6 +288,69 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
+ @TestMetadata("idea/testData/intentions/addJvmStatic")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class AddJvmStatic extends AbstractIntentionTest {
+ public void testAllFilesPresentInAddJvmStatic() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addJvmStatic"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("hasJvmStatic.kt")
+ public void testHasJvmStatic() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/hasJvmStatic.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inAnonymousObject.kt")
+ public void testInAnonymousObject() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/inAnonymousObject.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inClass.kt")
+ public void testInClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/inClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inCompanionObject.kt")
+ public void testInCompanionObject() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/inCompanionObject.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inObjedct.kt")
+ public void testInObjedct() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/inObjedct.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("inTopLevel.kt")
+ public void testInTopLevel() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/inTopLevel.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("mainJvmName.kt")
+ public void testMainJvmName() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/mainJvmName.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notMainJvmName.kt")
+ public void testNotMainJvmName() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/notMainJvmName.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notMainMethod.kt")
+ public void testNotMainMethod() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addJvmStatic/notMainMethod.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/intentions/addMissingDestructuring")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)