From 92465c32136f3838ed58a7f6f73d759a58b59756 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Thu, 16 Dec 2021 14:17:50 +0000
Subject: [PATCH] Improve performance of IDEA plugin

---
 buildSrc/src/main/kotlin/Versions.kt          |  6 +-
 .../src/diagnostics/MiraiConsoleErrors.kt     |  2 +-
 .../tools/intellij-plugin/build.gradle.kts    | 25 ++---
 .../projects/test-project/build.gradle.kts    |  6 +-
 .../org/example/myplugin/MyPluginMain.kt      |  2 +-
 .../example/myplugin/ReadOnlyPluginDataVar.kt |  4 +
 .../src/IDEContainerContributor.kt            | 23 ++++-
 .../ContextualParametersChecker.kt            | 91 +++++++++++--------
 .../diagnostics/PluginDataValuesChecker.kt    | 64 ++++++-------
 .../ResourceNotClosedInspection.kt            |  2 +-
 .../src/resolve/FunctionSignature.kt          |  2 +-
 .../src/resolve/resolveIdea.kt                | 53 +----------
 12 files changed, 126 insertions(+), 154 deletions(-)

diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt
index 33d2f2788..1018cc6da 100644
--- a/buildSrc/src/main/kotlin/Versions.kt
+++ b/buildSrc/src/main/kotlin/Versions.kt
@@ -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
 
 }
 
diff --git a/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt b/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt
index c80ad2d7c..c0d1caa47 100644
--- a/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt
+++ b/mirai-console/tools/compiler-common/src/diagnostics/MiraiConsoleErrors.kt
@@ -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)
diff --git a/mirai-console/tools/intellij-plugin/build.gradle.kts b/mirai-console/tools/intellij-plugin/build.gradle.kts
index 1d3b218ef..f6dedd2b0 100644
--- a/mirai-console/tools/intellij-plugin/build.gradle.kts
+++ b/mirai-console/tools/intellij-plugin/build.gradle.kts
@@ -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`)
 }
diff --git a/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts b/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts
index 3188dee80..2bc148209 100644
--- a/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts
+++ b/mirai-console/tools/intellij-plugin/run/projects/test-project/build.gradle.kts
@@ -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
 }
 
diff --git a/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt b/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt
index 64c9095ad..ce4891d25 100644
--- a/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt
+++ b/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt
@@ -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 || ")
diff --git a/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/ReadOnlyPluginDataVar.kt b/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/ReadOnlyPluginDataVar.kt
index 3c8990c78..4ed8c90db 100644
--- a/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/ReadOnlyPluginDataVar.kt
+++ b/mirai-console/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/ReadOnlyPluginDataVar.kt
@@ -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)
 }
diff --git a/mirai-console/tools/intellij-plugin/src/IDEContainerContributor.kt b/mirai-console/tools/intellij-plugin/src/IDEContainerContributor.kt
index 21ced0078..a9d852cce 100644
--- a/mirai-console/tools/intellij-plugin/src/IDEContainerContributor.kt
+++ b/mirai-console/tools/intellij-plugin/src/IDEContainerContributor.kt
@@ -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 {
diff --git a/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt b/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt
index 04c29d37b..aaceef753 100644
--- a/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt
+++ b/mirai-console/tools/intellij-plugin/src/diagnostics/ContextualParametersChecker.kt
@@ -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)
diff --git a/mirai-console/tools/intellij-plugin/src/diagnostics/PluginDataValuesChecker.kt b/mirai-console/tools/intellij-plugin/src/diagnostics/PluginDataValuesChecker.kt
index f8609513f..c12edee57 100644
--- a/mirai-console/tools/intellij-plugin/src/diagnostics/PluginDataValuesChecker.kt
+++ b/mirai-console/tools/intellij-plugin/src/diagnostics/PluginDataValuesChecker.kt
@@ -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
diff --git a/mirai-console/tools/intellij-plugin/src/diagnostics/ResourceNotClosedInspection.kt b/mirai-console/tools/intellij-plugin/src/diagnostics/ResourceNotClosedInspection.kt
index a6056de5d..144f403de 100644
--- a/mirai-console/tools/intellij-plugin/src/diagnostics/ResourceNotClosedInspection.kt
+++ b/mirai-console/tools/intellij-plugin/src/diagnostics/ResourceNotClosedInspection.kt
@@ -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
diff --git a/mirai-console/tools/intellij-plugin/src/resolve/FunctionSignature.kt b/mirai-console/tools/intellij-plugin/src/resolve/FunctionSignature.kt
index 84be5c9d2..49cb223ea 100644
--- a/mirai-console/tools/intellij-plugin/src/resolve/FunctionSignature.kt
+++ b/mirai-console/tools/intellij-plugin/src/resolve/FunctionSignature.kt
@@ -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
diff --git a/mirai-console/tools/intellij-plugin/src/resolve/resolveIdea.kt b/mirai-console/tools/intellij-plugin/src/resolve/resolveIdea.kt
index 0192be79b..a6c75d67c 100644
--- a/mirai-console/tools/intellij-plugin/src/resolve/resolveIdea.kt
+++ b/mirai-console/tools/intellij-plugin/src/resolve/resolveIdea.kt
@@ -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)
 }