User: joe Date: 22 Apr 25 11:28 Revision: 01ac7a1c9e47b38b01ff43d076dd6898ee6c7426 Summary: Allow assignment to @Final fields in PsiClassInitializer blocks. Fixes #2148 TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9949&personal=false Index: src/main/kotlin/platform/mixin/inspection/shadow/ShadowFinalInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/shadow/ShadowFinalInspection.kt (revision cc8a8458059b9b2dbc0046857612dc3e52fa34c8) +++ src/main/kotlin/platform/mixin/inspection/shadow/ShadowFinalInspection.kt (revision 01ac7a1c9e47b38b01ff43d076dd6898ee6c7426) @@ -23,13 +23,21 @@ import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.FINAL import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.MUTABLE +import com.demonwav.mcdev.util.findContainingClass import com.intellij.codeInsight.intention.AddAnnotationFix import com.intellij.codeInspection.ProblemsHolder import com.intellij.psi.JavaElementVisitor -import com.intellij.psi.PsiAssignmentExpression +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiClassInitializer import com.intellij.psi.PsiElementVisitor -import com.intellij.psi.PsiModifierListOwner +import com.intellij.psi.PsiField +import com.intellij.psi.PsiLambdaExpression +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiModifier +import com.intellij.psi.PsiModifierList import com.intellij.psi.PsiReferenceExpression +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.PsiUtil class ShadowFinalInspection : MixinInspection() { @@ -40,19 +48,44 @@ override fun buildVisitor(holder: ProblemsHolder): PsiElementVisitor = Visitor(holder) private class Visitor(private val holder: ProblemsHolder) : JavaElementVisitor() { + override fun visitReferenceExpression(expression: PsiReferenceExpression) { + if (!PsiUtil.isAccessedForWriting(expression)) { + return + } - override fun visitAssignmentExpression(expression: PsiAssignmentExpression) { - val left = expression.lExpression as? PsiReferenceExpression ?: return - val resolved = left.resolve() as? PsiModifierListOwner ?: return - val modifiers = resolved.modifierList ?: return + val field = expression.resolve() as? PsiField ?: return + val modifiers = field.modifierList ?: return - if (modifiers.findAnnotation(FINAL) != null && modifiers.findAnnotation(MUTABLE) == null) { + if (modifiers.hasAnnotation(FINAL) && !modifiers.hasAnnotation(MUTABLE)) { + if (!isAssignmentInAppropriateInitializer(expression, field, modifiers)) { - holder.registerProblem( - expression, - "@Final fields cannot be modified", + holder.registerProblem( + expression, + "@Final fields cannot be modified", - AddAnnotationFix(MUTABLE, resolved), + AddAnnotationFix(MUTABLE, field), - ) - } - } - } + ) + } + } + } + + private fun isAssignmentInAppropriateInitializer( + expression: PsiReferenceExpression, + field: PsiField, + modifiers: PsiModifierList + ): Boolean { + if (field.containingClass != expression.findContainingClass()) { + return false -} + } + + val initializer = PsiTreeUtil.getParentOfType( + expression, + PsiClassInitializer::class.java, + true, + PsiClass::class.java, + PsiMethod::class.java, + PsiLambdaExpression::class.java, + ) ?: return false + + return initializer.hasModifierProperty(PsiModifier.STATIC) == modifiers.hasModifierProperty(PsiModifier.STATIC) + } + } +}