[kotlin] Port AddAnnotationUseSiteTargetIntention to K2

^KTIJ-31237

GitOrigin-RevId: 1eb82f05e36ee335650023880761bca01fd1440a
This commit is contained in:
Andrey Cherkasov
2024-09-09 00:02:53 +04:00
committed by intellij-monorepo-bot
parent 71a3fd096f
commit 1caa8c6de1
9 changed files with 828 additions and 153 deletions

View File

@@ -0,0 +1,148 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.ListPopupStep
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.PsiComment
import com.intellij.util.PlatformIcons
import org.jetbrains.kotlin.analysis.api.KaExperimentalApi
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.addUseSiteTarget
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
object AddAnnotationUseSiteTargetUtils {
context(KaSession)
@OptIn(KaExperimentalApi::class)
fun KtAnnotationEntry.getApplicableUseSiteTargets(): List<AnnotationUseSiteTarget> {
val symbol = typeReference?.type?.expandedSymbol
val applicableTargets = symbol?.annotationApplicableTargets?.toSet().orEmpty()
return applicableUseSiteTargets(applicableTargets)
}
fun KtAnnotationEntry.applicableUseSiteTargets(applicableTargets: Set<KotlinTarget>): List<AnnotationUseSiteTarget> {
if (useSiteTarget != null) return emptyList()
val annotationShortName = this.shortName ?: return emptyList()
val modifierList = getStrictParentOfType<KtModifierList>() ?: return emptyList()
val annotated = modifierList.owner as? KtElement ?: return emptyList()
val candidateTargets = when (annotated) {
is KtParameter -> if (annotated.getStrictParentOfType<KtPrimaryConstructor>() != null) when (annotated.valOrVarKeyword?.node?.elementType) {
KtTokens.VAR_KEYWORD -> listOf(CONSTRUCTOR_PARAMETER, FIELD, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, SETTER_PARAMETER)
KtTokens.VAL_KEYWORD -> listOf(CONSTRUCTOR_PARAMETER, FIELD, PROPERTY, PROPERTY_GETTER)
else -> emptyList()
}
else emptyList()
is KtProperty -> when {
annotated.delegate != null -> listOf(PROPERTY, PROPERTY_GETTER, PROPERTY_DELEGATE_FIELD)
!annotated.isLocal -> {
val backingField = LightClassUtil.getLightClassPropertyMethods(annotated).backingField
if (annotated.isVar) {
if (backingField != null) listOf(FIELD, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, SETTER_PARAMETER)
else listOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, SETTER_PARAMETER)
} else {
if (backingField != null) listOf(FIELD, PROPERTY, PROPERTY_GETTER)
else listOf(PROPERTY, PROPERTY_GETTER)
}
}
else -> emptyList()
}
is KtTypeReference -> listOf(RECEIVER)
else -> emptyList()
}.toMutableList()
if (candidateTargets.isEmpty()) return emptyList()
val existingTargets = modifierList.annotationEntries.mapNotNull {
if (annotationShortName == it.shortName) it.useSiteTarget?.getAnnotationUseSiteTarget() else null
}
if (existingTargets.isNotEmpty()) {
candidateTargets.removeIf { it in existingTargets }
if (candidateTargets.isEmpty()) return emptyList()
}
if (applicableTargets.isNotEmpty()) {
candidateTargets.removeIf { KotlinTarget.USE_SITE_MAPPING[it] !in applicableTargets }
if (candidateTargets.isEmpty()) return emptyList()
}
return if (isUnitTestMode()) {
val chosenTarget = containingKtFile.findDescendantOfType<PsiComment>()
?.takeIf { it.text.startsWith("// CHOOSE_USE_SITE_TARGET:") }?.text?.split(":")?.getOrNull(1)?.trim()
if (chosenTarget.isNullOrBlank()) candidateTargets.take(1)
else candidateTargets.asSequence().filter { it.renderName == chosenTarget }.take(1).toList()
} else {
candidateTargets
}
}
fun KtAnnotationEntry.addUseSiteTarget(useSiteTargets: List<AnnotationUseSiteTarget>, editor: Editor?) {
val project = this.project
if (!isPhysical) { // For preview
if (useSiteTargets.isNotEmpty()) {
doAddUseSiteTarget(useSiteTargets.first())
}
return
}
CommandProcessor.getInstance().runUndoTransparentAction {
if (useSiteTargets.size == 1 || editor == null) addUseSiteTarget(useSiteTargets.first(), project)
else JBPopupFactory.getInstance().createListPopup(createListPopupStep(this, useSiteTargets, project))
.showInBestPositionFor(editor)
}
}
fun KtAnnotationEntry.addUseSiteTarget(
useSiteTarget: AnnotationUseSiteTarget, project: Project
) {
project.executeWriteCommand(KotlinBundle.message("add.use.site.target")) {
doAddUseSiteTarget(useSiteTarget)
}
}
}
private fun KtAnnotationEntry.doAddUseSiteTarget(useSiteTarget: AnnotationUseSiteTarget) {
replace(KtPsiFactory(project).createAnnotationEntry("@${useSiteTarget.renderName}:${text.drop(1)}"))
}
private fun createListPopupStep(
annotationEntry: KtAnnotationEntry, useSiteTargets: List<AnnotationUseSiteTarget>, project: Project
): ListPopupStep<*> {
return object : BaseListPopupStep<AnnotationUseSiteTarget>(KotlinBundle.message("title.choose.use.site.target"), useSiteTargets) {
override fun isAutoSelectionEnabled() = false
override fun onChosen(selectedValue: AnnotationUseSiteTarget, finalChoice: Boolean): PopupStep<*>? {
if (finalChoice) {
annotationEntry.addUseSiteTarget(selectedValue, project)
}
return PopupStep.FINAL_CHOICE
}
override fun getIconFor(value: AnnotationUseSiteTarget) = PlatformIcons.ANNOTATION_TYPE_ICON
override fun getTextFor(value: AnnotationUseSiteTarget): String {
@NlsSafe val renderName = value.renderName
return renderName
}
}
}

