Checking that target collection is always the same
This commit is contained in:
@@ -67,7 +67,7 @@ fun KtCallableDeclaration.hasUsages(inElements: Collection<KtElement>): Boolean
|
|||||||
// return ReferencesSearch.search(this, LocalSearchScope(inElements.toTypedArray())).any()
|
// return ReferencesSearch.search(this, LocalSearchScope(inElements.toTypedArray())).any()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtProperty.hasWriteUsages(): Boolean {
|
fun KtVariableDeclaration.hasWriteUsages(): Boolean {
|
||||||
assert(this.isPhysical)
|
assert(this.isPhysical)
|
||||||
if (!isVar) return false
|
if (!isVar) return false
|
||||||
return ReferencesSearch.search(this, useScope).any {
|
return ReferencesSearch.search(this, useScope).any {
|
||||||
@@ -75,23 +75,23 @@ fun KtProperty.hasWriteUsages(): Boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtProperty.countUsages(inElement: KtElement): Int {
|
fun KtCallableDeclaration.countUsages(inElement: KtElement): Int {
|
||||||
assert(this.isPhysical)
|
assert(this.isPhysical)
|
||||||
return ReferencesSearch.search(this, LocalSearchScope(inElement)).count()
|
return ReferencesSearch.search(this, LocalSearchScope(inElement)).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtProperty.countUsages(inElements: Collection<KtElement>): Int {
|
fun KtCallableDeclaration.countUsages(inElements: Collection<KtElement>): Int {
|
||||||
assert(this.isPhysical)
|
assert(this.isPhysical)
|
||||||
// TODO: it's a temporary workaround about strange dead-lock when running inspections
|
// TODO: it's a temporary workaround about strange dead-lock when running inspections
|
||||||
return inElements.sumBy { ReferencesSearch.search(this, LocalSearchScope(it)).count() }
|
return inElements.sumBy { ReferencesSearch.search(this, LocalSearchScope(it)).count() }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtProperty.countUsages(): Int {
|
fun KtCallableDeclaration.countUsages(): Int {
|
||||||
assert(this.isPhysical)
|
assert(this.isPhysical)
|
||||||
return ReferencesSearch.search(this, useScope).count()
|
return ReferencesSearch.search(this, useScope).count()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtProperty.countWriteUsages(inElement: KtElement): Int {
|
fun KtVariableDeclaration.countWriteUsages(inElement: KtElement): Int {
|
||||||
assert(this.isPhysical)
|
assert(this.isPhysical)
|
||||||
if (!isVar) return 0
|
if (!isVar) return 0
|
||||||
return ReferencesSearch.search(this, LocalSearchScope(inElement)).count {
|
return ReferencesSearch.search(this, LocalSearchScope(inElement)).count {
|
||||||
@@ -99,6 +99,14 @@ fun KtProperty.countWriteUsages(inElement: KtElement): Int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtVariableDeclaration.hasWriteUsages(inElement: KtElement): Boolean {
|
||||||
|
assert(this.isPhysical)
|
||||||
|
if (!isVar) return false
|
||||||
|
return ReferencesSearch.search(this, LocalSearchScope(inElement)).any {
|
||||||
|
(it as? KtSimpleNameReference)?.element?.readWriteAccess(useResolveForReadWrite = true)?.isWrite == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun KtExpressionWithLabel.targetLoop(): KtLoopExpression? {
|
fun KtExpressionWithLabel.targetLoop(): KtLoopExpression? {
|
||||||
val label = getTargetLabel()
|
val label = getTargetLabel()
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
@@ -148,4 +156,3 @@ fun PsiChildRange.withoutLastStatement(): PsiChildRange {
|
|||||||
val newLast = last!!.siblings(forward = false, withItself = false).first { it !is PsiWhiteSpace }
|
val newLast = last!!.siblings(forward = false, withItself = false).first { it !is PsiWhiteSpace }
|
||||||
return PsiChildRange(first, newLast)
|
return PsiChildRange(first, newLast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -93,13 +93,15 @@ class AddToCollectionTransformation(
|
|||||||
//TODO: it can be implicit 'this' too
|
//TODO: it can be implicit 'this' too
|
||||||
val qualifiedExpression = statement as? KtDotQualifiedExpression ?: return null
|
val qualifiedExpression = statement as? KtDotQualifiedExpression ?: return null
|
||||||
val targetCollection = qualifiedExpression.receiverExpression
|
val targetCollection = qualifiedExpression.receiverExpression
|
||||||
//TODO: check that receiver is stable!
|
|
||||||
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
|
val callExpression = qualifiedExpression.selectorExpression as? KtCallExpression ?: return null
|
||||||
if (callExpression.getCallNameExpression()?.getReferencedName() != "add") return null
|
if (callExpression.getCallNameExpression()?.getReferencedName() != "add") return null
|
||||||
//TODO: check that it's MutableCollection's add
|
//TODO: check that it's MutableCollection's add
|
||||||
val argument = callExpression.valueArguments.singleOrNull() ?: return null
|
val argument = callExpression.valueArguments.singleOrNull() ?: return null
|
||||||
val argumentValue = argument.getArgumentExpression() ?: return null
|
val argumentValue = argument.getArgumentExpression() ?: return null
|
||||||
|
|
||||||
|
//TODO: should work when ".asSequence()" used when there are other read-usages in the loop
|
||||||
|
if (!targetCollection.isStableInLoop(state.outerLoop, checkNoOtherUsagesInLoop = true)) return null
|
||||||
|
|
||||||
if (state.indexVariable == null) {
|
if (state.indexVariable == null) {
|
||||||
matchWithCollectionInitializationReplacement(state, targetCollection, argumentValue)
|
matchWithCollectionInitializationReplacement(state, targetCollection, argumentValue)
|
||||||
?.let { return it }
|
?.let { return it }
|
||||||
|
|||||||
@@ -19,10 +19,12 @@ package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
|||||||
import com.intellij.openapi.util.Key
|
import com.intellij.openapi.util.Key
|
||||||
import com.intellij.openapi.util.UserDataHolderBase
|
import com.intellij.openapi.util.UserDataHolderBase
|
||||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.core.copied
|
import org.jetbrains.kotlin.idea.core.copied
|
||||||
import org.jetbrains.kotlin.idea.core.replaced
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
import org.jetbrains.kotlin.idea.imports.importableFqName
|
import org.jetbrains.kotlin.idea.imports.importableFqName
|
||||||
@@ -257,3 +259,28 @@ fun canSwapExecutionOrder(expressionBefore: KtExpression, expressionAfter: KtExp
|
|||||||
//TODO: more cases
|
//TODO: more cases
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtExpression.isStableInLoop(loop: KtLoopExpression, checkNoOtherUsagesInLoop: Boolean): Boolean {
|
||||||
|
when {
|
||||||
|
isConstant() -> return true
|
||||||
|
|
||||||
|
this is KtSimpleNameExpression -> {
|
||||||
|
val declaration = mainReference.resolve() as? KtCallableDeclaration ?: return false
|
||||||
|
if (loop.isAncestor(declaration)) return false // should be declared outside the loop
|
||||||
|
val variable = declaration.resolveToDescriptorIfAny() as? VariableDescriptor ?: return false
|
||||||
|
|
||||||
|
if (checkNoOtherUsagesInLoop && declaration.countUsages(loop) > 1) return false
|
||||||
|
|
||||||
|
if (!variable.isVar) return true
|
||||||
|
if (declaration !is KtVariableDeclaration) return false
|
||||||
|
if (!KtPsiUtil.isLocal(declaration)) return false // it's difficult to analyze non-local declarations
|
||||||
|
//TODO: check that there are no local functions or lambdas that can modify it implicitly
|
||||||
|
return !declaration.hasWriteUsages(loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: qualified expression?
|
||||||
|
//TODO: this
|
||||||
|
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<MutableCollection<Int>>) {
|
||||||
|
<caret>for (collection in list) {
|
||||||
|
collection.add(collection.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
var globalCollection = ArrayList<Int>()
|
||||||
|
|
||||||
|
fun foo(list: List<Collection<Int>>) {
|
||||||
|
<caret>for (collection in list) {
|
||||||
|
globalCollection.add(collection.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'mapTo(){}'"
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
fun foo(list: List<MutableCollection<Int>>) {
|
||||||
|
var target: MutableCollection<Int>()
|
||||||
|
target = ArrayList<Int>()
|
||||||
|
|
||||||
|
<caret>for (collection in list) {
|
||||||
|
target.add(collection.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.size > 100) {
|
||||||
|
target = ArrayList<Int>()
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Replace with 'mapTo(){}'"
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
fun foo(list: List<MutableCollection<Int>>) {
|
||||||
|
var target: MutableCollection<Int>()
|
||||||
|
<caret>target = list.mapTo(ArrayList<Int>()) { it.size }
|
||||||
|
|
||||||
|
if (target.size > 100) {
|
||||||
|
target = ArrayList<Int>()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
// INTENTION_TEXT: "Replace with 'filter{}.mapTo(){}'"
|
// IS_APPLICABLE: false
|
||||||
import java.util.ArrayList
|
import java.util.ArrayList
|
||||||
|
|
||||||
fun foo(list: List<String>): List<Int> {
|
fun foo(list: List<String>): List<Int> {
|
||||||
|
|||||||
-11
@@ -1,11 +0,0 @@
|
|||||||
// WITH_RUNTIME
|
|
||||||
// INTENTION_TEXT: "Replace with 'filter{}.mapTo(){}'"
|
|
||||||
import java.util.ArrayList
|
|
||||||
|
|
||||||
fun foo(list: List<String>): List<Int> {
|
|
||||||
val result = ArrayList<Int>()
|
|
||||||
<caret>list
|
|
||||||
.filter { it.length > result.size }
|
|
||||||
.mapTo(result) { it.hashCode() }
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user