Fix fq-names of top-level extensions in intentions.
This commit is contained in:
@@ -34,7 +34,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
public class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpression>(javaClass(), "Add indices to 'for' loop"), LowPriorityAction {
|
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? {
|
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
||||||
if (element.loopParameter == null) return null
|
if (element.loopParameter == null) return null
|
||||||
@@ -43,14 +44,14 @@ public class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpre
|
|||||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
|
||||||
val resolvedCall = loopRange.getResolvedCall(bindingContext)
|
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 resolutionScope = element.getResolutionScope(bindingContext, element.getResolutionFacade())
|
||||||
val potentialExpression = createWithIndexExpression(loopRange)
|
val potentialExpression = createWithIndexExpression(loopRange)
|
||||||
|
|
||||||
val newBindingContext = potentialExpression.analyzeInContext(resolutionScope, loopRange)
|
val newBindingContext = potentialExpression.analyzeInContext(resolutionScope, loopRange)
|
||||||
val newResolvedCall = potentialExpression.getResolvedCall(newBindingContext) ?: return null
|
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)
|
return TextRange(element.startOffset, element.body?.startOffset ?: element.endOffset)
|
||||||
}
|
}
|
||||||
@@ -99,6 +100,6 @@ public class AddForLoopIndicesIntention : SelfTargetingRangeIntention<KtForExpre
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun createWithIndexExpression(originalExpression: KtExpression): KtExpression {
|
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
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||||
|
|
||||||
public class ConvertForEachToForLoopIntention : SelfTargetingOffsetIndependentIntention<KtSimpleNameExpression>(javaClass(), "Replace with a 'for' loop") {
|
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 {
|
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
|
val data = extractData(element) ?: return false
|
||||||
if (data.functionLiteral.getValueParameters().size() > 1) 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
|
} ?: return null) as KtExpression //TODO: submit bug
|
||||||
|
|
||||||
val resolvedCall = expression.getResolvedCall(expression.analyze()) ?: return null
|
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 receiver = resolvedCall.getCall().getExplicitReceiver() as? ExpressionReceiver ?: return null
|
||||||
val argument = resolvedCall.getCall().getValueArguments().singleOrNull() ?: 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") {
|
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? {
|
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
||||||
val loopRange = element.loopRange as? KtDotQualifiedExpression ?: return null
|
val loopRange = element.loopRange as? KtDotQualifiedExpression ?: return null
|
||||||
@@ -51,7 +52,7 @@ public class RemoveForLoopIndicesIntention : SelfTargetingRangeIntention<KtForEx
|
|||||||
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
val bindingContext = element.analyze(BodyResolveMode.PARTIAL)
|
||||||
|
|
||||||
val resolvedCall = loopRange.getResolvedCall(bindingContext)
|
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]
|
val indexVar = multiParameter.entries[0]
|
||||||
if (ReferencesSearch.search(indexVar).any()) return null
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(bar: CharSequence) {
|
||||||
|
for ((i<caret>,a) in bar.withIndex()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(bar: CharSequence) {
|
||||||
|
for (a in bar) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(bar: Sequence<String>) {
|
||||||
|
for ((i<caret>,a) in bar.withIndex()) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -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);
|
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")
|
@TestMetadata("typeArgumentPresent.kt")
|
||||||
public void testTypeArgumentPresent() throws Exception {
|
public void testTypeArgumentPresent() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/typeArgumentPresent.kt");
|
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");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/removeForLoopIndices/simpleLoopWithIndices.kt");
|
||||||
doTest(fileName);
|
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")
|
@TestMetadata("idea/testData/intentions/removeUnnecessaryParentheses")
|
||||||
|
|||||||
Reference in New Issue
Block a user