mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-05 07:30:09 +08:00
Improve performance of IDEA plugin
This commit is contained in:
parent
aa915085b8
commit
92465c3213
@ -54,9 +54,9 @@ object Versions {
|
||||
// If you the versions below, you need to sync changes to mirai-console/buildSrc/src/main/kotlin/Versions.kt
|
||||
|
||||
const val yamlkt = "0.10.2"
|
||||
const val intellijGradlePlugin = "1.1"
|
||||
const val kotlinIntellijPlugin = "211-1.5.20-release-284-IJ7442.40" // keep to newest as kotlinCompiler
|
||||
const val intellij = "2021.1.3" // don't update easily unless you want your disk space -= 500MB
|
||||
const val intellijGradlePlugin = "1.3.0"
|
||||
// const val kotlinIntellijPlugin = "211-1.5.20-release-284-IJ7442.40" // keep to newest as kotlinCompiler
|
||||
const val intellij = "2021.3" // don't update easily unless you want your disk space -= 500MB
|
||||
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ object MiraiConsoleErrors {
|
||||
val ILLEGAL_COMMAND_REGISTER_USE = create<PsiElement, KtNamedDeclaration, String>(ERROR)
|
||||
|
||||
@JvmField
|
||||
val RESTRICTED_CONSOLE_COMMAND_OWNER = create<KtElement>(WARNING)
|
||||
val RESTRICTED_CONSOLE_COMMAND_OWNER = create<PsiElement>(WARNING)
|
||||
|
||||
@JvmField
|
||||
val ILLEGAL_COMMAND_DECLARATION_RECEIVER = create<KtTypeReference>(ERROR)
|
||||
|
@ -24,11 +24,6 @@ repositories {
|
||||
version = Versions.console
|
||||
description = "IntelliJ plugin for Mirai Console"
|
||||
|
||||
// JVM fails to compile
|
||||
kotlin.target.compilations.forEach { kotlinCompilation ->
|
||||
kotlinCompilation.kotlinOptions.freeCompilerArgs += "-Xuse-ir"
|
||||
} // don't use `useIr()`, compatibility with mirai-console dedicated builds
|
||||
|
||||
// See https://github.com/JetBrains/gradle-intellij-plugin/
|
||||
intellij {
|
||||
version.set(Versions.intellij)
|
||||
@ -39,9 +34,10 @@ intellij {
|
||||
|
||||
plugins.set(
|
||||
listOf(
|
||||
"org.jetbrains.kotlin:${Versions.kotlinIntellijPlugin}", // @eap
|
||||
// "org.jetbrains.kotlin:${Versions.kotlinIntellijPlugin}", // @eap
|
||||
"java",
|
||||
"gradle"
|
||||
"gradle",
|
||||
"org.jetbrains.kotlin"
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -68,8 +64,6 @@ fun File.resolveMkdir(relative: String): File {
|
||||
|
||||
kotlin.target.compilations.all {
|
||||
kotlinOptions {
|
||||
apiVersion = "1.4"
|
||||
languageVersion = "1.4"
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
@ -83,7 +77,7 @@ tasks.withType<org.jetbrains.intellij.tasks.PatchPluginXmlTask> {
|
||||
|
||||
<h3>Features</h3>
|
||||
<ul>
|
||||
<li>Inspections for plugin properties, for example, checking PluginDescription.</li>
|
||||
<li>Inspections for plugin properties.</li>
|
||||
<li>Inspections for illegal calls.</li>
|
||||
<li>Intentions for resolving serialization problems.</li>
|
||||
</ul>
|
||||
@ -103,13 +97,6 @@ dependencies {
|
||||
|
||||
api(project(":mirai-console-compiler-common"))
|
||||
|
||||
compileOnly(`kotlin-stdlib-jdk8`)
|
||||
// compileOnly("com.jetbrains:ideaIC:${Versions.intellij}")
|
||||
// compileOnly(`kotlin-compiler`)
|
||||
|
||||
// compileOnly(files("libs/ide-common.jar"))
|
||||
compileOnly(fileTree("run/idea-sandbox/plugins/Kotlin/lib").filter {
|
||||
!it.name.contains("stdlib") && !it.name.contains("coroutines")
|
||||
})
|
||||
compileOnly(`kotlin-reflect`)
|
||||
implementation(`kotlin-stdlib-jdk8`)
|
||||
implementation(`kotlin-reflect`)
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
kotlin("jvm") version "1.4.20"
|
||||
kotlin("plugin.serialization") version "1.4.20"
|
||||
id("net.mamoe.mirai-console") version "2.4-M1"
|
||||
kotlin("jvm") version "1.6.0"
|
||||
kotlin("plugin.serialization") version "1.6.0"
|
||||
id("net.mamoe.mirai-console") version "2.9.0-M1"
|
||||
java
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ object MyPluginMain : KotlinPlugin(
|
||||
PermissionService.INSTANCE.register(permissionId("dvs"), "ok")
|
||||
PermissionService.INSTANCE.register(permissionId("perm with space"), "error")
|
||||
PermissionId("Namespace with space", "Name with space")
|
||||
SemVersion.parseRangeRequirement("")
|
||||
SemVersion.parseRangeRequirement("1.0")
|
||||
SemVersion.parseRangeRequirement("<br/>")
|
||||
SemVersion.parseRangeRequirement("SB YELLOW")
|
||||
SemVersion.parseRangeRequirement("1.0.0 || 2.0.0 || ")
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package org.example.myplugin
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.mamoe.mirai.console.data.ReadOnlyPluginConfig
|
||||
import net.mamoe.mirai.console.data.value
|
||||
import org.example.myplugin.DataTest1.provideDelegate
|
||||
@ -16,5 +17,8 @@ import org.example.myplugin.DataTest1.provideDelegate
|
||||
|
||||
object DataTest2 : ReadOnlyPluginConfig("data") {
|
||||
var pp by value<String>()
|
||||
val x by value<V>(V(""))
|
||||
// var should be reported
|
||||
|
||||
class V constructor(val s: String)
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
package net.mamoe.mirai.console.intellij
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import net.mamoe.mirai.console.compiler.common.castOrNull
|
||||
import net.mamoe.mirai.console.intellij.diagnostics.CommandDeclarationChecker
|
||||
import net.mamoe.mirai.console.intellij.diagnostics.ContextualParametersChecker
|
||||
@ -25,6 +26,9 @@ import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor
|
||||
import org.jetbrains.kotlin.idea.core.unwrapModuleSourceInfo
|
||||
import org.jetbrains.kotlin.idea.facet.KotlinFacet
|
||||
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||
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 org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import java.io.File
|
||||
@ -37,7 +41,8 @@ class IDEContainerContributor : StorageComponentContainerContributor {
|
||||
) {
|
||||
if (moduleDescriptor.hasMiraiConsoleDependency()) {
|
||||
container.useInstance(ContextualParametersChecker().wrapIgnoringExceptionIfNotDebug())
|
||||
container.useInstance(PluginDataValuesChecker().wrapIgnoringExceptionIfNotDebug())
|
||||
container.useInstance((PluginDataValuesChecker() as CallChecker).wrapIgnoringExceptionIfNotDebug())
|
||||
container.useInstance((PluginDataValuesChecker() as DeclarationChecker).wrapIgnoringExceptionIfNotDebug())
|
||||
container.useInstance(CommandDeclarationChecker().wrapIgnoringExceptionIfNotDebug())
|
||||
}
|
||||
}
|
||||
@ -49,6 +54,22 @@ class IDEContainerContributor : StorageComponentContainerContributor {
|
||||
return DeclarationCheckerIgnoringExceptions(this)
|
||||
}
|
||||
|
||||
private fun CallChecker.wrapIgnoringExceptionIfNotDebug(): CallChecker {
|
||||
if (DEBUG_ENABLED) {
|
||||
return this
|
||||
}
|
||||
return CallCheckerIgnoringExceptions(this)
|
||||
}
|
||||
|
||||
class CallCheckerIgnoringExceptions(
|
||||
private val delegate: CallChecker
|
||||
) : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
runIgnoringErrors { delegate.check(resolvedCall, reportOn, context) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DeclarationCheckerIgnoringExceptions(
|
||||
private val delegate: DeclarationChecker
|
||||
) : DeclarationChecker {
|
||||
|
@ -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)
|
||||
|
@ -11,11 +11,11 @@
|
||||
|
||||
package net.mamoe.mirai.console.intellij.diagnostics
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import net.mamoe.mirai.console.compiler.common.SERIALIZABLE_FQ_NAME
|
||||
import net.mamoe.mirai.console.compiler.common.castOrNull
|
||||
import net.mamoe.mirai.console.compiler.common.diagnostics.MiraiConsoleErrors
|
||||
import net.mamoe.mirai.console.compiler.common.resolve.*
|
||||
import net.mamoe.mirai.console.intellij.resolve.bodyCalls
|
||||
import net.mamoe.mirai.console.intellij.resolve.hasSuperType
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@ -26,6 +26,8 @@ import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
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 org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
@ -33,34 +35,30 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
class PluginDataValuesChecker : DeclarationChecker {
|
||||
/**
|
||||
* [KtObjectDeclaration], [KtParameter], [KtPrimaryConstructor], [KtClass], [KtNamedFunction], [KtProperty]
|
||||
*/
|
||||
class PluginDataValuesChecker : CallChecker, DeclarationChecker {
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
check(
|
||||
resolvedCall as ResolvedCall<out CallableDescriptor>,
|
||||
resolvedCall.call.callElement as? KtExpression ?: return,
|
||||
context
|
||||
)
|
||||
}
|
||||
|
||||
override fun check(
|
||||
declaration: KtDeclaration,
|
||||
descriptor: DeclarationDescriptor,
|
||||
context: DeclarationCheckerContext
|
||||
) {
|
||||
val bindingContext = context.bindingContext
|
||||
|
||||
//println(declaration::class.qualifiedName + "\t:" + declaration.text.take(10))
|
||||
|
||||
if (declaration is KtProperty) {
|
||||
checkReadOnly(declaration, context)
|
||||
}
|
||||
|
||||
val calls = declaration.bodyCalls(bindingContext) ?: return
|
||||
|
||||
for ((call, expr) in calls) {
|
||||
check(call, expr, context)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check `PluginData.value` calls
|
||||
*/
|
||||
fun check(call: ResolvedCall<out CallableDescriptor>, expr: KtExpression, context: DeclarationCheckerContext) {
|
||||
fun check(call: ResolvedCall<out CallableDescriptor>, expr: KtExpression, context: CallCheckerContext) {
|
||||
if (!call.isCalling(PLUGIN_DATA_VALUE_FUNCTIONS_FQ_FQ_NAME)) return
|
||||
|
||||
if (expr is KtCallExpression)
|
||||
@ -87,25 +85,28 @@ class PluginDataValuesChecker : DeclarationChecker {
|
||||
private fun checkConstructableAndSerializable(
|
||||
call: ResolvedCall<out CallableDescriptor>,
|
||||
expr: KtCallExpression,
|
||||
context: DeclarationCheckerContext
|
||||
context: CallCheckerContext
|
||||
) {
|
||||
if (call.resultingDescriptor.resolveContextKinds?.contains(ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR) != true) return
|
||||
|
||||
for ((typeParameterDescriptor, kotlinType) in call.typeArguments.entries) {
|
||||
if ((typeParameterDescriptor.isReified || typeParameterDescriptor.resolveContextKinds?.contains(
|
||||
ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR
|
||||
) == true)
|
||||
for ((entry, argument) in call.typeArguments.entries.zip(expr.typeArguments)) {
|
||||
val (parameter, kotlinType) = entry
|
||||
if ((parameter.isReified
|
||||
|| parameter.resolveContextKinds?.contains(ResolveContextKind.RESTRICTED_NO_ARG_CONSTRUCTOR) == true)
|
||||
&& kotlinType is SimpleType
|
||||
) {
|
||||
|
||||
checkConstructableAndSerializable(kotlinType, expr, context)
|
||||
checkFixType(kotlinType, expr, context)
|
||||
checkConstructableAndSerializable(kotlinType, expr, argument, context)
|
||||
checkFixType(kotlinType, expr, argument, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFixType(type: KotlinType, callExpr: KtCallExpression, context: DeclarationCheckerContext) {
|
||||
val inspectionTarget = retrieveInspectionTarget(type, callExpr) ?: return
|
||||
private fun checkFixType(
|
||||
type: KotlinType,
|
||||
callExpr: KtCallExpression,
|
||||
inspectionTarget: KtTypeProjection,
|
||||
context: CallCheckerContext
|
||||
) {
|
||||
val classDescriptor = type.classDescriptor() ?: return
|
||||
val jetTypeFqn = type.getJetTypeFqName(false)
|
||||
|
||||
@ -132,22 +133,21 @@ class PluginDataValuesChecker : DeclarationChecker {
|
||||
else -> return
|
||||
} ?: return
|
||||
|
||||
context.report(factory.on(inspectionTarget, callExpr, jetTypeFqn.substringAfterLast('.')))
|
||||
context.trace.report(factory.on(inspectionTarget, callExpr, jetTypeFqn.substringAfterLast('.')))
|
||||
}
|
||||
|
||||
private fun checkConstructableAndSerializable(
|
||||
type: KotlinType,
|
||||
callExpr: KtCallExpression,
|
||||
context: DeclarationCheckerContext
|
||||
inspectionTarget: KtTypeProjection,
|
||||
context: CallCheckerContext
|
||||
) {
|
||||
val classDescriptor = type.classDescriptor() ?: return
|
||||
|
||||
if (canBeSerializedInternally(classDescriptor)) return
|
||||
|
||||
val inspectionTarget = retrieveInspectionTarget(type, callExpr) ?: return
|
||||
|
||||
if (!classDescriptor.hasNoArgConstructor())
|
||||
return context.report(
|
||||
return context.trace.report(
|
||||
MiraiConsoleErrors.NOT_CONSTRUCTABLE_TYPE.on(
|
||||
inspectionTarget,
|
||||
callExpr,
|
||||
@ -156,7 +156,7 @@ class PluginDataValuesChecker : DeclarationChecker {
|
||||
)
|
||||
|
||||
if (!classDescriptor.hasAnnotation(SERIALIZABLE_FQ_NAME))
|
||||
return context.report(
|
||||
return context.trace.report(
|
||||
MiraiConsoleErrors.UNSERIALIZABLE_TYPE.on(
|
||||
inspectionTarget,
|
||||
classDescriptor
|
||||
|
@ -20,7 +20,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.inspections.AbstractKotlinInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.KotlinUniversalQuickFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinCrossLanguageQuickFixAction
|
||||
import org.jetbrains.kotlin.idea.search.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
@ -17,7 +17,7 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.getReturnTypeReference
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.fqName
|
||||
import org.jetbrains.kotlin.idea.search.getKotlinFqName
|
||||
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.nj2k.postProcessing.type
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
|
@ -160,57 +160,6 @@ val KtAnnotationEntry.annotationClass: KtClass?
|
||||
fun KtAnnotated.hasAnnotation(fqName: FqName): Boolean =
|
||||
this.annotationEntries.any { it.annotationClass?.getKotlinFqName() == fqName }
|
||||
|
||||
fun KtElement.resolveAllCalls(bindingContext: BindingContext): Sequence<ResolvedCall<*>> {
|
||||
return allChildrenWithSelfSequence
|
||||
.filterIsInstance<KtElement>()
|
||||
.mapNotNull { it.getResolvedCall(bindingContext) }
|
||||
}
|
||||
|
||||
data class ResolvedCallWithExpr<C : CallableDescriptor, E : KtExpression>(
|
||||
val call: ResolvedCall<C>,
|
||||
val expr: E
|
||||
)
|
||||
|
||||
/**
|
||||
* 只解决一层
|
||||
*/
|
||||
fun KtDeclaration.bodyCalls(bindingContext: BindingContext): Sequence<ResolvedCallWithExpr<out CallableDescriptor, KtExpression>>? {
|
||||
return when (val declaration = this) {
|
||||
is KtClassOrObject -> {
|
||||
declaration.superTypeListEntries.asSequence().flatMap {
|
||||
it.resolveAllCallsWithElement(bindingContext, true)
|
||||
}
|
||||
}
|
||||
is KtDeclarationWithBody -> {
|
||||
declaration.bodyExpression?.resolveAllCallsWithElement(bindingContext, false) ?: return null
|
||||
}
|
||||
is KtCallExpression -> {
|
||||
val call = declaration.getResolvedCall(bindingContext) ?: return null
|
||||
sequenceOf(ResolvedCallWithExpr(call, declaration))
|
||||
}
|
||||
is KtProperty -> {
|
||||
val expr = declaration.delegateExpression ?: return null
|
||||
val call = expr.getResolvedCall(bindingContext) ?: return null
|
||||
sequenceOf(ResolvedCallWithExpr(call, expr))
|
||||
}
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
fun KtElement.resolveAllCallsWithElement(
|
||||
bindingContext: BindingContext,
|
||||
recursive: Boolean = true
|
||||
): Sequence<ResolvedCallWithExpr<out CallableDescriptor, KtExpression>> {
|
||||
return (if (recursive) allChildrenWithSelfSequence else childrenWithSelf.asSequence())
|
||||
.filterIsInstance<KtExpression>()
|
||||
.mapNotNull { expr ->
|
||||
val callee = expr.getCalleeExpressionIfAny() ?: return@mapNotNull null
|
||||
val resolved = callee.getResolvedCall(bindingContext) ?: return@mapNotNull null
|
||||
|
||||
ResolvedCallWithExpr(resolved, expr)
|
||||
}
|
||||
}
|
||||
|
||||
fun ValueArgument.resolveStringConstantValues(bindingContext: BindingContext): Sequence<String>? {
|
||||
return this.getArgumentExpression()?.resolveStringConstantValues(bindingContext)
|
||||
}
|
||||
@ -234,7 +183,7 @@ fun KtReferenceExpression.typeFqName() = type()?.fqName
|
||||
fun KtExpression.typeFqName() = referenceExpression()?.typeFqName()
|
||||
|
||||
fun KtElement.getResolvedCall(
|
||||
context: BindingContext = analyze(BodyResolveMode.PARTIAL),
|
||||
context: BindingContext,
|
||||
): ResolvedCall<out CallableDescriptor>? {
|
||||
return this.getCall(context)?.getResolvedCall(context)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user