From 1d77c05b003a8bcfdafa94a2f7417df02057ab5e Mon Sep 17 00:00:00 2001
From: 7410 <85879080+O7410@users.noreply.github.com>
Date: Sun, 11 Aug 2024 02:07:09 +0300
Subject: [PATCH 1/2] Add inspection for targeting the same class multiple
times in a mixin
---
.../MixinDuplicateTargetInspection.kt | 145 ++++++++++++++++++
src/main/resources/META-INF/plugin.xml | 8 +
2 files changed, 153 insertions(+)
create mode 100644 src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt
diff --git a/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt b/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt
new file mode 100644
index 000000000..cad19feda
--- /dev/null
+++ b/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt
@@ -0,0 +1,145 @@
+/*
+ * Minecraft Development for IntelliJ
+ *
+ * https://mcdev.io/
+ *
+ * Copyright (C) 2024 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
+
+import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.MIXIN
+import com.demonwav.mcdev.platform.mixin.util.findClassNodeByPsiClass
+import com.demonwav.mcdev.platform.mixin.util.findClassNodeByQualifiedName
+import com.demonwav.mcdev.platform.mixin.util.mixinTargets
+import com.demonwav.mcdev.util.computeStringArray
+import com.demonwav.mcdev.util.constantStringValue
+import com.demonwav.mcdev.util.findModule
+import com.demonwav.mcdev.util.resolveClass
+import com.demonwav.mcdev.util.resolveClassArray
+import com.intellij.codeInsight.intention.QuickFixFactory
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.psi.JavaElementVisitor
+import com.intellij.psi.PsiAnnotation
+import com.intellij.psi.PsiArrayInitializerMemberValue
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiElementVisitor
+import com.intellij.psi.PsiExpression
+import org.objectweb.asm.tree.ClassNode
+
+class MixinDuplicateTargetInspection : MixinInspection() {
+
+ override fun getStaticDescription() =
+ "Targeting the same class multiple times in a mixin is redundant"
+
+ override fun buildVisitor(holder: ProblemsHolder): PsiElementVisitor = Visitor(holder)
+
+ private class Visitor(private val holder: ProblemsHolder) : JavaElementVisitor() {
+
+ override fun visitClass(psiClass: PsiClass) {
+ val mixinAnnotation = psiClass.modifierList?.findAnnotation(MIXIN) ?: return
+
+ if (psiClass.mixinTargets.size != psiClass.mixinTargets.distinct().size) {
+ goOverValues(psiClass, mixinAnnotation)
+ goOverTargets(psiClass, mixinAnnotation)
+ }
+ }
+
+ private fun goOverValues(psiClass: PsiClass, mixinAnnotation: PsiAnnotation) {
+
+ // Read class targets (value)
+ val value = mixinAnnotation.findDeclaredAttributeValue("value") ?: return
+ if (value is PsiArrayInitializerMemberValue) {
+ val classesElements = value.children.filterIsInstance()
+ val classTargets = value.resolveClassArray()
+ for (classTargetIndex in classTargets.indices) {
+ val classMixinTarget = classTargets[classTargetIndex]
+ val classNode = findClassNodeByPsiClass(classMixinTarget)
+ registerProblemIfProblematic(
+ psiClass,
+ classNode,
+ classTargetIndex,
+ classesElements[classTargetIndex]
+ )
+ }
+ } else {
+ val targetClass = value.resolveClass() ?: return
+ val classNode = findClassNodeByPsiClass(targetClass)
+ val elementToDelete = mixinAnnotation.parameterList.attributes.find { it.name == "value" } ?: value
+ registerProblemIfProblematic(psiClass, classNode, 0, elementToDelete)
+ }
+ }
+
+ private fun goOverTargets(psiClass: PsiClass, mixinAnnotation: PsiAnnotation) {
+ // Read string targets (targets)
+ val targets = mixinAnnotation.findDeclaredAttributeValue("targets") ?: return
+ val classTargetNames = targets.computeStringArray()
+ val valueTargetCount = mixinAnnotation.findDeclaredAttributeValue("value")?.resolveClassArray()?.size ?: 0
+ if (targets is PsiArrayInitializerMemberValue) {
+
+ val classChildren = targets.children.filterIsInstance()
+
+ for (i in classTargetNames.indices) {
+ val targetName = classTargetNames[i]
+
+ val classNode = findClassNodeByQualifiedName(
+ psiClass.project,
+ psiClass.findModule(),
+ targetName.replace('/', '.'),
+ )
+ registerProblemIfProblematic(psiClass, classNode, valueTargetCount + i, classChildren[i])
+ }
+ } else {
+ if (targets.constantStringValue == null) return
+ val classNode = findClassNodeByQualifiedName(
+ psiClass.project,
+ psiClass.findModule(),
+ targets.constantStringValue!!.replace('/', '.'),
+ )
+ val elementToDelete = mixinAnnotation.parameterList.attributes.find { it.name == "targets" } ?: targets
+ registerProblemIfProblematic(psiClass, classNode, valueTargetCount, elementToDelete)
+ }
+ }
+
+ private fun isClassNodeInTargets(
+ psiClass: PsiClass,
+ classNodeToCheck: ClassNode?,
+ indexToIgnore: Int
+ ): Boolean {
+ return classNodeToCheck in psiClass.mixinTargets.subList(0, indexToIgnore) ||
+ classNodeToCheck in psiClass.mixinTargets.subList(indexToIgnore + 1, psiClass.mixinTargets.size)
+ }
+
+ private fun registerProblemForDuplicate(duplicateExpression: PsiElement) {
+ holder.registerProblem(
+ duplicateExpression,
+ "Duplicate target is redundant",
+ QuickFixFactory.getInstance().createDeleteFix(duplicateExpression)
+ )
+ }
+
+ private fun registerProblemIfProblematic(
+ psiClass: PsiClass,
+ classNode: ClassNode?,
+ indexToIgnore: Int,
+ possibleDuplicate: PsiElement
+ ) {
+ if (isClassNodeInTargets(psiClass, classNode, indexToIgnore)) {
+ registerProblemForDuplicate(possibleDuplicate)
+ }
+ }
+ }
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 27a38c6a3..420595b23 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -830,6 +830,14 @@
level="ERROR"
hasStaticDescription="true"
implementationClass="com.demonwav.mcdev.platform.mixin.inspection.MixinTargetInspection"/>
+
Date: Wed, 14 Aug 2024 01:50:58 +0300
Subject: [PATCH 2/2] WIP - Added tests and modified way of detecting
duplicates
---
.../multipleTargetClasses/SimpleClass1.java | 14 +
.../multipleTargetClasses/SimpleClass2.java | 14 +
.../multipleTargetClasses/SimpleClass3.java | 14 +
.../MixinDuplicateTargetInspection.kt | 121 +++---
.../MixinDuplicateTargetInspectionTest.kt | 350 ++++++++++++++++++
5 files changed, 469 insertions(+), 44 deletions(-)
create mode 100644 mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass1.java
create mode 100644 mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass2.java
create mode 100644 mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass3.java
create mode 100644 src/test/kotlin/platform/mixin/MixinDuplicateTargetInspectionTest.kt
diff --git a/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass1.java b/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass1.java
new file mode 100644
index 000000000..99381ce46
--- /dev/null
+++ b/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass1.java
@@ -0,0 +1,14 @@
+/*
+ * Minecraft Dev for IntelliJ
+ *
+ * https://minecraftdev.org
+ *
+ * Copyright (c) 2021 minecraft-dev
+ *
+ * MIT License
+ */
+
+package com.demonwav.mcdev.mixintestdata.multipleTargetClasses;
+
+public class SimpleClass1 {
+}
diff --git a/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass2.java b/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass2.java
new file mode 100644
index 000000000..e3d530ca4
--- /dev/null
+++ b/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass2.java
@@ -0,0 +1,14 @@
+/*
+ * Minecraft Dev for IntelliJ
+ *
+ * https://minecraftdev.org
+ *
+ * Copyright (c) 2021 minecraft-dev
+ *
+ * MIT License
+ */
+
+package com.demonwav.mcdev.mixintestdata.multipleTargetClasses;
+
+public class SimpleClass2 {
+}
diff --git a/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass3.java b/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass3.java
new file mode 100644
index 000000000..c62650ffd
--- /dev/null
+++ b/mixin-test-data/src/main/java/com/demonwav/mcdev/mixintestdata/multipleTargetClasses/SimpleClass3.java
@@ -0,0 +1,14 @@
+/*
+ * Minecraft Dev for IntelliJ
+ *
+ * https://minecraftdev.org
+ *
+ * Copyright (c) 2021 minecraft-dev
+ *
+ * MIT License
+ */
+
+package com.demonwav.mcdev.mixintestdata.multipleTargetClasses;
+
+public class SimpleClass3 {
+}
diff --git a/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt b/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt
index cad19feda..e8ed272d0 100644
--- a/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt
+++ b/src/main/kotlin/platform/mixin/inspection/MixinDuplicateTargetInspection.kt
@@ -28,22 +28,27 @@ import com.demonwav.mcdev.util.computeStringArray
import com.demonwav.mcdev.util.constantStringValue
import com.demonwav.mcdev.util.findModule
import com.demonwav.mcdev.util.resolveClass
-import com.demonwav.mcdev.util.resolveClassArray
import com.intellij.codeInsight.intention.QuickFixFactory
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement
import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.project.Project
import com.intellij.psi.JavaElementVisitor
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiArrayInitializerMemberValue
import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiClassObjectAccessExpression
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.PsiExpression
+import com.intellij.psi.PsiFile
+import org.jetbrains.plugins.groovy.intentions.style.inference.resolve
import org.objectweb.asm.tree.ClassNode
class MixinDuplicateTargetInspection : MixinInspection() {
- override fun getStaticDescription() =
- "Targeting the same class multiple times in a mixin is redundant"
+ override fun getStaticDescription() = "Targeting the same class multiple times in a mixin is redundant"
override fun buildVisitor(holder: ProblemsHolder): PsiElementVisitor = Visitor(holder)
@@ -52,94 +57,122 @@ class MixinDuplicateTargetInspection : MixinInspection() {
override fun visitClass(psiClass: PsiClass) {
val mixinAnnotation = psiClass.modifierList?.findAnnotation(MIXIN) ?: return
- if (psiClass.mixinTargets.size != psiClass.mixinTargets.distinct().size) {
- goOverValues(psiClass, mixinAnnotation)
- goOverTargets(psiClass, mixinAnnotation)
+ val mixinTargets = psiClass.mixinTargets
+
+ if (mixinTargets.size != mixinTargets.distinct().size) {
+ val nonDuplicates = mutableListOf()
+ for (i in mixinTargets.indices) {
+ val mixinTarget = mixinTargets[i]
+ if (mixinTarget !in mixinTargets.subList(
+ i + 1, mixinTargets.size
+ ) && mixinTarget !in mixinTargets.subList(0, i)
+ ) {
+ nonDuplicates.add(mixinTarget)
+ }
+ }
+
+ goOverValues(nonDuplicates, mixinAnnotation)
+ goOverTargets(psiClass, nonDuplicates, mixinAnnotation)
}
}
- private fun goOverValues(psiClass: PsiClass, mixinAnnotation: PsiAnnotation) {
+ private fun goOverValues(nonDuplicates: List, mixinAnnotation: PsiAnnotation) {
// Read class targets (value)
val value = mixinAnnotation.findDeclaredAttributeValue("value") ?: return
if (value is PsiArrayInitializerMemberValue) {
- val classesElements = value.children.filterIsInstance()
- val classTargets = value.resolveClassArray()
- for (classTargetIndex in classTargets.indices) {
- val classMixinTarget = classTargets[classTargetIndex]
- val classNode = findClassNodeByPsiClass(classMixinTarget)
+ val classElements = value.children.filterIsInstance()
+ for (classTargetIndex in classElements.indices) {
+ val expression = classElements[classTargetIndex] as? PsiClassObjectAccessExpression ?: continue
+ val targetPsiClass = expression.operand.type.resolve() ?: continue
+ val targetClassNode = findClassNodeByPsiClass(targetPsiClass)
+
registerProblemIfProblematic(
- psiClass,
- classNode,
- classTargetIndex,
- classesElements[classTargetIndex]
+ nonDuplicates,
+ targetClassNode,
+ expression,
+ QuickFixFactory.getInstance().createDeleteFix(expression)
)
}
} else {
val targetClass = value.resolveClass() ?: return
val classNode = findClassNodeByPsiClass(targetClass)
- val elementToDelete = mixinAnnotation.parameterList.attributes.find { it.name == "value" } ?: value
- registerProblemIfProblematic(psiClass, classNode, 0, elementToDelete)
+ val possibleQuickFix = RemoveDuplicateTargetFix(mixinAnnotation, "value")
+ registerProblemIfProblematic(nonDuplicates, classNode, value, possibleQuickFix)
}
}
- private fun goOverTargets(psiClass: PsiClass, mixinAnnotation: PsiAnnotation) {
+ private fun goOverTargets(psiClass: PsiClass, nonDuplicates: List, mixinAnnotation: PsiAnnotation) {
// Read string targets (targets)
val targets = mixinAnnotation.findDeclaredAttributeValue("targets") ?: return
val classTargetNames = targets.computeStringArray()
- val valueTargetCount = mixinAnnotation.findDeclaredAttributeValue("value")?.resolveClassArray()?.size ?: 0
if (targets is PsiArrayInitializerMemberValue) {
-
val classChildren = targets.children.filterIsInstance()
- for (i in classTargetNames.indices) {
- val targetName = classTargetNames[i]
+ // TODO: may be misaligned if there are numbers for example in the class array because they will have different lengths
+ for (classTargetIndex in classTargetNames.indices) {
+ val targetName = classTargetNames[classTargetIndex]
val classNode = findClassNodeByQualifiedName(
psiClass.project,
psiClass.findModule(),
targetName.replace('/', '.'),
)
- registerProblemIfProblematic(psiClass, classNode, valueTargetCount + i, classChildren[i])
+ registerProblemIfProblematic(
+ nonDuplicates,
+ classNode,
+ classChildren[classTargetIndex],
+ QuickFixFactory.getInstance().createDeleteFix(classChildren[classTargetIndex])
+ )
}
} else {
- if (targets.constantStringValue == null) return
+ val stringValue = targets.constantStringValue ?: return
val classNode = findClassNodeByQualifiedName(
psiClass.project,
psiClass.findModule(),
- targets.constantStringValue!!.replace('/', '.'),
+ stringValue.replace('/', '.'),
)
- val elementToDelete = mixinAnnotation.parameterList.attributes.find { it.name == "targets" } ?: targets
- registerProblemIfProblematic(psiClass, classNode, valueTargetCount, elementToDelete)
+ val removeDuplicateTargetFix = RemoveDuplicateTargetFix(mixinAnnotation, "targets")
+ registerProblemIfProblematic(nonDuplicates, classNode, targets, removeDuplicateTargetFix)
}
}
- private fun isClassNodeInTargets(
- psiClass: PsiClass,
- classNodeToCheck: ClassNode?,
- indexToIgnore: Int
- ): Boolean {
- return classNodeToCheck in psiClass.mixinTargets.subList(0, indexToIgnore) ||
- classNodeToCheck in psiClass.mixinTargets.subList(indexToIgnore + 1, psiClass.mixinTargets.size)
- }
-
- private fun registerProblemForDuplicate(duplicateExpression: PsiElement) {
+ private fun registerProblemForDuplicate(duplicateExpression: PsiElement, quickFix: LocalQuickFix) {
holder.registerProblem(
duplicateExpression,
"Duplicate target is redundant",
- QuickFixFactory.getInstance().createDeleteFix(duplicateExpression)
+ quickFix,
)
}
private fun registerProblemIfProblematic(
- psiClass: PsiClass,
+ nonDuplicates: List,
classNode: ClassNode?,
- indexToIgnore: Int,
- possibleDuplicate: PsiElement
+ possibleDuplicate: PsiElement,
+ possibleQuickFix: LocalQuickFix
) {
- if (isClassNodeInTargets(psiClass, classNode, indexToIgnore)) {
- registerProblemForDuplicate(possibleDuplicate)
+ if (classNode != null && classNode !in nonDuplicates) {
+ registerProblemForDuplicate(possibleDuplicate, possibleQuickFix)
}
}
}
+
+ private class RemoveDuplicateTargetFix(annotation: PsiAnnotation, val annotationAttributeName: String) :
+ LocalQuickFixAndIntentionActionOnPsiElement(annotation) {
+
+ override fun getFamilyName() = "Remove duplicate target"
+
+ override fun getText() = familyName
+
+ override fun invoke(
+ project: Project,
+ file: PsiFile,
+ editor: Editor?,
+ startElement: PsiElement,
+ endElement: PsiElement
+ ) {
+ val annotation = startElement as? PsiAnnotation ?: return
+ annotation.setDeclaredAttributeValue(annotationAttributeName, null)
+ }
+ }
}
diff --git a/src/test/kotlin/platform/mixin/MixinDuplicateTargetInspectionTest.kt b/src/test/kotlin/platform/mixin/MixinDuplicateTargetInspectionTest.kt
new file mode 100644
index 000000000..5b498b391
--- /dev/null
+++ b/src/test/kotlin/platform/mixin/MixinDuplicateTargetInspectionTest.kt
@@ -0,0 +1,350 @@
+/*
+ * Minecraft Development for IntelliJ
+ *
+ * https://mcdev.io/
+ *
+ * Copyright (C) 2024 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
+
+import com.demonwav.mcdev.framework.EdtInterceptor
+import com.demonwav.mcdev.platform.mixin.inspection.MixinDuplicateTargetInspection
+import org.intellij.lang.annotations.Language
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+
+@ExtendWith(EdtInterceptor::class)
+@DisplayName("Mixin Duplicate Target Inspection Test")
+class MixinDuplicateTargetInspectionTest : BaseMixinTest() {
+
+ private fun doTest(@Language("JAVA") code: String) {
+ buildProject {
+ dir("test") {
+ java("TestMixin.java", code)
+ }
+ }
+
+ fixture.enableInspections(MixinDuplicateTargetInspection::class)
+ fixture.checkHighlighting(false, false, true)
+ }
+
+ @Test
+ @DisplayName("Simple class target")
+ fun simpleClassTarget() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(SimpleClass1.class)
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Simple class target in array")
+ fun simpleClassTargetInArray() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin({SimpleClass1.class})
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Simple class target in array with attribute name")
+ fun simpleClassTargetInArrayWithAttributeName() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = {SimpleClass1.class})
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Simple class target with attribute name")
+ fun simpleClassTargetWithAttributeName() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = SimpleClass1.class)
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Different targets in array")
+ fun differentTargetsInArray() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass2;
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass3;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin({SimpleClass1.class, SimpleClass2.class, SimpleClass3.class})
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Different targets in array with attribute name")
+ fun differentTargetsInArrayWithAttributeName() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass2;
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass3;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = {SimpleClass1.class, SimpleClass2.class, SimpleClass3.class})
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Different targets in string targets")
+ fun differentTargetsInStringTargets() {
+ doTest(
+ """
+ package test;
+
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(targets = {
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1",
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass2",
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass3"
+ })
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Unresolved duplicates")
+ fun unreslovedDuplicates() {
+ doTest(
+ """
+ package test;
+
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(
+ value = {
+ UnresolvedClass1.class,
+ UnresolvedClass1.class,
+ UnresolvedClass2.class,
+ UnresolvedClass2.class
+ },
+ targets = {
+ "UnresolvedClass1",
+ "UnresolvedClass1",
+ "UnresolvedClass2",
+ "UnresolvedClass2"
+ }
+ )
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Target twice in array")
+ fun targetTwiceInArray() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin({SimpleClass1.class, SimpleClass1.class})
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Target twice in array with attribute name")
+ fun targetTwiceInArrayWithAttributeName() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = {SimpleClass1.class, SimpleClass1.class})
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Target twice with unresolved targets next to it")
+ fun targetTwiceWithUnresolvedTargetsNextToIt() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = {
+ UnresolvedClass1.class,
+ SimpleClass1.class,
+ UnresolvedClass2.class,
+ SimpleClass1.class,
+ UnresolvedClass3.class
+ })
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Target three times with unresolved targets next to it")
+ fun targetThreeTimesWithUnresolvedTargetsNextToIt() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = {
+ UnresolvedClass1.class,
+ SimpleClass1.class,
+ UnresolvedClass2.class,
+ SimpleClass1.class,
+ UnresolvedClass3.class,
+ SimpleClass1.class,
+ UnresolvedClass4.class
+ })
+ public class TestMixin {
+ }
+ """,
+ )/*
+ UnresolvedClass4.class
+ is the expected
+ */
+ }
+
+ @Test
+ @DisplayName("Multiple targets multiple times")
+ fun multipleTargetsMultipleTimes() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass2;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = {
+ SimpleClass2.class,
+ UnresolvedClass1.class,
+ SimpleClass1.class,
+ UnresolvedClass2.class,
+ SimpleClass1.class,
+ SimpleClass2.class,
+ UnresolvedClass3.class
+ })
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("String target and class target")
+ fun stringTargetAndClassTarget() {
+ doTest(
+ """
+ package test;
+
+ import com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1;
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(value = SimpleClass1.class,
+ targets = "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1")
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+
+ @Test
+ @DisplayName("Multiple string targets")
+ fun multipleStringTargets() {
+ doTest(
+ """
+ package test;
+
+ import org.spongepowered.asm.mixin.Mixin;
+
+ @Mixin(targets = {
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass2",
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1",
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass1",
+ "com.demonwav.mcdev.mixintestdata.multipleTargetClasses.SimpleClass3"
+ }
+ )
+ public class TestMixin {
+ }
+ """,
+ )
+ }
+}