New inspection: RESTRICTED_CONSOLE_COMMAND_OWNER, close #216

This commit is contained in:
Him188 2020-11-29 16:12:49 +08:00
parent bc290f63bb
commit a66ecbf8c8
18 changed files with 96 additions and 63 deletions

View File

@ -16,6 +16,7 @@ import net.mamoe.mirai.console.command.descriptor.CommandSignature
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.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.permission.Permission
import net.mamoe.mirai.console.permission.PermissionId
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
@ -89,6 +90,7 @@ public interface Command {
* 指令拥有者.
* @see CommandOwner
*/
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER)
public val owner: CommandOwner
public companion object {

View File

@ -20,6 +20,7 @@ package net.mamoe.mirai.console.command
import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.internal.command.CommandReflector
import net.mamoe.mirai.console.internal.command.CompositeCommandSubCommandAnnotationResolver
import net.mamoe.mirai.console.permission.Permission
@ -82,7 +83,7 @@ import kotlin.annotation.AnnotationTarget.FUNCTION
* @see buildCommandArgumentContext
*/
public abstract class CompositeCommand(
owner: CommandOwner,
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER) owner: CommandOwner,
@ResolveContext(COMMAND_NAME) primaryName: String,
@ResolveContext(COMMAND_NAME) vararg secondaryNames: String,
description: String = "no description available",

View File

@ -15,6 +15,7 @@ import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.command.java.JRawCommand
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.internal.command.findOrCreateCommandPermission
import net.mamoe.mirai.console.internal.data.typeOf0
import net.mamoe.mirai.console.permission.Permission
@ -38,11 +39,14 @@ public abstract class RawCommand(
* 指令拥有者.
* @see CommandOwner
*/
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER)
public override val owner: CommandOwner,
/** 主指令名. */
@ResolveContext(COMMAND_NAME) public override val primaryName: String,
@ResolveContext(COMMAND_NAME)
public override val primaryName: String,
/** 次要指令名. */
@ResolveContext(COMMAND_NAME) public override vararg val secondaryNames: String,
@ResolveContext(COMMAND_NAME)
public override vararg val secondaryNames: String,
/** 用法说明, 用于发送给用户 */
public override val usage: String = "<no usages given>",
/** 指令描述, 用于显示在 [BuiltInCommands.HelpCommand] */

View File

@ -21,6 +21,7 @@ import net.mamoe.mirai.console.command.descriptor.*
import net.mamoe.mirai.console.command.java.JSimpleCommand
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.COMMAND_NAME
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.internal.command.CommandReflector
import net.mamoe.mirai.console.internal.command.IllegalCommandDeclarationException
import net.mamoe.mirai.console.internal.command.SimpleCommandSubCommandAnnotationResolver
@ -53,7 +54,7 @@ import kotlin.annotation.AnnotationTarget.VALUE_PARAMETER
* @see [CommandManager.executeCommand]
*/
public abstract class SimpleCommand(
owner: CommandOwner,
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER) owner: CommandOwner,
@ResolveContext(COMMAND_NAME) primaryName: String,
@ResolveContext(COMMAND_NAME) vararg secondaryNames: String,
description: String = "no description available",

View File

@ -17,6 +17,7 @@ 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
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.permission.Permission
/**
@ -71,7 +72,7 @@ import net.mamoe.mirai.console.permission.Permission
*/
public abstract class JCompositeCommand
@JvmOverloads constructor(
owner: CommandOwner,
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER) owner: CommandOwner,
@ResolveContext(COMMAND_NAME) primaryName: String,
@ResolveContext(COMMAND_NAME) vararg secondaryNames: String,
parentPermission: Permission = owner.parentPermission,

View File

@ -16,6 +16,7 @@ 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.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.internal.command.findOrCreateCommandPermission
import net.mamoe.mirai.console.permission.Permission
@ -51,9 +52,12 @@ public abstract class JRawCommand
* 指令拥有者.
* @see CommandOwner
*/
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER)
public override val owner: CommandOwner,
@ResolveContext(COMMAND_NAME) public override val primaryName: String,
@ResolveContext(COMMAND_NAME) public override vararg val secondaryNames: String,
@ResolveContext(COMMAND_NAME)
public override val primaryName: String,
@ResolveContext(COMMAND_NAME)
public override vararg val secondaryNames: String,
parentPermission: Permission = owner.parentPermission,
) : Command {
/** 用法说明, 用于发送给用户 */

View File

@ -16,6 +16,7 @@ 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.compiler.common.ResolveContext.Kind.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.permission.Permission
/**
@ -43,7 +44,7 @@ import net.mamoe.mirai.console.permission.Permission
* @see [CommandManager.executeCommand]
*/
public abstract class JSimpleCommand(
owner: CommandOwner,
@ResolveContext(RESTRICTED_CONSOLE_COMMAND_OWNER) owner: CommandOwner,
@ResolveContext(COMMAND_NAME) primaryName: String,
@ResolveContext(COMMAND_NAME) vararg secondaryNames: String,
basePermission: Permission,

View File

@ -59,7 +59,7 @@ public annotation class ResolveContext(
/**
* @see SemVersion.Companion.parseRangeRequirement
*/
VERSION_REQUIREMENT, // ILLEGAL_VERSION_REQUIREMENT // TODO
VERSION_REQUIREMENT, // ILLEGAL_VERSION_REQUIREMENT
/**
* @see Command.allNames
@ -87,5 +87,7 @@ public annotation class ResolveContext(
* @see PluginData.value
*/
RESTRICTED_NO_ARG_CONSTRUCTOR, // NOT_CONSTRUCTABLE_TYPE
RESTRICTED_CONSOLE_COMMAND_OWNER,
}
}

View File

@ -11,7 +11,7 @@
object Versions {
const val core = "1.3.3"
const val console = "1.1.0-dev-30"
const val console = "1.1.0-dev-32"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = console

View File

@ -15,10 +15,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1.create
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2.create
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.diagnostics.Severity.ERROR
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtTypeProjection
import org.jetbrains.kotlin.psi.KtTypeReference
import org.jetbrains.kotlin.psi.*
/**
* 如何增加一个错误:
@ -59,6 +56,9 @@ object MiraiConsoleErrors {
// @JvmField
// val INAPPLICABLE_COMMAND_ANNOTATION = create<PsiElement, String>(ERROR)
@JvmField
val RESTRICTED_CONSOLE_COMMAND_OWNER = create<KtElement>(ERROR)
@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.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.UNSERIALIZABLE_TYPE
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
@ -102,6 +103,11 @@ object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
"指令函数的接收者参数必须为 CommandSender 及其子类或无接收者.",
)
put(
RESTRICTED_CONSOLE_COMMAND_OWNER,
"插件不允许使用 ConsoleCommandOwner 构造指令, 请使用插件主类作为 CommandOwner",
)
// put(
// INAPPLICABLE_COMMAND_ANNOTATION,
// "''{0}'' 无法在顶层函数使用.",

View File

@ -33,6 +33,7 @@ val AUTO_SERVICE = FqName("com.google.auto.service.AutoService")
val COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.command.CompositeCommand.SubCommand")
val SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.command.SimpleCommand.Handler")
val COMMAND_SENDER_FQ_NAME = FqName("net.mamoe.mirai.console.command.CommandSender")
val CONSOLE_COMMAND_SENDER_FQ_NAME = FqName("net.mamoe.mirai.console.command.ConsoleCommandSender")
///////////////////////////////////////////////////////////////////////////
// Plugin
@ -70,7 +71,8 @@ enum class ResolveContextKind {
PERMISSION_NAME,
PERMISSION_ID,
RESTRICTED_NO_ARG_CONSTRUCTOR
RESTRICTED_NO_ARG_CONSTRUCTOR,
RESTRICTED_CONSOLE_COMMAND_OWNER,
;
companion object {

View File

@ -10,6 +10,6 @@
package net.mamoe.mirai.console.gradle
internal object VersionConstants {
const val CONSOLE_VERSION = "1.1.0-dev-30" // value is written here automatically during build
const val CONSOLE_VERSION = "1.1.0-dev-32" // value is written here automatically during build
const val CORE_VERSION = "1.3.3" // value is written here automatically during build
}

View File

@ -1,8 +1,7 @@
plugins {
kotlin("jvm") version "1.4.10"
kotlin("plugin.serialization") version "1.4.10"
kotlin("kapt") version "1.4.10"
id("com.github.johnrengelman.shadow") version "5.2.0"
kotlin("jvm") version "1.4.20"
kotlin("plugin.serialization") version "1.4.20"
id("net.mamoe.mirai-console") version "1.1.0-dev-32"
}
group = "org.example"
@ -12,32 +11,4 @@ repositories {
mavenLocal()
jcenter()
mavenCentral()
}
kotlin.sourceSets.all {
languageSettings.useExperimentalAnnotation("kotlin.RequiresOptIn")
}
dependencies {
compileOnly(kotlin("stdlib-jdk8"))
val core = "1.3.2"
val console = "1.0-RC-1"
compileOnly("net.mamoe:mirai-console:$console")
compileOnly("net.mamoe:mirai-core:$core")
val autoService = "1.0-rc7"
kapt("com.google.auto.service", "auto-service", autoService)
compileOnly("com.google.auto.service", "auto-service-annotations", autoService)
testImplementation("net.mamoe:mirai-console:$console")
testImplementation("net.mamoe:mirai-core:$core")
testImplementation("net.mamoe:mirai-console-terminal:$console")
testImplementation(kotlin("stdlib-jdk8"))
}
kotlin.target.compilations.all {
kotlinOptions.freeCompilerArgs += "-Xjvm-default=enable"
kotlinOptions.jvmTarget = "1.8"
}

View File

@ -1,2 +1,9 @@
rootProject.name = "test-project"
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
jcenter()
}
}

View File

@ -2,12 +2,13 @@ package org.example.myplugin
import kotlinx.serialization.Serializable
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.value
object MySimpleCommand000 : SimpleCommand(
MyPluginMain, "foo",
ConsoleCommandOwner, "foo",
description = "示例指令"
) {
@Handler

View File

@ -15,6 +15,8 @@ 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.RESTRICTED_CONSOLE_COMMAND_OWNER
import net.mamoe.mirai.console.compiler.common.resolve.COMMAND_SENDER_FQ_NAME
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
@ -24,9 +26,8 @@ import net.mamoe.mirai.console.intellij.util.RequirementHelper
import net.mamoe.mirai.console.intellij.util.RequirementParser
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
import java.util.*
@ -129,28 +130,44 @@ class ContextualParametersChecker : DeclarationChecker {
ILLEGAL_VERSION_REQUIREMENT.on(inspectionTarget, value, err.message ?: err.toString())
}
}
fun checkConsoleCommandOwner(context: DeclarationCheckerContext, inspectionTarget: KtElement, argument: ValueArgument): Diagnostic? {
val expr = argument.getArgumentExpression() ?: return null
if (expr is KtReferenceExpression) {
expr.getResolvedCall(context)?.isCalling(COMMAND_SENDER_FQ_NAME) ?: return null
}
return RESTRICTED_CONSOLE_COMMAND_OWNER.on(inspectionTarget)
}
}
fun interface ElementChecker {
operator fun invoke(declaration: KtElement, valueArgument: ValueArgument, value: String?): Diagnostic?
operator fun invoke(context: DeclarationCheckerContext, declaration: KtElement, valueArgument: ValueArgument, value: String?): Diagnostic?
}
private val stringCheckersMap: EnumMap<ResolveContextKind, ElementChecker> =
private val checkersMap: EnumMap<ResolveContextKind, ElementChecker> =
EnumMap<ResolveContextKind, ElementChecker>(ResolveContextKind::class.java).apply {
fun put(key: ResolveContextKind, value: KFunction2<KtElement, String, Diagnostic?>): ElementChecker? {
return put(key) { d, _, v ->
return put(key) { _, d, _, v ->
if (v != null) value(d, v)
else null
}
}
fun put(key: ResolveContextKind, value: KFunction2<KtElement, ValueArgument, Diagnostic?>): ElementChecker? {
return put(key) { d, v, _ ->
return put(key) { _, d, v, _ ->
value(d, v)
}
}
fun put(key: ResolveContextKind, value: (DeclarationCheckerContext, KtElement, ValueArgument) -> Diagnostic?): ElementChecker? {
return put(key) { c, d, v, _ ->
value(c, d, v)
}
}
put(ResolveContextKind.PLUGIN_NAME, ::checkPluginName)
put(ResolveContextKind.PLUGIN_ID, ::checkPluginId)
put(ResolveContextKind.SEMANTIC_VERSION, ::checkPluginVersion)
@ -159,6 +176,7 @@ class ContextualParametersChecker : DeclarationChecker {
put(ResolveContextKind.PERMISSION_NAMESPACE, ::checkPermissionNamespace)
put(ResolveContextKind.PERMISSION_ID, ::checkPermissionId)
put(ResolveContextKind.VERSION_REQUIREMENT, ::checkVersionRequirement)
put(ResolveContextKind.RESTRICTED_CONSOLE_COMMAND_OWNER, ::checkConsoleCommandOwner)
}
override fun check(
@ -166,6 +184,17 @@ class ContextualParametersChecker : DeclarationChecker {
descriptor: DeclarationDescriptor,
context: DeclarationCheckerContext,
) {
when (declaration) {
is KtClassOrObject -> {
}
is KtNamedFunction -> {
}
}
declaration.resolveAllCalls(context.bindingContext)
.asSequence()
.flatMap { call ->
@ -173,7 +202,7 @@ class ContextualParametersChecker : DeclarationChecker {
}
.mapNotNull { (p, a) ->
p.resolveContextKinds
?.map(stringCheckersMap::get)
?.map(checkersMap::get)
?.mapNotNull {
if (it == null) null else it to a
}
@ -182,11 +211,12 @@ class ContextualParametersChecker : DeclarationChecker {
.mapNotNull { (kind, argument) ->
Triple(kind, argument, argument.resolveStringConstantValues())
}
.forEach { (fn, argument, resolvedConstants) ->
if (resolvedConstants == null) {
fn(argument.asElement(), argument, null)?.let { context.report(it) }
.forEach { (fn, argument, resolvedConstantsSequence) ->
val resolvedConstants = resolvedConstantsSequence?.toList().orEmpty()
if (resolvedConstants.isEmpty()) {
fn(context, argument.asElement(), argument, null)?.let { context.report(it) }
} else for (resolvedConstant in resolvedConstants) {
fn(argument.asElement(), argument, resolvedConstant)?.let { context.report(it) }
fn(context, argument.asElement(), argument, resolvedConstant)?.let { context.report(it) }
}
}
return

View File

@ -123,8 +123,8 @@ fun KtAnnotated.hasAnnotation(fqName: FqName): Boolean =
fun KtDeclaration.resolveAllCalls(bindingContext: BindingContext): Sequence<ResolvedCall<*>> {
return allChildrenWithSelf
.filterIsInstance<KtCallExpression>()
.mapNotNull { it.calleeExpression?.getResolvedCall(bindingContext) }
.filterIsInstance<KtElement>()
.mapNotNull { it.getResolvedCall(bindingContext) }
}
fun KtDeclaration.resolveAllCallsWithElement(bindingContext: BindingContext): Sequence<Pair<ResolvedCall<out CallableDescriptor>, KtCallExpression>> {