User: joe Date: 22 Jan 24 22:01 Revision: 855d67c62fd4f4ef7f894bd57b9e237752069426 Summary: Add support for At.unsafe. Also fix being able to use @Inject before super() TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=9025&personal=false Index: src/main/kotlin/platform/mixin/inspection/injector/InjectIntoConstructorInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/injector/InjectIntoConstructorInspection.kt (revision 5ac0df3a6de5d666cefd30ad698efe3ae69db8a3) +++ src/main/kotlin/platform/mixin/inspection/injector/InjectIntoConstructorInspection.kt (revision 855d67c62fd4f4ef7f894bd57b9e237752069426) @@ -24,15 +24,27 @@ import com.demonwav.mcdev.platform.fabric.FabricModuleType import com.demonwav.mcdev.platform.mixin.handlers.InjectorAnnotationHandler import com.demonwav.mcdev.platform.mixin.handlers.MixinAnnotationHandler +import com.demonwav.mcdev.platform.mixin.handlers.injectionPoint.AtResolver import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.INJECT +import com.demonwav.mcdev.platform.mixin.util.findSuperConstructorCall import com.demonwav.mcdev.platform.mixin.util.isConstructor +import com.demonwav.mcdev.util.constantValue +import com.demonwav.mcdev.util.createLiteralExpression import com.demonwav.mcdev.util.findAnnotation +import com.demonwav.mcdev.util.findAnnotations import com.demonwav.mcdev.util.findModule +import com.intellij.codeInspection.LocalQuickFixOnPsiElement import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project import com.intellij.psi.JavaElementVisitor +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiAnnotation +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiFile import com.intellij.psi.PsiMethod import java.awt.FlowLayout import javax.swing.JCheckBox @@ -46,7 +58,7 @@ override fun createOptionsPanel(): JComponent { val panel = JPanel(FlowLayout(FlowLayout.LEFT)) - val checkbox = JCheckBox("Allow @Inject into constructors in Fabric", allowOnFabric) + val checkbox = JCheckBox("Always allow @Inject into constructors in Fabric", allowOnFabric) checkbox.addActionListener { allowOnFabric = checkbox.isSelected } @@ -57,34 +69,76 @@ override fun buildVisitor(holder: ProblemsHolder): PsiElementVisitor { val isFabric = holder.file.findModule()?.let { MinecraftFacet.getInstance(it) }?.isOfType(FabricModuleType) ?: false - if (isFabric && allowOnFabric) { - return PsiElementVisitor.EMPTY_VISITOR - } - return object : JavaElementVisitor() { override fun visitMethod(method: PsiMethod) { - super.visitMethod(method) val injectAnnotation = method.findAnnotation(INJECT) ?: return val problemElement = injectAnnotation.nameReferenceElement ?: return val handler = MixinAnnotationHandler.forMixinAnnotation(INJECT) as? InjectorAnnotationHandler ?: return val targets = handler.resolveTarget(injectAnnotation) + + val ats = injectAnnotation.findDeclaredAttributeValue("at") + ?.findAnnotations() + ?: emptyList() + for (target in targets) { if (target !is MethodTargetMember || !target.classAndMethod.method.isConstructor) { continue } val (targetClass, targetMethod) = target.classAndMethod - val instructions = handler.resolveInstructions(injectAnnotation, targetClass, targetMethod) - if (instructions.any { it.insn.opcode != Opcodes.RETURN }) { + + for (at in ats) { + val isUnsafe = at.findDeclaredAttributeValue("unsafe")?.constantValue as? Boolean + ?: (isFabric && allowOnFabric) + + val instructions = AtResolver(at, targetClass, targetMethod).resolveInstructions() + if (!isUnsafe && instructions.any { it.insn.opcode != Opcodes.RETURN }) { + val atClass = at.nameReferenceElement?.resolve() as? PsiClass + val atHasUnsafe = !atClass?.findMethodsByName("unsafe", false).isNullOrEmpty() + + val quickFixes = if (atHasUnsafe) { + arrayOf(AddUnsafeFix(at)) + } else { + emptyArray() + } + - holder.registerProblem( - problemElement, - "Cannot inject into constructors at non-return instructions", + holder.registerProblem( + problemElement, + "Cannot inject into constructors at non-return instructions", + *quickFixes, - ) - return - } + ) + return + } + + val superCtorCall = targetMethod.findSuperConstructorCall() + if (superCtorCall != null && + instructions.any { + val insnIndex = targetMethod.instructions.indexOf(it.insn) + val superCtorIndex = targetMethod.instructions.indexOf(superCtorCall) + insnIndex <= superCtorIndex - } + } + ) { + holder.registerProblem( + problemElement, + "Cannot inject before super() call", + ) + return - } - } - } + } + } + } + } + } + } override fun getStaticDescription() = "@Inject into Constructor" + + private class AddUnsafeFix(at: PsiAnnotation) : LocalQuickFixOnPsiElement(at) { + override fun getFamilyName() = "Add unsafe = true" + override fun getText() = "Add unsafe = true" + + override fun invoke(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement) { + val annotation = startElement as? PsiAnnotation ?: return + val trueExpr = JavaPsiFacade.getElementFactory(project).createLiteralExpression(true) + annotation.setDeclaredAttributeValue("unsafe", trueExpr) -} + } + } +}