mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Support ReadOnlyPluginData:
Add ReadOnlyPluginData, ReadOnlyPluginConfig, New inspection: READ_ONLY_VALUE_CANNOT_BE_VAR,
This commit is contained in:
parent
64e0d545f4
commit
a6d5656161
@ -48,7 +48,7 @@ public abstract class AbstractPluginData : PluginData, PluginDataImpl() {
|
||||
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||
*/
|
||||
@ConsoleExperimentalApi
|
||||
public fun <T : SerializerAwareValue<*>> track(
|
||||
public open fun <T : SerializerAwareValue<*>> track(
|
||||
value: T,
|
||||
/**
|
||||
* 值名称.
|
||||
|
21
backend/mirai-console/src/data/ReadOnlyPluginConfig.kt
Normal file
21
backend/mirai-console/src/data/ReadOnlyPluginConfig.kt
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
/**
|
||||
* 只读的 [PluginConfig]. 插件只能读取其值, 但值可能在后台被前端 (用户) 修改.
|
||||
*
|
||||
* @see PluginConfig
|
||||
* @see AutoSavePluginData
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public open class ReadOnlyPluginConfig public constructor(saveName: String) : ReadOnlyPluginData(saveName), PluginConfig
|
45
backend/mirai-console/src/data/ReadOnlyPluginData.kt
Normal file
45
backend/mirai-console/src/data/ReadOnlyPluginData.kt
Normal file
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.utils.error
|
||||
|
||||
/**
|
||||
* 只读的 [PluginData]. 插件只能读取其值, 但值可能在后台被前端 (用户) 修改.
|
||||
*
|
||||
* @see PluginData
|
||||
* @see AutoSavePluginData
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public open class ReadOnlyPluginData private constructor(
|
||||
// KEEP THIS PRIMARY CONSTRUCTOR FOR FUTURE USE: WE'LL SUPPORT SERIALIZERS_MODULE FOR POLYMORPHISM
|
||||
@Suppress("UNUSED_PARAMETER") primaryConstructorMark: Any?,
|
||||
) : AbstractPluginData() {
|
||||
public final override val saveName: String
|
||||
get() = _saveName
|
||||
|
||||
private lateinit var _saveName: String
|
||||
|
||||
public constructor(saveName: String) : this(null) {
|
||||
_saveName = saveName
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
override fun onInit(owner: PluginDataHolder, storage: PluginDataStorage) {
|
||||
}
|
||||
|
||||
@ConsoleExperimentalApi
|
||||
public final override fun onValueChanged(value: Value<*>) {
|
||||
debuggingLogger1.error { "onValueChanged: $value" }
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
|
||||
object Versions {
|
||||
const val core = "1.3.3"
|
||||
const val console = "1.1.0-dev-32"
|
||||
const val console = "1.1.0-dev-33"
|
||||
const val consoleGraphical = "0.0.7"
|
||||
const val consoleTerminal = console
|
||||
|
||||
|
@ -63,6 +63,9 @@ object MiraiConsoleErrors {
|
||||
@JvmField
|
||||
val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create<KtTypeReference>(ERROR)
|
||||
|
||||
@JvmField
|
||||
val READ_ONLY_VALUE_CANNOT_BE_VAR = create<PsiElement>(ERROR)
|
||||
|
||||
@Suppress("ObjectPropertyName", "unused")
|
||||
@JvmField
|
||||
@Deprecated("", level = DeprecationLevel.ERROR)
|
||||
|
@ -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.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
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
@ -108,6 +109,11 @@ object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
|
||||
"插件不允许使用 ConsoleCommandOwner 构造指令, 请使用插件主类作为 CommandOwner",
|
||||
)
|
||||
|
||||
put(
|
||||
READ_ONLY_VALUE_CANNOT_BE_VAR,
|
||||
"在 ReadOnlyPluginData 中不可定义 'var' by value",
|
||||
)
|
||||
|
||||
// put(
|
||||
// INAPPLICABLE_COMMAND_ANNOTATION,
|
||||
// "''{0}'' 无法在顶层函数使用.",
|
||||
|
@ -49,6 +49,7 @@ val SIMPLE_JVM_PLUGIN_DESCRIPTION_FQ_NAME = FqName("net.mamoe.mirai.console.plug
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
val PLUGIN_DATA_VALUE_FUNCTIONS_FQ_FQ_NAME = FqName("net.mamoe.mirai.console.data.value")
|
||||
val READ_ONLY_PLUGIN_DATA_FQ_NAME = FqName("net.mamoe.mirai.console.data.ReadOnlyPluginData")
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Resolve
|
||||
|
@ -10,6 +10,6 @@
|
||||
package net.mamoe.mirai.console.gradle
|
||||
|
||||
internal object VersionConstants {
|
||||
const val CONSOLE_VERSION = "1.1.0-dev-32" // value is written here automatically during build
|
||||
const val CONSOLE_VERSION = "1.1.0-dev-33" // value is written here automatically during build
|
||||
const val CORE_VERSION = "1.3.3" // value is written here automatically during build
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.4.20"
|
||||
kotlin("plugin.serialization") version "1.4.20"
|
||||
id("net.mamoe.mirai-console") version "1.1.0-dev-32"
|
||||
id("net.mamoe.mirai-console") version "1.1.0-dev-33"
|
||||
}
|
||||
|
||||
group = "org.example"
|
||||
|
@ -5,6 +5,7 @@ import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.console.command.ConsoleCommandOwner
|
||||
import net.mamoe.mirai.console.command.SimpleCommand
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||
import net.mamoe.mirai.console.data.ReadOnlyPluginConfig
|
||||
import net.mamoe.mirai.console.data.value
|
||||
|
||||
object MySimpleCommand0001 : SimpleCommand(
|
||||
@ -26,6 +27,10 @@ object DataTest : AutoSavePluginConfig("data") {
|
||||
val pp by value(NoDefaultValue(1))
|
||||
}
|
||||
|
||||
object DataTest1 : ReadOnlyPluginConfig("data") {
|
||||
var pp by value<String>()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class HasDefaultValue(
|
||||
val x: Int = 0,
|
||||
|
@ -16,12 +16,18 @@ import net.mamoe.mirai.console.compiler.common.resolve.*
|
||||
import net.mamoe.mirai.console.intellij.resolve.resolveAllCallsWithElement
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.debugger.sequence.psi.receiverType
|
||||
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
@ -41,24 +47,46 @@ class PluginDataValuesChecker : DeclarationChecker {
|
||||
&& t is SimpleType
|
||||
}.forEach { (e, callExpr) ->
|
||||
val (_, type) = e
|
||||
val classDescriptor = type.constructor.declarationDescriptor?.castOrNull<ClassDescriptor>() ?: return@forEach
|
||||
checkCallExpression(type, callExpr, context)
|
||||
}
|
||||
|
||||
if (canBeSerializedInternally(classDescriptor)) return@forEach
|
||||
declaration.resolveAllCallsWithElement(bindingContext)
|
||||
.filter { (call) -> call.isCalling(PLUGIN_DATA_VALUE_FUNCTIONS_FQ_FQ_NAME) }
|
||||
.forEach { (_, callExpr) ->
|
||||
checkReadOnly(callExpr, context)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun checkReadOnly(callExpr: KtCallExpression, context: DeclarationCheckerContext) {
|
||||
// first parent is KtPropertyDelegate, next is KtProperty
|
||||
val property = callExpr.parent.parent.castOrNull<KtProperty>() ?: return
|
||||
if (property.isVar &&
|
||||
callExpr.receiverType()?.toClassDescriptor?.getAllSuperClassifiers()?.any { it.fqNameOrNull() == READ_ONLY_PLUGIN_DATA_FQ_NAME } == true
|
||||
) {
|
||||
context.report(MiraiConsoleErrors.READ_ONLY_VALUE_CANNOT_BE_VAR.on(property.valOrVarKeyword))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCallExpression(type: KotlinType, callExpr: KtCallExpression, context: DeclarationCheckerContext) {
|
||||
val classDescriptor = type.constructor.declarationDescriptor?.castOrNull<ClassDescriptor>() ?: return
|
||||
|
||||
if (canBeSerializedInternally(classDescriptor)) return
|
||||
|
||||
val inspectionTarget = kotlin.run {
|
||||
val fqName = type.fqName ?: return@run null
|
||||
callExpr.typeArguments.find { it.typeReference?.isReferencing(fqName) == true }
|
||||
} ?: return@forEach
|
||||
} ?: return
|
||||
|
||||
if (!classDescriptor.hasNoArgConstructor())
|
||||
return@forEach context.report(MiraiConsoleErrors.NOT_CONSTRUCTABLE_TYPE.on(
|
||||
return context.report(MiraiConsoleErrors.NOT_CONSTRUCTABLE_TYPE.on(
|
||||
inspectionTarget,
|
||||
callExpr,
|
||||
type.fqName?.asString().toString())
|
||||
)
|
||||
|
||||
if (!classDescriptor.hasAnnotation(SERIALIZABLE_FQ_NAME))
|
||||
return@forEach context.report(MiraiConsoleErrors.UNSERIALIZABLE_TYPE.on(
|
||||
return context.report(MiraiConsoleErrors.UNSERIALIZABLE_TYPE.on(
|
||||
inspectionTarget,
|
||||
classDescriptor
|
||||
))
|
||||
|
Loading…
Reference in New Issue
Block a user