User: joe Date: 05 Nov 25 04:35 Revision: 32f74189c747727e589a603ea889b8531e8f0591 Summary: Add inspection for when two @Share annotations have different local types. Closes #2403 TeamCity URL: http://ci.mcdev.io:80/viewModification.html?tab=vcsModificationFiles&modId=10222&personal=false Index: src/main/kotlin/platform/mixin/inspection/mixinextras/ConflictingShareTypeInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/ConflictingShareTypeInspection.kt (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/ConflictingShareTypeInspection.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -23,7 +23,8 @@ import com.demonwav.mcdev.platform.mixin.handlers.mixinextras.ShareUtil import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection import com.demonwav.mcdev.platform.mixin.util.MixinConstants -import com.demonwav.mcdev.platform.mixin.util.MixinConstants.MixinExtras.unwrapLocalRef +import com.demonwav.mcdev.platform.mixin.util.isLocalRef +import com.demonwav.mcdev.platform.mixin.util.unwrapLocalRef import com.demonwav.mcdev.util.equivalentTo import com.demonwav.mcdev.util.findContainingClass import com.demonwav.mcdev.util.findContainingMethod @@ -33,7 +34,6 @@ import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiParameter import com.intellij.psi.PsiType -import com.intellij.psi.util.PsiTypesUtil class ConflictingShareTypeInspection : MixinInspection() { override fun getStaticDescription() = "Reports when two @Shares of the same variable have different types" @@ -83,8 +83,7 @@ private fun getShareType(share: PsiAnnotation): PsiType? { val param = share.parent?.parent as? PsiParameter ?: return null val paramType = param.type - val paramClass = PsiTypesUtil.getPsiClass(paramType) ?: return null - if (paramClass.qualifiedName?.startsWith(MixinConstants.MixinExtras.LOCAL_REF_PACKAGE) != true) { + if (paramType.isLocalRef) { return null } return paramType.unwrapLocalRef() Index: src/main/kotlin/platform/mixin/inspection/mixinextras/LocalArgsOnlyInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/LocalArgsOnlyInspection.kt (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/LocalArgsOnlyInspection.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -26,7 +26,7 @@ import com.demonwav.mcdev.platform.mixin.inspection.injector.ModifyVariableArgsOnlyInspection import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember import com.demonwav.mcdev.platform.mixin.util.MixinConstants -import com.demonwav.mcdev.platform.mixin.util.MixinConstants.MixinExtras.unwrapLocalRef +import com.demonwav.mcdev.platform.mixin.util.unwrapLocalRef import com.demonwav.mcdev.util.constantValue import com.demonwav.mcdev.util.findContainingMethod import com.demonwav.mcdev.util.ifEmpty Index: src/main/kotlin/platform/mixin/inspection/mixinextras/ShareNonLocalRefInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/ShareNonLocalRefInspection.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/ShareNonLocalRefInspection.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -0,0 +1,79 @@ +/* + * 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.mixinextras + +import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection +import com.demonwav.mcdev.platform.mixin.util.MixinConstants +import com.demonwav.mcdev.platform.mixin.util.isLocalRef +import com.demonwav.mcdev.platform.mixin.util.wrapLocalRef +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.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiParameter +import com.intellij.psi.PsiTypeElement +import com.intellij.psi.codeStyle.JavaCodeStyleManager + +class ShareNonLocalRefInspection : MixinInspection() { + override fun getStaticDescription() = "Reports when a @Share parameter is of a non-LocalRef type" + + override fun buildVisitor(holder: ProblemsHolder) = object : JavaElementVisitor() { + override fun visitAnnotation(annotation: PsiAnnotation) { + if (!annotation.hasQualifiedName(MixinConstants.MixinExtras.SHARE)) { + return + } + + val parameter = annotation.parent?.parent as? PsiParameter ?: return + val typeElement = parameter.typeElement ?: return + + if (!parameter.type.isLocalRef) { + holder.registerProblem( + typeElement, + "@Share parameter must be of type LocalRef", + ReplaceWithLocalRefFix(typeElement), + ) + } + } + } + + private class ReplaceWithLocalRefFix(typeElement: PsiTypeElement) : LocalQuickFixOnPsiElement(typeElement) { + override fun getText() = "Replace with LocalRef" + + override fun getFamilyName() = "Replace with LocalRef" + + override fun invoke( + project: Project, + file: PsiFile, + startElement: PsiElement, + endElement: PsiElement + ) { + val typeElement = startElement as? PsiTypeElement ?: return + val newTypeElement = + JavaPsiFacade.getElementFactory(project).createTypeElement(typeElement.type.wrapLocalRef(project)) + val elementToReformat = typeElement.replace(newTypeElement) + JavaCodeStyleManager.getInstance(project).shortenClassReferences(elementToReformat) + } + } +} Index: src/main/kotlin/platform/mixin/inspection/mixinextras/UnnecessaryMutableLocalInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/UnnecessaryMutableLocalInspection.kt (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/UnnecessaryMutableLocalInspection.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -25,7 +25,8 @@ import com.demonwav.mcdev.platform.mixin.handlers.mixinextras.WrapOperationHandler import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection import com.demonwav.mcdev.platform.mixin.util.MixinConstants -import com.demonwav.mcdev.platform.mixin.util.MixinConstants.MixinExtras.unwrapLocalRef +import com.demonwav.mcdev.platform.mixin.util.isLocalRef +import com.demonwav.mcdev.platform.mixin.util.unwrapLocalRef import com.demonwav.mcdev.util.findContainingMethod import com.intellij.codeInspection.LocalQuickFixOnPsiElement import com.intellij.codeInspection.ProblemsHolder @@ -42,10 +43,10 @@ import com.intellij.psi.PsiReferenceExpression import com.intellij.psi.search.searches.OverridingMethodsSearch import com.intellij.psi.search.searches.ReferencesSearch +import com.intellij.psi.util.PsiTypesUtil import com.intellij.psi.util.PsiUtil import com.intellij.psi.util.parentOfType import com.siyeh.ig.psiutils.MethodCallUtils -import org.jetbrains.plugins.groovy.intentions.style.inference.resolve class UnnecessaryMutableLocalInspection : MixinInspection() { override fun getStaticDescription() = "Unnecessary mutable reference to captured local" @@ -74,12 +75,13 @@ if (!param.hasAnnotation(MixinConstants.MixinExtras.LOCAL)) { continue } - val paramType = param.type.resolve() - if (paramType?.qualifiedName?.startsWith(MixinConstants.MixinExtras.LOCAL_REF_PACKAGE) != true) { + val paramType = param.type + if (!param.type.isLocalRef) { continue } - checkParameter(holder, method, param, i, paramType) + val paramClass = PsiTypesUtil.getPsiClass(paramType) ?: continue + checkParameter(holder, method, param, i, paramClass) } } } Index: src/main/kotlin/platform/mixin/inspection/mixinextras/UnresolvedLocalCaptureInspection.kt =================================================================== --- src/main/kotlin/platform/mixin/inspection/mixinextras/UnresolvedLocalCaptureInspection.kt (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/kotlin/platform/mixin/inspection/mixinextras/UnresolvedLocalCaptureInspection.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -26,7 +26,7 @@ import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection import com.demonwav.mcdev.platform.mixin.util.LocalInfo import com.demonwav.mcdev.platform.mixin.util.MixinConstants -import com.demonwav.mcdev.platform.mixin.util.MixinConstants.MixinExtras.unwrapLocalRef +import com.demonwav.mcdev.platform.mixin.util.unwrapLocalRef import com.demonwav.mcdev.util.findContainingMethod import com.demonwav.mcdev.util.findModule import com.demonwav.mcdev.util.mapFirstNotNull Index: src/main/kotlin/platform/mixin/util/Mixin.kt =================================================================== --- src/main/kotlin/platform/mixin/util/Mixin.kt (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/kotlin/platform/mixin/util/Mixin.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -27,6 +27,7 @@ import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.MIXIN import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Classes.CALLBACK_INFO import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Classes.CALLBACK_INFO_RETURNABLE +import com.demonwav.mcdev.platform.mixin.util.MixinConstants.MixinExtras.LOCAL_REF_PACKAGE import com.demonwav.mcdev.platform.mixin.util.MixinConstants.MixinExtras.OPERATION import com.demonwav.mcdev.util.SemanticVersion import com.demonwav.mcdev.util.cached @@ -35,6 +36,7 @@ import com.demonwav.mcdev.util.resolveClassArray import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project +import com.intellij.platform.ide.progress.ModalTaskOwner.project import com.intellij.psi.JavaPsiFacade import com.intellij.psi.PsiAnnotation import com.intellij.psi.PsiArrayType @@ -52,6 +54,7 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.util.InheritanceUtil import com.intellij.psi.util.PsiModificationTracker +import com.intellij.psi.util.PsiTypesUtil import com.intellij.psi.util.TypeConversionUtil import org.objectweb.asm.Opcodes import org.objectweb.asm.tree.ClassNode @@ -144,6 +147,57 @@ return annotations.any { it.qualifiedName?.contains(".mixinextras.sugar.") == true } } +val PsiType.isLocalRef: Boolean + get() { + return PsiTypesUtil.getPsiClass(this)?.qualifiedName?.startsWith(MixinConstants.MixinExtras.LOCAL_REF_PACKAGE) == true + } + +fun PsiType.unwrapLocalRef(): PsiType { + if (this !is PsiClassType) { + return this + } + val qName = resolve()?.qualifiedName ?: return this + if (!qName.startsWith(LOCAL_REF_PACKAGE)) { + return this + } + return when (qName.substringAfterLast('.')) { + "LocalBooleanRef" -> PsiTypes.booleanType() + "LocalCharRef" -> PsiTypes.charType() + "LocalDoubleRef" -> PsiTypes.doubleType() + "LocalFloatRef" -> PsiTypes.floatType() + "LocalIntRef" -> PsiTypes.intType() + "LocalLongRef" -> PsiTypes.longType() + "LocalShortRef" -> PsiTypes.shortType() + "LocalRef" -> parameters.getOrNull(0) ?: this + else -> this + } +} + +fun PsiType.wrapLocalRef(project: Project): PsiType { + if (isLocalRef) { + return this + } + + val elementFactory = JavaPsiFacade.getElementFactory(project) + return when (this) { + PsiTypes.booleanType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalBooleanRef") + PsiTypes.charType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalCharRef") + PsiTypes.doubleType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalDoubleRef") + PsiTypes.floatType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalFloatRef") + PsiTypes.intType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalIntRef") + PsiTypes.longType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalLongRef") + PsiTypes.shortType() -> elementFactory.createTypeByFQClassName(LOCAL_REF_PACKAGE + "LocalShortRef") + else -> { + val typeElement = elementFactory.createTypeElementFromText(LOCAL_REF_PACKAGE + "LocalRef", null) + typeElement.innermostComponentReferenceElement!! + .parameterList!! + .typeParameterElements[0]!! + .replace(elementFactory.createTypeElement(this)) + typeElement.type + } + } +} + fun callbackInfoType(project: Project): PsiType = PsiType.getTypeByName(CALLBACK_INFO, project, GlobalSearchScope.allScope(project)) Index: src/main/kotlin/platform/mixin/util/MixinConstants.kt =================================================================== --- src/main/kotlin/platform/mixin/util/MixinConstants.kt (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/kotlin/platform/mixin/util/MixinConstants.kt (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -20,10 +20,6 @@ package com.demonwav.mcdev.platform.mixin.util -import com.intellij.psi.PsiClassType -import com.intellij.psi.PsiType -import com.intellij.psi.PsiTypes - @Suppress("MemberVisibilityCanBePrivate") object MixinConstants { const val PACKAGE = "org.spongepowered.asm.mixin." @@ -98,26 +94,5 @@ const val MIXIN_EXTRAS_CONFIG = "com.llamalad7.mixinextras.config.MixinExtrasConfig" const val MIXIN_EXTRAS_CONFIG_KEY = "mixinextras" const val MIXIN_EXTRAS_SERIALIZED_NAME = "com.llamalad7.mixinextras.lib.gson.annotations.SerializedName" - - fun PsiType.unwrapLocalRef(): PsiType { - if (this !is PsiClassType) { - return this - } + } - val qName = resolve()?.qualifiedName ?: return this - if (!qName.startsWith(LOCAL_REF_PACKAGE)) { - return this - } +} - return when (qName.substringAfterLast('.')) { - "LocalBooleanRef" -> PsiTypes.booleanType() - "LocalCharRef" -> PsiTypes.charType() - "LocalDoubleRef" -> PsiTypes.doubleType() - "LocalFloatRef" -> PsiTypes.floatType() - "LocalIntRef" -> PsiTypes.intType() - "LocalLongRef" -> PsiTypes.longType() - "LocalShortRef" -> PsiTypes.shortType() - "LocalRef" -> parameters.getOrNull(0) ?: this - else -> this - } - } - } -} Index: src/main/resources/META-INF/plugin.xml =================================================================== --- src/main/resources/META-INF/plugin.xml (revision c451a26b4d8602a21da84c4f24a3acc13d577a60) +++ src/main/resources/META-INF/plugin.xml (revision 32f74189c747727e589a603ea889b8531e8f0591) @@ -1466,6 +1466,14 @@ level="ERROR" hasStaticDescription="true" implementationClass="com.demonwav.mcdev.platform.mixin.inspection.mixinextras.ConflictingShareTypeInspection"/> +