diff --git a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/implicitLambdaParameterUtils.kt b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/implicitLambdaParameterUtils.kt
index ad1b1a5d15e90..645e1b3249e48 100644
--- a/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/implicitLambdaParameterUtils.kt
+++ b/plugins/kotlin/code-insight/utils/src/org/jetbrains/kotlin/idea/codeinsight/utils/implicitLambdaParameterUtils.kt
@@ -12,7 +12,9 @@ import org.jetbrains.kotlin.analysis.api.symbols.KaAnonymousFunctionSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaValueParameterSymbol
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.idea.references.mainReference
+import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtFunctionLiteral
+import org.jetbrains.kotlin.psi.KtLambdaArgument
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
fun KtNameReferenceExpression.isReferenceToImplicitLambdaParameter(): Boolean {
@@ -51,3 +53,22 @@ fun KaValueParameterSymbol.getFunctionLiteralByImplicitLambdaParameterSymbol():
val lambda = containingDeclaration as? KaAnonymousFunctionSymbol ?: return null
return lambda.psi as? KtFunctionLiteral
}
+
+
+fun KtNameReferenceExpression.getLambdaFunctionLiteralByNameReference(): KtFunctionLiteral? {
+ val lambda = (this.parent as? KtCallExpression)?.lambdaArguments?.singleOrNull()?.getLambdaExpression() ?: return null
+ return lambda.functionLiteral
+}
+
+fun KtFunctionLiteral.getImplicitItLambdaParameterSymbol(): KaValueParameterSymbol? {
+ @OptIn(KaAllowAnalysisOnEdt::class)
+ allowAnalysisOnEdt {
+ analyze(this) {
+ if (valueParameters.isNotEmpty() || symbol.valueParameters.size != 1 || !symbol.valueParameters.first().isImplicitLambdaParameter) {
+ return null
+ } else {
+ return symbol.valueParameters.first()
+ }
+ }
+ }
+}
diff --git a/plugins/kotlin/highlighting/highlighting-k2/resources/intellij.kotlin.highlighting.xml b/plugins/kotlin/highlighting/highlighting-k2/resources/intellij.kotlin.highlighting.xml
index ac53b86f15c09..300bd010e3ac5 100644
--- a/plugins/kotlin/highlighting/highlighting-k2/resources/intellij.kotlin.highlighting.xml
+++ b/plugins/kotlin/highlighting/highlighting-k2/resources/intellij.kotlin.highlighting.xml
@@ -23,6 +23,7 @@
+
diff --git a/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/HighlightImplicitItHandlerTestGenerated.java b/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/HighlightImplicitItHandlerTestGenerated.java
new file mode 100644
index 0000000000000..2e580b3141b6c
--- /dev/null
+++ b/plugins/kotlin/highlighting/highlighting-k2/test/org/jetbrains/kotlin/idea/k2/highlighting/HighlightImplicitItHandlerTestGenerated.java
@@ -0,0 +1,43 @@
+// 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;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
+import org.jetbrains.kotlin.idea.base.test.TestRoot;
+import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
+import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.runner.RunWith;
+import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightImplicitItHandlerTest;
+
+/**
+ * This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
+ * DO NOT MODIFY MANUALLY.
+ */
+@SuppressWarnings("all")
+@TestRoot("highlighting/highlighting-k2")
+@TestDataPath("$CONTENT_ROOT")
+@RunWith(JUnit3RunnerWithInners.class)
+@TestMetadata("../../idea/tests/testData/implicitIt")
+public class HighlightImplicitItHandlerTestGenerated extends AbstractHighlightImplicitItHandlerTest {
+ @java.lang.Override
+ @org.jetbrains.annotations.NotNull
+ public final KotlinPluginMode getPluginMode() {
+ return KotlinPluginMode.K2;
+ }
+
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ @TestMetadata("HighlightCallAcceptingLambdaImplicitIt.kt")
+ public void testHighlightCallAcceptingLambdaImplicitIt() throws Exception {
+ runTest("../../idea/tests/testData/implicitIt/HighlightCallAcceptingLambdaImplicitIt.kt");
+ }
+
+ @TestMetadata("HighlightImplicitIt.kt")
+ public void testHighlightImplicitIt() throws Exception {
+ runTest("../../idea/tests/testData/implicitIt/HighlightImplicitIt.kt");
+ }
+}
diff --git a/plugins/kotlin/highlighting/highlighting-k1/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightImplicitItHandlerFactory.kt b/plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightImplicitItHandlerFactory.kt
similarity index 59%
rename from plugins/kotlin/highlighting/highlighting-k1/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightImplicitItHandlerFactory.kt
rename to plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightImplicitItHandlerFactory.kt
index 2a434c0468779..03ac5bef3e2c3 100644
--- a/plugins/kotlin/highlighting/highlighting-k1/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightImplicitItHandlerFactory.kt
+++ b/plugins/kotlin/highlighting/highlighting-shared/src/org/jetbrains/kotlin/idea/highlighter/KotlinHighlightImplicitItHandlerFactory.kt
@@ -7,12 +7,17 @@ import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactoryBase
import com.intellij.openapi.editor.Editor
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
-import com.intellij.psi.impl.source.tree.LeafPsiElement
+import com.intellij.psi.util.elementType
import com.intellij.util.Consumer
import org.jetbrains.kotlin.K1Deprecation
+import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.idea.codeinsight.utils.getFunctionLiteralByImplicitLambdaParameter
+import org.jetbrains.kotlin.idea.codeinsight.utils.getImplicitItLambdaParameterSymbol
+import org.jetbrains.kotlin.idea.codeinsight.utils.getLambdaFunctionLiteralByNameReference
+import org.jetbrains.kotlin.idea.references.getCalleeByLambdaArgument
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtLambdaExpression
import org.jetbrains.kotlin.psi.KtNameReferenceExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
@@ -20,14 +25,15 @@ import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
@K1Deprecation
class KotlinHighlightImplicitItHandlerFactory : HighlightUsagesHandlerFactoryBase() {
override fun createHighlightUsagesHandler(editor: Editor, file: PsiFile, target: PsiElement): HighlightUsagesHandlerBase<*>? {
- if (target !is LeafPsiElement
- || target.elementType !is KtToken // do not trigger loading of KtTokens in Java
+ if (target.elementType !is KtToken // do not trigger loading of KtTokens in Java
|| target.elementType != KtTokens.IDENTIFIER) {
return null
}
val refExpr = target.parent as? KtNameReferenceExpression ?: return null
- val lambda = refExpr.getFunctionLiteralByImplicitLambdaParameter() ?: return null
+ val lambda = refExpr.getFunctionLiteralByImplicitLambdaParameter()
+ ?: refExpr.getLambdaFunctionLiteralByNameReference()?.takeIf { it.getImplicitItLambdaParameterSymbol() != null }
+ ?: return null
return object : HighlightUsagesHandlerBase(editor, file) {
override fun getTargets() = listOf(refExpr)
@@ -37,15 +43,31 @@ class KotlinHighlightImplicitItHandlerFactory : HighlightUsagesHandlerFactoryBas
) = selectionConsumer.consume(targets)
override fun computeUsages(targets: List) {
+ var addLambdaOccurrence = false
lambda.accept(
object : KtTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
if (expression is KtNameReferenceExpression && expression.getFunctionLiteralByImplicitLambdaParameter() == lambda) {
addOccurrence(expression)
+ addLambdaOccurrence = true
+ }
+ }
+
+ //implicit lambda parameter is not available inside another lambda with implicit parameter or name `it`
+ override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
+ if (lambdaExpression.functionLiteral.getImplicitItLambdaParameterSymbol() == null &&
+ lambdaExpression.functionLiteral.valueParameters.find { it.name == StandardNames.IMPLICIT_LAMBDA_PARAMETER_NAME.asString() } == null
+ ) {
+ super.visitLambdaExpression(lambdaExpression)
}
}
}
)
+ if (addLambdaOccurrence) {
+ lambda.getCalleeByLambdaArgument()?.also {
+ addOccurrence(it)
+ }
+ }
}
}
}
diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightImplicitItHandlerTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightImplicitItHandlerTestGenerated.java
new file mode 100644
index 0000000000000..99736d236051a
--- /dev/null
+++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/highlighter/HighlightImplicitItHandlerTestGenerated.java
@@ -0,0 +1,42 @@
+// 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;
+
+import com.intellij.testFramework.TestDataPath;
+import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginMode;
+import org.jetbrains.kotlin.idea.base.test.TestRoot;
+import org.jetbrains.kotlin.idea.test.JUnit3RunnerWithInners;
+import org.jetbrains.kotlin.idea.test.KotlinTestUtils;
+import org.jetbrains.kotlin.test.TestMetadata;
+import org.junit.runner.RunWith;
+
+/**
+ * This class is generated by {@link org.jetbrains.kotlin.testGenerator.generator.TestGenerator}.
+ * DO NOT MODIFY MANUALLY.
+ */
+@SuppressWarnings("all")
+@TestRoot("idea/tests")
+@TestDataPath("$CONTENT_ROOT")
+@RunWith(JUnit3RunnerWithInners.class)
+@TestMetadata("testData/implicitIt")
+public class HighlightImplicitItHandlerTestGenerated extends AbstractHighlightImplicitItHandlerTest {
+ @java.lang.Override
+ @org.jetbrains.annotations.NotNull
+ public final KotlinPluginMode getPluginMode() {
+ return KotlinPluginMode.K1;
+ }
+
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ @TestMetadata("HighlightCallAcceptingLambdaImplicitIt.kt")
+ public void testHighlightCallAcceptingLambdaImplicitIt() throws Exception {
+ runTest("testData/implicitIt/HighlightCallAcceptingLambdaImplicitIt.kt");
+ }
+
+ @TestMetadata("HighlightImplicitIt.kt")
+ public void testHighlightImplicitIt() throws Exception {
+ runTest("testData/implicitIt/HighlightImplicitIt.kt");
+ }
+}
diff --git a/plugins/kotlin/idea/tests/testData/implicitIt/HighlightCallAcceptingLambdaImplicitIt.kt b/plugins/kotlin/idea/tests/testData/implicitIt/HighlightCallAcceptingLambdaImplicitIt.kt
new file mode 100644
index 0000000000000..fe70a43f41ff3
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/implicitIt/HighlightCallAcceptingLambdaImplicitIt.kt
@@ -0,0 +1,19 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun acceptL1(fn: (Int) -> Unit) {}
+fun acceptL2(fn: (Int, Int) -> Unit) {}
+
+fun nestLambdas() {
+ acceptL1 {
+ // More code.
+ ~acceptL1 {
+ // And more code.
+ acceptL2 { x, y ->
+ // And more code.
+ println(it)
+ acceptL1 {
+ it
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin/idea/tests/testData/implicitIt/HighlightImplicitIt.kt b/plugins/kotlin/idea/tests/testData/implicitIt/HighlightImplicitIt.kt
new file mode 100644
index 0000000000000..c15694bb2fa4c
--- /dev/null
+++ b/plugins/kotlin/idea/tests/testData/implicitIt/HighlightImplicitIt.kt
@@ -0,0 +1,19 @@
+// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+fun acceptL1(fn: (Int) -> Unit) {}
+fun acceptL2(fn: (Int, Int) -> Unit) {}
+
+fun nestLambdas() {
+ acceptL1 {
+ // More code.
+ acceptL1 {
+ // And more code.
+ acceptL2 { x, y ->
+ // And more code.
+ println(~it)
+ acceptL1 {
+ it
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/kotlin/intellij.kotlin.base.test/test/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightImplicitItHandlerTest.kt b/plugins/kotlin/intellij.kotlin.base.test/test/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightImplicitItHandlerTest.kt
new file mode 100644
index 0000000000000..48e7323949b18
--- /dev/null
+++ b/plugins/kotlin/intellij.kotlin.base.test/test/org/jetbrains/kotlin/idea/highlighter/AbstractHighlightImplicitItHandlerTest.kt
@@ -0,0 +1,79 @@
+// Copyright 2000-2025 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
+
+import com.intellij.codeInsight.daemon.impl.HighlightInfo
+import com.intellij.codeInsight.daemon.impl.HighlightInfoType
+import com.intellij.codeInsight.highlighting.HighlightUsagesHandler
+import com.intellij.codeInsight.highlighting.highlightUsages
+import com.intellij.openapi.application.ReadAction
+import com.intellij.openapi.editor.asTextRange
+import com.intellij.openapi.editor.colors.EditorColors
+import com.intellij.openapi.project.DumbService
+import com.intellij.psi.PsiElement
+import com.intellij.testFramework.ExpectedHighlightingData
+import com.intellij.util.concurrency.AppExecutorUtil
+import org.jetbrains.kotlin.idea.base.test.IgnoreTests
+import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
+import org.jetbrains.kotlin.idea.test.extractMarkerOffset
+import java.util.concurrent.Callable
+
+abstract class AbstractHighlightImplicitItHandlerTest : KotlinLightCodeInsightFixtureTestCase() {
+
+ companion object {
+ // Not standard to leave it in text after configureByFile and remove manually after collecting highlighting information
+ const val CARET_TAG = "~"
+ }
+
+ open fun doTest(unused: String) {
+ val disableDirective = IgnoreTests.DIRECTIVES.of(pluginMode)
+
+ IgnoreTests.runTestIfNotDisabledByFileDirective(
+ dataFile().toPath(),
+ disableDirective
+ ) {
+ myFixture.configureByFile(fileName())
+
+ val editor = myFixture.editor
+
+ val document = myFixture.editor.document
+ val data = ExpectedHighlightingData(document, false, false, true, false)
+ data.init()
+
+ val caret = document.extractMarkerOffset(project, CARET_TAG)
+ assert(caret != -1) { "Caret marker '${CARET_TAG}' expected" }
+ editor.caretModel.moveToOffset(caret)
+
+ val customHandler =
+ HighlightUsagesHandler.createCustomHandler(editor, myFixture.file)
+
+ if (customHandler != null) {
+ ReadAction.nonBlocking(Callable {
+ customHandler.highlightUsages()
+ }).submit(AppExecutorUtil.getAppExecutorService())
+ .get()
+ } else {
+ DumbService.getInstance(project).withAlternativeResolveEnabled(Runnable {
+ highlightUsages(project, editor, file)
+ })
+ }
+
+ val ranges = editor.markupModel.allHighlighters
+ .filter { it.textAttributesKey == EditorColors.SEARCH_RESULT_ATTRIBUTES || it.textAttributesKey == EditorColors.WRITE_SEARCH_RESULT_ATTRIBUTES }
+ .mapNotNull { it.asTextRange }
+
+ val infos = ranges.toHashSet()
+ .map {
+ var startOffset = it.startOffset
+ var endOffset = it.endOffset
+
+ if (startOffset > caret) startOffset += CARET_TAG.length
+ if (endOffset > caret) endOffset += CARET_TAG.length
+
+ HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION)
+ .range(startOffset, endOffset)
+ .create()
+ }
+
+ data.checkResult(myFixture.file, infos, StringBuilder(document.text).insert(caret, CARET_TAG).toString())
+ }}
+}
\ No newline at end of file
diff --git a/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt b/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt
index 889f4bda5b370..7b8eabba2d7ec 100644
--- a/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt
+++ b/plugins/kotlin/util/test-generator-fe10/test/org/jetbrains/kotlin/fe10/testGenerator/Fe10GenerateTests.kt
@@ -117,6 +117,7 @@ import org.jetbrains.kotlin.idea.hierarchy.AbstractHierarchyWithLibTest
import org.jetbrains.kotlin.idea.highlighter.AbstractCustomHighlightUsageHandlerTest
import org.jetbrains.kotlin.idea.highlighter.AbstractDiagnosticMessageTest
import org.jetbrains.kotlin.idea.highlighter.AbstractDslHighlighterTest
+import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightImplicitItHandlerTest
import org.jetbrains.kotlin.idea.highlighter.AbstractK1HighlightingMetaInfoTest
import org.jetbrains.kotlin.idea.highlighter.AbstractK1HighlightingTest
import org.jetbrains.kotlin.idea.highlighter.AbstractKotlinReceiverUsageHighlightingTest
@@ -1016,6 +1017,10 @@ private fun assembleWorkspace(): TWorkspace = workspace(KotlinPluginMode.K1) {
testClass {
model("multiModuleHighlighting/multiplatform/", isRecursive = false, pattern = DIRECTORY)
}
+
+ testClass("org.jetbrains.kotlin.idea.highlighter.HighlightImplicitItHandlerTestGenerated") {
+ model("implicitIt")
+ }
}
testGroup("idea/tests", category = CODE_INSIGHT) {
diff --git a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt
index a9878fa4b2fdf..6831823e31c1c 100644
--- a/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt
+++ b/plugins/kotlin/util/test-generator-fir/test/org/jetbrains/kotlin/fir/testGenerator/GenerateK2HighlighterTests.kt
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.idea.k2.highlighting.AbstractK2HighlightUsagesTest
import org.jetbrains.kotlin.idea.k2.highlighting.AbstractK2HighlightingMetaInfoTest
import org.jetbrains.kotlin.idea.k2.highlighting.AbstractK2HighlightingMetaInfoWithExtensionTest
import org.jetbrains.kotlin.idea.core.script.k2.definitions.AbstractScriptHighlightingMetaInfoTest
+import org.jetbrains.kotlin.idea.highlighter.AbstractHighlightImplicitItHandlerTest
import org.jetbrains.kotlin.idea.k2.highlighting.AbstractOutsiderHighlightingTest
import org.jetbrains.kotlin.idea.test.kmp.KMPTestPlatform
import org.jetbrains.kotlin.testGenerator.model.GroupCategory.HIGHLIGHTING
@@ -78,4 +79,10 @@ internal fun MutableTWorkspace.generateK2HighlighterTests() {
model("outsider", pattern = Patterns.DIRECTORY, isRecursive = false, passTestDataPath = false)
}
}
+
+ testGroup("highlighting/highlighting-k2", category = HIGHLIGHTING, testDataPath = "../../idea/tests/testData") {
+ testClass("org.jetbrains.kotlin.idea.k2.highlighting.HighlightImplicitItHandlerTestGenerated") {
+ model("implicitIt")
+ }
+ }
}
\ No newline at end of file