Skip to content

Commit ecb059b

Browse files
committed
Merge branch '2023.1' into 2023.2
2 parents eb9b102 + e989266 commit ecb059b

21 files changed

+278
-36
lines changed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ kotlin.code.style=official
2424
ideaVersion = 2023.2.2
2525
ideaVersionName = 2023.2.2
2626

27-
coreVersion = 1.6.11
27+
coreVersion = 1.6.12
2828
downloadIdeaSources = true
2929

3030
pluginTomlVersion = 232.8660.88

readme.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ Minecraft Development for IntelliJ
1111
<td align="right"><b>Main Build</b></td>
1212
<td colspan="2"><a href="https://ci.mcdev.io/viewType.html?buildTypeId=MinecraftDev_Build"><img src="https://ci.mcdev.io/app/rest/builds/buildType:(id:MinecraftDev_Build)/statusIcon.svg" alt="Teamcity Build Status" /></a></td>
1313
</tr>
14-
<tr>
15-
<td align="left">2022.3</td>
16-
<td align="left"><a href="https://ci.mcdev.io/viewType.html?buildTypeId=MinecraftDev_Nightly_20223"><img src="https://ci.mcdev.io/app/rest/builds/buildType:(id:MinecraftDev_Nightly_20223)/statusIcon.svg" alt="2022.3 Nightly Status" /></a></td>
17-
</tr>
1814
<tr>
1915
<td align="left">2023.1</td>
2016
<td align="left"><a href="https://ci.mcdev.io/viewType.html?buildTypeId=MinecraftDev_Nightly_20231"><img src="https://ci.mcdev.io/app/rest/builds/buildType:(id:MinecraftDev_Nightly_20231)/statusIcon.svg" alt="2023.1 Nightly Status" /></a></td>
@@ -35,7 +31,7 @@ Minecraft Development for IntelliJ
3531
</tr>
3632
</table>
3733

