User: llamalad7 Date: 19 May 25 07:35 Revision: 88fd81ca4652534512fa51a025dc71c14c11cfda Summary: Fix/mixin fixes (#2470) * Fix: Fix multiple MixinExtras Expressions in 1 annotation. * Fix: Ignore `this` in local capture. * Fix: Support generic params in WrongOperationParametersInspection. Also fix incorrect primitive conversion errors. TeamCity URL: https://ci.mcdev.io/viewModification.html?tab=vcsModificationFiles&modId=10013&personal=false Index: src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt =================================================================== --- src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/expression/MEExpressionCompletionUtil.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -953,19 +953,23 @@ isStore: Boolean, mixinClass: PsiClass, ): List { + val isStatic = targetMethod.hasAccess(Opcodes.ACC_STATIC) // ignore "this" - if (!targetMethod.hasAccess(Opcodes.ACC_STATIC) && index == 0) { + if (!isStatic && index == 0) { return emptyList() } var argumentsSize = Type.getArgumentsAndReturnSizes(targetMethod.desc) shr 2 - if (targetMethod.hasAccess(Opcodes.ACC_STATIC)) { + if (isStatic) { argumentsSize-- } val isArgsOnly = index < argumentsSize if (targetMethod.localVariables != null) { val localsHere = targetMethod.localVariables.filter { localVariable -> + if (!isStatic && localVariable.index == 0) { + return@filter false + } val firstValidInstruction = if (isStore) { generateSequence(localVariable.start) { it.previous } .firstOrNull { it.opcode >= 0 } @@ -1012,7 +1016,11 @@ // fallback to ASM dataflow val localTypes = AsmDfaUtil.getLocalVariableTypes(project, targetClass, targetMethod, originalInsn) + ?.toMutableList() ?: return emptyList() + if (!isStatic) { + localTypes[0] = null + } val localType = localTypes.getOrNull(index) ?: return emptyList() val ordinal = localTypes.asSequence().take(index).filter { it == localType }.count() val localName = localType.typeNameToInsert().replace("[]", "Array") + (ordinal + 1) Index: src/main/kotlin/platform/mixin/expression/MEExpressionInjector.kt =================================================================== --- src/main/kotlin/platform/mixin/expression/MEExpressionInjector.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/expression/MEExpressionInjector.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -23,6 +23,7 @@ import com.demonwav.mcdev.platform.mixin.util.MixinConstants import com.demonwav.mcdev.util.findContainingModifierList import com.demonwav.mcdev.util.findContainingNameValuePair +import com.demonwav.mcdev.util.parseArray import com.intellij.lang.injection.InjectedLanguageManager import com.intellij.lang.injection.MultiHostInjector import com.intellij.lang.injection.MultiHostRegistrar @@ -117,27 +118,28 @@ } } } else if (annotation.hasQualifiedName(MixinConstants.MixinExtras.EXPRESSION)) { - val valueExpr = annotation.findDeclaredAttributeValue("value") ?: continue + for (valueExpr in annotation.findDeclaredAttributeValue("value")?.parseArray { it }.orEmpty()) { - val places = mutableListOf>() - iterateConcatenation(valueExpr) { op -> - if (op is PsiLanguageInjectionHost) { - for (textRange in getTextRanges(op)) { - places += op to textRange - } - } else { - isFrankenstein = true - } - } - if (places.isNotEmpty()) { - for ((i, place) in places.withIndex()) { - val (host, range) = place - val prefix = "\ndo { ".takeIf { i == 0 } - val suffix = " }".takeIf { i == places.size - 1 } - registrar.addPlace(prefix, suffix, host, range) - } - } - } - } + val places = mutableListOf>() + iterateConcatenation(valueExpr) { op -> + if (op is PsiLanguageInjectionHost) { + for (textRange in getTextRanges(op)) { + places += op to textRange + } + } else { + isFrankenstein = true + } + } + if (places.isNotEmpty()) { + for ((i, place) in places.withIndex()) { + val (host, range) = place + val prefix = "\ndo { ".takeIf { i == 0 } + val suffix = " }".takeIf { i == places.size - 1 } + registrar.addPlace(prefix, suffix, host, range) + } + } + } + } + } registrar.doneInjecting() Index: src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt =================================================================== --- src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/expression/MEExpressionMatchUtil.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -219,9 +219,10 @@ physicalInsn } - val unfilteredLocals = localInfo.getLocals(module, targetClass, targetMethod, actualInsn) - ?: return@addMember false - val filteredLocals = localInfo.matchLocals(unfilteredLocals, CollectVisitor.Mode.MATCH_ALL) + val filteredLocals = localInfo.matchLocals( + module, targetClass, targetMethod, actualInsn, + CollectVisitor.Mode.MATCH_ALL + ) ?: return@addMember false filteredLocals.any { it.index == virtualInsn.`var` } } } Index: src/main/kotlin/platform/mixin/handlers/ModifyVariableHandler.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/ModifyVariableHandler.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/handlers/ModifyVariableHandler.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -63,8 +63,10 @@ val possibleTypes = mutableSetOf() for (insn in targets) { - val locals = info.getLocals(module, targetClass, targetMethod, insn.insn) ?: continue - val matchedLocals = info.matchLocals(locals, CollectVisitor.Mode.COMPLETION, matchType = false) + val matchedLocals = info.matchLocals( + module, targetClass, targetMethod, insn.insn, + CollectVisitor.Mode.COMPLETION, matchType = false + ) ?: continue for (local in matchedLocals) { possibleTypes += local.desc!! } Index: src/main/kotlin/platform/mixin/handlers/injectionPoint/LoadInjectionPoint.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/injectionPoint/LoadInjectionPoint.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/handlers/injectionPoint/LoadInjectionPoint.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -304,11 +304,11 @@ } val shiftedInsn = if (store) insn.next ?: insn else insn - val locals = info.getLocals(module, targetClass, methodNode, shiftedInsn) ?: continue + val locals = info.matchLocals(module, targetClass, methodNode, shiftedInsn, mode) ?: continue val elementFactory = JavaPsiFacade.getElementFactory(module.project) - for (result in info.matchLocals(locals, mode)) { + for (result in locals) { addResult(shiftedInsn, elementFactory.createExpressionFromText(result.name, null)) } } Index: src/main/kotlin/platform/mixin/handlers/mixinextras/ExpressionInjectionPoint.kt =================================================================== --- src/main/kotlin/platform/mixin/handlers/mixinextras/ExpressionInjectionPoint.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/handlers/mixinextras/ExpressionInjectionPoint.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -188,22 +188,19 @@ (exprAnnotation.findDeclaredAttributeValue("id")?.constantStringValue ?: "") == atId } .flatMap { exprAnnotation -> - val expressionElements = exprAnnotation.findDeclaredAttributeValue("value")?.parseArray { it } - ?: return@flatMap emptySequence>() - expressionElements.asSequence().mapNotNull { expressionElement -> - val text = expressionElement.constantStringValue ?: return@mapNotNull null + exprAnnotation.findDeclaredAttributeValue("value")?.parseArray { it }.orEmpty() + } + .mapIndexedNotNull { exprIndex, expressionElement -> + val text = expressionElement.constantStringValue ?: return@mapIndexedNotNull null - val rootStatementPsi = InjectedLanguageManager.getInstance(project) - .getInjectedPsiFiles(expressionElement)?.firstOrNull() - ?.let { + val rootStatementPsi = InjectedLanguageManager.getInstance(project) + .getInjectedPsiFiles(expressionElement)?.firstOrNull() + ?.let { - (it.first as? MEExpressionFile)?.statements?.firstOrNull { stmt -> - stmt.findMultiInjectionHost()?.parentOfType() == exprAnnotation + (it.first as? MEExpressionFile)?.statements?.getOrNull(exprIndex) - } + } - } - ?: project.meExpressionElementFactory.createFile("do {$text}").statements.singleOrNull() - ?: project.meExpressionElementFactory.createStatement("empty") - MEExpressionMatchUtil.createExpression(text)?.let { it to rootStatementPsi } - } + ?: project.meExpressionElementFactory.createFile("do {$text}").statements.singleOrNull() + ?: project.meExpressionElementFactory.createStatement("empty") + MEExpressionMatchUtil.createExpression(text)?.let { it to rootStatementPsi } + } - } .toList() } Index: src/main/kotlin/platform/mixin/inspection/injector/ModifyVariableArgsOnlyInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/injector/ModifyVariableArgsOnlyInspection.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/inspection/injector/ModifyVariableArgsOnlyInspection.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -88,10 +88,10 @@ val wantedDesc = wantedType.descriptor - for ((targetClass, targetMethod) in methodTargets) { + for ((_, targetMethod) in methodTargets) { val argTypes = mutableListOf() if (!targetMethod.hasAccess(Opcodes.ACC_STATIC)) { - argTypes += "L${targetClass.name};" + argTypes += null } for (arg in Type.getArgumentTypes(targetMethod.desc)) { argTypes += arg.descriptor Index: src/main/kotlin/platform/mixin/inspection/mixinextras/UnresolvedLocalCaptureInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/UnresolvedLocalCaptureInspection.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/UnresolvedLocalCaptureInspection.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -59,9 +59,10 @@ val localInfo = LocalInfo.fromAnnotation(parameter.type.unwrapLocalRef(), localAnnotation) for (target in targets) { - val locals = localInfo.getLocals(module, target.method.clazz, target.method.method, target.result.insn) - ?: continue - val matchingLocals = localInfo.matchLocals(locals, CollectVisitor.Mode.MATCH_ALL) + val matchingLocals = localInfo.matchLocals( + module, target.method.clazz, target.method.method, target.result.insn, + CollectVisitor.Mode.MATCH_ALL + ) ?: continue if (matchingLocals.size != 1) { holder.registerProblem( localAnnotation.nameReferenceElement ?: localAnnotation, Index: src/main/kotlin/platform/mixin/inspection/mixinextras/WrongOperationParametersInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/WrongOperationParametersInspection.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/WrongOperationParametersInspection.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -24,6 +24,7 @@ import com.demonwav.mcdev.platform.mixin.util.MixinConstants import com.demonwav.mcdev.platform.mixin.util.isAssignable import com.demonwav.mcdev.util.McdevDfaUtil +import com.demonwav.mcdev.util.toObjectType import com.intellij.codeInsight.intention.FileModifier.SafeFieldForPreview import com.intellij.codeInspection.LocalQuickFixOnPsiElement import com.intellij.codeInspection.ProblemsHolder @@ -52,6 +53,7 @@ if (expression.resolveMethod()?.containingClass?.qualifiedName != MixinConstants.MixinExtras.OPERATION) { return } + val project = expression.project val containingMethod = PsiTreeUtil.getParentOfType( expression, @@ -85,7 +87,7 @@ if (expression.argumentList.expressionCount == expectedParamTypes.size) { val allValid = expression.argumentList.expressions.zip(expectedParamTypes).all { (expr, expectedType) -> val exprType = McdevDfaUtil.getDataflowType(expr) ?: return@all true - isAssignable(expectedType, exprType, false) + isAssignable(expectedType.toObjectType(project), exprType.toObjectType(project)) } if (allValid) { return Index: src/main/kotlin/platform/mixin/util/LocalInfo.kt =================================================================== --- src/main/kotlin/platform/mixin/util/LocalInfo.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/util/LocalInfo.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -42,7 +42,7 @@ val ordinal: Int?, val names: Set, ) { - fun getLocals( + private fun getLocals( module: Module, targetClass: ClassNode, methodNode: MethodNode, @@ -69,10 +69,16 @@ } fun matchLocals( - locals: Array, + module: Module, + targetClass: ClassNode, + methodNode: MethodNode, + insn: AbstractInsnNode, mode: CollectVisitor.Mode, matchType: Boolean = true, - ): List { + ): List? { + val locals = getLocals(module, targetClass, methodNode, insn) + ?.drop(if (methodNode.hasAccess(Opcodes.ACC_STATIC)) 0 else 1) + ?: return null val typeDesc = type?.descriptor if (ordinal != null) { val ordinals = mutableMapOf() Index: src/main/kotlin/platform/mixin/util/SourceCodeLocationInfo.kt =================================================================== --- src/main/kotlin/platform/mixin/util/SourceCodeLocationInfo.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/platform/mixin/util/SourceCodeLocationInfo.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -70,8 +70,8 @@ } if (count++ == index) { - myResult = t if (lineNumber == null) { + myResult = t return true } } Index: src/main/kotlin/util/psi-utils.kt =================================================================== --- src/main/kotlin/util/psi-utils.kt (revision 4d62f22ecf818268b2002e4c2260507bb96b36d5) +++ src/main/kotlin/util/psi-utils.kt (revision 88fd81ca4652534512fa51a025dc71c14c11cfda) @@ -31,6 +31,7 @@ import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.module.ModuleUtilCore +import com.intellij.openapi.project.Project import com.intellij.openapi.roots.LibraryOrderEntry import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectFileIndex @@ -53,6 +54,7 @@ import com.intellij.psi.PsiFile import com.intellij.psi.PsiKeyword import com.intellij.psi.PsiLanguageInjectionHost +import com.intellij.psi.PsiManager import com.intellij.psi.PsiMember import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethodReferenceExpression @@ -62,11 +64,13 @@ import com.intellij.psi.PsiNameValuePair import com.intellij.psi.PsiParameter import com.intellij.psi.PsiParameterList +import com.intellij.psi.PsiPrimitiveType import com.intellij.psi.PsiReference import com.intellij.psi.PsiReferenceExpression import com.intellij.psi.PsiType import com.intellij.psi.ResolveResult import com.intellij.psi.filters.ElementFilter +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager @@ -277,6 +281,14 @@ return normalized } +fun PsiType.toObjectType(project: Project): PsiType = + when (val normalized = normalize()) { + is PsiPrimitiveType -> + normalized.getBoxedType(PsiManager.getInstance(project), GlobalSearchScope.allScope(project)) + ?: normalized + else -> normalized + } + val PsiMethod.nameAndParameterTypes: String get() = "$name(${parameterList.parameters.joinToString(", ") { it.type.presentableText }})"