mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Improve description checker: support compile-time constants
This commit is contained in:
parent
961bbfba53
commit
6a2ef97b41
BIN
tools/intellij-plugin/.images/ILLEGAL_PLUGIN_DESCRIPTION.png
Normal file
BIN
tools/intellij-plugin/.images/ILLEGAL_PLUGIN_DESCRIPTION.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -5,3 +5,13 @@ IntelliJ 平台的 Mirai Console 开发插件
|
||||
## 功能
|
||||
|
||||
### 诊断
|
||||
|
||||
#### ILLEGAL_PLUGIN_DESCRIPTION
|
||||
|
||||
[PluginDescriptionChecker.kt](src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/PluginDescriptionChecker.kt#L34)
|
||||
|
||||
- 使用 [ResolveContext](../../backend/mirai-console/src/main/kotlin/net/mamoe/mirai/console/compiler/common/ResolveContext.kt)
|
||||
- 检测 Plugin Id, Plugin Name, Plugin Version 的合法性. 并在非法时提示正确的语法.
|
||||
- 支持编译期常量
|
||||
|
||||
![ILLEGAL_PLUGIN_DESCRIPTION](.images/ILLEGAL_PLUGIN_DESCRIPTION.png)
|
||||
|
@ -3,9 +3,25 @@ package org.example.myplugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
|
||||
val T = "scas" + "pp" // 编译期常量
|
||||
|
||||
object MyPluginMain : KotlinPlugin(
|
||||
JvmPluginDescription(
|
||||
"net.mamoe.main",
|
||||
T,
|
||||
"0.1.0",
|
||||
) {
|
||||
name(".")
|
||||
id("")
|
||||
}
|
||||
) {
|
||||
fun test() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
object MyPluginMain2 : KotlinPlugin(
|
||||
JvmPluginDescription(
|
||||
"",
|
||||
"0.1.0",
|
||||
) {
|
||||
name(".")
|
||||
|
@ -13,7 +13,7 @@ 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
|
||||
import net.mamoe.mirai.console.intellij.resolve.findChild
|
||||
import net.mamoe.mirai.console.intellij.resolve.resolveStringConstantValue
|
||||
import net.mamoe.mirai.console.intellij.resolve.valueParameters
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
@ -36,28 +36,30 @@ class PluginDescriptionChecker : DeclarationChecker {
|
||||
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")
|
||||
|
||||
private const val syntax = """类似于 "net.mamoe.mirai.example-plugin", 其中 "net.mamoe.mirai" 为 groupId, "example-plugin" 为插件名. """
|
||||
|
||||
fun checkPluginId(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
||||
if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin id cannot be blank")
|
||||
if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 不能为空. \n插件 Id$syntax")
|
||||
if (value.none { it == '.' }) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget,
|
||||
"'$value' is illegal. Plugin id must consist of both domain and name. ")
|
||||
"插件 Id '$value' 无效. 插件 Id 必须同时包含 groupId 和插件名称. $syntax")
|
||||
|
||||
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}'.")
|
||||
return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 无效. 正确的插件 Id 应该满足正则表达式 '${ID_REGEX.pattern}', \n$syntax")
|
||||
}
|
||||
|
||||
FORBIDDEN_ID_NAMES.firstOrNull { it == lowercaseId }?.let { illegal ->
|
||||
return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin id contains illegal word: '$illegal'.")
|
||||
return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件 Id. 确保插件 Id 不完全是这个名称.")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun checkPluginName(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
||||
if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "Plugin name cannot be blank")
|
||||
if (value.isBlank()) return MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件名不能为空.")
|
||||
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 MiraiConsoleErrors.ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件名. 确保插件名不完全是这个名称.")
|
||||
}
|
||||
return null
|
||||
}
|
||||
@ -90,8 +92,7 @@ class PluginDescriptionChecker : DeclarationChecker {
|
||||
context: DeclarationCheckerContext,
|
||||
) {
|
||||
val call = expression.calleeExpression.getResolvedCallOrResolveToCall(context) ?: return // unresolved
|
||||
call.valueArgumentsByIndex?.forEach { resolvedValueArgument ->
|
||||
for ((parameter, argument) in call.valueParameters.zip(resolvedValueArgument.arguments)) {
|
||||
for ((parameter, argument) in call.valueParameters.zip(call.valueArgumentsByIndex?.mapNotNull { it.arguments.firstOrNull() }.orEmpty())) {
|
||||
val parameterContextKind = parameter.resolveContextKind
|
||||
if (checkersMap.containsKey(parameterContextKind)) {
|
||||
val value = argument.getArgumentExpression()
|
||||
@ -102,7 +103,6 @@ class PluginDescriptionChecker : DeclarationChecker {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun check(
|
||||
declaration: KtDeclaration,
|
||||
@ -113,9 +113,9 @@ class PluginDescriptionChecker : DeclarationChecker {
|
||||
when (declaration) {
|
||||
is KtObjectDeclaration -> {
|
||||
// check super type constructor
|
||||
val superTypeCallEntry = declaration.findChildren<KtSuperTypeList>()?.findChildren<KtSuperTypeCallEntry>() ?: return
|
||||
val superTypeCallEntry = declaration.findChild<KtSuperTypeList>()?.findChild<KtSuperTypeCallEntry>() ?: return
|
||||
// val constructorCall = superTypeCallEntry.findChildren<KtConstructorCalleeExpression>()?.resolveToCall() ?: return
|
||||
val valueArgumentList = superTypeCallEntry.findChildren<KtValueArgumentList>() ?: return
|
||||
val valueArgumentList = superTypeCallEntry.findChild<KtValueArgumentList>() ?: return
|
||||
valueArgumentList.arguments.asSequence().mapNotNull(KtValueArgument::getArgumentExpression).forEach {
|
||||
if (it.shouldPerformCheck()) {
|
||||
check(it as KtCallExpression, context)
|
||||
@ -126,10 +126,10 @@ class PluginDescriptionChecker : DeclarationChecker {
|
||||
is KtClassOrObject -> {
|
||||
// check constructor
|
||||
|
||||
val superTypeCallEntry = declaration.findChildren<KtSuperTypeList>()?.findChildren<KtSuperTypeCallEntry>() ?: return
|
||||
val superTypeCallEntry = declaration.findChild<KtSuperTypeList>()?.findChild<KtSuperTypeCallEntry>() ?: return
|
||||
|
||||
val constructorCall = superTypeCallEntry.findChildren<KtConstructorCalleeExpression>()?.resolveToCall() ?: return
|
||||
val valueArgumentList = superTypeCallEntry.findChildren<KtValueArgumentList>() ?: return
|
||||
val constructorCall = superTypeCallEntry.findChild<KtConstructorCalleeExpression>()?.resolveToCall() ?: return
|
||||
val valueArgumentList = superTypeCallEntry.findChild<KtValueArgumentList>() ?: return
|
||||
|
||||
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.console.intellij.resolve
|
||||
|
||||
import com.intellij.psi.PsiDeclarationStatement
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import net.mamoe.mirai.console.compiler.common.castOrNull
|
||||
@ -19,6 +20,8 @@ import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.resolve
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@ -28,6 +31,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.constants.StringValue
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||
|
||||
|
||||
/**
|
||||
@ -98,7 +102,7 @@ val PsiElement.allChildrenFlat: Sequence<PsiElement>
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified E : PsiElement> PsiElement.findChildren(): E? = this.children.find { it is E } as E?
|
||||
inline fun <reified E> PsiElement.findChild(): E? = this.children.find { it is E } as E?
|
||||
|
||||
fun KtElement?.getResolvedCallOrResolveToCall(
|
||||
context: BindingContext,
|
||||
@ -111,10 +115,23 @@ val ResolvedCall<out CallableDescriptor>.valueParameters: List<ValueParameterDes
|
||||
|
||||
fun KtExpression.resolveStringConstantValue(bindingContext: BindingContext): String? {
|
||||
when (this) {
|
||||
is KtNameReferenceExpression -> {
|
||||
when (val reference = references.firstIsInstance<KtSimpleNameReference>().resolve()) {
|
||||
is KtDeclaration -> {
|
||||
val descriptor = reference.descriptor.castOrNull<VariableDescriptor>() ?: return null
|
||||
val compileTimeConstant = descriptor.compileTimeInitializer ?: return null
|
||||
return compileTimeConstant.castOrNull<StringValue>()?.value
|
||||
}
|
||||
is PsiDeclarationStatement -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
is KtStringTemplateExpression -> {
|
||||
if (hasInterpolation()) return null
|
||||
return entries.joinToString("") { it.text }
|
||||
}
|
||||
/*
|
||||
is KtCallExpression -> {
|
||||
val callee = this.calleeExpression?.getResolvedCallOrResolveToCall(bindingContext)?.resultingDescriptor
|
||||
if (callee is VariableDescriptor) {
|
||||
@ -122,7 +139,7 @@ fun KtExpression.resolveStringConstantValue(bindingContext: BindingContext): Str
|
||||
return compileTimeConstant.castOrNull<StringValue>()?.value
|
||||
}
|
||||
return null
|
||||
}
|
||||
}*/
|
||||
is KtConstantExpression -> {
|
||||
// TODO: 2020/9/18 KtExpression.resolveStringConstantValue: KtConstantExpression
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user