diff --git a/idea/resources/inspectionDescriptions/CanSealedSubClassBeObject.html b/idea/resources/inspectionDescriptions/CanSealedSubClassBeObject.html
new file mode 100644
index 00000000000..4a8662c3ede
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/CanSealedSubClassBeObject.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports direct inheritors of sealed class that have default constructors only and thus can be objects.
+
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 7ec629bff6a..6d93620712d 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2765,6 +2765,15 @@
language="kotlin"
/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt
new file mode 100644
index 00000000000..4cac7bf5755
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/CanSealedSubClassBeObjectInspection.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.inspections
+
+import com.intellij.codeInspection.LocalInspectionToolSession
+import com.intellij.codeInspection.ProblemHighlightType
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.asJava.classes.KtLightClassImpl
+import org.jetbrains.kotlin.idea.core.getModalityFromDescriptor
+import org.jetbrains.kotlin.idea.quickfix.sealedSubClassToObject.ConvertSealedSubClassToObjectFix
+import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
+import org.jetbrains.kotlin.idea.search.declarationsSearch.searchInheritors
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtVisitorVoid
+
+class CanSealedSubClassBeObjectInspection : AbstractKotlinInspection() {
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
+ return object : KtVisitorVoid() {
+ override fun visitClass(klass: KtClass) {
+ if (!klass.hasModifier(KtTokens.SEALED_KEYWORD)) return
+ if (klass.getModalityFromDescriptor() != KtTokens.SEALED_KEYWORD) return
+
+ klass.getSubclasses()
+ .withEmptyConstructors()
+ .thatAreFinal()
+ .thatHasNoInnerClasses()
+ .thatHasNoCompanionObjects()
+ .forEach { reportPossibleObject(it) }
+ }
+
+ private fun reportPossibleObject(klass: KtClass) {
+ val keyword = klass.getClassOrInterfaceKeyword() ?: return
+ holder.registerProblem(
+ keyword,
+ "Sealed Sub-class should be changed To Object",
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
+ ConvertSealedSubClassToObjectFix()
+ )
+ }
+ }
+ }
+
+ private fun KtClass.getSubclasses(): List {
+ return HierarchySearchRequest(this, this.useScope, false)
+ .searchInheritors().filterIsInstance()
+ }
+
+ private fun List.withEmptyConstructors(): List {
+ return map { it.kotlinOrigin }.filterIsInstance()
+ .filter { it.primaryConstructorParameters.isEmpty() }
+ .filter { klass -> klass.secondaryConstructors.all { cons -> cons.valueParameters.isEmpty() } }
+ }
+
+ private fun List.thatHasNoCompanionObjects(): List {
+ return filter { klass -> klass.companionObjects.isEmpty() }
+ }
+
+ private fun List.thatAreFinal(): List {
+ return filter { klass -> klass.getModalityFromDescriptor() == KtTokens.FINAL_KEYWORD }
+ }
+
+ private fun List.thatHasNoInnerClasses(): List {
+ return filter { klass -> klass.hasNoInnerClass() }
+ }
+
+ private fun KtClass.hasNoInnerClass(): Boolean {
+ val internalClasses = getBody()
+ ?.declarations
+ ?.filterIsInstance() ?: return true
+
+ return internalClasses.none { klass -> klass.isInner() }
+ }
+}
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/sealedSubClassToObject/ConvertSealedSubClassToObjectFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/sealedSubClassToObject/ConvertSealedSubClassToObjectFix.kt
new file mode 100644
index 00000000000..641453103a7
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/sealedSubClassToObject/ConvertSealedSubClassToObjectFix.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2000-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.quickfix.sealedSubClassToObject
+
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.lang.Language
+import com.intellij.openapi.project.Project
+import com.intellij.psi.JavaPsiFacade
+import com.intellij.psi.PsiElement
+import com.intellij.psi.impl.source.tree.JavaElementType
+import com.intellij.psi.search.searches.ReferencesSearch
+import org.jetbrains.kotlin.KtNodeTypes
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtPsiFactory
+import org.jetbrains.kotlin.psi.buildExpression
+import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
+
+class ConvertSealedSubClassToObjectFix : LocalQuickFix {
+
+ override fun getFamilyName() = "Convert Sealed Sub-class to Object"
+
+ companion object {
+ val JAVA_LANG = Language.findLanguageByID("JAVA")
+ val KOTLIN_LANG = Language.findLanguageByID("kotlin")
+ }
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val klass = descriptor.psiElement.getParentOfType(false) ?: return
+
+ changeInstances(klass)
+ changeDeclaration(klass)
+ }
+
+ /**
+ * Changes declaration of class to object.
+ */
+ private fun changeDeclaration(element: KtClass) {
+ val factory = KtPsiFactory(element)
+
+ element.changeToObject(factory)
+ element.transformToObject(factory)
+ }
+
+ private fun KtClass.changeToObject(factory: KtPsiFactory) {
+ getClassOrInterfaceKeyword()?.replace(factory.createExpression(KtTokens.OBJECT_KEYWORD.value))
+ secondaryConstructors.forEach { delete() }
+ primaryConstructor?.delete()
+ }
+
+ private fun KtClass.transformToObject(factory: KtPsiFactory) {
+ replace(factory.createObject(text))
+ }
+
+ /**
+ * Replace instantiations of the class with links to the singleton instance of the object.
+ */
+ private fun changeInstances(klass: KtClass) {
+ mapReferencesByLanguage(klass)
+ .apply {
+ replaceKotlin(klass)
+ replaceJava(klass)
+ }
+ }
+
+ /**
+ * Map references to this class by language
+ */
+ private fun mapReferencesByLanguage(klass: KtClass) = ReferencesSearch.search(klass)
+ .groupBy({ it.element.language }, { it.element.parent })
+
+ /**
+ * Replace Kotlin instantiations to a straightforward call to the singleton.
+ */
+ private fun Map>.replaceKotlin(klass: KtClass) {
+ val list = this[KOTLIN_LANG] ?: return
+ val singletonCall = KtPsiFactory(klass).buildExpression { appendName(klass.nameAsSafeName) }
+
+ list.filter { it.node.elementType == KtNodeTypes.CALL_EXPRESSION }
+ .forEach { it.replace(singletonCall) }
+ }
+
+ /**
+ * Replace Java instantiations to an instance of the object, unless it is the only thing
+ * done in the statement, in which IDEA will consider wrong, so I delete the line.
+ */
+ private fun Map>.replaceJava(klass: KtClass) {
+ val list = this[JAVA_LANG] ?: return
+ val first = list.firstOrNull() ?: return
+ val elementFactory = JavaPsiFacade.getElementFactory(klass.project)
+ val javaSingletonCall = elementFactory.createExpressionFromText("${klass.name}.INSTANCE", first)
+
+ list.filter { it.node.elementType == JavaElementType.NEW_EXPRESSION }
+ .forEach {
+ when (it.parent.node.elementType) {
+ JavaElementType.EXPRESSION_STATEMENT -> it.delete()
+ else -> it.replace(javaSingletonCall)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/expected.xml b/idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/expected.xml
new file mode 100644
index 00000000000..da5d2ab5187
--- /dev/null
+++ b/idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/expected.xml
@@ -0,0 +1,50 @@
+
+
+ sealed.kt
+ 8
+ light_idea_test_case
+
+ Sealed Sub-class should be changed To Object
+ Sealed Sub-class should be changed To Object
+
+
+ sealed.kt
+ 10
+ light_idea_test_case
+
+ Sealed Sub-class should be changed To Object
+ Sealed Sub-class should be changed To Object
+
+
+ sealed.kt
+ 13
+ light_idea_test_case
+
+ Sealed Sub-class should be changed To Object
+ Sealed Sub-class should be changed To Object
+
+
+ sealed.kt
+ 15
+ light_idea_test_case
+
+ Sealed Sub-class should be changed To Object
+ Sealed Sub-class should be changed To Object
+
+
+ sealed.kt
+ 41
+ light_idea_test_case
+
+ Sealed Sub-class should be changed To Object
+ Sealed Sub-class should be changed To Object
+
+
+ sealed.kt
+ 45
+ light_idea_test_case
+
+ Sealed Sub-class should be changed To Object
+ Sealed Sub-class should be changed To Object
+
+
\ No newline at end of file
diff --git a/idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/inspections.test b/idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/inspections.test
new file mode 100644
index 00000000000..9eed2b5b197
--- /dev/null
+++ b/idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection
diff --git a/idea/testData/inspections/sealedSubClassCanBeObject/sealed.kt b/idea/testData/inspections/sealedSubClassCanBeObject/sealed.kt
new file mode 100644
index 00000000000..12e45003967
--- /dev/null
+++ b/idea/testData/inspections/sealedSubClassCanBeObject/sealed.kt
@@ -0,0 +1,47 @@
+sealed class Sealed {
+ open class OpenSubSealedNestedNoChild() : Sealed()
+
+ open class SubSealedNestedWithChild : Sealed()
+
+ class SubSealedNestedChild() : SubSealedNestedWithChild()
+
+ final class FinalSubSealedNested() : Sealed()
+
+ class SubSealedNested() : Sealed()
+}
+
+class SubSealedParentheses() : Sealed()
+
+class SubSealedNoParentheses : Sealed()
+
+open class SubSealedWithChild : Sealed()
+
+class SubSealedChild : SubSealedWithChild()
+
+class SubSealedParameters(val value : String) : Sealed()
+
+object SubSealedObject() : Sealed()
+
+class SubSealedObjectWithPrimaryConstructor(parameter: String) : Sealed()
+
+class SubSealedObjectWithSecondaryConstructor() : Sealed() {
+ constructor(parameter: String): this()
+}
+
+object AlreadyObject: Sealed()
+
+class SubSealedWithCompanion: Sealed() {
+ companion object
+}
+
+class SubSealedWithInner: Sealed() {
+ inner class Inner
+}
+
+class SubSealedWithNested: Sealed() {
+ class Nested
+}
+
+class SubSealedWithFunction: Sealed() {
+ fun internalFunction() { }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/.inspection b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/.inspection
new file mode 100644
index 00000000000..59b482c36d0
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt
new file mode 100644
index 00000000000..89ae91c31d3
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt
@@ -0,0 +1,3 @@
+sealed class Sealed
+
+private class SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt.after b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt.after
new file mode 100644
index 00000000000..80fb84760f6
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt.after
@@ -0,0 +1,3 @@
+sealed class Sealed
+
+private object SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt
new file mode 100644
index 00000000000..578fff8216d
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt
@@ -0,0 +1,3 @@
+sealed class Sealed
+
+class SubSealed() : Sealed()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt.after b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt.after
new file mode 100644
index 00000000000..9f4a9df9787
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt.after
@@ -0,0 +1,3 @@
+sealed class Sealed
+
+object SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt
new file mode 100644
index 00000000000..eac01055aba
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt
@@ -0,0 +1,3 @@
+sealed class Sealed
+
+class SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt.after b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt.after
new file mode 100644
index 00000000000..9f4a9df9787
--- /dev/null
+++ b/idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt.after
@@ -0,0 +1,3 @@
+sealed class Sealed
+
+object SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/JavaSealedTest.java b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/JavaSealedTest.java
new file mode 100644
index 00000000000..5d76aca2f85
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/JavaSealedTest.java
@@ -0,0 +1,11 @@
+import seal.*
+
+class JavaSealedTest {
+ public void testNesting() {
+ new SubSealed.Nested();
+ Supplier nestedSupplier = SubSealed.Nested::new;
+
+ SubSealed.INSTANCE.internalFunction();
+ Runnable noArgFunction = SubSealed.INSTANCE::internalFunction;
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/KotlinSealedTest.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/KotlinSealedTest.kt
new file mode 100644
index 00000000000..be1502b3233
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/KotlinSealedTest.kt
@@ -0,0 +1,16 @@
+import seal.*
+
+class KotlinSealedTest {
+ fun testSeal() {
+ val internalFunction = SubSealed::internalFunction
+
+ val nestedClass = SubSealed.Nested()
+
+ SubSealed.Nested()
+ SubSealed::Nested
+
+ SubSealed.internalFunction()
+ SubSealed::internalFunction
+ SubSealed::internalFunction
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/Seal.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/Seal.kt
new file mode 100644
index 00000000000..e0b9734ad13
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/after/Seal.kt
@@ -0,0 +1,9 @@
+package seal
+
+sealed class Sealed
+
+object SubSealed : Sealed() {
+ class Nested
+
+ fun internalFunction() {}
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/JavaSealedTest.java b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/JavaSealedTest.java
new file mode 100644
index 00000000000..511545700fa
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/JavaSealedTest.java
@@ -0,0 +1,11 @@
+import seal.*
+
+class JavaSealedTest {
+ public void testNesting() {
+ new SubSealed.Nested();
+ Supplier nestedSupplier = SubSealed.Nested::new;
+
+ new SubSealed().internalFunction();
+ Runnable noArgFunction = new SubSealed()::internalFunction;
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/KotlinSealedTest.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/KotlinSealedTest.kt
new file mode 100644
index 00000000000..d70b96dd53a
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/KotlinSealedTest.kt
@@ -0,0 +1,16 @@
+import seal.*
+
+class KotlinSealedTest {
+ fun testSeal() {
+ val internalFunction = SubSealed()::internalFunction
+
+ val nestedClass = SubSealed.Nested()
+
+ SubSealed.Nested()
+ SubSealed::Nested
+
+ SubSealed().internalFunction()
+ SubSealed()::internalFunction
+ SubSealed::internalFunction
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/Seal.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/Seal.kt
new file mode 100644
index 00000000000..1daa4520144
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/before/Seal.kt
@@ -0,0 +1,9 @@
+package seal
+
+sealed class Sealed
+
+class SubSealed : Sealed() {
+ class Nested
+
+ fun internalFunction() {}
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/convertCallableReferenceUsages.test b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/convertCallableReferenceUsages.test
new file mode 100644
index 00000000000..67c88f77340
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/convertCallableReferenceUsages.test
@@ -0,0 +1,5 @@
+{
+ "mainFile": "Seal.kt",
+ "inspectionClass": "org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection",
+ "fix": "Convert Sealed Sub-class to Object"
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/JavaSealedTest.java b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/JavaSealedTest.java
new file mode 100644
index 00000000000..7f565d3f8cf
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/JavaSealedTest.java
@@ -0,0 +1,13 @@
+import seal.*
+
+class JavaSealedTest {
+ Sealed sealedInsideClass = SubSealed.INSTANCE;
+
+ public void testSeal() {
+ Sealed sealedInsideMethod = SubSealed.INSTANCE;
+
+ SubSealed.INSTANCE.toString();
+
+ // Will be deleted because Java doesn't allow a expression to be used as a statement
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/KotlinSealedTest.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/KotlinSealedTest.kt
new file mode 100644
index 00000000000..1283112599b
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/KotlinSealedTest.kt
@@ -0,0 +1,13 @@
+import seal.*
+
+val sealedOutsideClass = SubSealed
+
+class KotlinSealedTest {
+ val sealedInsideClass = SubSealed
+
+ fun testSeal() {
+ val sealedInsideMethod = SubSealed
+
+ SubSealed
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/Seal.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/Seal.kt
new file mode 100644
index 00000000000..b0f627a9d2c
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/after/Seal.kt
@@ -0,0 +1,5 @@
+package seal
+
+sealed class Sealed
+
+object SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/JavaSealedTest.java b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/JavaSealedTest.java
new file mode 100644
index 00000000000..39bbbcd0705
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/JavaSealedTest.java
@@ -0,0 +1,14 @@
+import seal.*
+
+class JavaSealedTest {
+ Sealed sealedInsideClass = new SubSealed();
+
+ public void testSeal() {
+ Sealed sealedInsideMethod = new SubSealed();
+
+ new SubSealed().toString();
+
+ // Will be deleted because Java doesn't allow a expression to be used as a statement
+ new SubSealed();
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/KotlinSealedTest.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/KotlinSealedTest.kt
new file mode 100644
index 00000000000..7e713eec17c
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/KotlinSealedTest.kt
@@ -0,0 +1,13 @@
+import seal.*
+
+val sealedOutsideClass = SubSealed()
+
+class KotlinSealedTest {
+ val sealedInsideClass = SubSealed()
+
+ fun testSeal() {
+ val sealedInsideMethod = SubSealed()
+
+ SubSealed()
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/Seal.kt b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/Seal.kt
new file mode 100644
index 00000000000..f2e29658a7b
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/before/Seal.kt
@@ -0,0 +1,5 @@
+package seal
+
+sealed class Sealed
+
+class SubSealed : Sealed()
\ No newline at end of file
diff --git a/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/convertInOtherFiles.test b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/convertInOtherFiles.test
new file mode 100644
index 00000000000..67c88f77340
--- /dev/null
+++ b/idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/convertInOtherFiles.test
@@ -0,0 +1,5 @@
+{
+ "mainFile": "Seal.kt",
+ "inspectionClass": "org.jetbrains.kotlin.idea.inspections.CanSealedSubClassBeObjectInspection",
+ "fix": "Convert Sealed Sub-class to Object"
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
index a4d4ae43a50..ae6f9a5b4c9 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
@@ -372,6 +372,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
doTest(fileName);
}
+ @TestMetadata("sealedSubClassCanBeObject/inspectionData/inspections.test")
+ public void testSealedSubClassCanBeObject_inspectionData_Inspections_test() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/sealedSubClassCanBeObject/inspectionData/inspections.test");
+ doTest(fileName);
+ }
+
@TestMetadata("sortModifiers/inspectionData/inspections.test")
public void testSortModifiers_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/sortModifiers/inspectionData/inspections.test");
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index 656465d667a..aba22cfc6b5 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -1234,6 +1234,33 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/convertSealedSubClassToObject")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class ConvertSealedSubClassToObject extends AbstractLocalInspectionTest {
+ public void testAllFilesPresentInConvertSealedSubClassToObject() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/convertSealedSubClassToObject"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
+ }
+
+ @TestMetadata("convertSubClassWithModifiers.kt")
+ public void testConvertSubClassWithModifiers() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithModifiers.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("convertSubClassWithParentheses.kt")
+ public void testConvertSubClassWithParentheses() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithParentheses.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("convertSubClassWithoutParentheses.kt")
+ public void testConvertSubClassWithoutParentheses() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/convertSealedSubClassToObject/convertSubClassWithoutParentheses.kt");
+ doTest(fileName);
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/copyWithoutNamedArguments")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java
index cf03fee249c..24277d292ee 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java
@@ -25,6 +25,18 @@ public class MultiFileLocalInspectionTestGenerated extends AbstractMultiFileLoca
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/multiFileLocalInspections"), Pattern.compile("^(.+)\\.test$"), TargetBackend.ANY);
}
+ @TestMetadata("convertSealedSubClassToObject/convertCallableReferenceUsages/convertCallableReferenceUsages.test")
+ public void testConvertSealedSubClassToObject_convertCallableReferenceUsages_ConvertCallableReferenceUsages() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertCallableReferenceUsages/convertCallableReferenceUsages.test");
+ doTest(fileName);
+ }
+
+ @TestMetadata("convertSealedSubClassToObject/convertInOtherFiles/convertInOtherFiles.test")
+ public void testConvertSealedSubClassToObject_convertInOtherFiles_ConvertInOtherFiles() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileLocalInspections/convertSealedSubClassToObject/convertInOtherFiles/convertInOtherFiles.test");
+ doTest(fileName);
+ }
+
@TestMetadata("moveFileToPackageMatchingDirectory/moveToDefaultDirectory/moveToDefaultDirectory.test")
public void testMoveFileToPackageMatchingDirectory_moveToDefaultDirectory_MoveToDefaultDirectory() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectory/moveToDefaultDirectory.test");