Improve performance of IDEA plugin

This commit is contained in:
Him188 2021-12-16 14:17:50 +00:00
parent aa915085b8
commit 92465c3213
12 changed files with 126 additions and 154 deletions

View File

@ -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
}

View File

@ -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)

View File

@ -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`)
}

View File

@ -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
}

View File

@ -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 || ")

View File

@ -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)
}

View File

@ -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 {

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}