View File

@@ -387,6 +387,12 @@
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
<intentionAction>
<language>kotlin</language>
<className>org.jetbrains.kotlin.idea.k2.codeinsight.intentions.AddAnnotationUseSiteTargetIntention</className>
<bundleName>messages.KotlinBundle</bundleName>
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,33 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.k2.codeinsight.intentions
import com.intellij.modcommand.ActionContext
import com.intellij.modcommand.ModPsiUpdater
import org.jetbrains.kotlin.analysis.api.KaSession
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.intentions.KotlinApplicableModCommandAction
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.addUseSiteTarget
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.getApplicableUseSiteTargets
import org.jetbrains.kotlin.psi.KtAnnotationEntry
internal class AddAnnotationUseSiteTargetIntention :
KotlinApplicableModCommandAction<KtAnnotationEntry, List<AnnotationUseSiteTarget>>(KtAnnotationEntry::class) {
override fun getFamilyName(): String = KotlinBundle.message("add.use.site.target")
context(KaSession)
override fun prepareContext(element: KtAnnotationEntry): List<AnnotationUseSiteTarget>? {
val useSiteTargets = element.getApplicableUseSiteTargets()
return useSiteTargets.takeIf { it.isNotEmpty() }
}
override fun invoke(
actionContext: ActionContext,
element: KtAnnotationEntry,
elementContext: List<AnnotationUseSiteTarget>,
updater: ModPsiUpdater,
) {
element.addUseSiteTarget(elementContext, null)
}
}

View File

