Support multiple ResolveContext kinds in single declaration

This commit is contained in:
Him188 2020-10-25 13:16:27 +08:00
parent 0007a97d66
commit 717c908ccf
16 changed files with 93 additions and 56 deletions

View File

@ -11,7 +11,6 @@
package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.command.java.JRawCommand
import net.mamoe.mirai.console.compiler.common.ResolveContext
@ -51,6 +50,7 @@ public abstract class RawCommand(
/** 指令父权限 */
parentPermission: Permission = owner.parentPermission,
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
@OptIn(ExperimentalCommandDescriptors::class)
public override val prefixOptional: Boolean = false,
) : Command {
public override val permission: Permission by lazy { createOrFindCommandPermission(parentPermission) }

View File

@ -13,6 +13,7 @@ import net.mamoe.mirai.console.command.BuiltInCommands
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.command.CompositeCommand
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.command.descriptor.buildCommandArgumentContext
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
@ -85,6 +86,7 @@ public abstract class JCompositeCommand
protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
@ExperimentalCommandDescriptors
public final override var prefixOptional: Boolean = false
protected set

View File

@ -13,6 +13,7 @@ import net.mamoe.mirai.console.command.BuiltInCommands
import net.mamoe.mirai.console.command.Command
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.internal.command.createOrFindCommandPermission
@ -70,6 +71,7 @@ public abstract class JRawCommand
protected set
/** 为 `true` 时表示 [指令前缀][CommandManager.commandPrefix] 可选 */
@ExperimentalCommandDescriptors
public final override var prefixOptional: Boolean = false
protected set
}

View File

@ -10,10 +10,10 @@
package net.mamoe.mirai.console.command.java
import net.mamoe.mirai.console.command.CommandManager
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.executeCommand
import net.mamoe.mirai.console.command.CommandOwner
import net.mamoe.mirai.console.command.SimpleCommand
import net.mamoe.mirai.console.command.descriptor.CommandArgumentContext
import net.mamoe.mirai.console.command.descriptor.ExperimentalCommandDescriptors
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.permission.Permission
@ -52,9 +52,10 @@ public abstract class JSimpleCommand(
) : SimpleCommand(owner, primaryName, secondaryNames = secondaryNames, parentPermission = basePermission) {
public override var description: String = super.description
protected set
public override var permission: Permission = super.permission
protected set
@ExperimentalCommandDescriptors
public override var prefixOptional: Boolean = super.prefixOptional
protected set
public override var context: CommandArgumentContext = super.context

View File

@ -11,22 +11,21 @@
package net.mamoe.mirai.console.compiler.common
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
import net.mamoe.mirai.console.command.Command
import net.mamoe.mirai.console.data.PluginData
import net.mamoe.mirai.console.data.value
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.plugin.description.PluginDescription
import net.mamoe.mirai.console.util.SemVersion
import kotlin.annotation.AnnotationTarget.*
/**
* 标记一个参数的语境类型, 用于帮助编译器和 IntelliJ 插件进行语境推断.
*/
@ConsoleExperimentalApi
@Target(
VALUE_PARAMETER,
PROPERTY, FIELD,
FUNCTION,
TYPE, TYPE_PARAMETER
)
@Target(VALUE_PARAMETER, PROPERTY, FIELD, FUNCTION, TYPE, TYPE_PARAMETER)
@Retention(AnnotationRetention.BINARY)
public annotation class ResolveContext(
val kind: Kind,
vararg val kinds: Kind,
) {
/**
* 元素数量可能在任意时间被改动
@ -36,18 +35,57 @@ public annotation class ResolveContext(
// ConstantKind
///////////////////////////////////////////////////////////////////////////
PLUGIN_ID, // ILLEGAL_PLUGIN_DESCRIPTION
PLUGIN_NAME, // ILLEGAL_PLUGIN_DESCRIPTION
PLUGIN_VERSION, // ILLEGAL_PLUGIN_DESCRIPTION
/*
* WARNING: IF YOU CHANGE NAMES HERE,
* YOU SHOULD ALSO CHANGE THEIR COUNTERPARTS AT net.mamoe.mirai.console.compiler.common.resolve.ResolveContextKind
*/
/**
* @see PluginDescription.id
*/
PLUGIN_ID, // ILLEGAL_PLUGIN_DESCRIPTION
/**
* @see PluginDescription.name
*/
PLUGIN_NAME, // ILLEGAL_PLUGIN_DESCRIPTION
/**
* @see PluginDescription.version
* @see SemVersion.Companion.invoke
*/
SEMANTIC_VERSION, // ILLEGAL_PLUGIN_DESCRIPTION
/**
* @see SemVersion.Companion.parseRangeRequirement
*/
VERSION_REQUIREMENT, // ILLEGAL_VERSION_REQUIREMENT // TODO
/**
* @see Command.allNames
*/
COMMAND_NAME, // ILLEGAL_COMMAND_NAME
PERMISSION_NAMESPACE, // ILLEGAL_COMMAND_NAMESPACE
PERMISSION_NAME, // ILLEGAL_COMMAND_NAME
PERMISSION_ID, // ILLEGAL_COMMAND_ID
/**
* @see PermissionId.name
*/
PERMISSION_NAMESPACE, // ILLEGAL_PERMISSION_NAMESPACE
/**
* @see PermissionId.name
*/
PERMISSION_NAME, // ILLEGAL_PERMISSION_NAME
/**
* @see PermissionId.parseFromString
*/
PERMISSION_ID, // ILLEGAL_PERMISSION_ID
/**
* 标注一个泛型, 要求这个泛型必须拥有一个公开无参 (或所有参数都可选) 构造器.
*
* @see PluginData.value
*/
RESTRICTED_NO_ARG_CONSTRUCTOR, // NOT_CONSTRUCTABLE_TYPE
}
}

