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"/>
+