mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Port InfixCallToOrdinaryIntention to K2 (make shared) and turn it into inspection
^KTIJ-22973 fixed Merge-request: IJ-MR-128762 Merged-by: Victoria Petrakovich <Victoria.Petrakovich@jetbrains.com> GitOrigin-RevId: d6c72864321a6aaf34937ae8cf2bb7bdbb85880e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
57cc908f37
commit
86b35bceaa
@@ -10,6 +10,7 @@ group.names.other.problems=Other problems
|
||||
group.names.probable.bugs=Probable bugs
|
||||
group.names.redundant.constructs=Redundant constructs
|
||||
group.names.style.issues=Style issues
|
||||
group.names.code.migration=Code migration
|
||||
fix.insert.delegation.call=Insert ''{0}()'' call
|
||||
fix.introduce.non.null.assertion=Add non-null asserted (!!) call
|
||||
fix.remove.non.null.assertion=Remove unnecessary non-null assertion (!!)
|
||||
@@ -1788,6 +1789,7 @@ add.import.for.0=Add import for ''{0}''
|
||||
add.import.for.member=Add import for member
|
||||
indent.raw.string=Indent raw string
|
||||
replace.infix.call.with.ordinary.call=Replace infix call with ordinary call
|
||||
infix.call.may.be.dot.call=Infix call may be dot call
|
||||
insert.curly.braces.around.variable=Insert curly braces around variable
|
||||
add.explicit.type.arguments=Add explicit type arguments
|
||||
introduce.backing.property=Introduce backing property
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports for infix function calls that can be replaced with dot-qualified function calls.
|
||||
<p>Example:</p>
|
||||
<pre><code>
|
||||
1 xor 2
|
||||
</code></pre>
|
||||
<pre><code>
|
||||
1.xor(2)
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
-1
@@ -1 +0,0 @@
|
||||
1.xor(2)
|
||||
-1
@@ -1 +0,0 @@
|
||||
1 xor 2
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
Converts an infix function call to a dot-qualified function call.
|
||||
</body>
|
||||
</html>
|
||||
+8
@@ -331,5 +331,13 @@
|
||||
language="kotlin"
|
||||
key="inspection.redundant.labeled.return.on.last.expression.in.lambda.display.name" bundle="messages.KotlinBundle"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.codeInsight.inspections.shared.InfixCallToOrdinaryInspection"
|
||||
groupPath="Kotlin"
|
||||
groupBundle="messages.KotlinBundle" groupKey="group.names.code.migration"
|
||||
enabledByDefault="true"
|
||||
level="INFORMATION"
|
||||
language="kotlin"
|
||||
key="infix.call.may.be.dot.call" bundle="messages.KotlinBundle"/>
|
||||
|
||||
</extensions>
|
||||
</idea-plugin>
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// 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.codeInsight.inspections.shared
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession
|
||||
import com.intellij.codeInspection.ProblemsHolder
|
||||
import com.intellij.modcommand.ModPsiUpdater
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElementVisitor
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.AbstractKotlinApplicableInspection
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.KotlinApplicabilityRange
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.applicabilityRange
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.applicators.ApplicabilityRanges
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
|
||||
internal class InfixCallToOrdinaryInspection : AbstractKotlinApplicableInspection<KtBinaryExpression>() {
|
||||
override fun getProblemDescription(element: KtBinaryExpression) = KotlinBundle.message("replace.infix.call.with.ordinary.call")
|
||||
|
||||
override fun apply(element: KtBinaryExpression, project: Project, updater: ModPsiUpdater) {
|
||||
convertInfixCallToOrdinary(element)
|
||||
}
|
||||
|
||||
override fun getActionFamilyName() = KotlinBundle.message("replace.infix.call.with.ordinary.call")
|
||||
|
||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor =
|
||||
binaryExpressionVisitor {
|
||||
visitTargetElement(it, holder, isOnTheFly)
|
||||
}
|
||||
|
||||
override fun getActionName(element: KtBinaryExpression): String = KotlinBundle.message("replace.infix.call.with.ordinary.call")
|
||||
|
||||
override fun getApplicabilityRange(): KotlinApplicabilityRange<KtBinaryExpression> = applicabilityRange {
|
||||
it.operationReference.textRangeInParent
|
||||
}
|
||||
|
||||
override fun isApplicableByPsi(element: KtBinaryExpression): Boolean {
|
||||
return !(element.operationToken != KtTokens.IDENTIFIER || element.left == null || element.right == null)
|
||||
}
|
||||
}
|
||||
|
||||
fun convertInfixCallToOrdinary(element: KtBinaryExpression): KtExpression {
|
||||
val argument = KtPsiUtil.safeDeparenthesize(element.right!!)
|
||||
val pattern = "$0.$1" + when (argument) {
|
||||
is KtLambdaExpression -> " $2:'{}'"
|
||||
else -> "($2)"
|
||||
}
|
||||
|
||||
val replacement = KtPsiFactory(element.project).createExpressionByPattern(
|
||||
pattern,
|
||||
element.left!!,
|
||||
element.operationReference,
|
||||
argument
|
||||
)
|
||||
|
||||
return element.replace(replacement) as KtExpression
|
||||
}
|
||||
+38
@@ -428,6 +428,44 @@ public abstract class SharedK1LocalInspectionTestGenerated extends AbstractShare
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/inspectionsLocal/infixCallToOrdinary")
|
||||
public static class InfixCallToOrdinary extends AbstractSharedK1LocalInspectionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("functionCallAfterInfixCall.kt")
|
||||
public void testFunctionCallAfterInfixCall() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/functionCallAfterInfixCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteralArgument.kt")
|
||||
public void testFunctionLiteralArgument() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/functionLiteralArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonApplicableBinaryOperation.kt")
|
||||
public void testNonApplicableBinaryOperation() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/nonApplicableBinaryOperation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullAssertedCall.kt")
|
||||
public void testNullAssertedCall() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/nullAssertedCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parenthesesAroundRightHandArgument.kt")
|
||||
public void testParenthesesAroundRightHandArgument() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/parenthesesAroundRightHandArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleInfixFunctionCall.kt")
|
||||
public void testSimpleInfixFunctionCall() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/simpleInfixFunctionCall.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/inspectionsLocal/javaIoSerializableObjectMustHaveReadResolve")
|
||||
public static class JavaIoSerializableObjectMustHaveReadResolve extends AbstractSharedK1LocalInspectionTest {
|
||||
|
||||
+38
@@ -428,6 +428,44 @@ public abstract class SharedK2LocalInspectionTestGenerated extends AbstractShare
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/inspectionsLocal/infixCallToOrdinary")
|
||||
public static class InfixCallToOrdinary extends AbstractSharedK2LocalInspectionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("functionCallAfterInfixCall.kt")
|
||||
public void testFunctionCallAfterInfixCall() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/functionCallAfterInfixCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteralArgument.kt")
|
||||
public void testFunctionLiteralArgument() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/functionLiteralArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonApplicableBinaryOperation.kt")
|
||||
public void testNonApplicableBinaryOperation() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/nonApplicableBinaryOperation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullAssertedCall.kt")
|
||||
public void testNullAssertedCall() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/nullAssertedCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parenthesesAroundRightHandArgument.kt")
|
||||
public void testParenthesesAroundRightHandArgument() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/parenthesesAroundRightHandArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleInfixFunctionCall.kt")
|
||||
public void testSimpleInfixFunctionCall() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/infixCallToOrdinary/simpleInfixFunctionCall.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("../testData/inspectionsLocal/javaIoSerializableObjectMustHaveReadResolve")
|
||||
public static class JavaIoSerializableObjectMustHaveReadResolve extends AbstractSharedK2LocalInspectionTest {
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.codeInsight.inspections.shared.InfixCallToOrdinaryInspection
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// IS_APPLICABLE: false
|
||||
// PROBLEM: none
|
||||
fun foo(x: String) {
|
||||
x <caret>== x
|
||||
}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.kotlin.idea.inspections
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInspection.IntentionWrapper
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticWithParameters2
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtModifierListOwner
|
||||
import org.jetbrains.kotlin.psi.KtOperationReferenceExpression
|
||||
|
||||
object InfixCallFixActionFactory : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||
val functionDescriptor = (diagnostic as? DiagnosticWithParameters2<*, *, *>)?.a as? FunctionDescriptor ?: return null
|
||||
val target = DescriptorToSourceUtilsIde.getAnyDeclaration(diagnostic.psiFile.project, functionDescriptor)
|
||||
as? KtModifierListOwner
|
||||
if (target == null || target.canRefactor()) {
|
||||
// we'll fix the problem by adding the 'infix' modifier to the target
|
||||
return null
|
||||
}
|
||||
|
||||
if ((diagnostic.psiElement as? KtOperationReferenceExpression)?.parent !is KtBinaryExpression) return null
|
||||
return IntentionWrapper(InfixCallToOrdinaryIntention())
|
||||
}
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.jetbrains.kotlin.idea.intentions
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
|
||||
class InfixCallToOrdinaryIntention : SelfTargetingIntention<KtBinaryExpression>(
|
||||
KtBinaryExpression::class.java,
|
||||
KotlinBundle.lazyMessage("replace.infix.call.with.ordinary.call")
|
||||
) {
|
||||
override fun isApplicableTo(element: KtBinaryExpression, caretOffset: Int): Boolean {
|
||||
if (element.operationToken != KtTokens.IDENTIFIER || element.left == null || element.right == null) return false
|
||||
return element.operationReference.textRange.containsOffset(caretOffset)
|
||||
}
|
||||
|
||||
override fun applyTo(element: KtBinaryExpression, editor: Editor?) {
|
||||
Holder.convert(element)
|
||||
}
|
||||
|
||||
object Holder {
|
||||
fun convert(element: KtBinaryExpression): KtExpression {
|
||||
val argument = KtPsiUtil.safeDeparenthesize(element.right!!)
|
||||
val pattern = "$0.$1" + when (argument) {
|
||||
is KtLambdaExpression -> " $2:'{}'"
|
||||
else -> "($2)"
|
||||
}
|
||||
|
||||
val replacement = KtPsiFactory(element.project).createExpressionByPattern(
|
||||
pattern,
|
||||
element.left!!,
|
||||
element.operationReference.text,
|
||||
argument
|
||||
)
|
||||
|
||||
return element.replace(replacement) as KtExpression
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.idea.codeinsights.impl.base.quickFix.ChangeVariableM
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementAsConstructorParameter
|
||||
import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler
|
||||
import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory
|
||||
import org.jetbrains.kotlin.idea.inspections.InfixCallFixActionFactory
|
||||
import org.jetbrains.kotlin.idea.inspections.RemoveAnnotationFix
|
||||
import org.jetbrains.kotlin.idea.intentions.*
|
||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.*
|
||||
@@ -549,7 +548,6 @@ class QuickFixRegistrar : QuickFixContributor {
|
||||
OPERATOR_MODIFIER_REQUIRED.registerFactory(ImportForMissingOperatorFactory)
|
||||
|
||||
INFIX_MODIFIER_REQUIRED.registerFactory(AddModifierFixFactory(INFIX_KEYWORD))
|
||||
INFIX_MODIFIER_REQUIRED.registerFactory(InfixCallFixActionFactory)
|
||||
|
||||
UNDERSCORE_IS_RESERVED.registerFactory(RenameUnderscoreFix)
|
||||
|
||||
|
||||
+2
-2
@@ -27,6 +27,7 @@ import org.jetbrains.kotlin.idea.base.psi.unifier.KotlinPsiUnificationResult.Str
|
||||
import org.jetbrains.kotlin.idea.base.psi.unifier.KotlinPsiUnificationResult.WeakSuccess
|
||||
import org.jetbrains.kotlin.idea.base.psi.unifier.toRange
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.codeInsight.inspections.shared.convertInfixCallToOrdinary
|
||||
import org.jetbrains.kotlin.idea.codeinsights.impl.base.inspections.OperatorToFunctionConverter
|
||||
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||
import org.jetbrains.kotlin.idea.core.appendElement
|
||||
@@ -34,7 +35,6 @@ import org.jetbrains.kotlin.idea.core.moveInsideParenthesesAndReplaceWith
|
||||
import org.jetbrains.kotlin.idea.core.toVisibility
|
||||
import org.jetbrains.kotlin.idea.inspections.PublicApiImplicitTypeInspection
|
||||
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
|
||||
import org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
||||
import org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsIntention
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.*
|
||||
import org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine.OutputValue.*
|
||||
@@ -292,7 +292,7 @@ private fun makeCall(
|
||||
val newNameExpression = when (val operationExpression = anchor.parent as? KtOperationExpression ?: return null) {
|
||||
is KtUnaryExpression -> OperatorToFunctionConverter.convert(operationExpression).second
|
||||
is KtBinaryExpression -> {
|
||||
InfixCallToOrdinaryIntention.Holder.convert(operationExpression).getCalleeExpressionIfAny()
|
||||
convertInfixCallToOrdinary(operationExpression).getCalleeExpressionIfAny()
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
-38
@@ -10949,44 +10949,6 @@ public abstract class K1IntentionTestGenerated extends AbstractK1IntentionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/intentions/infixCallToOrdinary")
|
||||
public static class InfixCallToOrdinary extends AbstractK1IntentionTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||
}
|
||||
|
||||
@TestMetadata("functionCallAfterInfixCall.kt")
|
||||
public void testFunctionCallAfterInfixCall() throws Exception {
|
||||
runTest("testData/intentions/infixCallToOrdinary/functionCallAfterInfixCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionLiteralArgument.kt")
|
||||
public void testFunctionLiteralArgument() throws Exception {
|
||||
runTest("testData/intentions/infixCallToOrdinary/functionLiteralArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonApplicableBinaryOperation.kt")
|
||||
public void testNonApplicableBinaryOperation() throws Exception {
|
||||
runTest("testData/intentions/infixCallToOrdinary/nonApplicableBinaryOperation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullAssertedCall.kt")
|
||||
public void testNullAssertedCall() throws Exception {
|
||||
runTest("testData/intentions/infixCallToOrdinary/nullAssertedCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parenthesesAroundRightHandArgument.kt")
|
||||
public void testParenthesesAroundRightHandArgument() throws Exception {
|
||||
runTest("testData/intentions/infixCallToOrdinary/parenthesesAroundRightHandArgument.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleInfixFunctionCall.kt")
|
||||
public void testSimpleInfixFunctionCall() throws Exception {
|
||||
runTest("testData/intentions/infixCallToOrdinary/simpleInfixFunctionCall.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
@TestMetadata("testData/intentions/insertCurlyBracesToTemplate")
|
||||
public static class InsertCurlyBracesToTemplate extends AbstractK1IntentionTest {
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention
|
||||
@@ -2,7 +2,7 @@
|
||||
// ERROR: Unresolved reference: foo
|
||||
// ACTION: Create extension function 'H.foo'
|
||||
// ACTION: Create member function 'H.foo'
|
||||
// ACTION: Replace infix call with ordinary call
|
||||
// FIX: Replace infix call with ordinary call
|
||||
|
||||
package h
|
||||
|
||||
|
||||
@@ -217,13 +217,6 @@
|
||||
<categoryKey>group.names.kotlin</categoryKey>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<language>kotlin</language>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.InfixCallToOrdinaryIntention</className>
|
||||
<bundleName>messages.KotlinBundle</bundleName>
|
||||
<categoryKey>group.names.kotlin</categoryKey>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<language>kotlin</language>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ToInfixCallIntention</className>
|
||||
|
||||
Reference in New Issue
Block a user