Add @ResolveContext for usages of abstract message keys, implement relevant inspections, close #1363

This commit is contained in:
Him188 2021-12-23 00:14:57 +00:00
parent 63957b183b
commit 1cd354a7e3
9 changed files with 83 additions and 5 deletions

View File

@ -69,6 +69,9 @@ object MiraiConsoleErrors {
@JvmField
val RESTRICTED_CONSOLE_COMMAND_OWNER = create<PsiElement>(WARNING)
@JvmField
val PROHIBITED_ABSTRACT_MESSAGE_KEYS = create<PsiElement>(WARNING)
@JvmField
val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create<KtTypeReference>(ERROR)

View File

@ -19,6 +19,7 @@ import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.IL
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_VERSION_REQUIREMENT
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.NOT_CONSTRUCTABLE_TYPE
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.PROHIBITED_ABSTRACT_MESSAGE_KEYS
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.READ_ONLY_VALUE_CANNOT_BE_VAR
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.UNSERIALIZABLE_TYPE
@ -114,6 +115,11 @@ object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
"插件不允许使用 ConsoleCommandOwner 构造指令, 请使用插件主类作为 CommandOwner",
)
put(
PROHIBITED_ABSTRACT_MESSAGE_KEYS,
"使用 MessageContent.Key 等抽象消息类型的 Key 没有意义",
)
put(
READ_ONLY_VALUE_CANNOT_BE_VAR,
"在 ReadOnlyPluginData 中不可定义 'var' by value",

View File

@ -58,6 +58,10 @@ val READ_ONLY_PLUGIN_DATA_FQ_NAME = FqName("net.mamoe.mirai.console.data.ReadOnl
val RESOLVE_CONTEXT_FQ_NAME = FqName("net.mamoe.mirai.console.compiler.common.ResolveContext")
val PROHIBITED_MESSAGE_KEYS = arrayOf(
FqName("net.mamoe.mirai.message.data.MessageContent.Key")
)
/**
* net.mamoe.mirai.console.compiler.common.ResolveContext.Kind
*/

View File

@ -1,7 +1,7 @@
plugins {
kotlin("jvm") version "1.6.0"
kotlin("plugin.serialization") version "1.6.0"
id("net.mamoe.mirai-console") version "2.9.0-M1"
id("net.mamoe.mirai-console") version "2.99.0-local"
java
}
@ -13,6 +13,6 @@ dependencies {
}
repositories {
jcenter()
mavenCentral()
mavenLocal()
}

View File

@ -0,0 +1,18 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package org.example.myplugin
import net.mamoe.mirai.message.data.MessageContent
import net.mamoe.mirai.message.data.messageChainOf
fun main() {
val chain = messageChainOf()
chain[MessageContent]
}

View File

@ -17,8 +17,10 @@ import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.IL
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PERMISSION_NAMESPACE
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_VERSION_REQUIREMENT
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.PROHIBITED_ABSTRACT_MESSAGE_KEYS
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.compiler.common.resolve.CONSOLE_COMMAND_OWNER_FQ_NAME
import net.mamoe.mirai.console.compiler.common.resolve.PROHIBITED_MESSAGE_KEYS
import net.mamoe.mirai.console.compiler.common.resolve.ResolveContextKind
import net.mamoe.mirai.console.compiler.common.resolve.resolveContextKinds
import net.mamoe.mirai.console.intellij.resolve.getResolvedCall
@ -30,6 +32,7 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
import org.jetbrains.kotlin.psi.KtReferenceExpression
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.psi.psiUtil.referenceExpression
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@ -219,6 +222,23 @@ class ContextualParametersChecker : CallChecker {
return null
}
fun checkAbstractMessageKeys(
context: CallCheckerContext,
inspectionTarget: PsiElement,
argument: ValueArgument
): Diagnostic? {
val expr = argument.getArgumentExpression() ?: return null
if (expr is KtReferenceExpression) {
val call = expr.getResolvedCall(context.bindingContext) ?: return null
if (PROHIBITED_MESSAGE_KEYS.any { call.isCalling(it) }) {
return PROHIBITED_ABSTRACT_MESSAGE_KEYS.on(inspectionTarget)
}
}
return null
}
}
fun interface ElementChecker {
@ -268,6 +288,7 @@ class ContextualParametersChecker : CallChecker {
put(ResolveContextKind.PERMISSION_ID, ::checkPermissionId)
put(ResolveContextKind.VERSION_REQUIREMENT, ::checkVersionRequirement)
put(ResolveContextKind.RESTRICTED_CONSOLE_COMMAND_OWNER, ::checkConsoleCommandOwner)
put(ResolveContextKind.RESTRICTED_ABSTRACT_MESSAGE_KEYS, ::checkAbstractMessageKeys)
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2019-2021 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
package net.mamoe.mirai.console.intellij.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
class MessageChainGetCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
// if (resolvedCall.isCalling())
}
}

View File

@ -60,6 +60,7 @@ kotlin {
api(`ktor-client-okhttp`)
implementation(project(":mirai-core-utils"))
implementation(project(":mirai-console-compiler-annotations"))
implementation(`kotlinx-serialization-protobuf-jvm`)
implementation(`jetbrains-annotations`)
implementation(`log4j-api`)

View File

@ -21,6 +21,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.Json
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.message.MessageSerializers
@ -38,6 +39,7 @@ import net.mamoe.mirai.utils.safeCast
import java.util.stream.Stream
import kotlin.reflect.KProperty
import kotlin.streams.asSequence
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_ABSTRACT_MESSAGE_KEYS as RAMK
/**
* 消息链, `List<SingleMessage>`, [单个消息元素][SingleMessage] 的有序集合.
@ -211,7 +213,7 @@ public sealed interface MessageChain :
*
* @see MessageChain.getOrFail 在找不到此类型的元素时抛出 [NoSuchElementException]
*/
public operator fun <M : SingleMessage> get(key: MessageKey<M>): M? {
public operator fun <M : SingleMessage> get(@ResolveContext(RAMK) key: MessageKey<M>): M? {
@Suppress("UNCHECKED_CAST")
return firstOrNull { key.safeCast.invoke(it) != null } as M?
}
@ -242,7 +244,7 @@ public sealed interface MessageChain :
*
* @see MessageChain.getOrFail 在找不到此类型的元素时抛出 [NoSuchElementException]
*/
public operator fun <M : SingleMessage> contains(key: MessageKey<M>): Boolean =
public operator fun <M : SingleMessage> contains(@ResolveContext(RAMK) key: MessageKey<M>): Boolean =
any { key.safeCast.invoke(it) != null }
@MiraiExperimentalApi
@ -381,7 +383,7 @@ public object EmptyMessageChain : MessageChain, List<SingleMessage> by emptyList
*/
@JvmSynthetic
public inline fun <M : SingleMessage> MessageChain.getOrFail(
key: MessageKey<M>,
@ResolveContext(RAMK) key: MessageKey<M>,
crossinline lazyMessage: (key: MessageKey<M>) -> String = { key.toString() }
): M = get(key) ?: throw NoSuchElementException(lazyMessage(key))