Support checking plugin name and plugin id (ILLEGAL_PLUGIN_DESCRIPTION)

This commit is contained in:
Him188 2020-09-18 01:49:08 +08:00
parent 3fc2d7f5c1
commit 5dbf596582
9 changed files with 57 additions and 36 deletions

View File

@ -17,11 +17,13 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
* 标记一个参数的语境类型, 用于帮助编译器和 IntelliJ 插件进行语境推断.
*/
@ConsoleExperimentalApi
@Target(AnnotationTarget.VALUE_PARAMETER,
@Target(
AnnotationTarget.VALUE_PARAMETER,
AnnotationTarget.PROPERTY,
AnnotationTarget.FIELD,
AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
//AnnotationTarget.EXPRESSION
)
@Retention(AnnotationRetention.BINARY)
public annotation class ResolveContext(
val kind: Kind,
) {

View File

@ -10,6 +10,8 @@
package net.mamoe.mirai.console.plugin.description
import com.vdurmont.semver4j.Semver
import net.mamoe.mirai.console.compiler.common.ResolveContext
import net.mamoe.mirai.console.compiler.common.ResolveContext.Kind.*
import net.mamoe.mirai.console.plugin.Plugin
@ -46,6 +48,7 @@ public interface PluginDescription {
* @see ID_REGEX
* @see FORBIDDEN_ID_NAMES
*/
@ResolveContext(PLUGIN_ID)
public val id: String
/**
@ -60,6 +63,7 @@ public interface PluginDescription {
*
* @see FORBIDDEN_ID_NAMES
*/
@ResolveContext(PLUGIN_NAME)
public val name: String
/**
@ -88,6 +92,7 @@ public interface PluginDescription {
*
* @see Semver 语义化版本. 允许 [宽松][Semver.SemverType.LOOSE] 类型版本.
*/
@ResolveContext(PLUGIN_VERSION)
public val version: Semver
/**

View File

@ -9,7 +9,7 @@
object Versions {
const val core = "1.3.0"
const val console = "1.0-RC-dev-1"
const val console = "1.0-RC-dev-2"
const val consoleGraphical = "0.0.7"
const val consoleTerminal = "0.1.0"
const val consolePure = console

View File

@ -16,8 +16,7 @@ import org.jetbrains.kotlin.diagnostics.Errors;
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
public interface MiraiConsoleErrors {
DiagnosticFactory1<PsiElement, String> ILLEGAL_PLUGIN_ID = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> ILLEGAL_PLUGIN_NAME = DiagnosticFactory1.create(ERROR);
DiagnosticFactory1<PsiElement, String> ILLEGAL_PLUGIN_DESCRIPTION = DiagnosticFactory1.create(ERROR);
@Deprecated
Object _init = new Object() {

View File

@ -9,8 +9,7 @@
package net.mamoe.mirai.console.compiler.common.diagnostics
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PLUGIN_ID
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PLUGIN_NAME
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
@ -18,13 +17,8 @@ import org.jetbrains.kotlin.diagnostics.rendering.Renderers
object MiraiConsoleErrorsRendering : DefaultErrorMessages.Extension {
private val MAP = DiagnosticFactoryToRendererMap("MiraiConsole").apply {
put(
ILLEGAL_PLUGIN_ID,
"Illegal plugin id: '{0}'",
Renderers.STRING
)
put(
ILLEGAL_PLUGIN_NAME,
"Illegal plugin name: '{0}'",
ILLEGAL_PLUGIN_DESCRIPTION,
"{0}",
Renderers.STRING
)
}

View File

@ -10,6 +10,7 @@
package net.mamoe.mirai.console.compiler.common.resolve
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.EnumValue
@ -55,6 +56,6 @@ fun Annotated.isResolveContext(kind: ResolveContextKind) = this.resolveContextKi
val Annotated.resolveContextKind: ResolveContextKind?
get() {
val ann = this.findAnnotation(RESOLVE_CONTEXT_FQ_NAME) ?: return null
val (_, enumEntryName) = ann.allValueArguments.castOrNull<EnumValue>()?.value ?: return null // undetermined kind
val (_, enumEntryName) = ann.allValueArguments.firstValue().castOrNull<EnumValue>()?.value ?: return null // undetermined kind
return ResolveContextKind.valueOf(enumEntryName.asString())
}

View File

@ -22,7 +22,7 @@ dependencies {
compileOnly(kotlin("stdlib-jdk8"))
val core = "1.3.0"
val console = "1.0-RC-dev-1"
val console = "1.0-RC-dev-2"
compileOnly("net.mamoe:mirai-console:$console")
compileOnly("net.mamoe:mirai-core:$core")

View File

@ -5,15 +5,14 @@ import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
object MyPluginMain : KotlinPlugin(
JvmPluginDescription(
"org.example.example-plugin",
"0.1.0"
)
"net.mamoe.main",
"0.1.0",
) {
name(".")
id("")
}
) {
fun test() {
}
}
class PM : KotlinPlugin(
)
}

View File

@ -10,6 +10,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.intellij.resolve.findChildren
@ -32,15 +33,36 @@ import kotlin.contracts.contract
*/
class PluginDescriptionChecker : DeclarationChecker {
companion object {
fun checkPluginName(declaration: KtDeclaration, value: String): Diagnostic? {
return null // TODO: 2020/9/18 checkPluginName
private val ID_REGEX: Regex = Regex("""([a-zA-Z]+(?:\.[a-zA-Z0-9]+)*)\.([a-zA-Z]+(?:-[a-zA-Z0-9]+)*)""")
private val FORBIDDEN_ID_NAMES: Array<String> = arrayOf("main", "console", "plugin", "config", "data")
fun checkPluginId(inspectionTarget: PsiElement, value: String): Diagnostic? {
if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin id cannot be blank")
if (value.none { it == '.' }) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget,
"'$value' is illegal. Plugin id must consist of both domain and name. ")
val lowercaseId = value.toLowerCase()
if (ID_REGEX.matchEntire(value) == null) {
return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin does not match regex '${ID_REGEX.pattern}'.")
}
FORBIDDEN_ID_NAMES.firstOrNull { it == lowercaseId }?.let { illegal ->
return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin id contains illegal word: '$illegal'.")
}
return null
}
fun checkPluginId(declaration: KtDeclaration, value: String): Diagnostic? {
return null // TODO: 2020/9/18 checkPluginId
fun checkPluginName(inspectionTarget: PsiElement, value: String): Diagnostic? {
if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin name cannot be blank")
val lowercaseName = value.toLowerCase()
FORBIDDEN_ID_NAMES.firstOrNull { it == lowercaseName }?.let { illegal ->
return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin name is illegal: '$illegal'.")
}
return null
}
fun checkPluginVersion(declaration: KtDeclaration, value: String): Diagnostic? {
fun checkPluginVersion(inspectionTarget: PsiElement, value: String): Diagnostic? {
return null // TODO: 2020/9/18 checkPluginVersion
}
}
@ -56,15 +78,14 @@ class PluginDescriptionChecker : DeclarationChecker {
}
}
private val checkersMap: EnumMap<ResolveContextKind, (declaration: KtDeclaration, value: String) -> Diagnostic?> =
EnumMap<ResolveContextKind, (declaration: KtDeclaration, value: String) -> Diagnostic?>(ResolveContextKind::class.java).apply {
private val checkersMap: EnumMap<ResolveContextKind, (declaration: PsiElement, value: String) -> Diagnostic?> =
EnumMap<ResolveContextKind, (declaration: PsiElement, value: String) -> Diagnostic?>(ResolveContextKind::class.java).apply {
put(ResolveContextKind.PLUGIN_NAME, ::checkPluginName)
put(ResolveContextKind.PLUGIN_ID, ::checkPluginId)
put(ResolveContextKind.PLUGIN_VERSION, ::checkPluginVersion)
}
fun check(
declaration: KtDeclaration,
expression: KtCallExpression,
context: DeclarationCheckerContext,
) {
@ -76,7 +97,7 @@ class PluginDescriptionChecker : DeclarationChecker {
val value = argument.getArgumentExpression()
?.resolveStringConstantValue(context.bindingContext) ?: continue
for ((kind, fn) in checkersMap) {
if (parameterContextKind == kind) fn(declaration, value)?.let { context.report(it) }
if (parameterContextKind == kind) fn(argument.asElement(), value)?.let { context.report(it) }
}
}
}
@ -93,11 +114,11 @@ class PluginDescriptionChecker : DeclarationChecker {
is KtObjectDeclaration -> {
// check super type constructor
val superTypeCallEntry = declaration.findChildren<KtSuperTypeList>()?.findChildren<KtSuperTypeCallEntry>() ?: return
val constructorCall = superTypeCallEntry.findChildren<KtConstructorCalleeExpression>()?.resolveToCall() ?: return
// val constructorCall = superTypeCallEntry.findChildren<KtConstructorCalleeExpression>()?.resolveToCall() ?: return
val valueArgumentList = superTypeCallEntry.findChildren<KtValueArgumentList>() ?: return
valueArgumentList.arguments.asSequence().mapNotNull(KtValueArgument::getArgumentExpression).forEach {
if (it.shouldPerformCheck()) {
check(declaration, it as KtCallExpression, context)
check(it as KtCallExpression, context)
}
}