From 28d8d2d9037682b6c56ee58061f19a56d1e2dde3 Mon Sep 17 00:00:00 2001 From: Roman Golyshev Date: Fri, 26 Mar 2021 18:51:25 +0300 Subject: [PATCH] FIR IDE: Search for the first non-local declaration in `EnclosingDeclarationContext::detect` Local declarations do not have a stable `ClassId`, which is used in `declarationUtils.collectDesignation`. Local classes will always have `outerClassId.isLocal = true`, even when it is not true (see `org.jetbrains.kotlin.fir.builder.Context.currentClassId` for why this happens) --- .../basic/common/InsideAnonymousClass.kt | 1 + .../localClassConstructor.kt | 20 +++++++++++++++++++ .../primitiveCompletion/localClassMember.kt | 20 +++++++++++++++++++ .../test/JSBasicCompletionTestGenerated.java | 10 ++++++++++ .../test/JvmBasicCompletionTestGenerated.java | 10 ++++++++++ ...hLevelJvmBasicCompletionTestGenerated.java | 10 ++++++++++ .../fir/utils/EnclosingDeclarationContext.kt | 10 ++++++++-- 7 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt create mode 100644 idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt diff --git a/idea/idea-completion/testData/basic/common/InsideAnonymousClass.kt b/idea/idea-completion/testData/basic/common/InsideAnonymousClass.kt index 6b06f0d7020..fda324941bb 100644 --- a/idea/idea-completion/testData/basic/common/InsideAnonymousClass.kt +++ b/idea/idea-completion/testData/basic/common/InsideAnonymousClass.kt @@ -1,3 +1,4 @@ +// FIR_COMPARISON class A(private val xxx1: Int, val xxx2: Int) { private val xxx3: Int = 0 diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt new file mode 100644 index 00000000000..27d994eb486 --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt @@ -0,0 +1,20 @@ +package test + +class T + +fun topLevelFun(param: T) { + val firstVariable = T() + + class LocalClass(constructorParam: T) { + val localClassField = constructorParam + + init { + + } + } + + val secondVariable = T() +} + +// EXIST: firstVariable, param, constructorParam, localClassField +// ABSENT: secondVariable \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt b/idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt new file mode 100644 index 00000000000..6a49acb111b --- /dev/null +++ b/idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt @@ -0,0 +1,20 @@ +package test + +class T + +fun topLevelFun(param: T) { + val firstVariable = T() + + class LocalClass(constructorParam: T) { + val localClassField = constructorParam + + fun localClassMethod(localParam: T) { + + } + } + + val secondVariable = T() +} + +// EXIST: firstVariable, param, constructorParam, localClassField, localParam +// ABSENT: secondVariable \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java index 6683b7df16e..92bee62d87a 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JSBasicCompletionTestGenerated.java @@ -2328,6 +2328,16 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt"); } + @TestMetadata("localClassConstructor.kt") + public void testLocalClassConstructor() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt"); + } + + @TestMetadata("localClassMember.kt") + public void testLocalClassMember() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt"); + } + @TestMetadata("localVariablesAndFunctions.kt") public void testLocalVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java index 5461a889399..ff0b04bfbd1 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmBasicCompletionTestGenerated.java @@ -2328,6 +2328,16 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt"); } + @TestMetadata("localClassConstructor.kt") + public void testLocalClassConstructor() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt"); + } + + @TestMetadata("localClassMember.kt") + public void testLocalClassMember() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt"); + } + @TestMetadata("localVariablesAndFunctions.kt") public void testLocalVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); diff --git a/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java b/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java index 80fd8056e63..611c20013d4 100644 --- a/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java +++ b/idea/idea-fir/tests/org/jetbrains/kotlin/idea/completion/HighLevelJvmBasicCompletionTestGenerated.java @@ -2328,6 +2328,16 @@ public class HighLevelJvmBasicCompletionTestGenerated extends AbstractHighLevelJ runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/insideFunctionWithParameters.kt"); } + @TestMetadata("localClassConstructor.kt") + public void testLocalClassConstructor() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localClassConstructor.kt"); + } + + @TestMetadata("localClassMember.kt") + public void testLocalClassMember() throws Exception { + runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localClassMember.kt"); + } + @TestMetadata("localVariablesAndFunctions.kt") public void testLocalVariablesAndFunctions() throws Exception { runTest("idea/idea-completion/testData/basic/common/primitiveCompletion/localVariablesAndFunctions.kt"); diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/EnclosingDeclarationContext.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/EnclosingDeclarationContext.kt index 91c26357110..bf3295fb3fa 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/EnclosingDeclarationContext.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/utils/EnclosingDeclarationContext.kt @@ -12,12 +12,15 @@ import org.jetbrains.kotlin.idea.fir.low.level.api.api.LowLevelFirApiFacadeForCo import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration import org.jetbrains.kotlin.idea.util.getElementTextInContext import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType internal sealed class EnclosingDeclarationContext { companion object { fun detect(originalFile: KtFile, positionInFakeFile: KtElement): EnclosingDeclarationContext { - val fakeFunction = positionInFakeFile.parentsOfType().firstOrNull { !it.isLocal } + val fakeFunction = positionInFakeFile.parentsOfType().firstOrNull { + it.isTopLevel || it.containingClassOrObject?.isLocal == false + } if (fakeFunction != null) { val originalFunction = originalFile.findDeclarationOfTypeAt(fakeFunction.textOffset) ?: error("Cannot find original function matching to ${fakeFunction.getElementTextInContext()} in $originalFile") @@ -25,7 +28,10 @@ internal sealed class EnclosingDeclarationContext { return FunctionContext(fakeFunction, originalFunction) } - val fakeProperty = positionInFakeFile.parentsOfType().firstOrNull { !it.isLocal } + val fakeProperty = positionInFakeFile.parentsOfType().firstOrNull { + it.isTopLevel || it.containingClassOrObject?.isLocal == false + } + if (fakeProperty != null) { val originalProperty = originalFile.findDeclarationOfTypeAt(fakeProperty.textOffset) ?: error("Cannot find original property matching to ${fakeProperty.getElementTextInContext()} in $originalFile")