[kotlin] "Redundant 'inner' modifier": fix a receiver on the call site

#KTIJ-3020 Fixed

closes https://github.com/JetBrains/intellij-community/pull/1697

GitOrigin-RevId: f3019a729e947c6f40a538c6a94d81b28a93b91f
This commit is contained in:
Toshiaki Kameyama
2021-09-23 16:58:45 +00:00
committed by intellij-monorepo-bot
parent a0109ab276
commit fe68621f65
14 changed files with 190 additions and 8 deletions
@@ -2,11 +2,11 @@
package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.IntentionWrapper
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.parentsOfType
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
@@ -15,8 +15,8 @@ import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.intentions.receiverType
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.idea.util.getThisReceiverOwner
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
@@ -41,10 +42,7 @@ class RedundantInnerClassModifierInspection : AbstractKotlinInspection() {
innerModifier,
KotlinBundle.message("inspection.redundant.inner.class.modifier.descriptor"),
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
IntentionWrapper(
RemoveModifierFix(targetClass, KtTokens.INNER_KEYWORD, isRedundant = true),
targetClass.containingFile
)
RemoveInnerModifierFix()
)
})
@@ -115,4 +113,42 @@ class RedundantInnerClassModifierInspection : AbstractKotlinInspection() {
private fun PsiElement.receiverTypeReference(): KtTypeReference? {
return safeAs<KtNamedFunction>()?.receiverTypeReference ?: safeAs<KtProperty>()?.receiverTypeReference
}
private class RemoveInnerModifierFix : LocalQuickFix {
override fun getName() = KotlinBundle.message("remove.redundant.0.modifier", KtTokens.INNER_KEYWORD.value)
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val targetClass = descriptor.psiElement.getStrictParentOfType<KtClass>() ?: return
val fixedElements = fixReceiverOnCallSite(targetClass)
targetClass.removeModifier(KtTokens.INNER_KEYWORD)
fixedElements.filterIsInstance<KtQualifiedExpression>().forEach { ShortenReferences.DEFAULT.process(it) }
}
private fun fixReceiverOnCallSite(targetClass: KtClass): List<PsiElement> {
val containingClass = targetClass.getStrictParentOfType<KtClass>() ?: return emptyList()
val bindingContext = containingClass.analyze(BodyResolveMode.PARTIAL)
val fqName =
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, containingClass]?.fqNameOrNull()?.asString() ?: return emptyList()
val psiFactory = KtPsiFactory(targetClass)
val newReceiver = psiFactory.createExpression(fqName)
return ReferencesSearch.search(targetClass, targetClass.useScope).mapNotNull {
val callExpression = it.element.parent as? KtCallExpression ?: return@mapNotNull null
val qualifiedExpression = callExpression.getQualifiedExpressionForSelector()
val parentClass = callExpression.getStrictParentOfType<KtClass>()
when {
// Explicit receiver
qualifiedExpression != null ->
if (parentClass == containingClass) {
qualifiedExpression.replace(callExpression)
} else {
qualifiedExpression.receiverExpression.replace(newReceiver)
}
// Implicit receiver
else -> callExpression.replace(psiFactory.createExpressionByPattern("$0.$1", newReceiver, callExpression))
}
}
}
}
}
@@ -8184,6 +8184,36 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti
runTest("testData/inspectionsLocal/redundantInnerClassModifier/inLocalClass.kt");
}
@TestMetadata("innerClassConstructorCall.kt")
public void testInnerClassConstructorCall() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/innerClassConstructorCall.kt");
}
@TestMetadata("innerClassConstructorCall2.kt")
public void testInnerClassConstructorCall2() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/innerClassConstructorCall2.kt");
}
@TestMetadata("innerClassConstructorCall3.kt")
public void testInnerClassConstructorCall3() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/innerClassConstructorCall3.kt");
}
@TestMetadata("innerClassConstructorCall4.kt")
public void testInnerClassConstructorCall4() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/innerClassConstructorCall4.kt");
}
@TestMetadata("innerClassConstructorCall5.kt")
public void testInnerClassConstructorCall5() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/innerClassConstructorCall5.kt");
}
@TestMetadata("innerClassConstructorCall6.kt")
public void testInnerClassConstructorCall6() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/innerClassConstructorCall6.kt");
}
@TestMetadata("noOuterClassMemberReference.kt")
public void testNoOuterClassMemberReference() throws Exception {
runTest("testData/inspectionsLocal/redundantInnerClassModifier/noOuterClassMemberReference.kt");
@@ -0,0 +1,9 @@
package test
class Test {
<caret>inner class Inner
fun foo() {
Inner()
}
}
@@ -0,0 +1,9 @@
package test
class Test {
class Inner
fun foo() {
Inner()
}
}
@@ -0,0 +1,9 @@
package test
class Test {
<caret>inner class Inner
fun foo() {
this.Inner()
}
}
@@ -0,0 +1,9 @@
package test
class Test {
class Inner
fun foo() {
Inner()
}
}
@@ -0,0 +1,9 @@
package test
class Test {
<caret>inner class Inner
fun foo(t: Test) {
t.Inner()
}
}
@@ -0,0 +1,9 @@
package test
class Test {
class Inner
fun foo(t: Test) {
Inner()
}
}
@@ -0,0 +1,9 @@
package test
class Test {
<caret>inner class Inner
}
fun foo(t: Test) {
t.Inner()
}
@@ -0,0 +1,9 @@
package test
class Test {
class Inner
}
fun foo(t: Test) {
Test.Inner()
}
@@ -0,0 +1,11 @@
package test
class Test {
<caret>inner class Foo {
inner class Bar
}
}
fun Test.foo(t: Test) {
Foo().Bar()
}
@@ -0,0 +1,11 @@
package test
class Test {
class Foo {
inner class Bar
}
}
fun Test.foo(t: Test) {
Test.Foo().Bar()
}
@@ -0,0 +1,11 @@
package test
class Test {
class Foo {
<caret>inner class Bar
}
}
fun Test.foo(t: Test) {
Test.Foo().Bar()
}
@@ -0,0 +1,11 @@
package test
class Test {
class Foo {
class Bar
}
}
fun Test.foo(t: Test) {
Test.Foo.Bar()
}