@@ -7709,7 +7709,634 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget")
public abstract static class AddAnnotationUseSiteTarget extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor")
public abstract static class Constructor extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor")
public static class Uncategorized extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("parameter.kt")
public void testParameter() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/parameter.kt");
}
@TestMetadata("secondary.kt")
public void testSecondary() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/secondary.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val")
public static class Val extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/val/setparam.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var")
public static class Var extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/constructor/var/setparam.kt");
}
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension")
public abstract static class Extension extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function")
public static class Function extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/function/setparam.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property")
public static class Property extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/extension/property/setparam.kt");
}
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property")
public abstract static class Property extends AbstractK2IntentionTest {
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate")
public static class Delegate extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/delegate/setparam.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property")
public static class Uncategorized extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("local.kt")
public void testLocal() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/local.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val")
public static class Val extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/val/setparam.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking")
public static class ValNoBacking extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/valNoBacking/setparam.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var")
public static class Var extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/var/setparam.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking")
public static class VarNoBacking extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("delegate.kt")
public void testDelegate() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/delegate.kt");
}
@TestMetadata("field.kt")
public void testField() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/field.kt");
}
@TestMetadata("file.kt")
public void testFile() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/file.kt");
}
@TestMetadata("get.kt")
public void testGet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/get.kt");
}
@TestMetadata("param.kt")
public void testParam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/param.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/property.kt");
}
@TestMetadata("receiver.kt")
public void testReceiver() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/receiver.kt");
}
@TestMetadata("set.kt")
public void testSet() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/set.kt");
}
@TestMetadata("setparam.kt")
public void testSetparam() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/property/varNoBacking/setparam.kt");
}
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget")
public static class Uncategorized extends AbstractK2IntentionTest {
@java.lang.Override
@org.jetbrains.annotations.NotNull
public final KotlinPluginMode getPluginMode() {
return KotlinPluginMode.K2;
}
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("hasAnnotationArgs.kt")
public void testHasAnnotationArgs() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/hasAnnotationArgs.kt");
}
@TestMetadata("hasTarget1.kt")
public void testHasTarget1() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/hasTarget1.kt");
}
@TestMetadata("hasTarget2.kt")
public void testHasTarget2() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/hasTarget2.kt");
}
@TestMetadata("hasTarget3.kt")
public void testHasTarget3() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/hasTarget3.kt");
}
@TestMetadata("qualifiedAnnotationDoesNotLoseQualifier.kt")
public void testQualifiedAnnotationDoesNotLoseQualifier() throws Exception {
runTest("../../../idea/tests/testData/intentions/addAnnotationUseSiteTarget/qualifiedAnnotationDoesNotLoseQualifier.kt");
}
}
}

View File

