Skip to content

Commit c061f28

Browse files
committed
Fix source navigation to field initializers and other elements outside constructors and static blocks
1 parent 713e13b commit c061f28

File tree

6 files changed

+100
-26
lines changed

6 files changed

+100
-26
lines changed

src/main/kotlin/platform/mixin/handlers/injectionPoint/AtResolver.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import com.demonwav.mcdev.platform.mixin.reference.parseMixinSelector
2626
import com.demonwav.mcdev.platform.mixin.reference.target.TargetReference
2727
import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.SLICE
2828
import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Classes.SHIFT
29+
import com.demonwav.mcdev.platform.mixin.util.findBodyElements
2930
import com.demonwav.mcdev.platform.mixin.util.findSourceElement
3031
import com.demonwav.mcdev.util.computeStringArray
3132
import com.demonwav.mcdev.util.constantStringValue
@@ -221,18 +222,27 @@ class AtResolver(
221222
val bytecodeResults = resolveInstructions()
222223

223224
// Then attempt to find the corresponding source elements using the navigation visitor
224-
val targetElement = targetMethod.findSourceElement(
225+
val targetElements = targetMethod.findBodyElements(
225226
getTargetClass(target),
226227
at.project,
227228
GlobalSearchScope.allScope(at.project),
228-
canDecompile = true,
229-
) ?: return emptyList()
230-
val targetPsiClass = targetElement.parentOfType<PsiClass>() ?: return emptyList()
229+
).ifEmpty {
230+
return listOfNotNull(
231+
targetMethod.findSourceElement(
232+
getTargetClass(target),
233+
at.project,
234+
GlobalSearchScope.allScope(at.project),
235+
canDecompile = true,
236+
)
237+
)
238+
}
239+
240+
val targetPsiClass = targetElements.first().parentOfType<PsiClass>() ?: return emptyList()
231241
val targetPsiFile = targetPsiClass.containingFile ?: return emptyList()
232242

233243
val navigationVisitor = injectionPoint.createNavigationVisitor(at, target, targetPsiClass) ?: return emptyList()
234244
navigationVisitor.configureBytecodeTarget(targetClass, targetMethod)
235-
targetElement.accept(navigationVisitor)
245+
targetElements.forEach { it.accept(navigationVisitor) }
236246

237247
return bytecodeResults.mapNotNull { bytecodeResult ->
238248
val matcher = bytecodeResult.sourceLocationInfo.createMatcher<PsiElement>(targetPsiFile)

src/main/kotlin/platform/mixin/handlers/injectionPoint/CtorHeadInjectionPoint.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ package com.demonwav.mcdev.platform.mixin.handlers.injectionPoint
2323
import com.demonwav.mcdev.platform.mixin.inspection.injector.CtorHeadNoUnsafeInspection
2424
import com.demonwav.mcdev.platform.mixin.reference.MixinSelector
2525
import com.demonwav.mcdev.platform.mixin.util.findOrConstructSourceMethod
26-
import com.demonwav.mcdev.platform.mixin.util.findSuperConstructorCall
26+
import com.demonwav.mcdev.platform.mixin.util.findDelegateConstructorCall
2727
import com.demonwav.mcdev.platform.mixin.util.isConstructor
2828
import com.demonwav.mcdev.platform.mixin.util.isFabricMixin
2929
import com.demonwav.mcdev.util.createLiteralExpression
@@ -132,13 +132,13 @@ class CtorHeadInjectionPoint : InjectionPoint<PsiElement>() {
132132
return
133133
}
134134

135-
val superCtorCall = methodNode.findSuperConstructorCall() ?: run {
135+
val delegateCtorCall = methodNode.findDelegateConstructorCall() ?: run {
136136
super.accept(methodNode)
137137
return
138138
}
139139

140140
if (enforce == EnforceMode.POST_DELEGATE) {
141-
val insn = superCtorCall.next ?: return
141+
val insn = delegateCtorCall.next ?: return
142142
addResult(insn, methodNode.findOrConstructSourceMethod(clazz, project))
143143
return
144144
}
@@ -149,11 +149,11 @@ class CtorHeadInjectionPoint : InjectionPoint<PsiElement>() {
149149
// doesn't want to change the implementation in case of breaking mixins that rely on this
150150
// behavior, so it is now effectively intended, so it's what we'll use here.
151151
val lastFieldStore = generateSequence(insns.last) { it.previous }
152-
.takeWhile { it !== superCtorCall }
152+
.takeWhile { it !== delegateCtorCall }
153153
.firstOrNull { insn ->
154154
insn.opcode == Opcodes.PUTFIELD &&
155155
(insn as FieldInsnNode).owner == clazz.name
156-
} ?: superCtorCall
156+
} ?: delegateCtorCall
157157

158158
val lastFieldStoreNext = lastFieldStore.next ?: return
159159
addResult(lastFieldStoreNext, methodNode.findOrConstructSourceMethod(clazz, project))

src/main/kotlin/platform/mixin/inspection/injector/CancellableBeforeSuperCallInspection.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import com.demonwav.mcdev.platform.mixin.inspection.MixinCancellableInspection
2626
import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection
2727
import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember
2828
import com.demonwav.mcdev.platform.mixin.util.MixinConstants
29-
import com.demonwav.mcdev.platform.mixin.util.findSuperConstructorCall
29+
import com.demonwav.mcdev.platform.mixin.util.findDelegateConstructorCall
3030
import com.demonwav.mcdev.platform.mixin.util.isConstructor
3131
import com.demonwav.mcdev.util.constantValue
3232
import com.intellij.codeInspection.ProblemsHolder
@@ -72,10 +72,10 @@ class CancellableBeforeSuperCallInspection : MixinInspection() {
7272
continue
7373
}
7474
val methodInsns = target.classAndMethod.method.instructions ?: continue
75-
val superCtorCall = target.classAndMethod.method.findSuperConstructorCall() ?: continue
75+
val delegateCtorCall = target.classAndMethod.method.findDelegateConstructorCall() ?: continue
7676
val instructions =
7777
handler.resolveInstructions(annotation, target.classAndMethod.clazz, target.classAndMethod.method)
78-
if (instructions.any { methodInsns.indexOf(it.insn) <= methodInsns.indexOf(superCtorCall) }) {
78+
if (instructions.any { methodInsns.indexOf(it.insn) <= methodInsns.indexOf(delegateCtorCall) }) {
7979
return true
8080
}
8181
}

src/main/kotlin/platform/mixin/inspection/injector/InjectIntoConstructorInspection.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection
2727
import com.demonwav.mcdev.platform.mixin.inspection.fix.AnnotationAttributeFix
2828
import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember
2929
import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.INJECT
30-
import com.demonwav.mcdev.platform.mixin.util.findSuperConstructorCall
30+
import com.demonwav.mcdev.platform.mixin.util.findDelegateConstructorCall
3131
import com.demonwav.mcdev.platform.mixin.util.isConstructor
3232
import com.demonwav.mcdev.platform.mixin.util.isFabricMixin
3333
import com.demonwav.mcdev.util.constantValue
@@ -100,12 +100,12 @@ class InjectIntoConstructorInspection : MixinInspection() {
100100
return
101101
}
102102

103-
val superCtorCall = targetMethod.findSuperConstructorCall()
104-
if (superCtorCall != null &&
103+
val delegateCtorCall = targetMethod.findDelegateConstructorCall()
104+
if (delegateCtorCall != null &&
105105
instructions.any {
106106
val insnIndex = targetMethod.instructions.indexOf(it.insn)
107-
val superCtorIndex = targetMethod.instructions.indexOf(superCtorCall)
108-
insnIndex <= superCtorIndex
107+
val delegateCtorIndex = targetMethod.instructions.indexOf(delegateCtorCall)
108+
insnIndex <= delegateCtorIndex
109109
}
110110
) {
111111
holder.registerProblem(

src/main/kotlin/platform/mixin/inspection/injector/InvalidInjectorMethodSignatureInspection.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection
2626
import com.demonwav.mcdev.platform.mixin.reference.MethodReference
2727
import com.demonwav.mcdev.platform.mixin.util.MixinConstants
2828
import com.demonwav.mcdev.platform.mixin.util.MixinConstants.Annotations.COERCE
29-
import com.demonwav.mcdev.platform.mixin.util.findSuperConstructorCall
29+
import com.demonwav.mcdev.platform.mixin.util.findDelegateConstructorCall
3030
import com.demonwav.mcdev.platform.mixin.util.hasAccess
3131
import com.demonwav.mcdev.platform.mixin.util.isAssignable
3232
import com.demonwav.mcdev.platform.mixin.util.isConstructor
@@ -108,15 +108,15 @@ class InvalidInjectorMethodSignatureInspection : MixinInspection() {
108108
if (!shouldBeStatic && targetMethod.method.isConstructor) {
109109
// before the superclass constructor call, everything must be static
110110
val methodInsns = targetMethod.method.instructions
111-
val superCtorCall = targetMethod.method.findSuperConstructorCall()
112-
if (methodInsns != null && superCtorCall != null) {
111+
val delegateCtorCall = targetMethod.method.findDelegateConstructorCall()
112+
if (methodInsns != null && delegateCtorCall != null) {
113113
val insns = handler.resolveInstructions(
114114
annotation,
115115
targetMethod.clazz,
116116
targetMethod.method,
117117
)
118118
shouldBeStatic = insns.any {
119-
methodInsns.indexOf(it.insn) <= methodInsns.indexOf(superCtorCall)
119+
methodInsns.indexOf(it.insn) <= methodInsns.indexOf(delegateCtorCall)
120120
}
121121
}
122122
}

src/main/kotlin/platform/mixin/util/AsmUtil.kt

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,15 @@ import com.intellij.psi.PsiCompiledFile
5656
import com.intellij.psi.PsiElement
5757
import com.intellij.psi.PsiElementFactory
5858
import com.intellij.psi.PsiEllipsisType
59+
import com.intellij.psi.PsiEnumConstant
5960
import com.intellij.psi.PsiField
6061
import com.intellij.psi.PsiFileFactory
6162
import com.intellij.psi.PsiJavaFile
63+
import com.intellij.psi.PsiKeyword
6264
import com.intellij.psi.PsiLambdaExpression
6365
import com.intellij.psi.PsiManager
6466
import com.intellij.psi.PsiMethod
67+
import com.intellij.psi.PsiMethodCallExpression
6568
import com.intellij.psi.PsiMethodReferenceExpression
6669
import com.intellij.psi.PsiModifier
6770
import com.intellij.psi.PsiModifierList
@@ -636,18 +639,18 @@ val MethodNode.isClinit
636639
get() = this.name == "<clinit>"
637640

638641
/**
639-
* Finds the super() call in this method node, assuming it is a constructor
642+
* Finds the `this()` or `super()` call in this method node, assuming it is a constructor
640643
*/
641-
fun MethodNode.findSuperConstructorCall(): AbstractInsnNode? {
644+
fun MethodNode.findDelegateConstructorCall(): MethodInsnNode? {
642645
val insns = instructions ?: return null
643646
var superCtorCall = insns.first
644647
var newCount = 0
645648
while (superCtorCall != null) {
646649
if (superCtorCall.opcode == Opcodes.NEW) {
647650
newCount++
648651
} else if (superCtorCall.opcode == Opcodes.INVOKESPECIAL) {
649-
val methodCall = superCtorCall as MethodInsnNode
650-
if (methodCall.name == "<init>") {
652+
superCtorCall as MethodInsnNode
653+
if (superCtorCall.name == "<init>") {
651654
if (newCount == 0) {
652655
return superCtorCall
653656
} else {
@@ -976,6 +979,67 @@ fun MethodNode.findSourceElement(
976979
return findAssociatedLambda(psiClass, clazz, this)
977980
}
978981

982+
fun MethodNode.findBodyElements(clazz: ClassNode, project: Project, scope: GlobalSearchScope): List<PsiElement> {
983+
if (isClinit) {
984+
val psiClass = clazz.findSourceClass(project, scope, canDecompile = true) ?: return emptyList()
985+
val result = mutableListOf<PsiElement>()
986+
for (element in psiClass.children) {
987+
when (element) {
988+
is PsiEnumConstant -> element.argumentList?.expressions?.let { result += it }
989+
is PsiField -> {
990+
if (element.hasModifierProperty(PsiModifier.STATIC)) {
991+
element.initializer?.let { result += it }
992+
}
993+
}
994+
is PsiClassInitializer -> {
995+
if (element.hasModifierProperty(PsiModifier.STATIC)) {
996+
result += element.body
997+
}
998+
}
999+
}
1000+
}
1001+
return result
1002+
}
1003+
1004+
val sourceMethod = findSourceElement(clazz, project, scope, canDecompile = true) ?: return emptyList()
1005+
1006+
if (isConstructor && findDelegateConstructorCall()?.owner != clazz.name && sourceMethod is PsiMethod) {
1007+
val result = mutableListOf<PsiElement>()
1008+
val body = sourceMethod.body
1009+
if (body != null) {
1010+
val children = body.children
1011+
val superCtorIndex = children.indexOfFirst {
1012+
it is PsiMethodCallExpression && it.methodExpression.text == PsiKeyword.SUPER
1013+
}
1014+
result += children.take(superCtorIndex + 1)
1015+
sourceMethod.containingClass?.children?.forEach { element ->
1016+
when (element) {
1017+
is PsiField -> {
1018+
if (!element.hasModifierProperty(PsiModifier.STATIC)) {
1019+
element.initializer?.let { result += it }
1020+
}
1021+
}
1022+
is PsiClassInitializer -> {
1023+
if (!element.hasModifierProperty(PsiModifier.STATIC)) {
1024+
result += element.body
1025+
}
1026+
}
1027+
}
1028+
}
1029+
result += children.drop(superCtorIndex + 1)
1030+
return result
1031+
}
1032+
}
1033+
1034+
val body = when (sourceMethod) {
1035+
is PsiMethod -> sourceMethod.body
1036+
is PsiLambdaExpression -> sourceMethod.body
1037+
else -> null
1038+
}
1039+
1040+
return listOfNotNull(body)
1041+
}
1042+
9791043
/**
9801044
* Constructs a fake method node which could have been reached via this method instruction
9811045
*/

0 commit comments

Comments
 (0)