diff --git a/src/main/kotlin/platform/fabric/inspection/ModIdMismatchInspection.kt b/src/main/kotlin/platform/fabric/inspection/ModIdMismatchInspection.kt new file mode 100644 index 000000000..9b7345c58 --- /dev/null +++ b/src/main/kotlin/platform/fabric/inspection/ModIdMismatchInspection.kt @@ -0,0 +1,99 @@ +/* + * 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.fabric.inspection + +import com.demonwav.mcdev.util.constantStringValue +import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.json.psi.JsonFile +import com.intellij.json.psi.JsonObject +import com.intellij.json.psi.JsonStringLiteral +import com.intellij.openapi.project.Project +import com.intellij.psi.JavaElementVisitor +import com.intellij.psi.JavaPsiFacade +import com.intellij.psi.PsiElementVisitor +import com.intellij.psi.PsiField +import com.intellij.psi.PsiManager +import com.intellij.psi.search.FilenameIndex +import com.intellij.psi.search.GlobalSearchScope + +class ModIdMismatchInspection : LocalInspectionTool() { + + override fun getStaticDescription() = "Checks for a mismatch between the mod ID in fabric.mod.json and the java code." + + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = Visitor(holder) + + private class Visitor(private val holder: ProblemsHolder) : JavaElementVisitor() { + + override fun visitField(field: PsiField) { + super.visitField(field) + + if (field.name != "MOD_ID" && field.name != "MODID") return + val initializer = field.initializer + val javaModId = initializer?.constantStringValue ?: return + + val project = field.project + val jsonModId = getModIdFromJson(project, field.manager) ?: return + if ('$' in jsonModId) return + + if (javaModId != jsonModId) { + holder.registerProblem( + initializer, + "Mod ID '$javaModId' does not match mod ID '$jsonModId' from fabric.mod.json", + ChangeFieldInitializerQuickFix(jsonModId) + ) + } + } + + private fun getModIdFromJson(project: Project, manager: PsiManager): String? { + val files = FilenameIndex.getVirtualFilesByName( + "fabric.mod.json", + GlobalSearchScope.projectScope(project) + ) + + val file = files.firstOrNull() ?: return null + + val jsonFile = manager.findFile(file) as? JsonFile ?: return null + val topLevelObj = jsonFile.topLevelValue as? JsonObject ?: return null + + val stringLiteral = topLevelObj.findProperty("id")?.value as? JsonStringLiteral ?: return null + return stringLiteral.value + } + } + + private class ChangeFieldInitializerQuickFix( + private val newValue: String + ) : LocalQuickFix { + + override fun getName() = "Change mod ID field to \"$newValue\"" + + override fun getFamilyName() = "Change mod ID field" + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val initializer = descriptor.psiElement ?: return + val factory = JavaPsiFacade.getElementFactory(project) + val newInitializer = factory.createExpressionFromText("\"$newValue\"", null) + initializer.replace(newInitializer) + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d1c6407f0..a5eeb0f9c 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -991,6 +991,14 @@ level="ERROR" hasStaticDescription="true" implementationClass="com.demonwav.mcdev.platform.fabric.inspection.FabricEntrypointsInspection"/> +