diff --git a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/StandardKotlinNames.kt b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/StandardKotlinNames.kt index 7bf073805f8d..504eee6f098c 100644 --- a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/StandardKotlinNames.kt +++ b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/StandardKotlinNames.kt @@ -61,6 +61,10 @@ object StandardKotlinNames { @JvmField val Sequence: FqName = BASE_SEQUENCES_PACKAGE + "Sequence" + @JvmField val sequence: FqName = BASE_SEQUENCES_PACKAGE + "sequence" + @JvmField val yield: FqName = BASE_SEQUENCES_PACKAGE + "SequenceScope.yield" + @JvmField val yieldAll: FqName = BASE_SEQUENCES_PACKAGE + "SequenceScope.yieldAll" + @JvmField val terminations: List = collectionTerminationFunctionNames.map { BASE_SEQUENCES_PACKAGE + it } diff --git a/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightExitPointsTestGenerated.java b/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightExitPointsTestGenerated.java index ecee0d3404cc..08711050decc 100644 --- a/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightExitPointsTestGenerated.java +++ b/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/K2HighlightExitPointsTestGenerated.java @@ -1,4 +1,4 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.k2.highlighting; @@ -305,6 +305,56 @@ public class K2HighlightExitPointsTestGenerated extends AbstractK2HighlightExitP runTest("../../idea/tests/testData/exitPoints/returnWhenStatement.kt"); } + @TestMetadata("sequenceInLambda.kt") + public void testSequenceInLambda() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceInLambda.kt"); + } + + @TestMetadata("sequenceNested.kt") + public void testSequenceNested() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceNested.kt"); + } + + @TestMetadata("sequenceNested2.kt") + public void testSequenceNested2() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceNested2.kt"); + } + + @TestMetadata("sequenceNestedMixedSyntax.kt") + public void testSequenceNestedMixedSyntax() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceNestedMixedSyntax.kt"); + } + + @TestMetadata("sequenceNestedParenthesized.kt") + public void testSequenceNestedParenthesized() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceNestedParenthesized.kt"); + } + + @TestMetadata("sequenceSimple.kt") + public void testSequenceSimple() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceSimple.kt"); + } + + @TestMetadata("sequenceTryCatch.kt") + public void testSequenceTryCatch() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceTryCatch.kt"); + } + + @TestMetadata("sequenceWithControlFlow.kt") + public void testSequenceWithControlFlow() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceWithControlFlow.kt"); + } + + @TestMetadata("sequenceWithLambdaContinue.kt") + public void testSequenceWithLambdaContinue() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceWithLambdaContinue.kt"); + } + + @TestMetadata("sequenceWithReturn.kt") + public void testSequenceWithReturn() throws Exception { + runTest("../../idea/tests/testData/exitPoints/sequenceWithReturn.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("../../idea/tests/testData/exitPoints/simple.kt"); diff --git a/plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/base/highlighting/AbstractKotlinHighlightExitPointsHandlerFactory.kt b/plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/base/highlighting/AbstractKotlinHighlightExitPointsHandlerFactory.kt index db2187e0ae2d..365250a82a53 100644 --- a/plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/base/highlighting/AbstractKotlinHighlightExitPointsHandlerFactory.kt +++ b/plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/base/highlighting/AbstractKotlinHighlightExitPointsHandlerFactory.kt @@ -17,8 +17,12 @@ import com.intellij.psi.search.LocalSearchScope import com.intellij.psi.util.PsiTreeUtil import com.intellij.psi.util.elementType import com.intellij.util.Consumer +import org.jetbrains.kotlin.analysis.api.analyze +import org.jetbrains.kotlin.analysis.api.resolution.successfulFunctionCallOrNull +import org.jetbrains.kotlin.idea.codeinsight.utils.StandardKotlinNames import org.jetbrains.kotlin.idea.codeinsight.utils.doesBelongToLoop import org.jetbrains.kotlin.idea.codeinsight.utils.findRelevantLoopForExpression +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.idea.references.unwrappedTargets import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.* @@ -32,15 +36,14 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages is KtPropertyAccessor -> parent is KtReturnExpression, is KtThrowExpression -> parent is KtFunctionLiteral -> parent.takeIf { with((target as? ASTNode)?.elementType) { this == KtTokens.LBRACE || this == KtTokens.RBRACE } } - is KtLabelReferenceExpression -> - PsiTreeUtil.getParentOfType( - target, KtReturnExpression::class.java, KtThrowExpression::class.java, KtFunction::class.java - )?.takeUnless { - it is KtFunction - } + is KtLabelReferenceExpression -> PsiTreeUtil.getParentOfType( + target, KtReturnExpression::class.java, KtThrowExpression::class.java, KtFunction::class.java + )?.takeUnless { + it is KtFunction + } else -> null - } as? KtExpression ?: return null + } ?: return null return OnExitUsagesHandler(editor, file, null, expression, false) } @@ -54,9 +57,22 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages return OnLoopUsagesHandler(editor, file, expression) } + private fun getOnSequenceUsageHandler(editor: Editor, file: PsiFile, target: PsiElement): HighlightUsagesHandlerBase<*>? { + val expression = when (val parent = target.parent) { + is KtNameReferenceExpression -> parent.takeIf { + target.elementType == KtTokens.IDENTIFIER && parent.text in SEQUENCE_KEYWORDS + }?.let { it.parent as? KtCallExpression } + + else -> null + } as? KtExpression ?: return null + + return OnSequenceUsagesHandler(editor, file, expression) + } + override fun createHighlightUsagesHandler(editor: Editor, file: PsiFile, target: PsiElement): HighlightUsagesHandlerBase<*>? { return getOnReturnOrThrowOrLambdaUsageHandler(editor, file, target) ?: getOnBreakOrContinueUsageHandler(editor, file, target) + ?: getOnSequenceUsageHandler(editor, file, target) } protected abstract fun getRelevantReturnDeclaration(returnExpression: KtReturnExpression): KtDeclarationWithBody? @@ -95,8 +111,7 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages val referenceExpression: KtNameReferenceExpression?, val target: KtExpression, val highlightReferences: Boolean - ) : - HighlightUsagesHandlerBase(editor, file) { + ) : HighlightUsagesHandlerBase(editor, file) { override fun getTargets(): List = listOf(target) @@ -105,13 +120,12 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages } override fun computeUsages(targets: List) { - val relevantFunction: KtDeclarationWithBody? = - when (target) { - is KtFunctionLiteral -> target - is KtPropertyAccessor -> target - is KtNamedFunction -> target - else -> getRelevantDeclaration(target) - } + val relevantFunction: KtDeclarationWithBody? = when (target) { + is KtFunctionLiteral -> target + is KtPropertyAccessor -> target + is KtNamedFunction -> target + else -> getRelevantDeclaration(target) + } var targetOccurrenceAdded = false if (target is KtReturnExpression || target is KtThrowExpression || target is KtNamedFunction || target is KtPropertyAccessor) { @@ -135,11 +149,9 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages } val lastStatementExpressions = - if ((relevantFunction is KtFunctionLiteral && hasNonUnitReturnType(relevantFunction)) || - (relevantFunction is KtNamedFunction && relevantFunction.bodyBlockExpression == null) - ) { + if ((relevantFunction is KtFunctionLiteral && hasNonUnitReturnType(relevantFunction)) || (relevantFunction is KtNamedFunction && relevantFunction.bodyBlockExpression == null)) { val lastStatements = mutableSetOf(relevantFunction) - relevantFunction.acceptChildren(object : KtVisitorVoid(),PsiRecursiveVisitor { + relevantFunction.acceptChildren(object : KtVisitorVoid(), PsiRecursiveVisitor { override fun visitKtElement(element: KtElement) { ProgressIndicatorProvider.checkCanceled() element.acceptChildren(this) @@ -202,7 +214,7 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages emptySet() } - relevantFunction?.accept(object : KtVisitorVoid(),PsiRecursiveVisitor { + relevantFunction?.accept(object : KtVisitorVoid(), PsiRecursiveVisitor { override fun visitKtElement(element: KtElement) { ProgressIndicatorProvider.checkCanceled() element.acceptChildren(this) @@ -258,7 +270,7 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages when (expression.returnedExpression) { is KtIfExpression, is KtWhenExpression, is KtTryExpression -> { addOccurrence(expression.returnKeyword) - expression.acceptChildren(object : KtVisitorVoid(),PsiRecursiveVisitor { + expression.acceptChildren(object : KtVisitorVoid(), PsiRecursiveVisitor { override fun visitKtElement(element: KtElement) { ProgressIndicatorProvider.checkCanceled() element.acceptChildren(this) @@ -279,10 +291,9 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages } } - is KtWhenExpression -> - expression.entries.forEach { whenEntry -> - whenEntry.expression?.let { visitExpression(it) } - } + is KtWhenExpression -> expression.entries.forEach { whenEntry -> + whenEntry.expression?.let { visitExpression(it) } + } else -> addOccurrence(expression) } @@ -309,9 +320,11 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages (target as? PsiNameIdentifierOwner)?.nameIdentifier.takeIf { target.containingFile == containingFile } ?.let(::addOccurrence) - val handler: FindUsagesHandler? = (FindManager.getInstance(relevantFunction.project) as FindManagerImpl) - .findUsagesManager - .getFindUsagesHandler(target, true) + val handler: FindUsagesHandler? = + (FindManager.getInstance(relevantFunction.project) as FindManagerImpl).findUsagesManager.getFindUsagesHandler( + target, + true + ) handler?.findReferencesToHighlight(target, LocalSearchScope(containingFile)).let { ref -> ref?.forEach { addOccurrence(it.element) } } @@ -322,7 +335,7 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages override fun highlightReferences(): Boolean = highlightReferences } - private inner class OnLoopUsagesHandler(editor: Editor, file: PsiFile, val target: KtExpression) : + private class OnLoopUsagesHandler(editor: Editor, file: PsiFile, val target: KtExpression) : HighlightUsagesHandlerBase(editor, file) { override fun getTargets(): List = listOf(target) @@ -359,6 +372,7 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages if (loopLabelName == null) return } } + is KtBreakExpression, is KtContinueExpression -> { val expressionLabelName = (expression as? KtExpressionWithLabel)?.getLabelName() if (expressionLabelName != null && expressionLabelName == loopLabelName) { @@ -378,6 +392,94 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages } + private inner class OnSequenceUsagesHandler(editor: Editor, file: PsiFile, val target: KtExpression) : + HighlightUsagesHandlerBase(editor, file) { + + override fun getTargets(): List = listOf(target) + + override fun selectTargets(targets: List, selectionConsumer: Consumer>) { + selectionConsumer.consume(targets) + } + + override fun computeUsages(targets: List) { + val sequenceCall = findSequenceCall(target) ?: return + // Handles both trailing lambda syntax: sequence { } and parenthesized: sequence({ }) + val sequenceLambda = + sequenceCall.lambdaArguments.firstOrNull()?.getLambdaExpression() ?: (sequenceCall.valueArguments.firstOrNull() + ?.getArgumentExpression() as? KtLambdaExpression) ?: return + + // Highlight "sequence" keyword + sequenceCall.calleeExpression?.let { addOccurrence(it) } + + // Find all yield/yieldAll for current sequence lambda + sequenceLambda.accept(object : KtVisitorVoid(), PsiRecursiveVisitor { + override fun visitKtElement(element: KtElement) { + element.acceptChildren(this) + } + + override fun visitCallExpression(expression: KtCallExpression) { + super.visitCallExpression(expression) + // Check if this call belongs to our sequence (not nested) + if (belongsToSequence(expression, sequenceLambda) && isYieldCall(expression)) { + addOccurrence(expression) + } + } + }) + } + } + + private fun findSequenceCall(expression: KtExpression): KtCallExpression? { + // If clicked the "sequence" + if (expression is KtCallExpression && isSequenceBuilderCall(expression)) { + return expression + } + + // Clicked yield/yieldAll -> Find enclosing sequence call + return expression.parents.filterIsInstance().firstNotNullOfOrNull { lambdaExpr -> + val lambdaArg = lambdaExpr.parent + val call = when (lambdaArg) { + is KtLambdaArgument -> lambdaArg.parent as? KtCallExpression + is KtValueArgument -> (lambdaArg.parent as? KtValueArgumentList)?.parent as? KtCallExpression + else -> null + } + call?.takeIf { isSequenceBuilderCall(it) } + } + } + + private fun belongsToSequence(yieldCall: KtCallExpression, targetSequenceLambda: KtLambdaExpression): Boolean { + // Check if yield doesn't belong to a nested sequence + for (parent in yieldCall.parents) { + if (parent == targetSequenceLambda) return true + + if (parent is KtLambdaExpression && parent != targetSequenceLambda) { + val call = when (val lambdaParent = parent.parent) { + is KtLambdaArgument -> lambdaParent.parent as? KtCallExpression // sequence { } + is KtValueArgument -> (lambdaParent.parent as? KtValueArgumentList)?.parent as? KtCallExpression // sequence({ }) + else -> null + } + if (call != null && isSequenceBuilderCall(call)) { + // a new sequence + return false + } + } + } + return false + } + + private fun isSequenceBuilderCall(call: KtCallExpression): Boolean { + if (call.calleeExpression?.text != "sequence") return false + return call.resolvesToFqName(StandardKotlinNames.Sequences.sequence) + } + + private fun isYieldCall(call: KtCallExpression): Boolean { + val calleeName = call.calleeExpression?.text + if (calleeName != "yield" && calleeName != "yieldAll") return false + + return call.resolvesToFqName( + StandardKotlinNames.Sequences.yield, StandardKotlinNames.Sequences.yieldAll + ) + } + private fun MutableSet.addIfNotNullAndNotBlock(element: PsiElement?) { addIfNotNull(element.takeUnless { it is KtBlockExpression }) } @@ -392,4 +494,19 @@ abstract class AbstractKotlinHighlightExitPointsHandlerFactory : HighlightUsages return expression } + private fun KtCallExpression.resolvesToFqName(vararg expectedFqNames: FqName): Boolean { + analyze(this) { + val resolvedCall = resolveToCall()?.successfulFunctionCallOrNull() ?: return false + val symbol = resolvedCall.partiallyAppliedSymbol.signature.symbol + val callableId = symbol.callableId ?: return false + val fqName = callableId.asSingleFqName() + + return fqName in expectedFqNames + } + } + + companion object { + private val SEQUENCE_KEYWORDS = setOf("yield", "yieldAll", "sequence") + } + } \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightExitPointsTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightExitPointsTestGenerated.java index 15436188b6be..c4998f5b33e3 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightExitPointsTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightExitPointsTestGenerated.java @@ -1,4 +1,4 @@ -// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package org.jetbrains.kotlin.idea.highlighter; @@ -305,6 +305,56 @@ public class HighlightExitPointsTestGenerated extends AbstractCustomHighlightUsa runTest("testData/exitPoints/returnWhenStatement.kt"); } + @TestMetadata("sequenceInLambda.kt") + public void testSequenceInLambda() throws Exception { + runTest("testData/exitPoints/sequenceInLambda.kt"); + } + + @TestMetadata("sequenceNested.kt") + public void testSequenceNested() throws Exception { + runTest("testData/exitPoints/sequenceNested.kt"); + } + + @TestMetadata("sequenceNested2.kt") + public void testSequenceNested2() throws Exception { + runTest("testData/exitPoints/sequenceNested2.kt"); + } + + @TestMetadata("sequenceNestedMixedSyntax.kt") + public void testSequenceNestedMixedSyntax() throws Exception { + runTest("testData/exitPoints/sequenceNestedMixedSyntax.kt"); + } + + @TestMetadata("sequenceNestedParenthesized.kt") + public void testSequenceNestedParenthesized() throws Exception { + runTest("testData/exitPoints/sequenceNestedParenthesized.kt"); + } + + @TestMetadata("sequenceSimple.kt") + public void testSequenceSimple() throws Exception { + runTest("testData/exitPoints/sequenceSimple.kt"); + } + + @TestMetadata("sequenceTryCatch.kt") + public void testSequenceTryCatch() throws Exception { + runTest("testData/exitPoints/sequenceTryCatch.kt"); + } + + @TestMetadata("sequenceWithControlFlow.kt") + public void testSequenceWithControlFlow() throws Exception { + runTest("testData/exitPoints/sequenceWithControlFlow.kt"); + } + + @TestMetadata("sequenceWithLambdaContinue.kt") + public void testSequenceWithLambdaContinue() throws Exception { + runTest("testData/exitPoints/sequenceWithLambdaContinue.kt"); + } + + @TestMetadata("sequenceWithReturn.kt") + public void testSequenceWithReturn() throws Exception { + runTest("testData/exitPoints/sequenceWithReturn.kt"); + } + @TestMetadata("simple.kt") public void testSimple() throws Exception { runTest("testData/exitPoints/simple.kt"); diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceInLambda.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceInLambda.kt new file mode 100644 index 000000000000..4cea14be1c9a --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceInLambda.kt @@ -0,0 +1,9 @@ +// WITH_STDLIB +fun f() { + listOf(1, 2, 3).map { n -> + sequence { // Highlighted + ~yield(n) // Highlighted + yield(n * 2) // Highlighted + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNested.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNested.kt new file mode 100644 index 000000000000..4adf4871010e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNested.kt @@ -0,0 +1,14 @@ +// WITH_STDLIB +fun f() { + val outer = ~sequence { // Caret + yield(1) // Highlighted + + val inner = sequence { // Nested sequence + yield(99) // NOT highlighted + yield(100) // NOT highlighted + } + + yield(2) // Highlighted + yieldAll(inner) // Highlighted + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNested2.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNested2.kt new file mode 100644 index 000000000000..d531b7ddfb08 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNested2.kt @@ -0,0 +1,21 @@ +// WITH_STDLIB +fun f() { + val nested0 = sequence { // Level 1 + yield(1) + + val nested1 = sequence { // Level 2 + ~yield(10) + + val nested2 = sequence { // Level 3 + yield(100) + yield(200) + } + + yieldAll(nested2) + yield(20) + } + + yieldAll(nested1) + yield(2) + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNestedMixedSyntax.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNestedMixedSyntax.kt new file mode 100644 index 000000000000..fd75540a943b --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNestedMixedSyntax.kt @@ -0,0 +1,12 @@ +// WITH_STDLIB +fun f() { + val outer = sequence { // Trailing lambda + yield(1) + + val inner = sequence({ // Parenthesized + ~yield(99) + }) + + yield(2) + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNestedParenthesized.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNestedParenthesized.kt new file mode 100644 index 000000000000..9408e333a06e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceNestedParenthesized.kt @@ -0,0 +1,12 @@ +// WITH_STDLIB +fun f() { + val outer = sequence({ // Parenthesized outer + yield(1) + + val inner = sequence({ // Parenthesized inner + yield(99) + }) + + ~yield(2) + }) +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceSimple.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceSimple.kt new file mode 100644 index 000000000000..ce472fb62be6 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceSimple.kt @@ -0,0 +1,8 @@ +// WITH_STDLIB +fun f() { + val seq = ~sequence { // Caret + yield(1) // Highlighted + yield(2) // Highlighted + yieldAll(listOf(3, 4)) // Highlighted + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceTryCatch.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceTryCatch.kt new file mode 100644 index 000000000000..390c8bdbd645 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceTryCatch.kt @@ -0,0 +1,12 @@ +// WITH_STDLIB +fun f() { + val seq = ~sequence { // Caret + try { + yield(1) // Highlighted + } catch (e: Exception) { + yield(-1) // Highlighted + } finally { + // yield(0) // not allowed in finally + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithControlFlow.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithControlFlow.kt new file mode 100644 index 000000000000..81a61e0881c9 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithControlFlow.kt @@ -0,0 +1,19 @@ +// WITH_STDLIB +fun f() { + val seq = ~sequence { // Caret + for (i in 1..5) { + yield(i) // Highlighted + } + + if (true) { + yield(100) // Highlighted + } else { + yieldAll(emptyList()) // Highlighted + } + + when (1) { + 1 -> yield(200) // Highlighted + else -> yield(300) // Highlighted + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithLambdaContinue.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithLambdaContinue.kt new file mode 100644 index 000000000000..a2b1dd3a10d6 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithLambdaContinue.kt @@ -0,0 +1,11 @@ +// WITH_STDLIB +fun f(){ + val seq = sequence { + outer@ for (i in 1..5) { + for (j in 1..5){ + if (j == 2) continue@outer + ~yield(i * j) // Highlighted + } + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithReturn.kt b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithReturn.kt new file mode 100644 index 000000000000..6ca5bfd1da5c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/exitPoints/sequenceWithReturn.kt @@ -0,0 +1,13 @@ +// WITH_STDLIB +fun f(): Sequence { + return ~sequence { + yield(1) + + if(true) { + return @sequence + } + + yield(2) + } + // return 0 +} \ No newline at end of file