From 1cd354a7e3577abc90ea961d6797fee719750c1a Mon Sep 17 00:00:00 2001 From: Him188 Date: Thu, 23 Dec 2021 00:14:57 +0000 Subject: [PATCH] Add `@ResolveContext` for usages of abstract message keys, implement relevant inspections, close #1363 --- .../src/diagnostics/MiraiConsoleErrors.kt | 3 +++ .../MiraiConsoleErrorsRendering.kt | 6 +++++ .../src/resolve/resolveTypes.kt | 4 ++++ .../projects/test-project/build.gradle.kts | 4 ++-- .../myplugin/AbstractMessageKeysUsages.kt | 18 +++++++++++++++ .../ContextualParametersChecker.kt | 21 +++++++++++++++++ .../diagnostics/MessageChainGetCallChecker.kt | 23 +++++++++++++++++++ mirai-core-api/build.gradle.kts | 1 + .../kotlin/message/data/MessageChain.kt | 8 ++++--- 9 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/AbstractMessageKeysUsages.kt create mode 100644 mirai-console/tools/intellij-plugin/src/diagnostics/MessageChainGetCallChecker.kt diff --git a/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt b/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt index c0d1caa47..527742315 100644 --- a/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt +++ b/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt @@ -69,6 +69,9 @@ object MiraiConsoleErrors { @JvmField val RESTRICTED_CONSOLE_COMMAND_OWNER = create(WARNING) + @JvmField + val PROHIBITED_ABSTRACT_MESSAGE_KEYS = create(WARNING) + @JvmField val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create(ERROR) diff --git a/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrorsRendering.kt b/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrorsRendering.kt index d0159a480..ee935ec43 100644 --- a/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrorsRendering.kt +++ b/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrorsRendering.kt @@ -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", diff --git a/mirai-console/tools/compiler-common/src/resolve/resolveTypes.kt b/mirai-console/tools/compiler-common/src/resolve/resolveTypes.kt index 03030ac9a..923bce8be 100644 --- a/mirai-console/tools/compiler-common/src/resolve/resolveTypes.kt +++ b/mirai-console/tools/compiler-common/src/resolve/resolveTypes.kt @@ -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 */ diff --git a/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts b/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts index 2bc148209..51870ee5a 100644 --- a/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts +++ b/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts @@ -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() } \ No newline at end of file diff --git a/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/AbstractMessageKeysUsages.kt b/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/AbstractMessageKeysUsages.kt new file mode 100644 index 000000000..946531df1 --- /dev/null +++ b/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/AbstractMessageKeysUsages.kt @@ -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] +} \ No newline at end of file diff --git a/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt b/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt index f2c23cc0f..f8d7048eb 100644 --- a/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt +++ b/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt @@ -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) } } \ No newline at end of file diff --git a/mirai-console/tools/intellij-plugin/src/diagnostics/MessageChainGetCallChecker.kt b/mirai-console/tools/intellij-plugin/src/diagnostics/MessageChainGetCallChecker.kt new file mode 100644 index 000000000..cc6d7d7d8 --- /dev/null +++ b/mirai-console/tools/intellij-plugin/src/diagnostics/MessageChainGetCallChecker.kt @@ -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()) + } + +} \ No newline at end of file diff --git a/mirai-core-api/build.gradle.kts b/mirai-core-api/build.gradle.kts index ad5d22fbf..1a5442cc3 100644 --- a/mirai-core-api/build.gradle.kts +++ b/mirai-core-api/build.gradle.kts @@ -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`) diff --git a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt index a5f43a97b..3ebb02280 100644 --- a/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt +++ b/mirai-core-api/src/commonMain/kotlin/message/data/MessageChain.kt @@ -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] 的有序集合. @@ -211,7 +213,7 @@ public sealed interface MessageChain : * * @see MessageChain.getOrFail 在找不到此类型的元素时抛出 [NoSuchElementException] */ - public operator fun get(key: MessageKey): M? { + public operator fun get(@ResolveContext(RAMK) key: MessageKey): 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 contains(key: MessageKey): Boolean = + public operator fun contains(@ResolveContext(RAMK) key: MessageKey): Boolean = any { key.safeCast.invoke(it) != null } @MiraiExperimentalApi @@ -381,7 +383,7 @@ public object EmptyMessageChain : MessageChain, List by emptyList */ @JvmSynthetic public inline fun MessageChain.getOrFail( - key: MessageKey, + @ResolveContext(RAMK) key: MessageKey, crossinline lazyMessage: (key: MessageKey) -> String = { key.toString() } ): M = get(key) ?: throw NoSuchElementException(lazyMessage(key))