@@ -2,29 +2,14 @@
package org.jetbrains.kotlin.idea.intentions
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.ListPopupStep
import com.intellij.openapi.ui.popup.PopupStep
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
import com.intellij.openapi.util.NlsSafe
import com.intellij.psi.PsiComment
import com.intellij.util.PlatformIcons
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.isUnitTestMode
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.addUseSiteTarget
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.applicableUseSiteTargets
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.resolve.AnnotationChecker
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
@@ -35,7 +20,7 @@ class AddAnnotationUseSiteTargetIntention : SelfTargetingIntention<KtAnnotationE
) {
override fun isApplicableTo(element: KtAnnotationEntry, caretOffset: Int): Boolean {
val useSiteTargets = element.applicableUseSiteTargets()
val useSiteTargets = element.getApplicableUseSiteTargets()
if (useSiteTargets.isEmpty()) return false
if (useSiteTargets.size == 1) {
setTextGetter(KotlinBundle.lazyMessage("text.add.use.site.target.0", useSiteTargets.first().renderName))
@@ -46,139 +31,14 @@ class AddAnnotationUseSiteTargetIntention : SelfTargetingIntention<KtAnnotationE
}
override fun applyTo(element: KtAnnotationEntry, editor: Editor?) {
val useSiteTargets = element.applicableUseSiteTargets()
val useSiteTargets = element.getApplicableUseSiteTargets()
element.addUseSiteTarget(useSiteTargets, editor)
}
}
fun KtAnnotationEntry.addUseSiteTarget(useSiteTargets: List<AnnotationUseSiteTarget>, editor: Editor?) {
val project = this.project
if (!isPhysical) {
// For preview
if (useSiteTargets.isNotEmpty()) {
doAddUseSiteTarget(useSiteTargets.first())
}
return
}
CommandProcessor.getInstance().runUndoTransparentAction {
if (useSiteTargets.size == 1 || editor == null)
addUseSiteTarget(useSiteTargets.first(), project)
else
JBPopupFactory
.getInstance()
.createListPopup(createListPopupStep(this, useSiteTargets, project))
.showInBestPositionFor(editor)
}
}
private fun createListPopupStep(
annotationEntry: KtAnnotationEntry,
useSiteTargets: List<AnnotationUseSiteTarget>,
project: Project
): ListPopupStep<*> {
return object : BaseListPopupStep<AnnotationUseSiteTarget>(KotlinBundle.message("title.choose.use.site.target"), useSiteTargets) {
override fun isAutoSelectionEnabled() = false
override fun onChosen(selectedValue: AnnotationUseSiteTarget, finalChoice: Boolean): PopupStep<*>? {
if (finalChoice) {
annotationEntry.addUseSiteTarget(selectedValue, project)
}
return PopupStep.FINAL_CHOICE
}
override fun getIconFor(value: AnnotationUseSiteTarget) = PlatformIcons.ANNOTATION_TYPE_ICON
override fun getTextFor(value: AnnotationUseSiteTarget): String {
@Suppress("UnnecessaryVariable")
@NlsSafe val renderName = value.renderName
return renderName
}
}
}
fun KtAnnotationEntry.applicableUseSiteTargets(): List<AnnotationUseSiteTarget> {
if (useSiteTarget != null) return emptyList()
val annotationShortName = this.shortName ?: return emptyList()
val modifierList = getStrictParentOfType<KtModifierList>() ?: return emptyList()
val annotated = modifierList.owner as? KtElement ?: return emptyList()
val candidateTargets = when (annotated) {
is KtParameter ->
if (annotated.getStrictParentOfType<KtPrimaryConstructor>() != null)
when (annotated.valOrVarKeyword?.node?.elementType) {
KtTokens.VAR_KEYWORD ->
listOf(CONSTRUCTOR_PARAMETER, FIELD, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, SETTER_PARAMETER)
KtTokens.VAL_KEYWORD ->
listOf(CONSTRUCTOR_PARAMETER, FIELD, PROPERTY, PROPERTY_GETTER)
else ->
emptyList()
}
else
emptyList()
is KtProperty ->
when {
annotated.delegate != null ->
listOf(PROPERTY, PROPERTY_GETTER, PROPERTY_DELEGATE_FIELD)
!annotated.isLocal -> {
val backingField = LightClassUtil.getLightClassPropertyMethods(annotated).backingField
if (annotated.isVar) {
if (backingField != null)
listOf(FIELD, PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, SETTER_PARAMETER)
else
listOf(PROPERTY, PROPERTY_GETTER, PROPERTY_SETTER, SETTER_PARAMETER)
} else {
if (backingField != null)
listOf(FIELD, PROPERTY, PROPERTY_GETTER)
else
listOf(PROPERTY, PROPERTY_GETTER)
}
}
else ->
emptyList()
}
is KtTypeReference -> listOf(RECEIVER)
else -> emptyList()
}.toMutableList()
if (candidateTargets.isEmpty()) return emptyList()
val existingTargets = modifierList.annotationEntries.mapNotNull {
if (annotationShortName == it.shortName) it.useSiteTarget?.getAnnotationUseSiteTarget() else null
}
if (existingTargets.isNotEmpty()) {
candidateTargets.removeIf { it in existingTargets }
if (candidateTargets.isEmpty()) return emptyList()
}
fun KtAnnotationEntry.getApplicableUseSiteTargets(): List<AnnotationUseSiteTarget> {
val context = analyze(BodyResolveMode.PARTIAL)
val descriptor = context[BindingContext.ANNOTATION, this]
val applicableTargets = descriptor?.let { AnnotationChecker.applicableTargetSet(descriptor) }.orEmpty()
if (applicableTargets.isNotEmpty()) {
candidateTargets.removeIf { KotlinTarget.USE_SITE_MAPPING[it] !in applicableTargets }
if (candidateTargets.isEmpty()) return emptyList()
}
return if (isUnitTestMode()) {
val chosenTarget = containingKtFile.findDescendantOfType<PsiComment>()
?.takeIf { it.text.startsWith("// CHOOSE_USE_SITE_TARGET:") }
?.text
?.split(":")
?.getOrNull(1)
?.trim()
if (chosenTarget.isNullOrBlank())
candidateTargets.take(1)
else
candidateTargets.asSequence().filter { it.renderName == chosenTarget }.take(1).toList()
} else {
candidateTargets
}
}
fun KtAnnotationEntry.addUseSiteTarget(useSiteTarget: AnnotationUseSiteTarget, project: Project) {
project.executeWriteCommand(KotlinBundle.message("add.use.site.target")) {
doAddUseSiteTarget(useSiteTarget)
}
}
private fun KtAnnotationEntry.doAddUseSiteTarget(useSiteTarget: AnnotationUseSiteTarget) {
replace(KtPsiFactory(project).createAnnotationEntry("@${useSiteTarget.renderName}:${text.drop(1)}"))
return applicableUseSiteTargets(applicableTargets)
}

View File

@@ -8,8 +8,8 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.classic.quickfixes.KotlinQuickFixAction
import org.jetbrains.kotlin.idea.intentions.addUseSiteTarget
import org.jetbrains.kotlin.idea.intentions.applicableUseSiteTargets
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.addUseSiteTarget
import org.jetbrains.kotlin.idea.intentions.getApplicableUseSiteTargets
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtFile
@@ -36,7 +36,7 @@ class AddAnnotationUseSiteTargetFix(
companion object : KotlinSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): KotlinQuickFixAction<KtAnnotationEntry>? {
val entry = diagnostic.psiElement as? KtAnnotationEntry ?: return null
val applicableUseSiteTargets = entry.applicableUseSiteTargets()
val applicableUseSiteTargets = entry.getApplicableUseSiteTargets()
if (applicableUseSiteTargets.isEmpty()) return null
return AddAnnotationUseSiteTargetFix(entry, applicableUseSiteTargets)
}

View File

@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.k2.codeinsight.intentions.AddAnnotationUseSiteTargetIntention

View File

@@ -32,8 +32,8 @@ import org.jetbrains.kotlin.idea.codeinsight.utils.isRedundantGetter
import org.jetbrains.kotlin.idea.codeinsight.utils.isRedundantSetter
import org.jetbrains.kotlin.idea.codeinsight.utils.removeRedundantGetter
import org.jetbrains.kotlin.idea.codeinsight.utils.removeRedundantSetter
import org.jetbrains.kotlin.idea.codeinsights.impl.base.intentions.AddAnnotationUseSiteTargetUtils.addUseSiteTarget
import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.idea.intentions.addUseSiteTarget
import org.jetbrains.kotlin.idea.quickfix.AddAnnotationTargetFix.Companion.getExistingAnnotationTargets
import org.jetbrains.kotlin.idea.refactoring.isAbstract
import org.jetbrains.kotlin.idea.refactoring.isInterfaceClass

View File

@@ -6,7 +6,7 @@ import org.jetbrains.kotlin.idea.k2.intentions.tests.AbstractK2GotoTestOrCodeAct
import org.jetbrains.kotlin.idea.k2.intentions.tests.AbstractK2IntentionTest
import org.jetbrains.kotlin.idea.k2.intentions.tests.AbstractK2MultiFileIntentionTest
import org.jetbrains.kotlin.testGenerator.model.*
import org.jetbrains.kotlin.testGenerator.model.GroupCategory.*
import org.jetbrains.kotlin.testGenerator.model.GroupCategory.INTENTIONS
import org.jetbrains.kotlin.testGenerator.model.Patterns.TEST
@@ -121,7 +121,7 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
model("${idea}intentions/removeExplicitLambdaParameterTypes", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertPrimaryConstructorToSecondary", pattern = pattern)
model("${idea}intentions/convertArgumentToSet", pattern = pattern, isIgnored = true)
model("${idea}intentions/addAnnotationUseSiteTarget", pattern = pattern, isIgnored = true)
model("${idea}intentions/addAnnotationUseSiteTarget", pattern = pattern)
model("${idea}intentions/convertEnumToSealedClass", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertToIndexedFunctionCall", pattern = pattern, isIgnored = true)
model("${idea}intentions/samConversionToAnonymousObject", pattern = pattern, isIgnored = true)