From 3fc2d7f5c1fa3b2194bca3f87b64fef84a66aa7a Mon Sep 17 00:00:00 2001 From: Him188 Date: Fri, 18 Sep 2020 00:21:05 +0800 Subject: [PATCH] Diagnostics and resolve utilities --- buildSrc/src/main/kotlin/Versions.kt | 2 +- .../common/resolve/MiraiConsoleTypes.kt | 33 ++++ .../compiler/common/resolve/resolveCommon.kt | 16 ++ .../console/compiler/common/utilCommon.kt | 31 ++++ .../projects/test-project/build.gradle.kts | 4 +- .../org/example/myplugin/MyPluginMain.kt | 150 +----------------- .../diagnostics/PluginDescriptionChecker.kt | 97 ++++++++++- .../intellij/diagnostics/diagnosticsUtil.kt | 31 ++++ .../CommandDeclarationLineMarkerProvider.kt | 1 + .../marker/PluginMainLineMarkerProvider.kt | 3 + .../console/intellij/line/marker/util.kt | 51 ------ .../console/intellij/resolve/resolveIdea.kt | 107 ++++++++++++- 12 files changed, 322 insertions(+), 204 deletions(-) create mode 100644 tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/resolveCommon.kt create mode 100644 tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/utilCommon.kt create mode 100644 tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/diagnosticsUtil.kt delete mode 100644 tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/util.kt diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 53ee2e1a6..e61e28505 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -9,7 +9,7 @@ object Versions { const val core = "1.3.0" - const val console = "1.0-M4" + const val console = "1.0-RC-dev-1" const val consoleGraphical = "0.0.7" const val consoleTerminal = "0.1.0" const val consolePure = console diff --git a/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/MiraiConsoleTypes.kt b/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/MiraiConsoleTypes.kt index 10b1e5939..48ba53148 100644 --- a/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/MiraiConsoleTypes.kt +++ b/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/MiraiConsoleTypes.kt @@ -9,7 +9,10 @@ package net.mamoe.mirai.console.compiler.common.resolve +import net.mamoe.mirai.console.compiler.common.castOrNull +import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.constants.EnumValue /////////////////////////////////////////////////////////////////////////// // Command @@ -25,3 +28,33 @@ val SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME = FqName("net.mamoe.mirai.console.com val PLUGIN_FQ_NAME = FqName("net.mamoe.mirai.console.plugin.Plugin") val JVM_PLUGIN_DESCRIPTION_FQ_NAME = FqName("net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription") val SIMPLE_JVM_PLUGIN_DESCRIPTION_FQ_NAME = FqName("net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription") + +/////////////////////////////////////////////////////////////////////////// +// Resolve +/////////////////////////////////////////////////////////////////////////// + +val RESOLVE_CONTEXT_FQ_NAME = FqName("net.mamoe.mirai.console.compiler.common.ResolveContext") + +/** + * net.mamoe.mirai.console.compiler.common.ResolveContext.Kind + */ +enum class ResolveContextKind { + PLUGIN_ID, + PLUGIN_NAME, + PLUGIN_VERSION, + + ; + + companion object { + fun valueOfOrNull(string: String): ResolveContextKind? = ResolveContextKind.values().find { it.name == string } + } +} + +fun Annotated.isResolveContext(kind: ResolveContextKind) = this.resolveContextKind == kind + +val Annotated.resolveContextKind: ResolveContextKind? + get() { + val ann = this.findAnnotation(RESOLVE_CONTEXT_FQ_NAME) ?: return null + val (_, enumEntryName) = ann.allValueArguments.castOrNull()?.value ?: return null // undetermined kind + return ResolveContextKind.valueOf(enumEntryName.asString()) + } \ No newline at end of file diff --git a/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/resolveCommon.kt b/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/resolveCommon.kt new file mode 100644 index 000000000..4f2e6b05c --- /dev/null +++ b/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/resolve/resolveCommon.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.compiler.common.resolve + +import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.name.FqName + +fun Annotated.hasAnnotation(fqName: FqName) = this.annotations.hasAnnotation(fqName) +fun Annotated.findAnnotation(fqName: FqName) = this.annotations.findAnnotation(fqName) \ No newline at end of file diff --git a/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/utilCommon.kt b/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/utilCommon.kt new file mode 100644 index 000000000..8bdabfe92 --- /dev/null +++ b/tools/compiler-common/src/main/kotlin/net/mamoe/mirai/console/compiler/common/utilCommon.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.compiler.common + +import kotlin.contracts.contract + + +fun Map.firstValue(): V = this.entries.first().value +fun Map.firstKey(): K = this.entries.first().key + + +inline fun Any?.castOrNull(): T? { + contract { + returnsNotNull() implies (this@castOrNull is T) + } + return this as? T +} + +inline fun Any?.cast(): T { + contract { + returns() implies (this@cast is T) + } + return this as T +} \ No newline at end of file diff --git a/tools/intellij-plugin/run/projects/test-project/build.gradle.kts b/tools/intellij-plugin/run/projects/test-project/build.gradle.kts index e134da7b7..b247cee62 100644 --- a/tools/intellij-plugin/run/projects/test-project/build.gradle.kts +++ b/tools/intellij-plugin/run/projects/test-project/build.gradle.kts @@ -21,8 +21,8 @@ kotlin.sourceSets.all { dependencies { compileOnly(kotlin("stdlib-jdk8")) - val core = "1.2.3" - val console = "1.0-M4" + val core = "1.3.0" + val console = "1.0-RC-dev-1" compileOnly("net.mamoe:mirai-console:$console") compileOnly("net.mamoe:mirai-core:$core") diff --git a/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt b/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt index 4b0dfda16..9ee5ff208 100644 --- a/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt +++ b/tools/intellij-plugin/run/projects/test-project/src/main/kotlin/org/example/myplugin/MyPluginMain.kt @@ -1,163 +1,19 @@ -@file:Suppress("unused") - package org.example.myplugin -import com.google.auto.service.AutoService -import kotlinx.serialization.Serializable -import net.mamoe.mirai.Bot -import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register -import net.mamoe.mirai.console.command.CommandManager.INSTANCE.unregister -import net.mamoe.mirai.console.command.CommandSender -import net.mamoe.mirai.console.command.CompositeCommand -import net.mamoe.mirai.console.command.ConsoleCommandSender -import net.mamoe.mirai.console.command.SimpleCommand -import net.mamoe.mirai.console.data.AutoSavePluginConfig -import net.mamoe.mirai.console.data.AutoSavePluginData -import net.mamoe.mirai.console.data.PluginDataExtensions.mapKeys -import net.mamoe.mirai.console.data.PluginDataExtensions.withEmptyDefault -import net.mamoe.mirai.console.data.value -import net.mamoe.mirai.console.permission.PermissionService -import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission -import net.mamoe.mirai.console.plugin.jvm.JvmPlugin import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin -import net.mamoe.mirai.console.util.scopeWith -import net.mamoe.mirai.contact.Member -import net.mamoe.mirai.message.data.Image -import net.mamoe.mirai.utils.info -@AutoService(JvmPlugin::class) object MyPluginMain : KotlinPlugin( JvmPluginDescription( "org.example.example-plugin", "0.1.0" ) ) { + fun test() { - val PERMISSION_EXECUTE_1 = PermissionService.INSTANCE.register( - permissionId("execute1"), - "注册权限的示例" - ) - - - override fun onEnable() { - MySetting.reload() // 从数据库自动读取配置实例 - MyPluginData.reload() - - logger.info { "Hi: ${MySetting.name}" } // 输出一条日志. - logger.info("Hi: ${MySetting.name}") // 输出一条日志. 与上面一条相同, 但更推荐上面一条. - logger.verbose("Hi: ${MySetting.name}") // 多种日志级别可选 - - // 请不要使用 println, System.out.println 等标准输出方式. 请总是使用 logger. - - - MySetting.count++ // 对 Setting 的改动会自动在合适的时间保存 - - MySimpleCommand.register() // 注册指令 - } - - override fun onDisable() { - MySimpleCommand.unregister() // 取消注册指令 } } -// 定义插件数据 -// 插件 -object MyPluginData : AutoSavePluginData() { - var list: MutableList by value(mutableListOf("a", "b")) // mutableListOf("a", "b") 是初始值, 可以省略 - var long: Long by value(0L) // 允许 var - var int by value(0) // 可以使用类型推断, 但更推荐使用 `var long: Long by value(0)` 这种定义方式. +class PM : KotlinPlugin( - - // 带默认值的非空 map. - // notnullMap[1] 的返回值总是非 null 的 MutableMap - var notnullMap - by value>>().withEmptyDefault() - - // 可将 MutableMap 映射到 MutableMap. - val botToLongMap: MutableMap by value>().mapKeys(Bot::getInstance, Bot::id) -} - -// 定义一个配置. 所有属性都会被追踪修改, 并自动保存. -// 配置是插件与用户交互的接口, 但不能用来保存插件的数据. -object MySetting : AutoSavePluginConfig() { - val name by value("test") - - var count by value(0) - - val nested by value() // 嵌套类型是支持的 -} - -@Serializable -data class MyNestedData( - val list: List = listOf(), -) - -// 简单指令 -object MySimpleCommand : SimpleCommand( - MyPluginMain, "foo", - description = "示例指令" -) { - // 会自动创建一个 ID 为 "org.example.example-plugin:command.foo" 的权限. - - - // 通过 /foo 调用, 参数自动解析 - @Handler - suspend fun CommandSender.handle(int: Int, str: String) { // 函数名随意, 但参数需要按顺序放置. - - if (this.hasPermission(MyPluginMain.PERMISSION_EXECUTE_1)) { - sendMessage("你有 ${MyPluginMain.PERMISSION_EXECUTE_1.id} 权限.") - } else { - sendMessage( - """ - 你没有 ${MyPluginMain.PERMISSION_EXECUTE_1.id} 权限. - 可以在控制台使用 /permission 管理权限. - """.trimIndent() - ) - } - - sendMessage("/foo 的第一个参数是 $int, 第二个是 $str") - } -} - -// 复合指令 -object MyCompositeCommand : CompositeCommand( - MyPluginMain, "manage", - description = "示例指令", - // prefixOptional = true // 还有更多参数可填, 此处忽略 -) { - // 会自动创建一个 ID 为 "org.example.example-plugin:command.manage" 的权限. - - // - // 在控制台执行 "/manage <群号>.<群员> <持续时间>", - // 或在聊天群内发送 "/manage <@一个群员> <持续时间>", - // 或在聊天群内发送 "/manage <目标群员的群名> <持续时间>", - // 或在聊天群内发送 "/manage <目标群员的账号> <持续时间>" - @SubCommand - suspend fun CommandSender.mute(target: Member, duration: Int) { // 通过 /manage mute 调用 - sendMessage("/manage mute 被调用了, 参数为: $target, $duration") - - val result = kotlin.runCatching { - target.mute(duration).toString() - }.getOrElse { - it.stackTraceToString() - } // 失败时返回堆栈信息 - - - // 表示对 this 和 ConsoleCommandSender 一起操作 - this.scopeWith(ConsoleCommandSender) { - sendMessage("结果: $result") // 同时发送给 this@CommandSender 和 ConsoleCommandSender - } - } - - @SubCommand - suspend fun CommandSender.list() { // 执行 "/manage list" 时调用这个函数 - sendMessage("/manage list 被调用了") - } - - // 支持 Image 类型, 需在聊天中执行此指令. - @SubCommand - suspend fun CommandSender.test(image: Image) { // 执行 "/manage test <一张图片>" 时调用这个函数 - sendMessage("/manage image 被调用了, 图片是 ${image.imageId}") - } -} \ No newline at end of file +) \ No newline at end of file diff --git a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/PluginDescriptionChecker.kt b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/PluginDescriptionChecker.kt index 658a13eef..6b5841e8e 100644 --- a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/PluginDescriptionChecker.kt +++ b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/PluginDescriptionChecker.kt @@ -9,10 +9,21 @@ package net.mamoe.mirai.console.intellij.diagnostics +import com.intellij.psi.PsiElement +import net.mamoe.mirai.console.compiler.common.resolve.ResolveContextKind +import net.mamoe.mirai.console.compiler.common.resolve.resolveContextKind +import net.mamoe.mirai.console.intellij.resolve.findChildren +import net.mamoe.mirai.console.intellij.resolve.resolveStringConstantValue +import net.mamoe.mirai.console.intellij.resolve.valueParameters import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext +import java.util.* +import kotlin.contracts.contract /** * Checks: @@ -20,11 +31,95 @@ import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext * - plugin name */ class PluginDescriptionChecker : DeclarationChecker { + companion object { + fun checkPluginName(declaration: KtDeclaration, value: String): Diagnostic? { + return null // TODO: 2020/9/18 checkPluginName + } + + fun checkPluginId(declaration: KtDeclaration, value: String): Diagnostic? { + return null // TODO: 2020/9/18 checkPluginId + } + + fun checkPluginVersion(declaration: KtDeclaration, value: String): Diagnostic? { + return null // TODO: 2020/9/18 checkPluginVersion + } + } + + fun PsiElement.shouldPerformCheck(): Boolean { + contract { + returns(true) implies (this@shouldPerformCheck is KtCallExpression) + } + return when (this) { + is KtCallExpression, + -> true + else -> true + } + } + + private val checkersMap: EnumMap Diagnostic?> = + EnumMap Diagnostic?>(ResolveContextKind::class.java).apply { + put(ResolveContextKind.PLUGIN_NAME, ::checkPluginName) + put(ResolveContextKind.PLUGIN_ID, ::checkPluginId) + put(ResolveContextKind.PLUGIN_VERSION, ::checkPluginVersion) + } + + fun check( + declaration: KtDeclaration, + expression: KtCallExpression, + context: DeclarationCheckerContext, + ) { + val call = expression.calleeExpression.getResolvedCallOrResolveToCall(context) ?: return // unresolved + call.valueArgumentsByIndex?.forEach { resolvedValueArgument -> + for ((parameter, argument) in call.valueParameters.zip(resolvedValueArgument.arguments)) { + val parameterContextKind = parameter.resolveContextKind + if (checkersMap.containsKey(parameterContextKind)) { + val value = argument.getArgumentExpression() + ?.resolveStringConstantValue(context.bindingContext) ?: continue + for ((kind, fn) in checkersMap) { + if (parameterContextKind == kind) fn(declaration, value)?.let { context.report(it) } + } + } + } + } + } + override fun check( declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext, ) { + println("${declaration::class.qualifiedName} $declaration") + when (declaration) { + is KtObjectDeclaration -> { + // check super type constructor + val superTypeCallEntry = declaration.findChildren()?.findChildren() ?: return + val constructorCall = superTypeCallEntry.findChildren()?.resolveToCall() ?: return + val valueArgumentList = superTypeCallEntry.findChildren() ?: return + valueArgumentList.arguments.asSequence().mapNotNull(KtValueArgument::getArgumentExpression).forEach { + if (it.shouldPerformCheck()) { + check(declaration, it as KtCallExpression, context) + } + } + } + is KtClassOrObject -> { + // check constructor + + val superTypeCallEntry = declaration.findChildren()?.findChildren() ?: return + + val constructorCall = superTypeCallEntry.findChildren()?.resolveToCall() ?: return + val valueArgumentList = superTypeCallEntry.findChildren() ?: return + + + } + else -> { + declaration.children.filter { it.shouldPerformCheck() }.forEach { element -> + if (element is KtDeclaration) { + val desc = element.descriptor ?: return@forEach + check(element, desc, context) + } + } + } + } } } \ No newline at end of file diff --git a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/diagnosticsUtil.kt b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/diagnosticsUtil.kt new file mode 100644 index 000000000..6fd2874b4 --- /dev/null +++ b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/diagnostics/diagnosticsUtil.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2020 Mamoe Technologies and contributors. + * + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. + * + * https://github.com/mamoe/mirai/blob/master/LICENSE + */ + +package net.mamoe.mirai.console.intellij.diagnostics + +import net.mamoe.mirai.console.intellij.resolve.getResolvedCallOrResolveToCall +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.diagnostics.Diagnostic +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode + +fun DeclarationCheckerContext.report(diagnostic: Diagnostic) { + return this.trace.report(diagnostic) +} + +val DeclarationCheckerContext.bindingContext get() = this.trace.bindingContext + +fun KtElement?.getResolvedCallOrResolveToCall( + context: DeclarationCheckerContext, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL, +): ResolvedCall? { + return this.getResolvedCallOrResolveToCall(context.bindingContext, bodyResolveMode) +} \ No newline at end of file diff --git a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/CommandDeclarationLineMarkerProvider.kt b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/CommandDeclarationLineMarkerProvider.kt index 144ea41f9..43ba32e21 100644 --- a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/CommandDeclarationLineMarkerProvider.kt +++ b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/CommandDeclarationLineMarkerProvider.kt @@ -16,6 +16,7 @@ import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.editor.markup.GutterIconRenderer import com.intellij.psi.PsiElement import net.mamoe.mirai.console.intellij.Icons +import net.mamoe.mirai.console.intellij.resolve.getElementForLineMark import net.mamoe.mirai.console.intellij.resolve.isSimpleCommandHandlerOrCompositeCommandSubCommand import org.jetbrains.kotlin.psi.KtNamedFunction diff --git a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/PluginMainLineMarkerProvider.kt b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/PluginMainLineMarkerProvider.kt index 8f5d43708..f941bea98 100644 --- a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/PluginMainLineMarkerProvider.kt +++ b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/PluginMainLineMarkerProvider.kt @@ -18,6 +18,9 @@ import com.intellij.psi.PsiElement import com.intellij.util.castSafelyTo import net.mamoe.mirai.console.compiler.common.resolve.PLUGIN_FQ_NAME import net.mamoe.mirai.console.intellij.Icons +import net.mamoe.mirai.console.intellij.resolve.allSuperNames +import net.mamoe.mirai.console.intellij.resolve.getElementForLineMark +import net.mamoe.mirai.console.intellij.resolve.parents import org.jetbrains.kotlin.nj2k.postProcessing.resolve import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtConstructor diff --git a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/util.kt b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/util.kt deleted file mode 100644 index a407e5a6a..000000000 --- a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/line/marker/util.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2020 Mamoe Technologies and contributors. - * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. - * - * https://github.com/mamoe/mirai/blob/master/LICENSE - */ - -package net.mamoe.mirai.console.intellij.line.marker - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiFile -import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.nj2k.postProcessing.resolve -import org.jetbrains.kotlin.psi.* - - -internal val KtPureClassOrObject.allSuperTypes: Sequence - get() = sequence { - yieldAll(superTypeListEntries) - for (list in superTypeListEntries.asSequence()) { - yieldAll((list.typeAsUserType?.referenceExpression?.resolve() as? KtClass)?.allSuperTypes.orEmpty()) - } - } - -internal inline fun PsiElement.findParent(): E? = this.parents.filterIsInstance().firstOrNull() - -internal val KtClassOrObject.allSuperNames: Sequence get() = allSuperTypes.mapNotNull { it.getKotlinFqName() } - -val PsiElement.parents: Sequence - get() { - val seed = if (this is PsiFile) null else parent - return generateSequence(seed) { if (it is PsiFile) null else it.parent } - } - -internal fun getElementForLineMark(callElement: PsiElement): PsiElement = - when (callElement) { - is KtSimpleNameExpression -> callElement.getReferencedNameElement() - else -> - // a fallback, - //but who knows what to reference in KtArrayAccessExpression ? - generateSequence(callElement, { it.firstChild }).last() - } - -internal val KtAnnotationEntry.annotationClass: KtClass? - get() = calleeExpression?.constructorReferenceExpression?.resolve()?.findParent() - -internal fun KtAnnotated.hasAnnotation(fqName: FqName): Boolean = - this.annotationEntries.any { it.annotationClass?.getKotlinFqName() == fqName } \ No newline at end of file diff --git a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/resolve/resolveIdea.kt b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/resolve/resolveIdea.kt index 6fba5b74b..ee7acee78 100644 --- a/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/resolve/resolveIdea.kt +++ b/tools/intellij-plugin/src/main/kotlin/net/mamoe/mirai/console/intellij/resolve/resolveIdea.kt @@ -9,10 +9,25 @@ package net.mamoe.mirai.console.intellij.resolve +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import net.mamoe.mirai.console.compiler.common.castOrNull import net.mamoe.mirai.console.compiler.common.resolve.COMPOSITE_COMMAND_SUB_COMMAND_FQ_NAME import net.mamoe.mirai.console.compiler.common.resolve.SIMPLE_COMMAND_HANDLER_COMMAND_FQ_NAME -import net.mamoe.mirai.console.intellij.line.marker.hasAnnotation -import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.descriptors.VariableDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall +import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.nj2k.postProcessing.resolve +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.callUtil.getCall +import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.constants.StringValue +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode /** @@ -27,3 +42,91 @@ fun KtNamedFunction.isSimpleCommandHandler(): Boolean = this.hasAnnotation(SIMPL fun KtNamedFunction.isSimpleCommandHandlerOrCompositeCommandSubCommand(): Boolean = this.isSimpleCommandHandler() || this.isCompositeCommandSubCommand() + + +val KtPureClassOrObject.allSuperTypes: Sequence + get() = sequence { + yieldAll(superTypeListEntries) + for (list in superTypeListEntries.asSequence()) { + yieldAll((list.typeAsUserType?.referenceExpression?.resolve() as? KtClass)?.allSuperTypes.orEmpty()) + } + } + +fun KtConstructorCalleeExpression.getTypeAsUserType(): KtUserType? { + val reference = typeReference + if (reference != null) { + val element = reference.typeElement + if (element is KtUserType) { + return element + } + } + return null +} + +inline fun PsiElement.findParent(): E? = this.parents.filterIsInstance().firstOrNull() + +val KtClassOrObject.allSuperNames: Sequence get() = allSuperTypes.mapNotNull { it.getKotlinFqName() } + +val PsiElement.parents: Sequence + get() { + val seed = if (this is PsiFile) null else parent + return generateSequence(seed) { if (it is PsiFile) null else it.parent } + } + +fun getElementForLineMark(callElement: PsiElement): PsiElement = + when (callElement) { + is KtSimpleNameExpression -> callElement.getReferencedNameElement() + else -> + // a fallback, + //but who knows what to reference in KtArrayAccessExpression ? + generateSequence(callElement, { it.firstChild }).last() + } + +val KtAnnotationEntry.annotationClass: KtClass? + get() = calleeExpression?.constructorReferenceExpression?.resolve()?.findParent() + +fun KtAnnotated.hasAnnotation(fqName: FqName): Boolean = + this.annotationEntries.any { it.annotationClass?.getKotlinFqName() == fqName } + +val PsiElement.allChildrenFlat: Sequence + get() { + return sequence { + for (child in children) { + yield(child) + yieldAll(child.allChildrenFlat) + } + } + } + +inline fun PsiElement.findChildren(): E? = this.children.find { it is E } as E? + +fun KtElement?.getResolvedCallOrResolveToCall( + context: BindingContext, + bodyResolveMode: BodyResolveMode = BodyResolveMode.PARTIAL, +): ResolvedCall? { + return this?.getCall(context)?.getResolvedCall(context) ?: this?.resolveToCall(bodyResolveMode) +} + +val ResolvedCall.valueParameters: List get() = this.resultingDescriptor.valueParameters + +fun KtExpression.resolveStringConstantValue(bindingContext: BindingContext): String? { + when (this) { + is KtStringTemplateExpression -> { + if (hasInterpolation()) return null + return entries.joinToString("") { it.text } + } + is KtCallExpression -> { + val callee = this.calleeExpression?.getResolvedCallOrResolveToCall(bindingContext)?.resultingDescriptor + if (callee is VariableDescriptor) { + val compileTimeConstant = callee.compileTimeInitializer ?: return null + return compileTimeConstant.castOrNull()?.value + } + return null + } + is KtConstantExpression -> { + // TODO: 2020/9/18 KtExpression.resolveStringConstantValue: KtConstantExpression + } + else -> return null + } + return null +} \ No newline at end of file