mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-27 17:00:14 +08:00
New inspection: ILLEGAL_COMMAND_DECLARATION_RECEIVER, close #174
This commit is contained in:
parent
062227e072
commit
bc290f63bb
@ -10,6 +10,7 @@ package net.mamoe.mirai.console.compiler.common.diagnostics
|
|||||||
|
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0.create
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1.create
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1.create
|
||||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2.create
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2.create
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
@ -17,6 +18,7 @@ import org.jetbrains.kotlin.diagnostics.Severity.ERROR
|
|||||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtTypeProjection
|
import org.jetbrains.kotlin.psi.KtTypeProjection
|
||||||
|
import org.jetbrains.kotlin.psi.KtTypeReference
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 如何增加一个错误:
|
* 如何增加一个错误:
|
||||||
@ -54,6 +56,12 @@ object MiraiConsoleErrors {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val ILLEGAL_VERSION_REQUIREMENT = create<PsiElement, String, String>(ERROR)
|
val ILLEGAL_VERSION_REQUIREMENT = create<PsiElement, String, String>(ERROR)
|
||||||
|
|
||||||
|
// @JvmField
|
||||||
|
// val INAPPLICABLE_COMMAND_ANNOTATION = create<PsiElement, String>(ERROR)
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create<KtTypeReference>(ERROR)
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "unused")
|
@Suppress("ObjectPropertyName", "unused")
|
||||||
@JvmField
|
@JvmField
|
||||||
@Deprecated("", level = DeprecationLevel.ERROR)
|
@Deprecated("", level = DeprecationLevel.ERROR)
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.compiler.common.diagnostics
|
package net.mamoe.mirai.console.compiler.common.diagnostics
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_DECLARATION_RECEIVER
|
||||||
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_NAME
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_NAME
|
||||||
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_REGISTER_USE
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_REGISTER_USE
|
||||||
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PERMISSION_ID
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PERMISSION_ID
|
||||||
@ -95,6 +96,17 @@ object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
Renderers.STRING,
|
Renderers.STRING,
|
||||||
Renderers.STRING
|
Renderers.STRING
|
||||||
)
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
ILLEGAL_COMMAND_DECLARATION_RECEIVER,
|
||||||
|
"指令函数的接收者参数必须为 CommandSender 及其子类或无接收者.",
|
||||||
|
)
|
||||||
|
|
||||||
|
// put(
|
||||||
|
// INAPPLICABLE_COMMAND_ANNOTATION,
|
||||||
|
// "''{0}'' 无法在顶层函数使用.",
|
||||||
|
// Renderers.STRING,
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getMap() = MAP
|
override fun getMap() = MAP
|
||||||
|
@ -32,6 +32,7 @@ val AUTO_SERVICE = FqName("com.google.auto.service.AutoService")
|
|||||||
|
|
||||||
val COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.command.CompositeCommand.SubCommand")
|
val COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.command.CompositeCommand.SubCommand")
|
||||||
val SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.command.SimpleCommand.Handler")
|
val SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.command.SimpleCommand.Handler")
|
||||||
|
val COMMAND_SENDER_FQ_NAME = FqName("net.mamoe.mirai.console.command.CommandSender")
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Plugin
|
// Plugin
|
||||||
|
@ -17,7 +17,7 @@ object MySimpleCommand000 : SimpleCommand(
|
|||||||
}
|
}
|
||||||
|
|
||||||
object DataTest : AutoSavePluginConfig("data") {
|
object DataTest : AutoSavePluginConfig("data") {
|
||||||
val pp by value<NoDefaultValue>(NoDefaultValue(1))
|
val pp by value(NoDefaultValue(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.intellij
|
package net.mamoe.mirai.console.intellij
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.intellij.diagnostics.CommandDeclarationChecker
|
||||||
import net.mamoe.mirai.console.intellij.diagnostics.ContextualParametersChecker
|
import net.mamoe.mirai.console.intellij.diagnostics.ContextualParametersChecker
|
||||||
import net.mamoe.mirai.console.intellij.diagnostics.PluginDataValuesChecker
|
import net.mamoe.mirai.console.intellij.diagnostics.PluginDataValuesChecker
|
||||||
import org.jetbrains.kotlin.container.StorageComponentContainer
|
import org.jetbrains.kotlin.container.StorageComponentContainer
|
||||||
@ -24,5 +25,6 @@ class IDEContainerContributor : StorageComponentContainerContributor {
|
|||||||
) {
|
) {
|
||||||
container.useInstance(ContextualParametersChecker())
|
container.useInstance(ContextualParametersChecker())
|
||||||
container.useInstance(PluginDataValuesChecker())
|
container.useInstance(PluginDataValuesChecker())
|
||||||
|
container.useInstance(CommandDeclarationChecker())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package net.mamoe.mirai.console.intellij.diagnostics
|
||||||
|
|
||||||
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_DECLARATION_RECEIVER
|
||||||
|
import net.mamoe.mirai.console.compiler.common.resolve.COMMAND_SENDER_FQ_NAME
|
||||||
|
import net.mamoe.mirai.console.intellij.resolve.hasSuperType
|
||||||
|
import net.mamoe.mirai.console.intellij.resolve.isCompositeCommandSubCommand
|
||||||
|
import net.mamoe.mirai.console.intellij.resolve.isSimpleCommandHandler
|
||||||
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||||
|
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||||
|
|
||||||
|
class CommandDeclarationChecker : DeclarationChecker {
|
||||||
|
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||||
|
if (declaration !is KtNamedFunction) return
|
||||||
|
|
||||||
|
// exclusive checks
|
||||||
|
when {
|
||||||
|
declaration.isSimpleCommandHandler() -> {
|
||||||
|
}
|
||||||
|
|
||||||
|
declaration.isCompositeCommandSubCommand() -> {
|
||||||
|
}
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
|
||||||
|
// common checks
|
||||||
|
checkCommandReceiverParameter(declaration)?.let { context.report(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun checkCommandReceiverParameter(declaration: KtNamedFunction): Diagnostic? {
|
||||||
|
val receiverTypeRef = declaration.receiverTypeReference ?: return null // no receiver, accept.
|
||||||
|
val receiver = receiverTypeRef.resolveReferencedType() ?: return null // unresolved type
|
||||||
|
if (!receiver.hasSuperType(COMMAND_SENDER_FQ_NAME)) {
|
||||||
|
return ILLEGAL_COMMAND_DECLARATION_RECEIVER.on(receiverTypeRef)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
package net.mamoe.mirai.console.intellij.resolve
|
package net.mamoe.mirai.console.intellij.resolve
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiClass
|
||||||
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 com.intellij.psi.util.parentsWithSelf
|
||||||
@ -60,6 +61,19 @@ val KtPureClassOrObject.allSuperTypes: Sequence<KtSuperTypeListEntry>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val PsiClass.allSuperTypes: Sequence<PsiClass>
|
||||||
|
get() = sequence {
|
||||||
|
interfaces.forEach {
|
||||||
|
yield(it)
|
||||||
|
yieldAll(it.allSuperTypes)
|
||||||
|
}
|
||||||
|
val superClass = superClass
|
||||||
|
if (superClass != null) {
|
||||||
|
yield(superClass)
|
||||||
|
yieldAll(superClass.allSuperTypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun KtConstructorCalleeExpression.getTypeAsUserType(): KtUserType? {
|
fun KtConstructorCalleeExpression.getTypeAsUserType(): KtUserType? {
|
||||||
val reference = typeReference
|
val reference = typeReference
|
||||||
if (reference != null) {
|
if (reference != null) {
|
||||||
@ -71,7 +85,26 @@ fun KtConstructorCalleeExpression.getTypeAsUserType(): KtUserType? {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtClassOrObject.hasSuperType(fqName: FqName): Boolean = allSuperNames.contains(fqName)
|
||||||
|
fun KtClass.hasSuperType(fqName: FqName): Boolean = allSuperNames.contains(fqName)
|
||||||
|
|
||||||
|
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
@kotlin.internal.LowPriorityInOverloadResolution
|
||||||
|
fun PsiElement.hasSuperType(fqName: FqName): Boolean = allSuperNames.contains(fqName)
|
||||||
|
|
||||||
val KtClassOrObject.allSuperNames: Sequence<FqName> get() = allSuperTypes.mapNotNull { it.getKotlinFqName() }
|
val KtClassOrObject.allSuperNames: Sequence<FqName> get() = allSuperTypes.mapNotNull { it.getKotlinFqName() }
|
||||||
|
val PsiClass.allSuperNames: Sequence<FqName> get() = allSuperTypes.mapNotNull { clazz -> clazz.qualifiedName?.let { FqName(it) } }
|
||||||
|
|
||||||
|
@Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
|
||||||
|
@kotlin.internal.LowPriorityInOverloadResolution
|
||||||
|
val PsiElement.allSuperNames: Sequence<FqName>
|
||||||
|
get() {
|
||||||
|
return when (this) {
|
||||||
|
is KtClassOrObject -> allSuperNames
|
||||||
|
is PsiClass -> allSuperNames
|
||||||
|
else -> emptySequence()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun getElementForLineMark(callElement: PsiElement): PsiElement =
|
fun getElementForLineMark(callElement: PsiElement): PsiElement =
|
||||||
when (callElement) {
|
when (callElement) {
|
||||||
|
Loading…
Reference in New Issue
Block a user