Initial implementation of loop-to-call-chain intention
This commit is contained in:
+2133
File diff suppressed because it is too large
Load Diff
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.psi;
|
package org.jetbrains.kotlin.psi;
|
||||||
|
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.KtNodeTypes;
|
import org.jetbrains.kotlin.KtNodeTypes;
|
||||||
@@ -48,7 +49,12 @@ public class KtForExpression extends KtLoopExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Nullable @IfNotParsed
|
@Nullable @IfNotParsed
|
||||||
public ASTNode getInKeywordNode() {
|
public PsiElement getInKeyword() {
|
||||||
return getNode().findChildByType(KtTokens.IN_KEYWORD);
|
return findChildByType(KtTokens.IN_KEYWORD);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public PsiElement getForKeyword() {
|
||||||
|
return findChildByType(KtTokens.FOR_KEYWORD);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -691,6 +691,11 @@ public abstract class KotlinBuiltIns {
|
|||||||
return getString().getDefaultType();
|
return getString().getDefaultType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public KotlinType getIterableType() {
|
||||||
|
return getIterable().getDefaultType();
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public KotlinType getArrayElementType(@NotNull KotlinType arrayType) {
|
public KotlinType getArrayElementType(@NotNull KotlinType arrayType) {
|
||||||
if (isArray(arrayType)) {
|
if (isArray(arrayType)) {
|
||||||
|
|||||||
+10
-6
@@ -37,18 +37,22 @@ abstract class IntentionBasedInspection<TElement : PsiElement>(
|
|||||||
) : AbstractKotlinInspection() {
|
) : AbstractKotlinInspection() {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
intention: SelfTargetingRangeIntention<TElement>
|
intention: SelfTargetingRangeIntention<TElement>,
|
||||||
) : this(listOf(IntentionData(intention)), null, intention.elementType)
|
problemText: String? = null
|
||||||
|
) : this(listOf(IntentionData(intention)), problemText, intention.elementType)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
intention: SelfTargetingRangeIntention<TElement>,
|
intention: SelfTargetingRangeIntention<TElement>,
|
||||||
additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean
|
additionalChecker: (TElement, IntentionBasedInspection<TElement>) -> Boolean,
|
||||||
) : this(listOf(IntentionData(intention, additionalChecker)), null, intention.elementType)
|
problemText: String? = null
|
||||||
|
) : this(listOf(IntentionData(intention, additionalChecker)), problemText, intention.elementType)
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
intention: SelfTargetingRangeIntention<TElement>,
|
intention: SelfTargetingRangeIntention<TElement>,
|
||||||
additionalChecker: (TElement) -> Boolean
|
additionalChecker: (TElement) -> Boolean,
|
||||||
) : this(listOf(IntentionData(intention, { element, inspection -> additionalChecker(element) } )), null, intention.elementType)
|
problemText: String? = null
|
||||||
|
) : this(listOf(IntentionData(intention, { element, inspection -> additionalChecker(element) } )), problemText, intention.elementType)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
data class IntentionData<TElement : PsiElement>(
|
data class IntentionData<TElement : PsiElement>(
|
||||||
|
|||||||
@@ -25,12 +25,12 @@ import java.util.*
|
|||||||
class KtForLoopInReference(element: KtForExpression) : KtMultiReference<KtForExpression>(element) {
|
class KtForLoopInReference(element: KtForExpression) : KtMultiReference<KtForExpression>(element) {
|
||||||
|
|
||||||
override fun getRangeInElement(): TextRange {
|
override fun getRangeInElement(): TextRange {
|
||||||
val inKeywordNode = expression.inKeywordNode
|
val inKeyword = expression.inKeyword
|
||||||
if (inKeywordNode == null)
|
if (inKeyword == null)
|
||||||
return TextRange.EMPTY_RANGE
|
return TextRange.EMPTY_RANGE
|
||||||
|
|
||||||
val offset = inKeywordNode.psi!!.startOffsetInParent
|
val offset = inKeyword.startOffsetInParent
|
||||||
return TextRange(offset, offset + inKeywordNode.textLength)
|
return TextRange(offset, offset + inKeyword.textLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
|
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This inspection reports for-loops that can be replaced with a sequence of stdlib-operations (like "map", "filter" etc)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<spot>return list.firstOrNull { s -> s.length > 0 }</spot>
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<spot>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null</spot>
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention converts a for-loop into a sequence of stdlib-operations (like "map", "filter" etc)
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1013,6 +1013,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.loopToCallChain.LoopToCallChainIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.SwapBinaryExpressionIntention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.SwapBinaryExpressionIntention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
@@ -1507,6 +1512,13 @@
|
|||||||
language="kotlin"
|
language="kotlin"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.loopToCallChain.LoopToCallChainInspection"
|
||||||
|
displayName="Loop can be replaced with stdlib operations"
|
||||||
|
groupName="Kotlin"
|
||||||
|
enabledByDefault="true"
|
||||||
|
level="INFO"
|
||||||
|
/>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ConflictingExtensionPropertyInspection"
|
||||||
displayName="Extension property conflicting with synthetic one"
|
displayName="Extension property conflicting with synthetic one"
|
||||||
groupName="Kotlin"
|
groupName="Kotlin"
|
||||||
|
|||||||
@@ -32,9 +32,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
class RemoveForLoopIndicesInspection : IntentionBasedInspection<KtForExpression>(
|
class RemoveForLoopIndicesInspection : IntentionBasedInspection<KtForExpression>(
|
||||||
listOf(IntentionBasedInspection.IntentionData(RemoveForLoopIndicesIntention())),
|
RemoveForLoopIndicesIntention(),
|
||||||
"Index is not used in the loop body",
|
problemText = "Index is not used in the loop body"
|
||||||
KtForExpression::class.java
|
|
||||||
) {
|
) {
|
||||||
override val problemHighlightType: ProblemHighlightType
|
override val problemHighlightType: ProblemHighlightType
|
||||||
get() = ProblemHighlightType.LIKE_UNUSED_SYMBOL
|
get() = ProblemHighlightType.LIKE_UNUSED_SYMBOL
|
||||||
|
|||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
||||||
|
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import org.jetbrains.kotlin.idea.core.moveCaret
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.SelfTargetingRangeIntention
|
||||||
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||||
|
|
||||||
|
class LoopToCallChainInspection : IntentionBasedInspection<KtForExpression>(
|
||||||
|
LoopToCallChainIntention(),
|
||||||
|
problemText = "Loop can be replaced with stdlib operations")
|
||||||
|
|
||||||
|
class LoopToCallChainIntention : SelfTargetingRangeIntention<KtForExpression>(
|
||||||
|
KtForExpression::class.java,
|
||||||
|
"Replace with stdlib operations"
|
||||||
|
) {
|
||||||
|
override fun applicabilityRange(element: KtForExpression): TextRange? {
|
||||||
|
return if (match(element) != null) element.forKeyword.textRange else null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyTo(element: KtForExpression, editor: Editor?) {
|
||||||
|
val match = match(element)!!
|
||||||
|
val result = convertLoop(element, match)
|
||||||
|
editor?.moveCaret(result.startOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
||||||
|
|
||||||
|
interface ChainedCallGenerator {
|
||||||
|
fun generate(pattern: String, vararg args: Any): KtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Transformation {
|
||||||
|
val inputVariable: KtCallableDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SequenceTransformation : Transformation {
|
||||||
|
val affectsIndex: Boolean
|
||||||
|
|
||||||
|
fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResultTransformation : Transformation {
|
||||||
|
val canIncludeFilter: Boolean
|
||||||
|
val canIncludeMap: Boolean
|
||||||
|
|
||||||
|
val commentSavingRange: PsiChildRange
|
||||||
|
val commentRestoringRange: PsiChildRange
|
||||||
|
|
||||||
|
fun generateCode(
|
||||||
|
chainedCallGenerator: ChainedCallGenerator,
|
||||||
|
filter: FilterOrMap?,
|
||||||
|
map: FilterOrMap?
|
||||||
|
): KtExpression
|
||||||
|
|
||||||
|
fun convertLoop(resultCallChain: KtExpression): KtExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
data class FilterOrMap(val expression: KtExpression, val workingVariable: KtCallableDeclaration)
|
||||||
|
|
||||||
|
data class MatchingState(
|
||||||
|
val statements: Collection<KtExpression>,
|
||||||
|
val workingVariable: KtCallableDeclaration,
|
||||||
|
val indexVariable: KtCallableDeclaration?,
|
||||||
|
val loop: KtForExpression
|
||||||
|
)
|
||||||
|
|
||||||
|
interface SequenceTransformationMatcher {
|
||||||
|
fun match(state: MatchingState): SequenceTransformationMatch?
|
||||||
|
}
|
||||||
|
|
||||||
|
class SequenceTransformationMatch(
|
||||||
|
val transformations: Collection<SequenceTransformation>,
|
||||||
|
val newState: MatchingState
|
||||||
|
) {
|
||||||
|
init {
|
||||||
|
assert(transformations.isNotEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(transformation: SequenceTransformation, newState: MatchingState) : this(listOf(transformation), newState)
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResultTransformationMatcher {
|
||||||
|
fun match(state: MatchingState): ResultTransformationMatch?
|
||||||
|
}
|
||||||
|
|
||||||
|
class ResultTransformationMatch(
|
||||||
|
val resultTransformation: ResultTransformation,
|
||||||
|
val sequenceTransformations: Collection<SequenceTransformation> = listOf()
|
||||||
|
)
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.FindAndAssignTransformation
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.result.FindAndReturnTransformation
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FilterTransformation
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.FlatMapTransformation
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence.MapTransformation
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.negate
|
||||||
|
import org.jetbrains.kotlin.idea.util.CommentSaver
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object MatcherRegistrar {
|
||||||
|
val sequenceMatchers: Collection<SequenceTransformationMatcher> = listOf(
|
||||||
|
FilterTransformation.Matcher,
|
||||||
|
MapTransformation.Matcher,
|
||||||
|
FlatMapTransformation.Matcher
|
||||||
|
)
|
||||||
|
|
||||||
|
val resultMatchers: Collection<ResultTransformationMatcher> = listOf(
|
||||||
|
FindAndReturnTransformation.Matcher,
|
||||||
|
FindAndAssignTransformation.Matcher
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun match(loop: KtForExpression): ResultTransformationMatch? {
|
||||||
|
val sequenceTransformations = ArrayList<SequenceTransformation>()
|
||||||
|
var state = MatchingState(
|
||||||
|
statements = listOf(loop.body ?: return null),
|
||||||
|
workingVariable = loop.loopParameter ?: return null,
|
||||||
|
indexVariable = null,
|
||||||
|
loop = loop
|
||||||
|
)
|
||||||
|
|
||||||
|
MatchLoop@
|
||||||
|
while (true) {
|
||||||
|
val block = state.statements.singleOrNull() as? KtBlockExpression
|
||||||
|
if (block != null) {
|
||||||
|
state = state.copy(statements = block.statements)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (matcher in MatcherRegistrar.resultMatchers) {
|
||||||
|
val match = matcher.match(state)
|
||||||
|
if (match != null) {
|
||||||
|
sequenceTransformations.addAll(match.sequenceTransformations)
|
||||||
|
return ResultTransformationMatch(match.resultTransformation, sequenceTransformations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (matcher in MatcherRegistrar.sequenceMatchers) {
|
||||||
|
val match = matcher.match(state)
|
||||||
|
if (match != null) {
|
||||||
|
sequenceTransformations.addAll(match.transformations)
|
||||||
|
state = match.newState
|
||||||
|
continue@MatchLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: offer to use of .asSequence() as an option
|
||||||
|
fun convertLoop(loop: KtForExpression, matchResult: ResultTransformationMatch): KtExpression {
|
||||||
|
val commentSaver = CommentSaver(matchResult.resultTransformation.commentSavingRange)
|
||||||
|
|
||||||
|
var callChain = matchResult.generateCallChain(loop)
|
||||||
|
|
||||||
|
val result = matchResult.resultTransformation.convertLoop(callChain)
|
||||||
|
|
||||||
|
commentSaver.restore(matchResult.resultTransformation.commentRestoringRange)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ResultTransformationMatch.generateCallChain(loop: KtForExpression): KtExpression {
|
||||||
|
var sequenceTransformations = sequenceTransformations
|
||||||
|
val last = sequenceTransformations.lastOrNull()
|
||||||
|
var lastFilter: FilterOrMap? = null
|
||||||
|
var lastMap: FilterOrMap? = null //TODO
|
||||||
|
if (last is FilterTransformation && resultTransformation.canIncludeFilter) {
|
||||||
|
val condition = if (last.isInverse) last.condition.negate() else last.condition
|
||||||
|
lastFilter = FilterOrMap(condition, last.inputVariable)
|
||||||
|
sequenceTransformations = sequenceTransformations.take(sequenceTransformations.size - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lineBreak = if (sequenceTransformations.isNotEmpty()) "\n" else ""
|
||||||
|
|
||||||
|
var callChain = loop.loopRange!!
|
||||||
|
|
||||||
|
val psiFactory = KtPsiFactory(loop)
|
||||||
|
fun chainedCallGenerator(): ChainedCallGenerator {
|
||||||
|
return object : ChainedCallGenerator {
|
||||||
|
override fun generate(pattern: String, vararg args: Any): KtExpression {
|
||||||
|
val newPattern = "\$${args.size}$lineBreak.$pattern"
|
||||||
|
return psiFactory.createExpressionByPattern(newPattern, *args, callChain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (transformation in sequenceTransformations) {
|
||||||
|
callChain = transformation.generateCode(chainedCallGenerator())
|
||||||
|
}
|
||||||
|
|
||||||
|
callChain = resultTransformation.generateCode(chainedCallGenerator(), lastFilter, lastMap)
|
||||||
|
return callChain
|
||||||
|
}
|
||||||
|
|
||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
|
||||||
|
|
||||||
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
||||||
|
|
||||||
|
class FindAndAssignTransformation(
|
||||||
|
private val loop: KtForExpression,
|
||||||
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
private val stdlibFunName: String,
|
||||||
|
private val initialDeclaration: KtProperty
|
||||||
|
) : ResultTransformation {
|
||||||
|
|
||||||
|
override val commentSavingRange = PsiChildRange(initialDeclaration, loop.unwrapIfLabeled())
|
||||||
|
override val commentRestoringRange = commentSavingRange.withoutLastStatement()
|
||||||
|
|
||||||
|
override val canIncludeFilter: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override val canIncludeMap: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator, filter: FilterOrMap?, map: FilterOrMap?): KtExpression {
|
||||||
|
return if (filter == null) {
|
||||||
|
chainedCallGenerator.generate("$stdlibFunName()")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val lambda = generateLambda(filter.workingVariable, filter.expression)
|
||||||
|
chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertLoop(resultCallChain: KtExpression): KtExpression {
|
||||||
|
initialDeclaration.initializer!!.replace(resultCallChain)
|
||||||
|
loop.deleteWithLabels()
|
||||||
|
|
||||||
|
if (!initialDeclaration.hasWriteUsages()) { // change variable to 'val' if possible
|
||||||
|
initialDeclaration.valOrVarKeyword.replace(KtPsiFactory(initialDeclaration).createValKeyword())
|
||||||
|
}
|
||||||
|
|
||||||
|
return initialDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
object Matcher : ResultTransformationMatcher {
|
||||||
|
override fun match(state: MatchingState): ResultTransformationMatch? {
|
||||||
|
//TODO: pass indexVariable as null if not used
|
||||||
|
if (state.indexVariable != null) return null
|
||||||
|
|
||||||
|
if (state.statements.size != 2) return null
|
||||||
|
|
||||||
|
val breakExpression = state.statements.last() as? KtBreakExpression ?: return null
|
||||||
|
if (!breakExpression.isBreakOrContinueOfLoop(state.loop)) return null
|
||||||
|
|
||||||
|
val binaryExpression = state.statements.first() as? KtBinaryExpression ?: return null
|
||||||
|
if (binaryExpression.operationToken != KtTokens.EQ) return null
|
||||||
|
val left = binaryExpression.left ?: return null
|
||||||
|
val right = binaryExpression.right ?: return null
|
||||||
|
|
||||||
|
//TODO: support also assignment instead of declaration
|
||||||
|
val declarationBeforeLoop = state.loop.previousStatement() as? KtProperty ?: return null
|
||||||
|
val initializer = declarationBeforeLoop.initializer ?: return null
|
||||||
|
if (!left.isVariableReference(declarationBeforeLoop)) return null
|
||||||
|
|
||||||
|
val usageCountInLoop = ReferencesSearch.search(declarationBeforeLoop, LocalSearchScope(state.loop)).count()
|
||||||
|
if (usageCountInLoop != 1) return null // this should be the only usage of this variable inside the loop
|
||||||
|
|
||||||
|
val stdlibFunName = stdlibFunNameForFind(right, initializer, state.workingVariable) ?: return null
|
||||||
|
|
||||||
|
val transformation = FindAndAssignTransformation(state.loop, state.workingVariable, stdlibFunName, declarationBeforeLoop)
|
||||||
|
return ResultTransformationMatch(transformation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.result
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtReturnExpression
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange
|
||||||
|
|
||||||
|
class FindAndReturnTransformation(
|
||||||
|
private val loop: KtForExpression,
|
||||||
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
private val stdlibFunName: String,
|
||||||
|
private val endReturn: KtReturnExpression
|
||||||
|
) : ResultTransformation {
|
||||||
|
|
||||||
|
override val commentSavingRange = PsiChildRange(loop.unwrapIfLabeled(), endReturn)
|
||||||
|
|
||||||
|
override val commentRestoringRange = commentSavingRange.withoutFirstStatement()
|
||||||
|
|
||||||
|
override val canIncludeFilter: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override val canIncludeMap: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator, filter: FilterOrMap?, map: FilterOrMap?): KtExpression {
|
||||||
|
return if (filter == null) {
|
||||||
|
chainedCallGenerator.generate("$stdlibFunName()")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val lambda = generateLambda(filter.workingVariable, filter.expression)
|
||||||
|
chainedCallGenerator.generate("$stdlibFunName $0:'{}'", lambda)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun convertLoop(resultCallChain: KtExpression): KtExpression {
|
||||||
|
endReturn.returnedExpression!!.replace(resultCallChain)
|
||||||
|
loop.deleteWithLabels()
|
||||||
|
return endReturn
|
||||||
|
}
|
||||||
|
|
||||||
|
object Matcher : ResultTransformationMatcher {
|
||||||
|
override fun match(state: MatchingState): ResultTransformationMatch? {
|
||||||
|
//TODO: pass indexVariable as null if not used
|
||||||
|
if (state.indexVariable != null) return null
|
||||||
|
|
||||||
|
val returnInLoop = state.statements.singleOrNull() as? KtReturnExpression ?: return null
|
||||||
|
val returnAfterLoop = state.loop.nextStatement() as? KtReturnExpression ?: return null
|
||||||
|
if (returnInLoop.getLabelName() != returnAfterLoop.getLabelName()) return null
|
||||||
|
|
||||||
|
val returnValueInLoop = returnInLoop.returnedExpression ?: return null
|
||||||
|
val returnValueAfterLoop = returnAfterLoop.returnedExpression ?: return null
|
||||||
|
|
||||||
|
val stdlibFunName = stdlibFunNameForFind(returnValueInLoop, returnValueAfterLoop, state.workingVariable) ?: return null
|
||||||
|
|
||||||
|
val transformation = FindAndReturnTransformation(state.loop, state.workingVariable, stdlibFunName, returnAfterLoop)
|
||||||
|
return ResultTransformationMatch(transformation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.negate
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.blockExpressionsOrSingle
|
||||||
|
|
||||||
|
class FilterTransformation(
|
||||||
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
val condition: KtExpression,
|
||||||
|
val isInverse: Boolean
|
||||||
|
) : SequenceTransformation {
|
||||||
|
|
||||||
|
init {
|
||||||
|
assert(condition.isPhysical)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val affectsIndex: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
val lambda = generateLambda(inputVariable, condition)
|
||||||
|
val name = if (isInverse) "filterNot" else "filter"
|
||||||
|
return chainedCallGenerator.generate("$0$1:'{}'", name, lambda)
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: merge subsequent filters
|
||||||
|
object Matcher : SequenceTransformationMatcher {
|
||||||
|
override fun match(state: MatchingState): SequenceTransformationMatch? {
|
||||||
|
if (state.indexVariable != null) return null //TODO?
|
||||||
|
|
||||||
|
val ifStatement = state.statements.firstOrNull() as? KtIfExpression ?: return null
|
||||||
|
if (ifStatement.`else` != null) return null
|
||||||
|
val condition = ifStatement.condition ?: return null
|
||||||
|
val then = ifStatement.then ?: return null
|
||||||
|
|
||||||
|
if (state.statements.size == 1) {
|
||||||
|
val transformation = createFilterTransformation(state.workingVariable, condition, isInverse = false)
|
||||||
|
val newState = state.copy(statements = listOf(then))
|
||||||
|
return SequenceTransformationMatch(transformation, newState)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val continueExpression = then.blockExpressionsOrSingle().singleOrNull() as? KtContinueExpression ?: return null
|
||||||
|
if (!continueExpression.isBreakOrContinueOfLoop(state.loop)) return null
|
||||||
|
val transformation = createFilterTransformation(state.workingVariable, condition, isInverse = true)
|
||||||
|
val newState = state.copy(statements = state.statements.drop(1))
|
||||||
|
return SequenceTransformationMatch(transformation, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: choose filter or filterNot depending on condition
|
||||||
|
private fun createFilterTransformation(
|
||||||
|
inputVariable: KtCallableDeclaration,
|
||||||
|
condition: KtExpression,
|
||||||
|
isInverse: Boolean): SequenceTransformation {
|
||||||
|
|
||||||
|
val realCondition = if (isInverse) condition.negate() else condition
|
||||||
|
|
||||||
|
if (realCondition is KtIsExpression
|
||||||
|
&& !realCondition.isNegated
|
||||||
|
&& realCondition.leftHandSide.isSimpleName(inputVariable.nameAsSafeName) // we cannot use isVariableReference here because expression can be non-physical
|
||||||
|
) {
|
||||||
|
val typeRef = realCondition.typeReference
|
||||||
|
if (typeRef != null) {
|
||||||
|
return FilterIsInstanceTransformation(inputVariable, typeRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (realCondition is KtBinaryExpression
|
||||||
|
&& realCondition.operationToken == KtTokens.EXCLEQ
|
||||||
|
&& realCondition.right.isNullExpression()
|
||||||
|
&& realCondition.left.isSimpleName(inputVariable.nameAsSafeName)
|
||||||
|
) {
|
||||||
|
return FilterNotNullTransformation(inputVariable)
|
||||||
|
}
|
||||||
|
|
||||||
|
return FilterTransformation(inputVariable, condition, isInverse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilterIsInstanceTransformation(
|
||||||
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
private val type: KtTypeReference
|
||||||
|
) : SequenceTransformation {
|
||||||
|
|
||||||
|
override val affectsIndex: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
return chainedCallGenerator.generate("filterIsInstance<$0>()", type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FilterNotNullTransformation(
|
||||||
|
override val inputVariable: KtCallableDeclaration
|
||||||
|
) : SequenceTransformation {
|
||||||
|
|
||||||
|
override val affectsIndex: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
return chainedCallGenerator.generate("filterNotNull()")
|
||||||
|
}
|
||||||
|
}
|
||||||
+68
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
|
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
|
class FlatMapTransformation(
|
||||||
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
private val transform: KtExpression
|
||||||
|
) : SequenceTransformation {
|
||||||
|
|
||||||
|
init {
|
||||||
|
assert(transform.isPhysical)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val affectsIndex: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
val lambda = generateLambda(inputVariable, transform)
|
||||||
|
return chainedCallGenerator.generate("flatMap$0:'{}'", lambda)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Matcher : SequenceTransformationMatcher {
|
||||||
|
override fun match(state: MatchingState): SequenceTransformationMatch? {
|
||||||
|
if (state.indexVariable != null) return null
|
||||||
|
|
||||||
|
val nestedLoop = state.statements.singleOrNull() as? KtForExpression ?: return null
|
||||||
|
|
||||||
|
val transform = nestedLoop.loopRange ?: return null
|
||||||
|
// check that we iterate over Iterable
|
||||||
|
val nestedSequenceType = transform.analyze(BodyResolveMode.PARTIAL).getType(transform) ?: return null
|
||||||
|
val builtIns = transform.getResolutionFacade().moduleDescriptor.builtIns
|
||||||
|
val iterableType = FuzzyType(builtIns.iterableType, builtIns.iterable.declaredTypeParameters)
|
||||||
|
if (iterableType.checkIsSuperTypeOf(nestedSequenceType) == null) return null
|
||||||
|
|
||||||
|
val newWorkingVariable = nestedLoop.loopParameter ?: return null
|
||||||
|
val loopBody = nestedLoop.body ?: return null
|
||||||
|
val transformation = FlatMapTransformation(state.workingVariable, transform)
|
||||||
|
val newState = state.copy(
|
||||||
|
statements = listOf(loopBody),
|
||||||
|
workingVariable = newWorkingVariable
|
||||||
|
)
|
||||||
|
return SequenceTransformationMatch(transformation, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain.sequence
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.loopToCallChain.*
|
||||||
|
import org.jetbrains.kotlin.psi.KtCallableDeclaration
|
||||||
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtProperty
|
||||||
|
|
||||||
|
class MapTransformation(
|
||||||
|
override val inputVariable: KtCallableDeclaration,
|
||||||
|
val mapping: KtExpression
|
||||||
|
) : SequenceTransformation {
|
||||||
|
|
||||||
|
init {
|
||||||
|
assert(mapping.isPhysical)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val affectsIndex: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override fun generateCode(chainedCallGenerator: ChainedCallGenerator): KtExpression {
|
||||||
|
val lambda = generateLambda(inputVariable, mapping)
|
||||||
|
return chainedCallGenerator.generate("map$0:'{}'", lambda)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Matcher : SequenceTransformationMatcher {
|
||||||
|
override fun match(state: MatchingState): SequenceTransformationMatch? {
|
||||||
|
if (state.indexVariable != null) return null //TODO?
|
||||||
|
|
||||||
|
val declaration = state.statements.firstOrNull() as? KtProperty ?: return null //TODO: support multi-variables
|
||||||
|
val initializer = declaration.initializer ?: return null
|
||||||
|
if (declaration.hasWriteUsages()) return null
|
||||||
|
val restStatements = state.statements.drop(1)
|
||||||
|
if (state.workingVariable.hasUsages(restStatements)) return null // workingVariable is still needed - cannot transform to map()
|
||||||
|
|
||||||
|
val transformation = MapTransformation(state.workingVariable, initializer)
|
||||||
|
val newState = state.copy(statements = restStatements, workingVariable = declaration)
|
||||||
|
return SequenceTransformationMatch(transformation, newState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions.loopToCallChain
|
||||||
|
|
||||||
|
import com.intellij.psi.PsiWhiteSpace
|
||||||
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
|
import org.jetbrains.kotlin.KtNodeTypes
|
||||||
|
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.isNullExpression
|
||||||
|
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
|
||||||
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
|
import org.jetbrains.kotlin.idea.references.readWriteAccess
|
||||||
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
|
||||||
|
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||||
|
|
||||||
|
fun generateLambda(workingVariable: KtCallableDeclaration, expression: KtExpression): KtLambdaExpression {
|
||||||
|
val psiFactory = KtPsiFactory(expression)
|
||||||
|
|
||||||
|
val lambdaExpression = psiFactory.createExpressionByPattern("{ $0 -> $1 }", workingVariable.nameAsSafeName, expression) as KtLambdaExpression
|
||||||
|
|
||||||
|
val isItUsedInside = expression.anyDescendantOfType<KtNameReferenceExpression> {
|
||||||
|
it.getQualifiedExpressionForSelector() == null && it.getReferencedName() == "it"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isItUsedInside) return lambdaExpression
|
||||||
|
|
||||||
|
val resolutionScope = workingVariable.getResolutionScope(workingVariable.analyze(BodyResolveMode.FULL), workingVariable.getResolutionFacade())
|
||||||
|
val bindingContext = lambdaExpression.analyzeInContext(resolutionScope, contextExpression = workingVariable)
|
||||||
|
val lambdaParam = lambdaExpression.valueParameters.single()
|
||||||
|
val lambdaParamDescriptor = bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, lambdaParam]
|
||||||
|
val usages = lambdaExpression.collectDescendantsOfType<KtNameReferenceExpression> {
|
||||||
|
it.mainReference.resolveToDescriptors(bindingContext).singleOrNull() == lambdaParamDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
val itExpr = psiFactory.createSimpleName("it")
|
||||||
|
for (usage in usages) {
|
||||||
|
usage.replace(itExpr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return psiFactory.createExpressionByPattern("{ $0 }", lambdaExpression.bodyExpression!!) as KtLambdaExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression?.isTrueConstant()
|
||||||
|
= this != null && node?.elementType == KtNodeTypes.BOOLEAN_CONSTANT && text == "true"
|
||||||
|
|
||||||
|
fun KtExpression?.isFalseConstant()
|
||||||
|
= this != null && node?.elementType == KtNodeTypes.BOOLEAN_CONSTANT && text == "false"
|
||||||
|
|
||||||
|
fun KtExpression?.isVariableReference(variable: KtCallableDeclaration): Boolean {
|
||||||
|
return this is KtNameReferenceExpression && this.mainReference.isReferenceTo(variable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression?.isSimpleName(name: Name): Boolean {
|
||||||
|
return this is KtNameReferenceExpression && this.getQualifiedExpressionForSelector() == null && this.getReferencedNameAsName() == name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtCallableDeclaration.hasUsages(inElements: Collection<KtElement>): Boolean {
|
||||||
|
return ReferencesSearch.search(this, LocalSearchScope(inElements.toTypedArray())).any()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtProperty.hasWriteUsages(): Boolean {
|
||||||
|
if (!isVar) return false
|
||||||
|
return ReferencesSearch.search(this, useScope).any {
|
||||||
|
(it as? KtSimpleNameReference)?.element?.readWriteAccess(useResolveForReadWrite = true)?.isWrite == true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stdlibFunNameForFind(valueIfFound: KtExpression, valueIfNotFound: KtExpression, workingVariable: KtCallableDeclaration): String? {
|
||||||
|
return when {
|
||||||
|
valueIfNotFound.isNullExpression() && valueIfFound.isVariableReference(workingVariable) -> "firstOrNull"
|
||||||
|
|
||||||
|
valueIfFound.isTrueConstant() && valueIfNotFound.isFalseConstant() -> "any"
|
||||||
|
|
||||||
|
valueIfFound.isFalseConstant() && valueIfNotFound.isTrueConstant() -> "none"
|
||||||
|
|
||||||
|
else -> /*TODO: allow other constants*/ null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpressionWithLabel.isBreakOrContinueOfLoop(loop: KtLoopExpression): Boolean {
|
||||||
|
val label = getTargetLabel()
|
||||||
|
if (label == null) {
|
||||||
|
val closestLoop = parents.firstIsInstance<KtLoopExpression>()
|
||||||
|
return loop == closestLoop
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//TODO: does PARTIAL always work here?
|
||||||
|
val targetLoop = analyze(BodyResolveMode.PARTIAL)[BindingContext.LABEL_TARGET, label]
|
||||||
|
return targetLoop == loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression.previousStatement(): KtExpression? {
|
||||||
|
var statement = unwrapIfLabeled()
|
||||||
|
if (statement.parent !is KtBlockExpression) return null
|
||||||
|
return statement.siblings(forward = false, withItself = false).firstIsInstanceOrNull<KtExpression>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression.nextStatement(): KtExpression? {
|
||||||
|
var statement = unwrapIfLabeled()
|
||||||
|
if (statement.parent !is KtBlockExpression) return null
|
||||||
|
return statement.siblings(forward = true, withItself = false).firstIsInstanceOrNull<KtExpression>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtExpression.unwrapIfLabeled(): KtExpression {
|
||||||
|
var statement = this
|
||||||
|
while (true) {
|
||||||
|
statement = statement.parent as? KtLabeledExpression ?: return statement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtLoopExpression.deleteWithLabels() {
|
||||||
|
unwrapIfLabeled().delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PsiChildRange.withoutFirstStatement(): PsiChildRange {
|
||||||
|
val newFirst = first!!.siblings(forward = true, withItself = false).first { it !is PsiWhiteSpace }
|
||||||
|
return PsiChildRange(newFirst, last)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun PsiChildRange.withoutLastStatement(): PsiChildRange {
|
||||||
|
val newLast = last!!.siblings(forward = false, withItself = false).first { it !is PsiWhiteSpace }
|
||||||
|
return PsiChildRange(first, newLast)
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
fun getText(): String = "xxx" //TODO
|
fun getText(): String = "xxx" //TODO
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
fun getText(): String = // let's return xxx
|
fun getText(): String = // let's return xxx
|
||||||
"xxx" //TODO
|
"xxx" //TODO
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo(p: Int) {
|
||||||
|
<caret>if (p !in 1..10) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fun foo(p: Int) {
|
||||||
|
if (p in 1..10) return
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.loopToCallChain.LoopToCallChainIntention
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
var found = false
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
<caret>val found = list.any { it.length > 0 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Boolean {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Boolean {
|
||||||
|
<caret>return list.any { it.length > 0 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Boolean {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Boolean {
|
||||||
|
<caret>return list.any()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<Any>): Int? {
|
||||||
|
<caret>for (o in list) {
|
||||||
|
if (o is String) {
|
||||||
|
val length = o.length
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<Any>): Int? {
|
||||||
|
return list
|
||||||
|
.filterIsInstance<String>()
|
||||||
|
.map { it.length }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<Any>): Int? {
|
||||||
|
<caret>for (o in list) {
|
||||||
|
if (o !is String) continue
|
||||||
|
val length = o.length
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<Any>): Int? {
|
||||||
|
return list
|
||||||
|
.filterIsInstance<String>()
|
||||||
|
.map { it.length }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String?>): Int? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s != null) {
|
||||||
|
val length = s.length
|
||||||
|
return length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String?>): Int? {
|
||||||
|
<caret>return list
|
||||||
|
.filterNotNull()
|
||||||
|
.map { it.length }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<Any?>): Int? {
|
||||||
|
<caret>for (o in list) {
|
||||||
|
if (o == null) continue
|
||||||
|
val code = o.hashCode()
|
||||||
|
return code
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<Any?>): Int? {
|
||||||
|
<caret>return list
|
||||||
|
.filterNotNull()
|
||||||
|
.map { it.hashCode() }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.isEmpty()) continue
|
||||||
|
val l = s.length
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>return list
|
||||||
|
.filterNot { it.isEmpty() }
|
||||||
|
.map { it.length }
|
||||||
|
.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.isEmpty()) continue
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>return list.firstOrNull { !it.isEmpty() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (!s.isEmpty()) continue
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>return list.firstOrNull { it.isEmpty() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.isEmpty()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>return list.firstOrNull { !it.isEmpty() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(): String? {
|
||||||
|
Loop@
|
||||||
|
while (x()) {
|
||||||
|
<caret>for (s in list()) {
|
||||||
|
if (s.isEmpty()) continue@Loop
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun x(): Boolean = false
|
||||||
|
fun list(): List<String> = listOf()
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
var result: String? = null
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
result = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
<caret>val result: String? = list.firstOrNull { it.length > 0 }
|
||||||
|
}
|
||||||
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo() {
|
||||||
|
MainLoop@
|
||||||
|
for (i in 1..10) {
|
||||||
|
var result: String? = null
|
||||||
|
<caret>for (s in list()) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
result = s
|
||||||
|
break@MainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun list(): List<String> = listOf()
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
var result: String? = null
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
result = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result += "1"
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
<caret>var result: String? = list.firstOrNull { it.length > 0 }
|
||||||
|
|
||||||
|
result += "1"
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
var result: String? = null
|
||||||
|
<caret>for (s in list) { // search for first non-empty string in the list
|
||||||
|
if (s.length > 0) { // string should be non-empty
|
||||||
|
result = s // save it into result
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
<caret>val result: String? = list.firstOrNull { // search for first non-empty string in the list
|
||||||
|
it.length > 0
|
||||||
|
}// string should be non-empty
|
||||||
|
// save it into result
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>) {
|
||||||
|
var result: String? = null
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s != result) {
|
||||||
|
result = s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>return list.firstOrNull { it.length > 0 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>return list.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.isNotEmpty()) {
|
||||||
|
return s.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>): String {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.isNotEmpty()) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
// return null if not found
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
// return null if not found
|
||||||
|
return list.firstOrNull()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
for (line in s.lines()) {
|
||||||
|
if (line.isNotBlank()) return line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>return list
|
||||||
|
.flatMap { it.lines() }
|
||||||
|
.firstOrNull { it.isNotBlank() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
var result: String? = null
|
||||||
|
MainLoop@
|
||||||
|
<caret>for (s in list) {
|
||||||
|
for (line in s.lines()) {
|
||||||
|
if (line.isNotBlank()) {
|
||||||
|
result = line
|
||||||
|
break@MainLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>val result: String? = list
|
||||||
|
.flatMap { it.lines() }
|
||||||
|
.firstOrNull { it.isNotBlank() }
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
var result: String? = null
|
||||||
|
<caret>for (s in list) {
|
||||||
|
for (line in s.lines()) {
|
||||||
|
if (line.isNotBlank()) {
|
||||||
|
result = line
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>): Char? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
for (c in s) {
|
||||||
|
if (c != ' ') return c
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>, it: Int) {
|
||||||
|
var found = false
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > it) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>, it: Int) {
|
||||||
|
<caret>val found = list.any { s -> s.length > it }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
val length = s.length
|
||||||
|
if (length > 0) return length
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>return list
|
||||||
|
.map { it.length }
|
||||||
|
.firstOrNull { it > 0 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
var length = s.length
|
||||||
|
if (length > 0) return length
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>return list
|
||||||
|
.map { it.length }
|
||||||
|
.firstOrNull { it > 0 }
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>): Int? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
var length = s.length
|
||||||
|
if (length > 0) return ++length
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// IS_APPLICABLE: false
|
||||||
|
fun foo(list: List<String>): String? {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
val length = s.length
|
||||||
|
if (length > 0) return s + length
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Boolean {
|
||||||
|
<caret>for (s in list) {
|
||||||
|
if (s.length > 0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
fun foo(list: List<String>): Boolean {
|
||||||
|
<caret>return list.none { it.length > 0 }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user