38-
Info and Documentation [![Current Release](https://img.shields.io/badge/release-1.6.11-orange.svg?style=flat-square)](https://plugins.jetbrains.com/plugin/8327)
34+
Info and Documentation [![Current Release](https://img.shields.io/badge/release-1.6.12-orange.svg?style=flat-square)](https://plugins.jetbrains.com/plugin/8327)
3935
----------------------
4036

4137
<a href="https://discord.gg/j6UNcfr"><img src="https://i.imgur.com/JXu9C1G.png" height="48px"></img></a>

src/main/kotlin/MinecraftConfigurable.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ class MinecraftConfigurable : Configurable {
8989
settings.isShowEventListenerGutterIcons = showEventListenerGutterCheckBox.isSelected
9090
settings.isShowChatColorGutterIcons = showChatGutterIconsCheckBox.isSelected
9191
settings.isShowChatColorUnderlines = showChatColorUnderlinesCheckBox.isSelected
92-
settings.underlineType = chatColorUnderlinesComboBox.selectedItem as MinecraftSettings.UnderlineType
92+
settings.underlineType = chatColorUnderlinesComboBox.selectedItem as? MinecraftSettings.UnderlineType
93+
?: MinecraftSettings.UnderlineType.DOTTED
9394
}
9495

9596
override fun reset() {

src/main/kotlin/insight/generation/GenerateEventListenerAction.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@
2020

2121
package com.demonwav.mcdev.insight.generation
2222

23+
import com.demonwav.mcdev.asset.MCDevBundle
2324
import com.intellij.codeInsight.generation.actions.BaseGenerateAction
25+
import com.intellij.openapi.actionSystem.AnActionEvent
2426

25-
class GenerateEventListenerAction : BaseGenerateAction(GenerateEventListenerHandler())
27+
class GenerateEventListenerAction : BaseGenerateAction(GenerateEventListenerHandler()) {
28+
29+
override fun update(e: AnActionEvent) {
30+
super.update(e)
31+
e.presentation.text = MCDevBundle("generate.event_listener.title")
32+
}
33+
}

src/main/kotlin/platform/bukkit/creator/bukkit-platforms.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ import com.demonwav.mcdev.platform.PlatformType
2626
import com.demonwav.mcdev.util.MinecraftTemplates
2727
import com.demonwav.mcdev.util.MinecraftVersions
2828
import com.demonwav.mcdev.util.SemanticVersion
29+
import com.intellij.icons.AllIcons
2930
import com.intellij.openapi.observable.util.bindBooleanStorage
3031
import com.intellij.openapi.ui.validation.WHEN_GRAPH_PROPAGATION_FINISHED
32+
import com.intellij.ui.content.AlertIcon
3133
import com.intellij.ui.dsl.builder.Panel
3234
import com.intellij.ui.dsl.builder.bindSelected
3335

@@ -74,7 +76,11 @@ class PaperPlatformStep(parent: BukkitPlatformStep) : AbstractBukkitPlatformStep
7476
override fun setupUI(builder: Panel) {
7577
super.setupUI(builder)
7678
with(builder) {
77-
row("Paper manifest:") {
79+
row("Paper Manifest:") {
80+
icon(AlertIcon(AllIcons.General.Warning)).comment(
81+
"Paper plugins are <a href=\"https://docs.papermc.io/paper/dev/getting-started/paper-plugins\">" +
82+
"still experimental</a>, their usage is discouraged for general purpose development. "
83+
)
7884
checkBox("Use paper-plugin.yml")
7985
.bindSelected(usePaperManifestProperty)
8086
.validationRequestor(WHEN_GRAPH_PROPAGATION_FINISHED(propertyGraph))

src/main/kotlin/platform/mixin/action/GenerateOverwriteAction.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,11 +127,10 @@ class GenerateOverwriteAction : MixinCodeInsightAction() {
127127
return
128128
}
129129

130-
// Generate needed shadows
131-
val newShadows = createShadowMembers(project, psiClass, filterNewShadows(requiredMembers, psiClass))
132-
133130
disableAnnotationWrapping(project) {
134131
runWriteAction {
132+
// Generate needed shadows
133+
val newShadows = createShadowMembers(project, psiClass, filterNewShadows(requiredMembers, psiClass))
135134
// Insert shadows
136135
insertShadows(psiClass, newShadows)
137136
}

src/main/kotlin/platform/mixin/handlers/injectionPoint/MethodInjectionPoint.kt renamed to src/main/kotlin/platform/mixin/handlers/injectionPoint/InvokeInjectionPoint.kt

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import org.objectweb.asm.tree.ClassNode
3737
import org.objectweb.asm.tree.MethodInsnNode
3838
import org.objectweb.asm.tree.MethodNode
3939

40-
class MethodInjectionPoint : AbstractMethodInjectionPoint() {
40+
abstract class AbstractInvokeInjectionPoint(private val assign: Boolean) : AbstractMethodInjectionPoint() {
4141
override fun createNavigationVisitor(
4242
at: PsiAnnotation,
4343
target: MixinSelector?,
@@ -53,9 +53,9 @@ class MethodInjectionPoint : AbstractMethodInjectionPoint() {
5353
mode: CollectVisitor.Mode,
5454
): CollectVisitor<PsiMethod>? {
5555
if (mode == CollectVisitor.Mode.COMPLETION) {
56-
return MyCollectVisitor(mode, at.project, MemberReference(""))
56+
return MyCollectVisitor(mode, at.project, MemberReference(""), assign)
5757
}
58-
return target?.let { MyCollectVisitor(mode, at.project, it) }
58+
return target?.let { MyCollectVisitor(mode, at.project, it, assign) }
5959
}
6060

6161
private class MyNavigationVisitor(
@@ -141,6 +141,7 @@ class MethodInjectionPoint : AbstractMethodInjectionPoint() {
141141
mode: Mode,
142142
private val project: Project,
143143
private val selector: MixinSelector,
144+
private val assign: Boolean,
144145
) : CollectVisitor<PsiMethod>(mode) {
145146
override fun accept(methodNode: MethodNode) {
146147
val insns = methodNode.instructions ?: return
@@ -150,12 +151,19 @@ class MethodInjectionPoint : AbstractMethodInjectionPoint() {
150151
}
151152

152153
val sourceMethod = nodeMatchesSelector(insn, mode, selector, project) ?: return@forEachRemaining
153-
addResult(
154-
insn,
155-
sourceMethod,
156-
qualifier = insn.owner.replace('/', '.'),
157-
)
154+
val actualInsn = if (assign) insn.next else insn
155+
if (actualInsn != null) {
156+
addResult(
157+
actualInsn,
158+
sourceMethod,
159+
qualifier = insn.owner.replace('/', '.'),
160+
)
161+
}
158162
}
159163
}
160164
}
161165
}
166+
167+
class InvokeInjectionPoint : AbstractInvokeInjectionPoint(false)
168+
169+
class InvokeAssignInjectionPoint : AbstractInvokeInjectionPoint(true)

src/main/kotlin/platform/mixin/handlers/mixinextras/MixinExtrasInjectorAnnotationHandler.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ abstract class MixinExtrasInjectorAnnotationHandler : InjectorAnnotationHandler(
149149
.resolveAsm(annotation.project) as? MethodTargetMember
150150
)?.classAndMethod
151151
sourceClassAndMethod?.method?.getGenericReturnType(sourceClassAndMethod.clazz, annotation.project)
152-
?: Type.getType(insn.desc).toPsiType(elementFactory)
152+
?: Type.getReturnType(insn.desc).toPsiType(elementFactory)
153153
}
154154

155155
is FieldInsnNode -> {

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,13 @@ import com.intellij.psi.PsiClassType
4747
import com.intellij.psi.PsiElementVisitor
4848
import com.intellij.psi.PsiMethod
4949
import com.intellij.psi.PsiModifier
50+
import com.intellij.psi.PsiNameHelper
5051
import com.intellij.psi.PsiParameterList
5152
import com.intellij.psi.PsiPrimitiveType
5253
import com.intellij.psi.PsiType
5354
import com.intellij.psi.codeStyle.JavaCodeStyleManager
5455
import com.intellij.psi.codeStyle.VariableKind
56+
import com.intellij.psi.util.PsiUtil
5557
import com.intellij.psi.util.TypeConversionUtil
5658
import org.objectweb.asm.Opcodes
5759
import org.objectweb.asm.tree.AbstractInsnNode
@@ -318,13 +320,15 @@ class InvalidInjectorMethodSignatureInspection : MixinInspection() {
318320

319321
val newParams = expected.flatMapTo(mutableListOf()) {
320322
if (it.default) {
323+
val nameHelper = PsiNameHelper.getInstance(project)
324+
val languageLevel = PsiUtil.getLanguageLevel(parameters)
321325
it.parameters.mapIndexed { i: Int, p: Parameter ->
322-
JavaPsiFacade.getElementFactory(project).createParameter(
323-
p.name ?: JavaCodeStyleManager.getInstance(project)
326+
val paramName = p.name?.takeIf { name -> nameHelper.isIdentifier(name, languageLevel) }
327+
?: JavaCodeStyleManager.getInstance(project)
324328
.suggestVariableName(VariableKind.PARAMETER, null, null, p.type).names
325-
.firstOrNull() ?: "var$i",
326-
p.type,
327-
)
329+
.firstOrNull()
330+
?: "var$i"
331+
JavaPsiFacade.getElementFactory(project).createParameter(paramName, p.type)
328332
}
329333
} else {
330334
emptyList()
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
/*
2+
* Minecraft Development for IntelliJ
3+
*
4+
* https://mcdev.io/
5+
*
6+
* Copyright (C) 2023 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.mixinextras
22+
23+
import com.demonwav.mcdev.platform.mixin.handlers.InjectorAnnotationHandler
24+
import com.demonwav.mcdev.platform.mixin.handlers.MixinAnnotationHandler
25+
import com.demonwav.mcdev.platform.mixin.handlers.mixinextras.WrapOperationHandler
26+
import com.demonwav.mcdev.platform.mixin.inspection.MixinInspection
27+
import com.demonwav.mcdev.platform.mixin.util.MixinConstants
28+
import com.demonwav.mcdev.util.findContainingMethod
29+
import com.intellij.codeInspection.LocalQuickFixOnPsiElement
30+
import com.intellij.codeInspection.ProblemsHolder
31+
import com.intellij.openapi.project.Project
32+
import com.intellij.psi.JavaElementVisitor
33+
import com.intellij.psi.PsiClass
34+
import com.intellij.psi.PsiClassType
35+
import com.intellij.psi.PsiElement
36+
import com.intellij.psi.PsiElementFactory
37+
import com.intellij.psi.PsiExpressionList
38+
import com.intellij.psi.PsiFile
39+
import com.intellij.psi.PsiMethod
40+
import com.intellij.psi.PsiMethodCallExpression
41+
import com.intellij.psi.PsiParameter
42+
import com.intellij.psi.PsiReferenceExpression
43+
import com.intellij.psi.PsiType
44+
import com.intellij.psi.PsiTypes
45+
import com.intellij.psi.search.searches.OverridingMethodsSearch
46+
import com.intellij.psi.search.searches.ReferencesSearch
47+
import com.intellij.psi.util.PsiUtil
48+
import com.intellij.psi.util.parentOfType
49+
import com.siyeh.ig.psiutils.MethodCallUtils
50+
import org.jetbrains.plugins.groovy.intentions.style.inference.resolve
51+
52+
class UnnecessaryMutableLocalInspection : MixinInspection() {
53+
override fun getStaticDescription() = "Unnecessary mutable reference to captured local"
54+
55+
override fun buildVisitor(holder: ProblemsHolder) = object : JavaElementVisitor() {
56+
override fun visitMethod(method: PsiMethod) {
57+
val project = method.project
58+
val hasValidMixinAnnotation = method.annotations.any { ann ->
59+
ann.qualifiedName?.let { MixinAnnotationHandler.forMixinAnnotation(it, project) }
60+
// Mutable Local references do have different semantics inside a WrapOperation.
61+
?.let { it is InjectorAnnotationHandler && it !is WrapOperationHandler } == true
62+
}
63+
if (!hasValidMixinAnnotation) {
64+
return
65+
}
66+
67+
// ignore if method has any references
68+
val hasReferences = ReferencesSearch.search(method)
69+
.mapNotNull { PsiUtil.skipParenthesizedExprUp(it.element).parent as? PsiMethodCallExpression }
70+
.any { !MethodCallUtils.hasSuperQualifier(it) }
71+
if (hasReferences) {
72+
return
73+
}
74+
75+
for ((i, param) in method.parameterList.parameters.withIndex()) {
76+
if (!param.hasAnnotation(MixinConstants.MixinExtras.LOCAL)) {
77+
continue
78+
}
79+
val paramType = param.type.resolve()
80+
if (paramType?.qualifiedName?.startsWith(MixinConstants.MixinExtras.LOCAL_REF_PACKAGE) != true) {
81+
continue
82+
}
83+
84+
checkParameter(holder, method, param, i, paramType)
85+
}
86+
}
87+
}
88+
89+
private fun checkParameter(
90+
holder: ProblemsHolder,
91+
originalMethod: PsiMethod,
92+
originalParam: PsiParameter,
93+
paramIndex: Int,
94+
paramType: PsiClass
95+
) {
96+
var hasAnyGets = false
97+
for (method in OverridingMethodsSearch.search(originalMethod).findAll() + listOf(originalMethod)) {
98+
val param = method.parameterList.getParameter(paramIndex) ?: return
99+
val getMethod = paramType.findMethodsByName("get", false).firstOrNull() ?: return
100+
for (ref in ReferencesSearch.search(param)) {
101+
if (isDelegationToSuper(ref.element, paramIndex)) {
102+
continue
103+
}
104+
val parent = PsiUtil.skipParenthesizedExprUp(ref.element.parent) as? PsiReferenceExpression ?: return
105+
if (parent.references.any { it.isReferenceTo(getMethod) }) {
106+
hasAnyGets = true
107+
} else {
108+
return
109+
}
110+
}
111+
}
112+
if (!hasAnyGets) {
113+
// Don't annoy them if they've just made the parameter
114+
return
115+
}
116+
holder.registerProblem(
117+
originalParam.typeElement ?: originalParam,
118+
"@Local could be captured immutably",
119+
SwitchToImmutableCaptureFix(originalParam)
120+
)
121+
}
122+
123+
// Ignore super delegations in subclasses. super.foo(myLocalRef) has no effect on whether the local can be converted
124+
private fun isDelegationToSuper(ref: PsiElement, paramIndex: Int): Boolean {
125+
val method = ref.findContainingMethod() ?: return false
126+
val superMethod = method.findSuperMethods().firstOrNull { it.containingClass?.isInterface == false }
127+
?: return false
128+
129+
// For some reason ref is sometimes the identifier rather than the reference expression. Get the reference expr
130+
val actualRef = if (ref is PsiReferenceExpression) {
131+
ref
132+
} else {
133+
PsiUtil.skipParenthesizedExprUp(ref.parent) as? PsiReferenceExpression ?: return false
134+
}
135+
val param = PsiUtil.skipParenthesizedExprUp(actualRef)
136+
val paramList = param.parent as? PsiExpressionList ?: return false
137+
val methodCall = paramList.parent as? PsiMethodCallExpression ?: return false
138+
139+
// Check that the method call is a super call
140+
if (!MethodCallUtils.hasSuperQualifier(methodCall)) {
141+
return false
142+
}
143+
144+
// Check that our reference is in the correct parameter index
145+
if (paramList.expressions.getOrNull(paramIndex) != param) {
146+
return false
147+
}
148+
149+
// Check that the super call is referencing the correct super method.
150+
return methodCall.resolveMethod() == superMethod
151+
}
152+
153+
private class SwitchToImmutableCaptureFix(param: PsiParameter) : LocalQuickFixOnPsiElement(param) {
154+
override fun getFamilyName() = "Switch to immutable capture"
155+
override fun getText() = "Switch to immutable capture"
156+
157+
override fun invoke(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement) {
158+
val param = startElement as? PsiParameter ?: return
159+
val method = param.parentOfType<PsiMethod>() ?: return
160+
val paramIndex = method.parameterList.getParameterIndex(param)
161+
val methods = mutableListOf(method)
162+
if (file.isPhysical) {
163+
methods.addAll(OverridingMethodsSearch.search(method))
164+
}
165+
for (impl in methods) {
166+
fixMethod(impl, paramIndex)
167+
}
168+
}
169+
170+
private fun fixMethod(method: PsiMethod, paramIndex: Int) {
171+
val param = method.parameterList.getParameter(paramIndex) ?: return
172+
val paramType = param.type as? PsiClassType ?: return
173+
val innerType = paramType.innerRefType ?: return
174+
val factory = PsiElementFactory.getInstance(method.project)
175+
param.typeElement?.replace(factory.createTypeElement(innerType))
176+
for (ref in ReferencesSearch.search(param)) {
177+
val refExpression = PsiUtil.skipParenthesizedExprUp(ref.element.parent) as? PsiReferenceExpression
178+
?: continue
179+
val call = refExpression.parent as? PsiMethodCallExpression ?: continue
180+
call.replace(ref.element)
181+
}
182+
}
183+
184+
private val PsiClassType.innerRefType: PsiType?
185+
get() =
186+
when (resolve()?.qualifiedName?.substringAfterLast('.')) {
187+
"LocalBooleanRef" -> PsiTypes.booleanType()
188+
"LocalCharRef" -> PsiTypes.charType()
189+
"LocalDoubleRef" -> PsiTypes.doubleType()
190+
"LocalFloatRef" -> PsiTypes.floatType()
191+
"LocalIntRef" -> PsiTypes.intType()
192+
"LocalLongRef" -> PsiTypes.longType()
193+
"LocalShortRef" -> PsiTypes.shortType()
194+
"LocalRef" -> parameters.getOrNull(0)
195+
else -> null
196+
}
197+
}
198+
}

0 commit comments

Comments
 (0)