KT-15311 "Add Import" intention generates incorrect code
#KT-15311 fixed
This commit is contained in:
committed by
Simon Ogorodnik
parent
1613ad0505
commit
cd73d17bd4
@@ -128,6 +128,10 @@ inline fun <reified T : PsiElement> PsiElement.getNonStrictParentOfType(): T? {
|
|||||||
return PsiTreeUtil.getParentOfType(this, T::class.java, false)
|
return PsiTreeUtil.getParentOfType(this, T::class.java, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : PsiElement> PsiElement.getTopmostParentOfType(): T? {
|
||||||
|
return PsiTreeUtil.getTopmostParentOfType(this, T::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
inline fun <reified T : PsiElement> PsiElement.getChildOfType(): T? {
|
inline fun <reified T : PsiElement> PsiElement.getChildOfType(): T? {
|
||||||
return PsiTreeUtil.getChildOfType(this, T::class.java)
|
return PsiTreeUtil.getChildOfType(this, T::class.java)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
|
|||||||
import org.jetbrains.kotlin.idea.util.ImportDescriptorResult
|
import org.jetbrains.kotlin.idea.util.ImportDescriptorResult
|
||||||
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
import org.jetbrains.kotlin.idea.util.ImportInsertHelper
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
|
||||||
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
|
|
||||||
import org.jetbrains.kotlin.psi.KtUserType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
@@ -37,25 +34,36 @@ class ImportMemberIntention : SelfTargetingOffsetIndependentIntention<KtNameRefe
|
|||||||
KtNameReferenceExpression::class.java,
|
KtNameReferenceExpression::class.java,
|
||||||
"Add import for member"
|
"Add import for member"
|
||||||
){
|
){
|
||||||
|
|
||||||
|
private fun getFullQualifier(element: KtNameReferenceExpression): KtQualifiedExpression?
|
||||||
|
= element.getTopmostParentOfType<KtQualifiedExpression>()
|
||||||
|
|
||||||
override fun isApplicableTo(element: KtNameReferenceExpression): Boolean {
|
override fun isApplicableTo(element: KtNameReferenceExpression): Boolean {
|
||||||
val qualifiedElement = element.getQualifiedElement()
|
if (element.getQualifiedElement() == element) return false //Ignore simple name expressions
|
||||||
if (qualifiedElement == element) return false
|
|
||||||
|
val qualifiedExpression = getFullQualifier(element) ?: element.getQualifiedElement()
|
||||||
|
|
||||||
if (element.isInImportDirective()) return false
|
if (element.isInImportDirective()) return false
|
||||||
|
|
||||||
val fqName = targetFqName(qualifiedElement) ?: return false
|
val fqName = targetFqName(qualifiedExpression) ?: return false
|
||||||
|
|
||||||
text = "Add import for '${fqName.asString()}'"
|
text = "Add import for '${fqName.asString()}'"
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: KtNameReferenceExpression, editor: Editor?) {
|
override fun applyTo(element: KtNameReferenceExpression, editor: Editor?) {
|
||||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
|
||||||
val targets = element.mainReference.resolveToDescriptors(bindingContext)
|
val qualifiedElement = getFullQualifier(element)
|
||||||
|
|
||||||
|
// If expression is fqn reference, take full qualified selector, otherwise (Type reference) take element
|
||||||
|
val targetElement = qualifiedElement?.selectorExpression?.getQualifiedElementSelector() ?: element
|
||||||
|
|
||||||
|
val bindingContext = targetElement.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
val targets = targetElement.mainReference?.resolveToDescriptors(bindingContext) ?: return
|
||||||
val fqName = targets.map { it.importableFqName!! }.single()
|
val fqName = targets.map { it.importableFqName!! }.single()
|
||||||
|
|
||||||
val file = element.containingKtFile
|
val file = targetElement.containingKtFile
|
||||||
val helper = ImportInsertHelper.getInstance(element.project)
|
val helper = ImportInsertHelper.getInstance(targetElement.project)
|
||||||
if (helper.importDescriptor(file, targets.first()) == ImportDescriptorResult.FAIL) return
|
if (helper.importDescriptor(file, targets.first()) == ImportDescriptorResult.FAIL) return
|
||||||
|
|
||||||
val qualifiedExpressions = file.collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression ->
|
val qualifiedExpressions = file.collectDescendantsOfType<KtDotQualifiedExpression> { qualifiedExpression ->
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'java.util.ArrayList'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val myList = java.util<caret>.ArrayList<Int>()
|
||||||
|
val otherList = java.util.ArrayList<String>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
// INTENTION_TEXT: "Add import for 'java.util.ArrayList'"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val myList = ArrayList<Int>()
|
||||||
|
val otherList = ArrayList<String>()
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
object A {
|
||||||
|
var some: Any? = null
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun usage() {
|
||||||
|
println(p.A.some?.<caret>toString())
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
object Container {
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
object Container {
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'p.Container.Nested'"
|
||||||
|
fun test() {
|
||||||
|
p.Container.<caret>Nested()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import p.Container.Nested
|
||||||
|
|
||||||
|
// INTENTION_TEXT: "Add import for 'p.Container.Nested'"
|
||||||
|
fun test() {
|
||||||
|
Nested()
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
object Container {
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package p
|
||||||
|
|
||||||
|
object Container {
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// INTENTION_TEXT: "Add import for 'p.Container.Nested'"
|
||||||
|
fun test() {
|
||||||
|
p.<caret>Container.Nested()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import p.Container.Nested
|
||||||
|
|
||||||
|
// INTENTION_TEXT: "Add import for 'p.Container.Nested'"
|
||||||
|
fun test() {
|
||||||
|
Nested()
|
||||||
|
}
|
||||||
@@ -7850,6 +7850,30 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FqReference.kt")
|
||||||
|
public void testFqReference() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReference.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FqReferenceCall.kt")
|
||||||
|
public void testFqReferenceCall() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReferenceCall.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FqReferenceForNestedMember.kt")
|
||||||
|
public void testFqReferenceForNestedMember() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReferenceForNestedMember.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("FqReferenceForNestedMember2.kt")
|
||||||
|
public void testFqReferenceForNestedMember2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FqReferenceForNestedMember2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("FromCompanionObject.kt")
|
@TestMetadata("FromCompanionObject.kt")
|
||||||
public void testFromCompanionObject() throws Exception {
|
public void testFromCompanionObject() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FromCompanionObject.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/importMember/FromCompanionObject.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user