Fix fq-names of top-level extensions in intentions.

This commit is contained in:
Ilya Gorbunov
2015-12-07 05:40:50 +03:00
parent 5df94da216
commit d296d91272
12 changed files with 90 additions and 8 deletions
@@ -34,7 +34,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
public class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(javaClass(), "Add indices to 'for' loop"), LowPriorityAction {
private val WITH_INDEX_FQ_NAME = "kotlin.withIndex"
private val WITH_INDEX_NAME = "withIndex"
private val WITH_INDEX_FQ_NAMES = listOf("collections", "sequences", "text", "ranges").map { "kotlin.$it.$WITH_INDEX_NAME" }.toSet()
override fun applicabilityRange(element: KtForExpression): TextRange? {
if (element.loopParameter == null) return null
@@ -43,14 +44,14 @@ public class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpre
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = loopRange.getResolvedCall(bindingContext)
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() == WITH_INDEX_FQ_NAME) return null // already withIndex() call
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() in WITH_INDEX_FQ_NAMES) return null // already withIndex() call
val resolutionScope = element.getResolutionScope(bindingContext, element.getResolutionFacade())
val potentialExpression = createWithIndexExpression(loopRange)
val newBindingContext = potentialExpression.analyzeInContext(resolutionScope, loopRange)
val newResolvedCall = potentialExpression.getResolvedCall(newBindingContext) ?: return null
if (newResolvedCall.resultingDescriptor.fqNameUnsafe.asString() != WITH_INDEX_FQ_NAME) return null
if (newResolvedCall.resultingDescriptor.fqNameUnsafe.asString() !in WITH_INDEX_FQ_NAMES) return null
return TextRange(element.startOffset, element.body?.startOffset ?: element.endOffset)
}
@@ -99,6 +100,6 @@ public class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpre
}
private fun createWithIndexExpression(originalExpression: KtExpression): KtExpression {
return KtPsiFactory(originalExpression).createExpressionByPattern("$0.withIndex()", originalExpression)
return KtPsiFactory(originalExpression).createExpressionByPattern("$0.$WITH_INDEX_NAME()", originalExpression)
}
}
@@ -25,8 +25,12 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
public class ConvertForEachToForLoopIntention : SelfTargetingOffsetIndependentIntention<KtSimpleNameExpression>(javaClass(), "Replace with a 'for' loop") {
private val FOR_EACH_NAME = "forEach"
private val FOR_EACH_FQ_NAMES = listOf("collections", "sequences", "text", "ranges").map { "kotlin.$it.$FOR_EACH_NAME" }.toSet()
override fun isApplicableTo(element: KtSimpleNameExpression): Boolean {
if (element.getReferencedName() != "forEach") return false
if (element.getReferencedName() != FOR_EACH_NAME) return false
val data = extractData(element) ?: return false
if (data.functionLiteral.getValueParameters().size() > 1) return false
@@ -61,7 +65,7 @@ public class ConvertForEachToForLoopIntention : SelfTargetingOffsetIndependentIn
} ?: return null) as KtExpression //TODO: submit bug
val resolvedCall = expression.getResolvedCall(expression.analyze()) ?: return null
if (DescriptorUtils.getFqName(resolvedCall.getResultingDescriptor()).toString() != "kotlin.forEach") return null
if (DescriptorUtils.getFqName(resolvedCall.getResultingDescriptor()).toString() !in FOR_EACH_FQ_NAMES) return null
val receiver = resolvedCall.getCall().getExplicitReceiver() as? ExpressionReceiver ?: return null
val argument = resolvedCall.getCall().getValueArguments().singleOrNull() ?: return null
@@ -41,7 +41,8 @@ public class RemoveForLoopIndicesInspection : IntentionBasedInspection<KtForExpr
}
public class RemoveForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(javaClass(), "Remove indices in 'for' loop") {
private val WITH_INDEX_FQ_NAME = "kotlin.withIndex"
private val WITH_INDEX_NAME = "withIndex"
private val WITH_INDEX_FQ_NAMES = listOf("collections", "sequences", "text", "ranges").map { "kotlin.$it.$WITH_INDEX_NAME" }.toSet()
override fun applicabilityRange(element: KtForExpression): TextRange? {
val loopRange = element.loopRange as? KtDotQualifiedExpression ?: return null
@@ -51,7 +52,7 @@ public class RemoveForLoopIndicesIntention : SelfTargetingRangeIntention<KtForEx
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = loopRange.getResolvedCall(bindingContext)
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() != WITH_INDEX_FQ_NAME) return null
if (resolvedCall?.resultingDescriptor?.fqNameUnsafe?.asString() !in WITH_INDEX_FQ_NAMES) return null
val indexVar = multiParameter.entries[0]
if (ReferencesSearch.search(indexVar).any()) return null
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo() {
val x = "abcd"
x.forEach<caret> { it.equals('a') }
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
fun foo() {
val x = "abcd"
for (it in x) {
it.equals('a')
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo() {
val x = (1..4).asSequence()
x.forEach<caret> { it.equals(1) }
}
@@ -0,0 +1,8 @@
// WITH_RUNTIME
fun foo() {
val x = (1..4).asSequence()
for (it in x) {
it.equals(1)
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo(bar: CharSequence) {
for ((i<caret>,a) in bar.withIndex()) {
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo(bar: CharSequence) {
for (a in bar) {
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo(bar: Sequence<String>) {
for ((i<caret>,a) in bar.withIndex()) {
}
}
@@ -0,0 +1,6 @@
// WITH_RUNTIME
fun foo(bar: Sequence<String>) {
for (a in bar) {
}
}
@@ -3187,6 +3187,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
doTest(fileName);
}
@TestMetadata("simpleCharSequence.kt")
public void testSimpleCharSequence() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/simpleCharSequence.kt");
doTest(fileName);
}
@TestMetadata("simpleSequence.kt")
public void testSimpleSequence() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/simpleSequence.kt");
doTest(fileName);
}
@TestMetadata("typeArgumentPresent.kt")
public void testTypeArgumentPresent() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/typeArgumentPresent.kt");
@@ -6932,6 +6944,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeForLoopIndices/simpleLoopWithIndices.kt");
doTest(fileName);
}
@TestMetadata("simpleLoopWithIndicesOverCharSequence.kt")
public void testSimpleLoopWithIndicesOverCharSequence() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeForLoopIndices/simpleLoopWithIndicesOverCharSequence.kt");
doTest(fileName);
}
@TestMetadata("simpleLoopWithIndicesOverSequence.kt")
public void testSimpleLoopWithIndicesOverSequence() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeForLoopIndices/simpleLoopWithIndicesOverSequence.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/intentions/removeUnnecessaryParentheses")