mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-26 16:10:11 +08:00
Support PluginMainServiceNotConfiguredInspection and ConfigurePluginMainServiceFix
This commit is contained in:
parent
e37ac17b82
commit
0c3a4c735d
@ -19,10 +19,11 @@ import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
|||||||
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
import org.jetbrains.kotlin.resolve.constants.EnumValue
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Serializer
|
// OTHERS
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
val SERIALIZABLE_FQ_NAME = FqName("kotlinx.serialization.Serializable")
|
val SERIALIZABLE_FQ_NAME = FqName("kotlinx.serialization.Serializable")
|
||||||
|
val AUTO_SERVICE = FqName("com.google.auto.service.AutoService")
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -32,6 +32,12 @@
|
|||||||
<codeInsight.lineMarkerProvider language="kotlin"
|
<codeInsight.lineMarkerProvider language="kotlin"
|
||||||
implementationClass="net.mamoe.mirai.console.intellij.line.marker.CommandDeclarationLineMarkerProvider"/>
|
implementationClass="net.mamoe.mirai.console.intellij.line.marker.CommandDeclarationLineMarkerProvider"/>
|
||||||
|
|
||||||
|
<localInspection groupPath="Mirai console" language="kotlin" shortName="PluginMainServiceNotConfigured"
|
||||||
|
bundle="messages.InspectionGadgetsBundle"
|
||||||
|
key="plugin.service.not.configured.display.name" groupBundle="messages.InspectionsBundle"
|
||||||
|
groupKey="group.names.plugin.service.issues" enabledByDefault="true" level="WARNING"
|
||||||
|
implementationClass="net.mamoe.mirai.console.intellij.diagnostics.PluginMainServiceNotConfiguredInspection"/>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>net.mamoe.mirai.console.intellij.diagnostics.fix.AbuseYellowIntention</className>
|
<className>net.mamoe.mirai.console.intellij.diagnostics.fix.AbuseYellowIntention</className>
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
#
|
||||||
|
# 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
# Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link.
|
||||||
|
#
|
||||||
|
# https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
#
|
||||||
|
plugin.service.not.configured.display.name=Plugin main not configured
|
@ -0,0 +1,9 @@
|
|||||||
|
#
|
||||||
|
# Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
#
|
||||||
|
# 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
# Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link.
|
||||||
|
#
|
||||||
|
# https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
#
|
||||||
|
group.names.plugin.service.issues=Plugin main class issues
|
@ -29,6 +29,3 @@ object MyPluginMain : KotlinPlugin(
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val y = "傻逼 yellow"
|
|
@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.intellij.diagnostics
|
||||||
|
|
||||||
|
import com.intellij.codeInspection.ProblemHighlightType
|
||||||
|
import com.intellij.codeInspection.ProblemsHolder
|
||||||
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import net.mamoe.mirai.console.compiler.common.resolve.AUTO_SERVICE
|
||||||
|
import net.mamoe.mirai.console.intellij.diagnostics.fix.ConfigurePluginMainServiceFix
|
||||||
|
import net.mamoe.mirai.console.intellij.resolve.hasAnnotation
|
||||||
|
import org.jetbrains.kotlin.idea.debugger.readAction
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||||
|
import org.jetbrains.kotlin.idea.util.module
|
||||||
|
import org.jetbrains.kotlin.idea.util.rootManager
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
import org.jetbrains.kotlin.psi.referenceExpressionVisitor
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/*
|
||||||
|
private val bundle by lazy {
|
||||||
|
BundleUtil.loadLanguageBundle(PluginMainServiceNotConfiguredInspection::class.java.classLoader, "messages.InspectionGadgetsBundle")!!
|
||||||
|
}*/
|
||||||
|
|
||||||
|
class PluginMainServiceNotConfiguredInspection : AbstractKotlinInspection() {
|
||||||
|
companion object {
|
||||||
|
private val SERVICE_FILE_NAMES = arrayOf(
|
||||||
|
"net.mamoe.mirai.console.plugin.jvm.JvmPlugin",
|
||||||
|
"net.mamoe.mirai.console.plugin.jvm.KotlinPlugin",
|
||||||
|
"net.mamoe.mirai.console.plugin.jvm.JavaPlugin",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||||
|
return referenceExpressionVisitor visitor@{ referenceExpr ->
|
||||||
|
val ktClass = referenceExpr.resolveMiraiPluginDeclaration() ?: return@visitor
|
||||||
|
val fqName = ktClass.fqName?.asString() ?: return@visitor
|
||||||
|
|
||||||
|
val found = isServiceConfiguredWithAutoService(ktClass)
|
||||||
|
|| isServiceConfiguredWithResource(ktClass, fqName)
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
holder.registerProblem(
|
||||||
|
ktClass.nameIdentifier ?: ktClass.identifyingElement ?: ktClass,
|
||||||
|
"插件主类服务未配置",
|
||||||
|
ProblemHighlightType.WARNING,
|
||||||
|
ConfigurePluginMainServiceFix(ktClass)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isServiceConfiguredWithAutoService(
|
||||||
|
ktClass: KtClassOrObject,
|
||||||
|
): Boolean = ktClass.hasAnnotation(AUTO_SERVICE)
|
||||||
|
|
||||||
|
private fun isServiceConfiguredWithResource(
|
||||||
|
ktClass: KtClassOrObject,
|
||||||
|
fqName: String,
|
||||||
|
): Boolean {
|
||||||
|
val sourceRoots = ktClass.module?.rootManager?.sourceRoots ?: return false
|
||||||
|
val services = sourceRoots.asSequence().flatMap { file ->
|
||||||
|
SERVICE_FILE_NAMES.asSequence().mapNotNull { serviceFileName ->
|
||||||
|
file.findFileByRelativePath("META-INF/services/$serviceFileName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return services.any { serviceFile ->
|
||||||
|
serviceFile.readAction { f -> f.inputStream.bufferedReader().use { it.readLine() }.trim() == fqName }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,16 +9,19 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.intellij.diagnostics
|
package net.mamoe.mirai.console.intellij.diagnostics
|
||||||
|
|
||||||
|
import com.intellij.util.castSafelyTo
|
||||||
import net.mamoe.mirai.console.compiler.common.castOrNull
|
import net.mamoe.mirai.console.compiler.common.castOrNull
|
||||||
|
import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
|
||||||
|
import net.mamoe.mirai.console.compiler.common.resolve.parents
|
||||||
|
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
|
||||||
import net.mamoe.mirai.console.intellij.resolve.getResolvedCall
|
import net.mamoe.mirai.console.intellij.resolve.getResolvedCall
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
|
||||||
import org.jetbrains.kotlin.psi.KtTypeReference
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtUserType
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||||
|
|
||||||
@ -40,4 +43,13 @@ fun KtTypeReference.isReferencing(fqName: FqName): Boolean {
|
|||||||
|
|
||||||
val KtTypeReference.referencedUserType: KtUserType? get() = this.typeElement.castOrNull()
|
val KtTypeReference.referencedUserType: KtUserType? get() = this.typeElement.castOrNull()
|
||||||
|
|
||||||
fun KtTypeReference.resolveReferencedType() = referencedUserType?.referenceExpression?.mainReference?.resolve()
|
fun KtTypeReference.resolveReferencedType() = referencedUserType?.referenceExpression?.mainReference?.resolve()
|
||||||
|
|
||||||
|
fun KtReferenceExpression.resolveMiraiPluginDeclaration(): KtClassOrObject? {
|
||||||
|
val main =
|
||||||
|
parents.filterIsInstance<KtClassOrObject>().firstOrNull() ?: return null
|
||||||
|
val kotlinPluginClass =
|
||||||
|
resolve().castSafelyTo<KtConstructor<*>>()?.parent?.castSafelyTo<KtClass>() ?: return null
|
||||||
|
if (kotlinPluginClass.allSuperNames.none { it == PLUGIN_FQ_NAME }) return null
|
||||||
|
return main
|
||||||
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2019-2020 Mamoe Technologies and contributors.
|
||||||
|
*
|
||||||
|
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||||
|
* Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link.
|
||||||
|
*
|
||||||
|
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.console.intellij.diagnostics.fix
|
||||||
|
|
||||||
|
import com.intellij.codeInspection.LocalQuickFix
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.project.rootManager
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import com.intellij.testFramework.writeChild
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.KotlinCrossLanguageQuickFixAction
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
|
import org.jetbrains.kotlin.idea.util.module
|
||||||
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigurePluginMainServiceFix(
|
||||||
|
element: KtClassOrObject,
|
||||||
|
) : KotlinCrossLanguageQuickFixAction<KtClassOrObject>(element), KotlinUniversalQuickFix, LocalQuickFix {
|
||||||
|
|
||||||
|
override fun getFamilyName(): String = "Mirai Console"
|
||||||
|
override fun getText(): String = "配置插件主类服务"
|
||||||
|
|
||||||
|
override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) {
|
||||||
|
val elementFqName = element?.fqName ?: return
|
||||||
|
val sourceRoots = file.module?.rootManager?.sourceRoots ?: return
|
||||||
|
|
||||||
|
val sourceRoot = sourceRoots.find { it.name.endsWith("resources") }
|
||||||
|
?: sourceRoots.find { it.name.contains("resources") }
|
||||||
|
?: sourceRoots.last()
|
||||||
|
|
||||||
|
project.executeWriteCommand(name) {
|
||||||
|
sourceRoot.writeChild("META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin", elementFqName.asString().toByteArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,27 +15,16 @@ import com.intellij.codeInsight.daemon.LineMarkerProvider
|
|||||||
import com.intellij.openapi.actionSystem.AnAction
|
import com.intellij.openapi.actionSystem.AnAction
|
||||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.util.castSafelyTo
|
|
||||||
import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME
|
|
||||||
import net.mamoe.mirai.console.compiler.common.resolve.parents
|
|
||||||
import net.mamoe.mirai.console.intellij.Icons
|
import net.mamoe.mirai.console.intellij.Icons
|
||||||
import net.mamoe.mirai.console.intellij.resolve.allSuperNames
|
import net.mamoe.mirai.console.intellij.diagnostics.resolveMiraiPluginDeclaration
|
||||||
import net.mamoe.mirai.console.intellij.resolve.getElementForLineMark
|
import net.mamoe.mirai.console.intellij.resolve.getElementForLineMark
|
||||||
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
|
|
||||||
import org.jetbrains.kotlin.psi.KtClass
|
|
||||||
import org.jetbrains.kotlin.psi.KtConstructor
|
|
||||||
import org.jetbrains.kotlin.psi.KtObjectDeclaration
|
|
||||||
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
||||||
|
|
||||||
class PluginMainLineMarkerProvider : LineMarkerProvider {
|
class PluginMainLineMarkerProvider : LineMarkerProvider {
|
||||||
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
|
override fun getLineMarkerInfo(element: PsiElement): LineMarkerInfo<*>? {
|
||||||
if (element !is KtReferenceExpression) return null
|
if (element !is KtReferenceExpression) return null
|
||||||
val objectDeclaration =
|
val main = element.resolveMiraiPluginDeclaration() ?: return null
|
||||||
element.parents.filterIsInstance<KtObjectDeclaration>().firstOrNull() ?: return null
|
return Info(getElementForLineMark(main))
|
||||||
val kotlinPluginClass =
|
|
||||||
element.resolve().castSafelyTo<KtConstructor<*>>()?.parent?.castSafelyTo<KtClass>() ?: return null
|
|
||||||
if (kotlinPluginClass.allSuperNames.none { it == PLUGIN_FQ_NAME }) return null
|
|
||||||
return Info(getElementForLineMark(objectDeclaration))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@ -11,6 +11,7 @@ package net.mamoe.mirai.console.intellij.resolve
|
|||||||
|
|
||||||
import com.intellij.psi.PsiDeclarationStatement
|
import com.intellij.psi.PsiDeclarationStatement
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.util.parentsWithSelf
|
||||||
import net.mamoe.mirai.console.compiler.common.castOrNull
|
import net.mamoe.mirai.console.compiler.common.castOrNull
|
||||||
import net.mamoe.mirai.console.compiler.common.resolve.COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME
|
import net.mamoe.mirai.console.compiler.common.resolve.COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME
|
||||||
import net.mamoe.mirai.console.compiler.common.resolve.SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME
|
import net.mamoe.mirai.console.compiler.common.resolve.SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME
|
||||||
@ -19,9 +20,9 @@ import net.mamoe.mirai.console.compiler.common.resolve.findParent
|
|||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
|
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@ -32,6 +33,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
|||||||
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
import org.jetbrains.kotlin.resolve.constants.ArrayValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue
|
||||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
|
|
||||||
|
|
||||||
@ -53,7 +55,8 @@ val KtPureClassOrObject.allSuperTypes: Sequence<KtSuperTypeListEntry>
|
|||||||
get() = sequence {
|
get() = sequence {
|
||||||
yieldAll(superTypeListEntries)
|
yieldAll(superTypeListEntries)
|
||||||
for (list in superTypeListEntries.asSequence()) {
|
for (list in superTypeListEntries.asSequence()) {
|
||||||
yieldAll((list.typeAsUserType?.referenceExpression?.resolve() as? KtClass)?.allSuperTypes.orEmpty())
|
yieldAll((list.typeAsUserType?.referenceExpression?.resolve()?.parentsWithSelf?.filterIsInstance<KtClass>()
|
||||||
|
?.firstOrNull())?.allSuperTypes.orEmpty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +148,7 @@ fun KtExpression.resolveStringConstantValues(): Sequence<String> {
|
|||||||
is KtNameReferenceExpression -> {
|
is KtNameReferenceExpression -> {
|
||||||
when (val reference = references.firstIsInstance<KtSimpleNameReference>().resolve()) {
|
when (val reference = references.firstIsInstance<KtSimpleNameReference>().resolve()) {
|
||||||
is KtDeclaration -> {
|
is KtDeclaration -> {
|
||||||
val descriptor = reference.descriptor.castOrNull<VariableDescriptor>() ?: return emptySequence()
|
val descriptor = reference.resolveToDescriptorIfAny(BodyResolveMode.FULL).castOrNull<VariableDescriptor>() ?: return emptySequence()
|
||||||
val compileTimeConstant = descriptor.compileTimeInitializer ?: return emptySequence()
|
val compileTimeConstant = descriptor.compileTimeInitializer ?: return emptySequence()
|
||||||
return compileTimeConstant.selfOrChildrenConstantStrings()
|
return compileTimeConstant.selfOrChildrenConstantStrings()
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user