Report "use of non-const Kotlin property" also on some assignments
#KT-25536 Fixed
This commit is contained in:
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.asJava.elements.KtLightFieldImpl.KtLightFieldForDecl
|
||||
import org.jetbrains.kotlin.idea.inspections.MayBeConstantInspection.Status.*
|
||||
import org.jetbrains.kotlin.idea.quickfix.AddConstModifierFix
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
|
||||
class FakeJvmFieldConstantInspection : AbstractKotlinInspection() {
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||
@@ -44,19 +45,52 @@ class FakeJvmFieldConstantInspection : AbstractKotlinInspection() {
|
||||
val valueExpression = statement.caseValue ?: return
|
||||
checkExpression(valueExpression, holder)
|
||||
}
|
||||
|
||||
override fun visitAssignmentExpression(expression: PsiAssignmentExpression) {
|
||||
super.visitAssignmentExpression(expression)
|
||||
|
||||
if (expression.operationTokenType != JavaTokenType.EQ) return
|
||||
val leftType = expression.lExpression.type as? PsiPrimitiveType ?: return
|
||||
checkAssignmentChildren(expression.rExpression ?: return, leftType, holder)
|
||||
}
|
||||
|
||||
override fun visitVariable(variable: PsiVariable) {
|
||||
super.visitVariable(variable)
|
||||
|
||||
val leftType = variable.type as? PsiPrimitiveType ?: return
|
||||
val initializer = variable.initializer ?: return
|
||||
checkAssignmentChildren(initializer, leftType, holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkExpression(valueExpression: PsiExpression, holder: ProblemsHolder) {
|
||||
private fun checkAssignmentChildren(right: PsiExpression, leftType: PsiPrimitiveType, holder: ProblemsHolder) {
|
||||
if (leftType == PsiType.BOOLEAN || leftType == PsiType.CHAR || leftType == PsiType.VOID) return
|
||||
right.forEachDescendantOfType<PsiExpression>(canGoInside = { parentElement ->
|
||||
parentElement !is PsiCallExpression && parentElement !is PsiTypeCastExpression
|
||||
}) { rightPart ->
|
||||
checkExpression(rightPart, holder) { resolvedPropertyType ->
|
||||
leftType != resolvedPropertyType && !leftType.isAssignableFrom(resolvedPropertyType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkExpression(
|
||||
valueExpression: PsiExpression,
|
||||
holder: ProblemsHolder,
|
||||
additionalTypeCheck: (PsiType) -> Boolean = { true }
|
||||
) {
|
||||
val resolvedLightField = (valueExpression as? PsiReference)?.resolve() as? KtLightFieldForDeclaration ?: return
|
||||
val resolvedProperty = resolvedLightField.kotlinOrigin as? KtProperty ?: return
|
||||
with(MayBeConstantInspection) {
|
||||
if (resolvedProperty.annotationEntries.isEmpty()) return@with
|
||||
if (resolvedProperty.annotationEntries.isEmpty()) return
|
||||
val resolvedPropertyStatus = resolvedProperty.getStatus()
|
||||
if (resolvedPropertyStatus == JVM_FIELD_MIGHT_BE_CONST ||
|
||||
resolvedPropertyStatus == JVM_FIELD_MIGHT_BE_CONST_NO_INITIALIZER ||
|
||||
resolvedPropertyStatus == JVM_FIELD_MIGHT_BE_CONST_ERRONEOUS
|
||||
) {
|
||||
val resolvedPropertyType = resolvedLightField.type
|
||||
if (!additionalTypeCheck(resolvedPropertyType)) return
|
||||
val fixes = mutableListOf<LocalQuickFix>()
|
||||
if (resolvedPropertyStatus == JVM_FIELD_MIGHT_BE_CONST) {
|
||||
fixes += IntentionWrapper(AddConstModifierFix(resolvedProperty), resolvedProperty.containingFile)
|
||||
|
||||
+20
@@ -21,4 +21,24 @@ public class JavaUser {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void fau() {
|
||||
byte b1 = KotlinPropertiesKt.importantNumber; // Dangerous
|
||||
byte b2 = KotlinPropertiesKt.constantNumber; // Safe (const)
|
||||
byte b3 = 0 + KotlinPropertiesKt.importantNumber; // Dangerous
|
||||
int b4 = 0 + KotlinPropertiesKt.importantNumber; // Safe (types are the same)
|
||||
b4 += KotlinPropertiesKt.importantNumber; // Safe (modification)
|
||||
long l1 = KotlinPropertiesKt.importantNumber; // Safe (type widening)
|
||||
b2 = KotlinPropertiesKt.importantNumber; // Dangerous
|
||||
b1 = KotlinPropertiesKt.constantNumber; // Safe (const)
|
||||
b3 = (0 + KotlinPropertiesKt.importantNumber); // Dangerous
|
||||
b3 = (byte) (KotlinPropertiesKt.importantNumber); // Safe (explicit cast)
|
||||
b3 = convert(KotlinPropertiesKt.importantNumber); // Safe (explicit cast)
|
||||
}
|
||||
|
||||
public byte convert(int x) {
|
||||
return (byte) x;
|
||||
}
|
||||
|
||||
byte importantNumber = KotlinPropertiesKt.importantNumber; // Also dangerous
|
||||
}
|
||||
Vendored
+5
-1
@@ -4,4 +4,8 @@ annotation class AnnValue(val value: String)
|
||||
|
||||
@JvmField val importantString = "important"
|
||||
|
||||
const val constantString = "constant"
|
||||
const val constantString = "constant"
|
||||
|
||||
@JvmField val importantNumber = 42
|
||||
|
||||
const val constantNumber = 42
|
||||
@@ -26,4 +26,49 @@
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Kotlin non-const property used as Java constant</problem_class>
|
||||
<description>Use of non-const Kotlin property as Java constant is incorrect. Will be forbidden in 1.4</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>JavaUser.java</file>
|
||||
<line>26</line>
|
||||
<module>First</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="method" FQNAME="JavaUser void fau()" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Kotlin non-const property used as Java constant</problem_class>
|
||||
<description>Use of non-const Kotlin property as Java constant is incorrect. Will be forbidden in 1.4</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>JavaUser.java</file>
|
||||
<line>28</line>
|
||||
<module>First</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="method" FQNAME="JavaUser void fau()" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Kotlin non-const property used as Java constant</problem_class>
|
||||
<description>Use of non-const Kotlin property as Java constant is incorrect. Will be forbidden in 1.4</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>JavaUser.java</file>
|
||||
<line>32</line>
|
||||
<module>First</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="method" FQNAME="JavaUser void fau()" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Kotlin non-const property used as Java constant</problem_class>
|
||||
<description>Use of non-const Kotlin property as Java constant is incorrect. Will be forbidden in 1.4</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>JavaUser.java</file>
|
||||
<line>34</line>
|
||||
<module>First</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="method" FQNAME="JavaUser void fau()" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Kotlin non-const property used as Java constant</problem_class>
|
||||
<description>Use of non-const Kotlin property as Java constant is incorrect. Will be forbidden in 1.4</description>
|
||||
</problem>
|
||||
<problem>
|
||||
<file>JavaUser.java</file>
|
||||
<line>43</line>
|
||||
<module>First</module>
|
||||
<package><default></package>
|
||||
<entry_point TYPE="field" FQNAME="JavaUser importantNumber" />
|
||||
<problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">Kotlin non-const property used as Java constant</problem_class>
|
||||
<description>Use of non-const Kotlin property as Java constant is incorrect. Will be forbidden in 1.4</description>
|
||||
</problem>
|
||||
</problems>
|
||||
|
||||
Reference in New Issue
Block a user