View File

@ -48,7 +48,7 @@ internal abstract class JvmPluginInternal(
final override val parentPermission: Permission by lazy {
PermissionService.INSTANCE.register(
PermissionService.INSTANCE.allocatePermissionIdForPlugin(this, "*", PermissionService.PluginPermissionIdRequestType.PLUGIN_ROOT_PERMISSION),
PermissionService.INSTANCE.allocatePermissionIdForPlugin(this, "*"),
"The base permission"
)
}

View File

@ -97,8 +97,7 @@ public interface PermissionService<P : Permission> {
public fun allocatePermissionIdForPlugin(
plugin: Plugin,
@ResolveContext(COMMAND_NAME) permissionName: String,
reason: PluginPermissionIdRequestType
): PermissionId = allocatePermissionIdForPluginDefaultImplement(plugin, permissionName, reason)
): PermissionId = allocatePermissionIdForPluginDefaultImplement(plugin, permissionName)
///////////////////////////////////////////////////////////////////////////
@ -127,16 +126,6 @@ public interface PermissionService<P : Permission> {
@Throws(UnsupportedOperationException::class)
public fun cancel(permitteeId: PermitteeId, permission: P, recursive: Boolean)
/** [Plugin] 尝试分配的 [PermissionId] 来源 */
@ConsoleExperimentalApi
public enum class PluginPermissionIdRequestType {
/** For [Plugin.parentPermission] */
PLUGIN_ROOT_PERMISSION,
/** For [Plugin.permissionId] */
NORMAL
}
public companion object {
internal var instanceField: PermissionService<*>? = null
@ -155,7 +144,6 @@ public interface PermissionService<P : Permission> {
internal fun PermissionService<*>.allocatePermissionIdForPluginDefaultImplement(
plugin: Plugin,
@ResolveContext(COMMAND_NAME) permissionName: String,
reason: PluginPermissionIdRequestType
) = PermissionId(
plugin.description.id.toLowerCase(),
permissionName.toLowerCase()

View File

@ -92,7 +92,7 @@ public interface PluginDescription {
*
* @see Semver 语义化版本. 允许 [宽松][Semver.SemverType.LOOSE] 类型版本.
*/
@ResolveContext(PLUGIN_VERSION)
@ResolveContext(SEMANTIC_VERSION)
public val version: SemVersion
/**

View File

@ -39,7 +39,7 @@ public abstract class AbstractJvmPlugin @JvmOverloads constructor(
public final override val loader: JvmPluginLoader get() = super<JvmPluginInternal>.loader
public final override fun permissionId(name: String): PermissionId =
PermissionService.INSTANCE.allocatePermissionIdForPlugin(this, name, PermissionService.PluginPermissionIdRequestType.NORMAL)
PermissionService.INSTANCE.allocatePermissionIdForPlugin(this, name)
/**
* 重载 [PluginData]

View File

@ -43,7 +43,7 @@ public interface JvmPluginDescription : PluginDescription {
/**
* @see [PluginDescription.version]
*/
@ResolveContext(PLUGIN_VERSION) version: String,
@ResolveContext(SEMANTIC_VERSION) version: String,
/**
* @see [PluginDescription.name]
*/
@ -102,7 +102,7 @@ public class JvmPluginDescriptionBuilder(
) {
public constructor(
@ResolveContext(PLUGIN_ID) id: String,
@ResolveContext(PLUGIN_VERSION) version: String,
@ResolveContext(SEMANTIC_VERSION) version: String,
) : this(id, SemVersion(version))
private var name: String = id
@ -115,7 +115,7 @@ public class JvmPluginDescriptionBuilder(
apply { this.name = value.trim() }
@ILoveKuriyamaMiraiForever
public fun version(@ResolveContext(PLUGIN_VERSION) value: String): JvmPluginDescriptionBuilder =
public fun version(@ResolveContext(SEMANTIC_VERSION) value: String): JvmPluginDescriptionBuilder =
apply { this.version = SemVersion(value) }
@ILoveKuriyamaMiraiForever

View File

@ -21,7 +21,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
import kotlinx.serialization.builtins.serializer
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.PLUGIN_VERSION
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.SEMANTIC_VERSION
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.VERSION_REQUIREMENT
import net.mamoe.mirai.console.internal.data.map
import net.mamoe.mirai.console.internal.util.semver.SemVersionInternal
@ -115,7 +115,7 @@ internal constructor(
@JvmStatic
@JvmName("parse")
@Throws(IllegalArgumentException::class, NumberFormatException::class)
public operator fun invoke(@ResolveContext(PLUGIN_VERSION) version: String): SemVersion = SemVersionInternal.parse(version)
public operator fun invoke(@ResolveContext(SEMANTIC_VERSION) version: String): SemVersion = SemVersionInternal.parse(version)
/**
* 解析一条依赖需求描述, 在无法解析的时候抛出 [IllegalArgumentException]
@ -155,7 +155,7 @@ internal constructor(
/** @see [Requirement.test] */
@JvmStatic
@Throws(IllegalArgumentException::class, NumberFormatException::class)
public fun Requirement.test(@ResolveContext(PLUGIN_VERSION) version: String): Boolean = test(invoke(version))
public fun Requirement.test(@ResolveContext(SEMANTIC_VERSION) version: String): Boolean = test(invoke(version))
/**
* 当满足 [requirement] 时返回 true, 否则返回 false
@ -178,7 +178,7 @@ internal constructor(
/** for Kotlin only */
@JvmStatic
@JvmSynthetic
public operator fun Requirement.contains(@ResolveContext(PLUGIN_VERSION) version: String): Boolean = test(version)
public operator fun Requirement.contains(@ResolveContext(SEMANTIC_VERSION) version: String): Boolean = test(version)
}
@Transient

View File

@ -13,6 +13,7 @@ import net.mamoe.mirai.console.compiler.common.castOrNull
import net.mamoe.mirai.console.compiler.common.firstValue
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.EnumValue
///////////////////////////////////////////////////////////////////////////
@ -73,11 +74,14 @@ enum class ResolveContextKind {
}
}
fun Annotated.isResolveContext(kind: ResolveContextKind) = this.resolveContextKind == kind
val Annotated.resolveContextKind: ResolveContextKind?
val Annotated.resolveContextKinds: List<ResolveContextKind>?
get() {
val ann = this.findAnnotation(RESOLVE_CONTEXT_FQ_NAME) ?: return null
val (_, enumEntryName) = ann.allValueArguments.firstValue().castOrNull<EnumValue>()?.value ?: return null // undetermined kind
return ResolveContextKind.valueOf(enumEntryName.asString())
val kinds =
ann.allValueArguments.firstValue().castOrNull<ArrayValue>()?.value?.mapNotNull { it.castOrNull<EnumValue>()?.value }
?: return null // undetermined kind
return kinds.map { (_, enumEntryName) ->
ResolveContextKind.valueOf(enumEntryName.asString())
}
}

View File

@ -12,7 +12,7 @@ package net.mamoe.mirai.console.intellij.diagnostics
import com.intellij.psi.PsiElement
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.*
import net.mamoe.mirai.console.compiler.common.resolve.ResolveContextKind
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.resolveAllCalls
import net.mamoe.mirai.console.intellij.resolve.resolveStringConstantValues
import net.mamoe.mirai.console.intellij.resolve.valueParametersWithArguments
@ -134,12 +134,18 @@ class ContextualParametersChecker : DeclarationChecker {
context: DeclarationCheckerContext,
) {
declaration.resolveAllCalls(context.bindingContext)
.asSequence()
.flatMap { call ->
call.valueParametersWithArguments().asSequence()
}
.mapNotNull { (p, a) ->
p.resolveContextKind?.let(checkersMap::get)?.let { it to a }
p.resolveContextKinds
?.map(checkersMap::get)
?.mapNotNull {
if (it == null) null else it to a
}
}
.flatMap { it.asSequence() }
.mapNotNull { (kind, argument) ->
argument.resolveStringConstantValues()?.let { const ->
Triple(kind, argument, const)

View File

@ -31,12 +31,12 @@ class PluginDataValuesChecker : DeclarationChecker {
declaration.resolveAllCallsWithElement(bindingContext)
.filter { (call) -> call.isCalling(PLUGIN_DATA_VALUE_FUNCTIONS_FQ_FQ_NAME) }
.filter { (call) ->
call.resultingDescriptor.resolveContextKind == ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR
call.resultingDescriptor.resolveContextKinds?.contains(ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR) == true
}.flatMap { (call, element) ->
call.typeArguments.entries.associateWith { element }.asSequence()
}.filter { (e, _) ->
val (p, t) = e
(p.isReified || p.resolveContextKind == ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR)
(p.isReified || p.resolveContextKinds?.contains(ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR) == true)
&& t is SimpleType
}.forEach { (e, callExpr) ->
val (_, type) = e

View File

@ -15,7 +15,6 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
fun DeclarationCheckerContext.report(diagnostic: Diagnostic) {
return this.trace.report(diagnostic)
@ -25,7 +24,6 @@ val DeclarationCheckerContext.bindingContext get() = this.trace.bindingContext
fun KtElement?.getResolvedCallOrResolveToCall(
context: DeclarationCheckerContext,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL,
): ResolvedCall<out CallableDescriptor>? {
return this.getResolvedCallOrResolveToCall(context.bindingContext, bodyResolveMode)
return this.getResolvedCallOrResolveToCall(context.bindingContext)
}

View File

@ -32,7 +32,6 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.ArrayValue
import org.jetbrains.kotlin.resolve.constants.ConstantValue
import org.jetbrains.kotlin.resolve.constants.StringValue
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
@ -125,9 +124,8 @@ inline fun <reified E> PsiElement.findChild(): E? = this.children.find { it is E
fun KtElement?.getResolvedCallOrResolveToCall(
context: BindingContext,
bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL,
): ResolvedCall<out CallableDescriptor>? {
return this?.getCall(context)?.getResolvedCall(context)// ?: this?.resolveToCall(bodyResolveMode)
return this?.getCall(context)?.getResolvedCall(context)
}
val ResolvedCall<out CallableDescriptor>.valueParameters: List<ValueParameterDescriptor> get() = this.resultingDescriptor.valueParameters