Skip to content

Commit 496cc32

Browse files
committed
Add inspection for calling a constructor in a mixin which doesn't exist in the target class
1 parent 5767790 commit 496cc32

2 files changed

Lines changed: 90 additions & 0 deletions

File tree

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2026 minecraft-dev
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Lesser General Public License as published
10+
* by the Free Software Foundation, version 3.0 only.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU Lesser General Public License
18+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
*/
20+
21+
package com.demonwav.mcdev.platform.mixin.inspection
22+
23+
import com.demonwav.mcdev.platform.mixin.util.findMethod
24+
import com.demonwav.mcdev.platform.mixin.util.isMixin
25+
import com.demonwav.mcdev.platform.mixin.util.mixinTargets
26+
import com.demonwav.mcdev.util.MemberReference
27+
import com.demonwav.mcdev.util.descriptor
28+
import com.demonwav.mcdev.util.findContainingClass
29+
import com.intellij.codeInspection.ProblemsHolder
30+
import com.intellij.psi.JavaElementVisitor
31+
import com.intellij.psi.PsiClass
32+
import com.intellij.psi.PsiConstructorCall
33+
import com.intellij.psi.PsiElement
34+
import com.intellij.psi.PsiEnumConstant
35+
import com.intellij.psi.PsiModifier
36+
import com.intellij.psi.PsiNewExpression
37+
38+
class MixinInstantiationWithinMixinInspection : MixinInspection() {
39+
override fun getStaticDescription() = "Reports when a mixin class is instantiated within that same class using a constructor which doesn't exist in the target class"
40+
41+
override fun buildVisitor(holder: ProblemsHolder) = object : JavaElementVisitor() {
42+
override fun visitNewExpression(expression: PsiNewExpression) {
43+
if (expression.isArrayCreation || expression.anonymousClass != null) {
44+
return
45+
}
46+
val classReference = expression.classReference ?: return
47+
val constructedClass = classReference.resolve() as? PsiClass ?: return
48+
visitConstructorCall(expression, constructedClass, classReference)
49+
}
50+
51+
override fun visitEnumConstant(enumConstant: PsiEnumConstant) {
52+
val constructedClass = enumConstant.containingClass ?: return
53+
visitConstructorCall(enumConstant, constructedClass, enumConstant.nameIdentifier)
54+
}
55+
56+
private fun visitConstructorCall(constructorCall: PsiConstructorCall, constructedClass: PsiClass, problemElement: PsiElement) {
57+
if (!constructedClass.isMixin || constructedClass != constructorCall.findContainingClass()) {
58+
return
59+
}
60+
val resolvedConstructorDesc = constructorCall.resolveConstructor()?.descriptor ?: when {
61+
constructedClass.isEnum -> "(Ljava/lang/String;)V"
62+
!constructedClass.hasModifierProperty(PsiModifier.STATIC) && constructedClass.containingClass != null ->
63+
"(${constructedClass.containingClass!!.descriptor})V"
64+
else -> "()V"
65+
}
66+
67+
val constructorExistsInAllTargets = constructedClass.mixinTargets.all {
68+
it.findMethod(
69+
MemberReference(
70+
"<init>",
71+
resolvedConstructorDesc
72+
)
73+
) != null
74+
}
75+
if (constructorExistsInAllTargets) {
76+
return
77+
}
78+
79+
holder.registerProblem(problemElement, "Constructor does not exist in target class")
80+
}
81+
}
82+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,6 +1122,14 @@
11221122
level="ERROR"
11231123
hasStaticDescription="true"
11241124
implementationClass="com.demonwav.mcdev.platform.mixin.inspection.EnumMixinSwitchStatementInspection"/>
1125+
<localInspection displayName="Mixin instantiated within that same mixin using a constructor which doesn't exist"
1126+
shortName="MixinConstructorDoesntExist"
1127+
groupName="Mixin"
1128+
language="JAVA"
1129+
enabledByDefault="true"
1130+
level="ERROR"
1131+
hasStaticDescription="true"
1132+
implementationClass="com.demonwav.mcdev.platform.mixin.inspection.MixinInstantiationWithinMixinInspection"/>
11251133
<localInspection displayName="Inner classes in Mixins"
11261134
shortName="MixinInnerClass"
11271135
groupName="Mixin"

0 commit comments

Comments
 (0)