User: joe Date: 04 Nov 25 04:43 Revision: 6f5a7955af8778d0564f4bb55314424dc79ee547 Summary: Actually use InjectorAnnotationHandler.isInsnAllowed. Errors are reported, auto completion is filtered, and invalid injector inspection is disabled if there is an invalid insn. Closes #2290 TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10197&personal=false Index: src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/InjectorAnnotationHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -164,10 +164,12 @@ targetMethod: MethodNode, ): List? - open fun isInsnAllowed(insn: AbstractInsnNode): Boolean { + open fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { return true } + open val allowedInsnDescription = "all instructions" + override fun createUnresolvedMessage(annotation: PsiAnnotation): String? { return "Cannot resolve any target instructions in target class" } Index: src/main/kotlin/platform/mixin/handlers/ModifyArgHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ModifyArgHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/ModifyArgHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -38,10 +38,12 @@ import org.objectweb.asm.tree.MethodNode class ModifyArgHandler : InjectorAnnotationHandler() { - override fun isInsnAllowed(insn: AbstractInsnNode): Boolean { + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { return insn is MethodInsnNode } + override val allowedInsnDescription = "method invocations" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/handlers/ModifyArgsHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ModifyArgsHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/ModifyArgsHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -34,10 +34,12 @@ import org.objectweb.asm.tree.MethodNode class ModifyArgsHandler : InjectorAnnotationHandler() { - override fun isInsnAllowed(insn: AbstractInsnNode): Boolean { + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { return insn is MethodInsnNode } + override val allowedInsnDescription = "method invocations" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/handlers/ModifyConstantHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ModifyConstantHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/ModifyConstantHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -129,9 +129,11 @@ }.toList() } - override fun isInsnAllowed(insn: AbstractInsnNode): Boolean { + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { return insn.opcode in allowedOpcodes } + override val allowedInsnDescription = "constants" + override val mixinExtrasExpressionContextType = ExpressionContext.Type.MODIFY_CONSTANT } Index: src/main/kotlin/platform/mixin/handlers/RedirectInjectorHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/RedirectInjectorHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/RedirectInjectorHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -78,10 +78,12 @@ } } - override fun isInsnAllowed(insn: AbstractInsnNode): Boolean { + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { return getRedirectType(insn)?.isInsnAllowed(insn) ?: false } + override val allowedInsnDescription = "redirect targets (see docs)" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -20,6 +20,8 @@ package com.demonwav.mcdev.platform.mixin.handlers.injectionPoint +import com.demonwav.mcdev.platform.mixin.handlers.InjectorAnnotationHandler +import com.demonwav.mcdev.platform.mixin.handlers.MixinAnnotationHandler import com.demonwav.mcdev.platform.mixin.handlers.desugar.DesugarContext import com.demonwav.mcdev.platform.mixin.handlers.desugar.DesugarUtil import com.demonwav.mcdev.platform.mixin.reference.MixinSelector @@ -276,6 +278,8 @@ val injectionPoint = getInjectionPoint(at) ?: return emptyList() val targetAttr = at.findAttributeValue("target") val target = targetAttr?.let { parseMixinSelector(it) } + val injector = findInjectorAnnotation(at)?.let(MixinAnnotationHandler::forMixinAnnotation) + as? InjectorAnnotationHandler // Collect all possible targets fun doCollectVariants(injectionPoint: InjectionPoint): List { @@ -287,6 +291,7 @@ return visitor.visit(targetMethod) .results + .filter { result -> injector?.isInsnAllowed(result.insn, result.decorations) != false } .mapNotNull { result -> injectionPoint.createLookup(getTargetClass(target), result)?.let { completionHandler(it) } } Index: src/main/kotlin/platform/mixin/handlers/mixinextras/MixinExtrasInjectorAnnotationHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/MixinExtrasInjectorAnnotationHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/MixinExtrasInjectorAnnotationHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -95,7 +95,9 @@ abstract val supportedInstructionTypes: Collection - open fun extraTargetRestrictions(insn: AbstractInsnNode): Boolean = true + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { + return supportedInstructionTypes.any { it.matches(TargetInsn(insn, decorations)) } + } abstract fun expectedMethodSignature( annotation: PsiAnnotation, @@ -118,7 +120,6 @@ val insns = resolveInstructions(annotation, targetClass, targetMethod) .ifEmpty { return emptyList() } .map { TargetInsn(it.insn, it.decorations) } - if (insns.any { insn -> supportedInstructionTypes.none { it.matches(insn) } }) return emptyList() val signatures = insns.map { insn -> expectedMethodSignature(annotation, targetClass, targetMethod, insn) } Index: src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyExpressionValueHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyExpressionValueHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyExpressionValueHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -41,11 +41,17 @@ InstructionType.SIMPLE_EXPRESSION, InstructionType.STRING_CONCAT_EXPRESSION ) - override fun extraTargetRestrictions(insn: AbstractInsnNode): Boolean { + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map): Boolean { + if (!super.isInsnAllowed(insn, decorations)) { + return false + } + val returnType = getInsnReturnType(insn) ?: return false return returnType != Type.VOID_TYPE } + override val allowedInsnDescription = "instructions that return a value" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyReceiverHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyReceiverHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyReceiverHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -34,13 +34,15 @@ InstructionType.METHOD_CALL, InstructionType.FIELD_GET, InstructionType.FIELD_SET ) - override fun extraTargetRestrictions(insn: AbstractInsnNode) = when (insn.opcode) { + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map) = when (insn.opcode) { Opcodes.INVOKEVIRTUAL, Opcodes.INVOKESPECIAL, Opcodes.INVOKEINTERFACE, Opcodes.GETFIELD, Opcodes.PUTFIELD -> true else -> false } + override val allowedInsnDescription = "non-static method invocations and field references" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyReturnValueHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyReturnValueHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/ModifyReturnValueHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -32,6 +32,8 @@ class ModifyReturnValueHandler : MixinExtrasInjectorAnnotationHandler() { override val supportedInstructionTypes = listOf(InstructionType.RETURN) + override val allowedInsnDescription = "return instructions" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/handlers/mixinextras/WrapOperationHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/WrapOperationHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/WrapOperationHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -41,6 +41,8 @@ InstructionType.INSTANTIATION, InstructionType.SIMPLE_OPERATION ) + override val allowedInsnDescription = "wrap operation targets (see docs)" + override fun getAtKey(annotation: PsiAnnotation): String { return if (annotation.hasAttribute("constant")) "constant" else "at" } Index: src/main/kotlin/platform/mixin/handlers/mixinextras/WrapWithConditionHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/WrapWithConditionHandler.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/WrapWithConditionHandler.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -37,8 +37,10 @@ InstructionType.METHOD_CALL, InstructionType.FIELD_SET ) - override fun extraTargetRestrictions(insn: AbstractInsnNode) = getInsnReturnType(insn) == Type.VOID_TYPE + override fun isInsnAllowed(insn: AbstractInsnNode, decorations: Map) = super.isInsnAllowed(insn, decorations) && getInsnReturnType(insn) == Type.VOID_TYPE + override val allowedInsnDescription = "void method invocations and field assignments" + override fun expectedMethodSignature( annotation: PsiAnnotation, targetClass: ClassNode, Index: src/main/kotlin/platform/mixin/inspection/injector/DisallowedTargetInsnInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/injector/DisallowedTargetInsnInspection.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) +++ src/main/kotlin/platform/mixin/inspection/injector/DisallowedTargetInsnInspection.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -0,0 +1,70 @@ +/* + * Minecraft Development for IntelliJ + * + * https://mcdev.io/ + * + * Copyright (C) 2025 minecraft-dev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.demonwav.mcdev.platform.mixin.inspection.injector + +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 +import com.demonwav.mcdev.platform.mixin.util.mixinTargets +import com.demonwav.mcdev.util.findContainingClass +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.psi.JavaElementVisitor +import com.intellij.psi.PsiAnnotation + +class DisallowedTargetInsnInspection : MixinInspection() { + override fun getStaticDescription() = "Reports disallowed injector targets" + + override fun buildVisitor(holder: ProblemsHolder) = object : JavaElementVisitor() { + override fun visitAnnotation(annotation: PsiAnnotation) { + if (!annotation.hasQualifiedName(MixinConstants.Annotations.AT)) { + return + } + + val injectorAnnotation = AtResolver.findInjectorAnnotation(annotation) ?: return + val injector = MixinAnnotationHandler.forMixinAnnotation(injectorAnnotation, annotation.project) + as? InjectorAnnotationHandler ?: return + val containingClass = injectorAnnotation.findContainingClass() ?: return + val hasInvalidInstructions = containingClass.mixinTargets.any { targetClass -> + injector.resolveTarget(injectorAnnotation, targetClass).any { targetMember -> + if (targetMember !is MethodTargetMember) { + return@any false + } + + AtResolver(annotation, targetMember.classAndMethod.clazz, targetMember.classAndMethod.method) + .resolveInstructions() + .any { + !injector.isInsnAllowed(it.insn, it.decorations) + } + } + } + + if (hasInvalidInstructions) { + holder.registerProblem( + annotation, + "This injector can only target ${injector.allowedInsnDescription}" + ) + } + } + } +} Index: src/main/kotlin/platform/mixin/inspection/injector/InvalidInjectorMethodSignatureInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/injector/InvalidInjectorMethodSignatureInspection.kt (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/kotlin/platform/mixin/inspection/injector/InvalidInjectorMethodSignatureInspection.kt (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -22,6 +22,7 @@ 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.reference.MethodReference import com.demonwav.mcdev.platform.mixin.util.MixinConstants @@ -100,6 +101,17 @@ val methodAttribute = annotation.findDeclaredAttributeValue("method") ?: continue val targetMethods = MethodReference.resolveAllIfNotAmbiguous(methodAttribute) ?: continue + val hasDisallowedInsns = targetMethods.any { classAndMethod -> + handler.resolveInstructions( + annotation, + classAndMethod.clazz, + classAndMethod.method + ).any { !handler.isInsnAllowed(it.insn, it.decorations) } + } + if (hasDisallowedInsns) { + continue + } + for (targetMethod in targetMethods) { if (!reportedStatic) { var shouldBeStatic = targetMethod.method.hasAccess(Opcodes.ACC_STATIC) Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision 285b72b2ed527463de446c4c63971064fda4e1c9) +++ src/main/resources/META-INF/plugin.xml (revision 6f5a7955af8778d0564f4bb55314424dc79ee547) @@ -1286,6 +1286,14 @@ level="WARNING" hasStaticDescription="true" implementationClass="com.demonwav.mcdev.platform.mixin.inspection.injector.DiscouragedShiftInspection"/> +