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]
|
* 供手动实现时值跟踪使用 (如 Java 用户). 一般 Kotlin 用户需使用 [provideDelegate]
|
||||||
*/
|
*/
|
||||||
@ConsoleExperimentalApi
|
@ConsoleExperimentalApi
|
||||||
public fun <T : SerializerAwareValue<*>> track(
|
public open fun <T : SerializerAwareValue<*>> track(
|
||||||
value: T,
|
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 {
|
object Versions {
|
||||||
const val core = "1.3.3"
|
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 consoleGraphical = "0.0.7"
|
||||||
const val consoleTerminal = console
|
const val consoleTerminal = console
|
||||||
|
|
||||||
|
@ -63,6 +63,9 @@ object MiraiConsoleErrors {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create<KtTypeReference>(ERROR)
|
val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create<KtTypeReference>(ERROR)
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
val READ_ONLY_VALUE_CANNOT_BE_VAR = create<PsiElement>(ERROR)
|
||||||
|
|
||||||
@Suppress("ObjectPropertyName", "unused")
|
@Suppress("ObjectPropertyName", "unused")
|
||||||
@JvmField
|
@JvmField
|
||||||
@Deprecated("", level = DeprecationLevel.ERROR)
|
@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_PLUGIN_DESCRIPTION
|
||||||
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_VERSION_REQUIREMENT
|
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.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.RESTRICTED_CONSOLE_COMMAND_OWNER
|
||||||
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.UNSERIALIZABLE_TYPE
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.UNSERIALIZABLE_TYPE
|
||||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||||
@ -108,6 +109,11 @@ object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
|
|||||||
"插件不允许使用 ConsoleCommandOwner 构造指令, 请使用插件主类作为 CommandOwner",
|
"插件不允许使用 ConsoleCommandOwner 构造指令, 请使用插件主类作为 CommandOwner",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
put(
|
||||||
|
READ_ONLY_VALUE_CANNOT_BE_VAR,
|
||||||
|
"在 ReadOnlyPluginData 中不可定义 'var' by value",
|
||||||
|
)
|
||||||
|
|
||||||
// put(
|
// put(
|
||||||
// INAPPLICABLE_COMMAND_ANNOTATION,
|
// INAPPLICABLE_COMMAND_ANNOTATION,
|
||||||
// "''{0}'' 无法在顶层函数使用.",
|
// "''{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 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
|
// Resolve
|
||||||
|
@ -10,6 +10,6 @@
|
|||||||
package net.mamoe.mirai.console.gradle
|
package net.mamoe.mirai.console.gradle
|
||||||
|
|
||||||
internal object VersionConstants {
|
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
|
const val CORE_VERSION = "1.3.3" // value is written here automatically during build
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
plugins {
|
plugins {
|
||||||
kotlin("jvm") version "1.4.20"
|
kotlin("jvm") version "1.4.20"
|
||||||
kotlin("plugin.serialization") 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"
|
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.ConsoleCommandOwner
|
||||||
import net.mamoe.mirai.console.command.SimpleCommand
|
import net.mamoe.mirai.console.command.SimpleCommand
|
||||||
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
import net.mamoe.mirai.console.data.AutoSavePluginConfig
|
||||||
|
import net.mamoe.mirai.console.data.ReadOnlyPluginConfig
|
||||||
import net.mamoe.mirai.console.data.value
|
import net.mamoe.mirai.console.data.value
|
||||||
|
|
||||||
object MySimpleCommand0001 : SimpleCommand(
|
object MySimpleCommand0001 : SimpleCommand(
|
||||||
@ -26,6 +27,10 @@ object DataTest : AutoSavePluginConfig("data") {
|
|||||||
val pp by value(NoDefaultValue(1))
|
val pp by value(NoDefaultValue(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object DataTest1 : ReadOnlyPluginConfig("data") {
|
||||||
|
var pp by value<String>()
|
||||||
|
}
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class HasDefaultValue(
|
data class HasDefaultValue(
|
||||||
val x: Int = 0,
|
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 net.mamoe.mirai.console.intellij.resolve.resolveAllCallsWithElement
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
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.inspections.collections.isCalling
|
||||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
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.DeclarationChecker
|
||||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
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.kotlin.types.SimpleType
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||||
|
|
||||||
@ -41,24 +47,46 @@ class PluginDataValuesChecker : DeclarationChecker {
|
|||||||
&& t is SimpleType
|
&& t is SimpleType
|
||||||
}.forEach { (e, callExpr) ->
|
}.forEach { (e, callExpr) ->
|
||||||
val (_, type) = e
|
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 inspectionTarget = kotlin.run {
|
||||||
val fqName = type.fqName ?: return@run null
|
val fqName = type.fqName ?: return@run null
|
||||||
callExpr.typeArguments.find { it.typeReference?.isReferencing(fqName) == true }
|
callExpr.typeArguments.find { it.typeReference?.isReferencing(fqName) == true }
|
||||||
} ?: return@forEach
|
} ?: return
|
||||||
|
|
||||||
if (!classDescriptor.hasNoArgConstructor())
|
if (!classDescriptor.hasNoArgConstructor())
|
||||||
return@forEach context.report(MiraiConsoleErrors.NOT_CONSTRUCTABLE_TYPE.on(
|
return context.report(MiraiConsoleErrors.NOT_CONSTRUCTABLE_TYPE.on(
|
||||||
inspectionTarget,
|
inspectionTarget,
|
||||||
callExpr,
|
callExpr,
|
||||||
type.fqName?.asString().toString())
|
type.fqName?.asString().toString())
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!classDescriptor.hasAnnotation(SERIALIZABLE_FQ_NAME))
|
if (!classDescriptor.hasAnnotation(SERIALIZABLE_FQ_NAME))
|
||||||
return@forEach context.report(MiraiConsoleErrors.UNSERIALIZABLE_TYPE.on(
|
return context.report(MiraiConsoleErrors.UNSERIALIZABLE_TYPE.on(
|
||||||
inspectionTarget,
|
inspectionTarget,
|
||||||
classDescriptor
|
classDescriptor
|
||||||
))
|
))
|
||||||
|
Loading…
Reference in New Issue
Block a user