KT-9839 related, secondary constructor to primary: inspection for the single constructor
(cherry picked from commit aec0090)
This commit is contained in:
committed by
Mikhail Glukhikh
parent
3508bea391
commit
0a4542c6c0
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This inspection reports secondary constructor that can be replaced with more concise primary constructor
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1807,6 +1807,14 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ConvertSecondaryConstructorToPrimaryInspection"
|
||||||
|
displayName="Convert to primary constructor"
|
||||||
|
groupName="Kotlin"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="WARNING"
|
||||||
|
language="kotlin"
|
||||||
|
/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
+20
-11
@@ -17,17 +17,26 @@
|
|||||||
package org.jetbrains.kotlin.idea.intentions
|
package org.jetbrains.kotlin.idea.intentions
|
||||||
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||||
import org.jetbrains.kotlin.idea.util.CommentSaver
|
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.containingClass
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
|
|
||||||
class ConvertSecondaryConstructorToPrimaryIntention : SelfTargetingIntention<KtSecondaryConstructor>(
|
class ConvertSecondaryConstructorToPrimaryInspection : IntentionBasedInspection<KtSecondaryConstructor>(
|
||||||
|
ConvertSecondaryConstructorToPrimaryIntention::class,
|
||||||
|
{ constructor -> constructor.containingClass()?.getSecondaryConstructors()?.size == 1 }
|
||||||
|
) {
|
||||||
|
override fun inspectionTarget(element: KtSecondaryConstructor) = element.getConstructorKeyword()
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConvertSecondaryConstructorToPrimaryIntention : SelfTargetingRangeIntention<KtSecondaryConstructor>(
|
||||||
KtSecondaryConstructor::class.java,
|
KtSecondaryConstructor::class.java,
|
||||||
"Convert to primary constructor"
|
"Convert to primary constructor"
|
||||||
) {
|
) {
|
||||||
@@ -41,22 +50,22 @@ class ConvertSecondaryConstructorToPrimaryIntention : SelfTargetingIntention<KtS
|
|||||||
return isReachableByDelegationFrom(delegationDescriptor, context, visited + constructor)
|
return isReachableByDelegationFrom(delegationDescriptor, context, visited + constructor)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isApplicableTo(element: KtSecondaryConstructor, caretOffset: Int): Boolean {
|
override fun applicabilityRange(element: KtSecondaryConstructor): TextRange? {
|
||||||
val delegationCall = element.getDelegationCall()
|
val delegationCall = element.getDelegationCall()
|
||||||
if (delegationCall.isCallToThis) return false
|
if (delegationCall.isCallToThis) return null
|
||||||
val klass = element.containingClassOrObject ?: return false
|
val klass = element.containingClassOrObject ?: return null
|
||||||
if (klass.hasPrimaryConstructor()) return false
|
if (klass.hasPrimaryConstructor()) return null
|
||||||
|
|
||||||
val context = klass.analyze()
|
val context = klass.analyzeFully()
|
||||||
val classDescriptor = context[BindingContext.CLASS, klass] ?: return false
|
val classDescriptor = context[BindingContext.CLASS, klass] ?: return null
|
||||||
val elementDescriptor = context[BindingContext.CONSTRUCTOR, element] ?: return false
|
val elementDescriptor = context[BindingContext.CONSTRUCTOR, element] ?: return null
|
||||||
|
|
||||||
for (constructorDescriptor in classDescriptor.constructors) {
|
for (constructorDescriptor in classDescriptor.constructors) {
|
||||||
if (constructorDescriptor == elementDescriptor) continue
|
if (constructorDescriptor == elementDescriptor) continue
|
||||||
if (!elementDescriptor.isReachableByDelegationFrom(constructorDescriptor, context)) return false
|
if (!elementDescriptor.isReachableByDelegationFrom(constructorDescriptor, context)) return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return element.textRange
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.tryConvertToPropertyByParameterInitialization(
|
private fun KtExpression.tryConvertToPropertyByParameterInitialization(
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<problems>
|
||||||
|
<problem>
|
||||||
|
<file>test.kt</file>
|
||||||
|
<line>5</line>
|
||||||
|
<module>light_idea_test_case</module>
|
||||||
|
<entry_point TYPE="file" FQNAME="temp:///src/test.kt" />
|
||||||
|
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Convert to primary constructor</problem_class>
|
||||||
|
<description>Convert to primary constructor</description>
|
||||||
|
</problem>
|
||||||
|
</problems>
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
// INSPECTION_CLASS: org.jetbrains.kotlin.idea.intentions.ConvertSecondaryConstructorToPrimaryInspection
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
class Single {
|
||||||
|
|
||||||
|
val x: Int
|
||||||
|
|
||||||
|
constructor(x: Int) {
|
||||||
|
this.x = x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotSingle {
|
||||||
|
|
||||||
|
val x: Int
|
||||||
|
|
||||||
|
constructor(): this(42)
|
||||||
|
|
||||||
|
constructor(x: Int) {
|
||||||
|
this.x = x
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -154,6 +154,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("convertSecondaryToPrimary/inspectionData/inspections.test")
|
||||||
|
public void testConvertSecondaryToPrimary_inspectionData_Inspections_test() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/convertSecondaryToPrimary/inspectionData/inspections.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("equalsAndHashCode/inspectionData/inspections.test")
|
@TestMetadata("equalsAndHashCode/inspectionData/inspections.test")
|
||||||
public void testEqualsAndHashCode_inspectionData_Inspections_test() throws Exception {
|
public void testEqualsAndHashCode_inspectionData_Inspections_test() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/equalsAndHashCode/inspectionData/inspections.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspections/equalsAndHashCode/inspectionData/inspections.test");
|
||||||
|
|||||||
Reference in New Issue
Block a user