"Convert to primary constructor" inspection: do not report when property has setter

#KT-22142 Fixed
This commit is contained in:
Toshiaki Kameyama
2020-02-04 18:54:25 +09:00
committed by Yan Zhulanow
parent 08f31758b8
commit 64f6ed586b
2 changed files with 47 additions and 21 deletions
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
import org.jetbrains.kotlin.idea.intentions.ConvertSecondaryConstructorToPrimaryIntention.Companion.tryConvertToPropertyByParameterInitialization
import org.jetbrains.kotlin.idea.util.CommentSaver
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
@@ -25,7 +26,18 @@ import org.jetbrains.kotlin.util.kind
class ConvertSecondaryConstructorToPrimaryInspection : IntentionBasedInspection<KtSecondaryConstructor>(
ConvertSecondaryConstructorToPrimaryIntention::class,
{ constructor -> constructor.containingClass()?.secondaryConstructors?.size == 1 }
fun(constructor: KtSecondaryConstructor): Boolean {
val containingClass = constructor.containingClass() ?: return false
if (containingClass.secondaryConstructors.size != 1) return false
val context = containingClass.analyzeWithContent()
val constructorDescriptor = context[BindingContext.CONSTRUCTOR, constructor] ?: return false
for (statement in constructor.bodyExpression?.statements.orEmpty()) {
val propertyDescriptor = statement.tryConvertToPropertyByParameterInitialization(constructorDescriptor, context)?.second
val property = propertyDescriptor?.let { DescriptorToSourceUtils.descriptorToDeclaration(it) as? KtProperty }
if (property?.setter?.hasBody() == true) return false
}
return true
}
) {
override fun inspectionTarget(element: KtSecondaryConstructor) = element.getConstructorKeyword()
}
@@ -62,26 +74,6 @@ class ConvertSecondaryConstructorToPrimaryIntention : SelfTargetingRangeIntentio
return TextRange(element.startOffset, element.valueParameterList?.endOffset ?: element.getConstructorKeyword().endOffset)
}
private fun KtExpression.tryConvertToPropertyByParameterInitialization(
constructorDescriptor: ConstructorDescriptor, context: BindingContext
): Pair<ValueParameterDescriptor, PropertyDescriptor>? {
if (this !is KtBinaryExpression || operationToken != KtTokens.EQ) return null
val rightReference = right as? KtReferenceExpression ?: return null
val rightDescriptor = context[BindingContext.REFERENCE_TARGET, rightReference] as? ValueParameterDescriptor ?: return null
if (rightDescriptor.containingDeclaration != constructorDescriptor) return null
val leftReference = when (val left = left) {
is KtReferenceExpression ->
left
is KtDotQualifiedExpression ->
if (left.receiverExpression is KtThisExpression) left.selectorExpression as? KtReferenceExpression else null
else ->
null
}
val leftDescriptor = context[BindingContext.REFERENCE_TARGET, leftReference] as? PropertyDescriptor ?: return null
return rightDescriptor to leftDescriptor
}
private fun KtSecondaryConstructor.extractInitializer(
parameterToPropertyMap: MutableMap<ValueParameterDescriptor, PropertyDescriptor>,
context: BindingContext,
@@ -189,4 +181,25 @@ class ConvertSecondaryConstructorToPrimaryIntention : SelfTargetingRangeIntentio
element.delete()
}
}
companion object {
fun KtExpression.tryConvertToPropertyByParameterInitialization(
constructorDescriptor: ConstructorDescriptor, context: BindingContext
): Pair<ValueParameterDescriptor, PropertyDescriptor>? {
if (this !is KtBinaryExpression || operationToken != KtTokens.EQ) return null
val rightReference = right as? KtReferenceExpression ?: return null
val rightDescriptor = context[BindingContext.REFERENCE_TARGET, rightReference] as? ValueParameterDescriptor ?: return null
if (rightDescriptor.containingDeclaration != constructorDescriptor) return null
val leftReference = when (val left = left) {
is KtReferenceExpression ->
left
is KtDotQualifiedExpression ->
if (left.receiverExpression is KtThisExpression) left.selectorExpression as? KtReferenceExpression else null
else ->
null
}
val leftDescriptor = context[BindingContext.REFERENCE_TARGET, leftReference] as? PropertyDescriptor ?: return null
return rightDescriptor to leftDescriptor
}
}
}
@@ -16,4 +16,17 @@ class NotSingle {
constructor(x: Int) {
this.x = x
}
}
// KT-22142
class Person {
constructor(email: String) {
this.email = email
}
var email: String
set(value) {
require(value.isNotBlank(), { "The email cannot be blank" })
field = value
}
}