|
|
@@ -9,6 +9,7 @@
|
|
|
|
|
|
package net.mamoe.mirai.console.intellij.diagnostics
|
|
|
|
|
|
+import com.intellij.psi.PsiElement
|
|
|
import net.mamoe.mirai.console.compiler.common.CheckerConstants
|
|
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_COMMAND_NAME
|
|
|
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.ILLEGAL_PERMISSION_ID
|
|
|
@@ -20,47 +21,57 @@ import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors.RE
|
|
|
import net.mamoe.mirai.console.compiler.common.resolve.CONSOLE_COMMAND_OWNER_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.bodyCalls
|
|
|
+import net.mamoe.mirai.console.intellij.resolve.getResolvedCall
|
|
|
import net.mamoe.mirai.console.intellij.resolve.resolveStringConstantValues
|
|
|
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.descriptors.ValueParameterDescriptor
|
|
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
|
|
import org.jetbrains.kotlin.idea.inspections.collections.isCalling
|
|
|
-import org.jetbrains.kotlin.psi.KtDeclaration
|
|
|
import org.jetbrains.kotlin.psi.KtElement
|
|
|
import org.jetbrains.kotlin.psi.KtReferenceExpression
|
|
|
import org.jetbrains.kotlin.psi.ValueArgument
|
|
|
-import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
|
|
-import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
|
|
+import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
|
|
+import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
|
|
+import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
|
|
import java.util.*
|
|
|
import kotlin.reflect.KFunction2
|
|
|
|
|
|
+val CallCheckerContext.bindingContext get() = trace.bindingContext
|
|
|
+
|
|
|
/**
|
|
|
* Checks parameters with [ResolveContextKind]
|
|
|
*/
|
|
|
-class ContextualParametersChecker : DeclarationChecker {
|
|
|
- override fun check(
|
|
|
- declaration: KtDeclaration,
|
|
|
- descriptor: DeclarationDescriptor,
|
|
|
- context: DeclarationCheckerContext,
|
|
|
- ) {
|
|
|
- val calls = declaration.bodyCalls(context.bindingContext) ?: return
|
|
|
-
|
|
|
- for ((call, _) in calls) {
|
|
|
- for ((parameter, resolvedArgument) in call.valueArguments) {
|
|
|
- for (valueArgument in resolvedArgument.arguments) {
|
|
|
- checkArgument(parameter, valueArgument, context)
|
|
|
- }
|
|
|
+class ContextualParametersChecker : CallChecker {
|
|
|
+ override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
|
|
+ for ((parameter, resolvedArgument) in resolvedCall.valueArguments) {
|
|
|
+ for (valueArgument in resolvedArgument.arguments) {
|
|
|
+ checkArgument(parameter, valueArgument, context, valueArgument.asElement())
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+//
|
|
|
+// override fun check(
|
|
|
+// declaration: KtDeclaration,
|
|
|
+// descriptor: DeclarationDescriptor,
|
|
|
+// context: DeclarationCheckerContext,
|
|
|
+// ) {
|
|
|
+// val calls = declaration.bodyCalls(context.bindingContext) ?: return
|
|
|
+//
|
|
|
+// for ((call, _) in calls) {
|
|
|
+// for ((parameter, resolvedArgument) in call.valueArguments) {
|
|
|
+// for (valueArgument in resolvedArgument.arguments) {
|
|
|
+// checkArgument(parameter, valueArgument, context)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
|
|
|
private fun checkArgument(
|
|
|
parameter: ValueParameterDescriptor,
|
|
|
argument: ValueArgument,
|
|
|
- context: DeclarationCheckerContext,
|
|
|
+ context: CallCheckerContext,
|
|
|
+ inspectionTarget: PsiElement,
|
|
|
) {
|
|
|
val elementCheckers = parameter.resolveContextKinds?.mapNotNull(checkersMap::get) ?: return
|
|
|
if (elementCheckers.isEmpty()) return
|
|
|
@@ -69,15 +80,15 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
|
|
|
for (elementChecker in elementCheckers) {
|
|
|
if (resolvedConstants.isEmpty()) {
|
|
|
- elementChecker(context, argument.asElement(), argument, null)?.let { context.report(it) }
|
|
|
+ elementChecker(context, inspectionTarget, argument, null)?.let { context.trace.report(it) }
|
|
|
} else {
|
|
|
for (resolvedConstant in resolvedConstants) {
|
|
|
elementChecker(
|
|
|
context,
|
|
|
- argument.asElement(),
|
|
|
+ inspectionTarget,
|
|
|
argument,
|
|
|
resolvedConstant
|
|
|
- )?.let { context.report(it) }
|
|
|
+ )?.let { context.trace.report(it) }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -98,14 +109,14 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
*/
|
|
|
private val SEMANTIC_VERSIONING_REGEX = Regex(SEMANTIC_VERSIONING_PATTERN)
|
|
|
|
|
|
- fun checkPluginId(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkPluginId(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
if (value.isBlank()) return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件 Id 不能为空. \n插件 Id$syntax")
|
|
|
if (value.none { it == '.' }) return ILLEGAL_PLUGIN_DESCRIPTION.on(
|
|
|
inspectionTarget,
|
|
|
"插件 Id '$value' 无效. 插件 Id 必须同时包含 groupId 和插件名称. $syntax"
|
|
|
)
|
|
|
|
|
|
- val lowercaseId = value.toLowerCase()
|
|
|
+ val lowercaseId = value.lowercase()
|
|
|
|
|
|
if (ID_REGEX.matchEntire(value) == null) {
|
|
|
return ILLEGAL_PLUGIN_DESCRIPTION.on(
|
|
|
@@ -120,16 +131,16 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
- fun checkPluginName(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkPluginName(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
if (value.isBlank()) return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "插件名不能为空")
|
|
|
- val lowercaseName = value.toLowerCase()
|
|
|
+ val lowercaseName = value.lowercase()
|
|
|
FORBIDDEN_ID_NAMES.firstOrNull { it == lowercaseName }?.let { illegal ->
|
|
|
return ILLEGAL_PLUGIN_DESCRIPTION.on(inspectionTarget, "'$illegal' 不允许作为插件名. 确保插件名不完全是这个名称")
|
|
|
}
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
- fun checkPluginVersion(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkPluginVersion(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
if (!SEMANTIC_VERSIONING_REGEX.matches(value)) {
|
|
|
return ILLEGAL_PLUGIN_DESCRIPTION.on(
|
|
|
inspectionTarget,
|
|
|
@@ -139,7 +150,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
return null
|
|
|
}
|
|
|
|
|
|
- fun checkCommandName(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkCommandName(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
return when {
|
|
|
value.isBlank() -> ILLEGAL_COMMAND_NAME.on(inspectionTarget, value, "指令名不能为空")
|
|
|
value.any { it.isWhitespace() } -> ILLEGAL_COMMAND_NAME.on(inspectionTarget, value, "暂时不允许指令名中存在空格")
|
|
|
@@ -149,7 +160,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun checkPermissionNamespace(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkPermissionNamespace(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
return when {
|
|
|
value.isBlank() -> ILLEGAL_PERMISSION_NAMESPACE.on(inspectionTarget, value, "权限命名空间不能为空")
|
|
|
value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAMESPACE.on(
|
|
|
@@ -162,7 +173,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun checkPermissionName(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkPermissionName(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
return when {
|
|
|
value.isBlank() -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "权限名称不能为空")
|
|
|
value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_NAME.on(inspectionTarget, value, "不允许权限名称中存在空格")
|
|
|
@@ -171,7 +182,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- fun checkPermissionId(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkPermissionId(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
return when {
|
|
|
value.isBlank() -> ILLEGAL_PERMISSION_ID.on(inspectionTarget, value, "权限 Id 不能为空")
|
|
|
value.any { it.isWhitespace() } -> ILLEGAL_PERMISSION_ID.on(inspectionTarget, value, "暂时不允许权限 Id 中存在空格")
|
|
|
@@ -185,7 +196,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
}
|
|
|
|
|
|
@Suppress("UNUSED_PARAMETER")
|
|
|
- fun checkVersionRequirement(inspectionTarget: KtElement, value: String): Diagnostic? {
|
|
|
+ fun checkVersionRequirement(inspectionTarget: PsiElement, value: String): Diagnostic? {
|
|
|
return try {
|
|
|
RequirementHelper.RequirementChecker.processLine(RequirementParser.TokenReader(value))
|
|
|
null
|
|
|
@@ -195,14 +206,14 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
}
|
|
|
|
|
|
fun checkConsoleCommandOwner(
|
|
|
- context: DeclarationCheckerContext,
|
|
|
- inspectionTarget: KtElement,
|
|
|
+ context: CallCheckerContext,
|
|
|
+ inspectionTarget: PsiElement,
|
|
|
argument: ValueArgument
|
|
|
): Diagnostic? {
|
|
|
val expr = argument.getArgumentExpression() ?: return null
|
|
|
|
|
|
if (expr is KtReferenceExpression) {
|
|
|
- if (expr.getResolvedCall(context)?.isCalling(CONSOLE_COMMAND_OWNER_FQ_NAME) == true) {
|
|
|
+ if (expr.getResolvedCall(context.bindingContext)?.isCalling(CONSOLE_COMMAND_OWNER_FQ_NAME) == true) {
|
|
|
return RESTRICTED_CONSOLE_COMMAND_OWNER.on(inspectionTarget)
|
|
|
}
|
|
|
}
|
|
|
@@ -213,8 +224,8 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
|
|
|
fun interface ElementChecker {
|
|
|
operator fun invoke(
|
|
|
- context: DeclarationCheckerContext,
|
|
|
- declaration: KtElement,
|
|
|
+ context: CallCheckerContext,
|
|
|
+ declaration: PsiElement,
|
|
|
valueArgument: ValueArgument,
|
|
|
value: String?
|
|
|
): Diagnostic?
|
|
|
@@ -224,7 +235,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
private val checkersMap: EnumMap<ResolveContextKind, ElementChecker> =
|
|
|
EnumMap<ResolveContextKind, ElementChecker>(ResolveContextKind::class.java).apply {
|
|
|
|
|
|
- fun put(key: ResolveContextKind, value: KFunction2<KtElement, String, Diagnostic?>): ElementChecker? {
|
|
|
+ fun put(key: ResolveContextKind, value: KFunction2<PsiElement, String, Diagnostic?>): ElementChecker? {
|
|
|
return put(key) { _, d, _, v ->
|
|
|
if (v != null) value(d, v)
|
|
|
else null
|
|
|
@@ -233,7 +244,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
|
|
|
fun put(
|
|
|
key: ResolveContextKind,
|
|
|
- value: KFunction2<KtElement, ValueArgument, Diagnostic?>
|
|
|
+ value: KFunction2<PsiElement, ValueArgument, Diagnostic?>
|
|
|
): ElementChecker? {
|
|
|
return put(key) { _, d, v, _ ->
|
|
|
value(d, v)
|
|
|
@@ -242,7 +253,7 @@ class ContextualParametersChecker : DeclarationChecker {
|
|
|
|
|
|
fun put(
|
|
|
key: ResolveContextKind,
|
|
|
- value: (DeclarationCheckerContext, KtElement, ValueArgument) -> Diagnostic?
|
|
|
+ value: (CallCheckerContext, PsiElement, ValueArgument) -> Diagnostic?
|
|
|
): ElementChecker? {
|
|
|
return put(key) { c, d, v, _ ->
|
|
|
value(c, d, v)
|