diff --git a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt
index d6556cbbcd4..b9abec86e8c 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/idea/MainFunctionDetector.kt
@@ -101,7 +101,11 @@ class MainFunctionDetector {
companion object {
- fun isMain(descriptor: DeclarationDescriptor, checkJvmStaticAnnotation: Boolean = true): Boolean {
+ fun isMain(
+ descriptor: DeclarationDescriptor,
+ checkJvmStaticAnnotation: Boolean = true,
+ checkReturnType: Boolean = true
+ ): Boolean {
if (descriptor !is FunctionDescriptor) return false
if (getJVMFunctionName(descriptor) != "main") {
@@ -127,8 +131,7 @@ class MainFunctionDetector {
return false
}
- val returnType = descriptor.returnType
- if (returnType == null || !KotlinBuiltIns.isUnit(returnType)) return false
+ if (checkReturnType && !isMainReturnType(descriptor)) return false
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) return true
@@ -138,6 +141,11 @@ class MainFunctionDetector {
&& (descriptor.hasJvmStaticAnnotation() || !checkJvmStaticAnnotation)
}
+ fun isMainReturnType(descriptor: FunctionDescriptor): Boolean {
+ val returnType = descriptor.returnType
+ return returnType != null && KotlinBuiltIns.isUnit(returnType)
+ }
+
private fun getJVMFunctionName(functionDescriptor: FunctionDescriptor): String {
return DescriptorUtils.getJvmName(functionDescriptor) ?: functionDescriptor.name.asString()
}
diff --git a/idea/resources/inspectionDescriptions/MainFunctionReturnUnit.html b/idea/resources/inspectionDescriptions/MainFunctionReturnUnit.html
new file mode 100644
index 00000000000..7043219f03d
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/MainFunctionReturnUnit.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports a main function with incorrect return type (should be Unit).
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 196f8452f4f..c3e81245147 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2783,6 +2783,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.172 b/idea/src/META-INF/plugin.xml.172
index 477db1fa1cf..689faae300d 100644
--- a/idea/src/META-INF/plugin.xml.172
+++ b/idea/src/META-INF/plugin.xml.172
@@ -2783,6 +2783,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.181 b/idea/src/META-INF/plugin.xml.181
index 18197316d2a..74bc207d48d 100644
--- a/idea/src/META-INF/plugin.xml.181
+++ b/idea/src/META-INF/plugin.xml.181
@@ -2783,6 +2783,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.182 b/idea/src/META-INF/plugin.xml.182
index cc83f28386c..61210eddac4 100644
--- a/idea/src/META-INF/plugin.xml.182
+++ b/idea/src/META-INF/plugin.xml.182
@@ -2784,6 +2784,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.as31 b/idea/src/META-INF/plugin.xml.as31
index 278f670124e..e3065d4f451 100644
--- a/idea/src/META-INF/plugin.xml.as31
+++ b/idea/src/META-INF/plugin.xml.as31
@@ -2784,6 +2784,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/META-INF/plugin.xml.as32 b/idea/src/META-INF/plugin.xml.as32
index 6c8ca8ab306..ffa290fd8c0 100644
--- a/idea/src/META-INF/plugin.xml.as32
+++ b/idea/src/META-INF/plugin.xml.as32
@@ -2785,6 +2785,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/MainFunctionReturnUnitInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/MainFunctionReturnUnitInspection.kt
new file mode 100644
index 00000000000..c3162ab24b9
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/MainFunctionReturnUnitInspection.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections
+
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.builtins.KotlinBuiltIns
+import org.jetbrains.kotlin.descriptors.FunctionDescriptor
+import org.jetbrains.kotlin.idea.MainFunctionDetector
+import org.jetbrains.kotlin.idea.core.setType
+import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.namedFunctionVisitor
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+class MainFunctionReturnUnitInspection : AbstractKotlinInspection() {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+ return namedFunctionVisitor(fun(function: KtNamedFunction) {
+ if (function.name != "main") return
+
+ val descriptor = function.descriptor as? FunctionDescriptor ?: return
+ if (!MainFunctionDetector.isMain(descriptor, checkReturnType = false)) return
+ if (MainFunctionDetector.isMainReturnType(descriptor)) return
+
+ val funKeyword = function.funKeyword ?: return
+ val valueParameterList = function.valueParameterList ?: return
+ val typeReference = function.typeReference
+ val start = function.startOffset
+ val highlightRange = TextRange(
+ funKeyword.startOffset - start,
+ if (typeReference != null) typeReference.endOffset - start else valueParameterList.endOffset - start
+ )
+
+ holder.registerProblem(
+ holder.manager.createProblemDescriptor(
+ function,
+ highlightRange,
+ "main should return Unit",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ isOnTheFly,
+ ChangeMainFunctionReturnTypeToUnitFix(function.typeReference != null)
+ )
+ )
+ })
+ }
+}
+
+private class ChangeMainFunctionReturnTypeToUnitFix(private val hasExplicitReturnType: Boolean) : LocalQuickFix {
+ override fun getName() = if (hasExplicitReturnType) "Change return type to Unit" else "Add explicit Unit return type"
+
+ override fun getFamilyName() = name
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val function = descriptor.psiElement as? KtNamedFunction ?: return
+ function.setType(KotlinBuiltIns.FQ_NAMES.unit.asString())
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/.inspection b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/.inspection
new file mode 100644
index 00000000000..0037a5f015e
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.MainFunctionReturnUnitInspection
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notMain.kt b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notMain.kt
new file mode 100644
index 00000000000..338945e05c4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notMain.kt
@@ -0,0 +1,2 @@
+// PROBLEM: none
+fun test(args: Array): Int = 1
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt
new file mode 100644
index 00000000000..a19f7a6cffc
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt
@@ -0,0 +1,3 @@
+// FIX: Add explicit Unit return type
+// DISABLE-ERRORS
+fun main(args: Array) = 1
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt.after b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt.after
new file mode 100644
index 00000000000..bff1ac1d1b5
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt.after
@@ -0,0 +1,3 @@
+// FIX: Add explicit Unit return type
+// DISABLE-ERRORS
+fun main(args: Array): Unit = 1
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt
new file mode 100644
index 00000000000..2bdf40a4e09
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt
@@ -0,0 +1,3 @@
+// FIX: Change return type to Unit
+// DISABLE-ERRORS
+fun main(args: Array): Int = 1
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt.after b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt.after
new file mode 100644
index 00000000000..a7e4e3d933b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt.after
@@ -0,0 +1,3 @@
+// FIX: Change return type to Unit
+// DISABLE-ERRORS
+fun main(args: Array): Unit = 1
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt
new file mode 100644
index 00000000000..313a98e71ae
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt
@@ -0,0 +1,4 @@
+// DISABLE-ERRORS
+fun main(args: Array): Int {
+ return 1
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt.after b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt.after
new file mode 100644
index 00000000000..f326f1244ab
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt.after
@@ -0,0 +1,4 @@
+// DISABLE-ERRORS
+fun main(args: Array): Unit {
+ return 1
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/mainFunctionReturnUnit/unit.kt b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/unit.kt
new file mode 100644
index 00000000000..95ed374bd53
--- /dev/null
+++ b/idea/testData/inspectionsLocal/mainFunctionReturnUnit/unit.kt
@@ -0,0 +1,2 @@
+// PROBLEM: none
+fun main(args: Array) {}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index 6a84d27fc76..3fc4018179c 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -2320,6 +2320,45 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/mainFunctionReturnUnit")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class MainFunctionReturnUnit extends AbstractLocalInspectionTest {
+ public void testAllFilesPresentInMainFunctionReturnUnit() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/mainFunctionReturnUnit"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("notMain.kt")
+ public void testNotMain() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mainFunctionReturnUnit/notMain.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notUnit1.kt")
+ public void testNotUnit1() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit1.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notUnit2.kt")
+ public void testNotUnit2() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit2.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("notUnit3.kt")
+ public void testNotUnit3() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mainFunctionReturnUnit/notUnit3.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("unit.kt")
+ public void testUnit() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/mainFunctionReturnUnit/unit.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/mayBeConstant")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)