FIR IDE: add completion support to classes and to type aliases

This commit is contained in:
Ilya Kirillov
2021-04-19 20:15:21 +02:00
committed by TeamCityServer
parent a5c33c8d42
commit da6c5e13d5
30 changed files with 504 additions and 34 deletions
@@ -31,7 +31,6 @@ class RawFirFragmentForLazyBodiesBuilder private constructor(
designation: List<FirDeclaration>,
declaration: KtDeclaration,
): FirDeclaration {
require(declaration is KtNamedFunction || declaration is KtProperty) { "Not implemented for ${declaration::class.qualifiedName}" }
val builder = RawFirFragmentForLazyBodiesBuilder(session, baseScopeProvider, declaration)
builder.context.packageFqName = declaration.containingKtFile.packageFqName
return builder.moveNext(designation.iterator())
@@ -365,6 +365,7 @@ open class FirDeclarationsResolveTransformer(transformer: FirBodyResolveTransfor
}
doTransformTypeParameters(typeAlias)
typeAlias.transformAnnotations(transformer, data)
transformer.onBeforeDeclarationContentResolve(typeAlias)
typeAlias.transformExpandedTypeRef(transformer, data)
return typeAlias
}
@@ -81,3 +81,27 @@ inline fun buildRegularClass(init: FirRegularClassBuilder.() -> Unit): FirRegula
}
return FirRegularClassBuilder().apply(init).build()
}
@OptIn(ExperimentalContracts::class)
inline fun buildRegularClassCopy(original: FirRegularClass, init: FirRegularClassBuilder.() -> Unit): FirRegularClass {
contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = FirRegularClassBuilder()
copyBuilder.source = original.source
copyBuilder.session = original.session
copyBuilder.resolvePhase = original.resolvePhase
copyBuilder.origin = original.origin
copyBuilder.attributes = original.attributes.copy()
copyBuilder.annotations.addAll(original.annotations)
copyBuilder.typeParameters.addAll(original.typeParameters)
copyBuilder.status = original.status
copyBuilder.classKind = original.classKind
copyBuilder.declarations.addAll(original.declarations)
copyBuilder.scopeProvider = original.scopeProvider
copyBuilder.name = original.name
copyBuilder.symbol = original.symbol
copyBuilder.companionObject = original.companionObject
copyBuilder.superTypeRefs.addAll(original.superTypeRefs)
return copyBuilder.apply(init).build()
}
@@ -68,3 +68,23 @@ inline fun buildTypeAlias(init: FirTypeAliasBuilder.() -> Unit): FirTypeAlias {
}
return FirTypeAliasBuilder().apply(init).build()
}
@OptIn(ExperimentalContracts::class)
inline fun buildTypeAliasCopy(original: FirTypeAlias, init: FirTypeAliasBuilder.() -> Unit): FirTypeAlias {
contract {
callsInPlace(init, kotlin.contracts.InvocationKind.EXACTLY_ONCE)
}
val copyBuilder = FirTypeAliasBuilder()
copyBuilder.source = original.source
copyBuilder.session = original.session
copyBuilder.resolvePhase = original.resolvePhase
copyBuilder.origin = original.origin
copyBuilder.attributes = original.attributes.copy()
copyBuilder.status = original.status
copyBuilder.typeParameters.addAll(original.typeParameters)
copyBuilder.name = original.name
copyBuilder.symbol = original.symbol
copyBuilder.expandedTypeRef = original.expandedTypeRef
copyBuilder.annotations.addAll(original.annotations)
return copyBuilder.apply(init).build()
}
@@ -28,7 +28,7 @@ inline fun <reified T : ConeKotlinType> FirTypeRef.coneTypeSafe(): T? {
return (this as? FirResolvedTypeRef)?.type as? T
}
inline val FirTypeRef.coneType: ConeKotlinType
val FirTypeRef.coneType: ConeKotlinType
get() = coneTypeSafe()
?: error("Expected FirResolvedTypeRef with ConeKotlinType but was ${this::class.simpleName} ${render()}")
@@ -37,6 +37,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
parents += typeParameterRefsOwnerBuilder
defaultNull("companionObject")
openBuilder()
withCopy()
}
val qualifiedAccessBuilder by builder {
@@ -95,6 +96,7 @@ object BuilderConfigurator : AbstractBuilderConfigurator<FirTreeBuilder>(FirTree
builder(typeAlias) {
parents += typeParametersOwnerBuilder
withCopy()
}
builder(annotationCall) {
@@ -11,3 +11,4 @@ class A() : My<caret> {
}
// EXIST: MySecondClass, MyFirstClass
// FIR_COMPARISON
@@ -8,4 +8,5 @@ class ChromePageManager(): PageManager(object : Runnable {
}) {
}
// EXIST: Tab
// EXIST: Tab
// FIR_COMPARISON
@@ -11,4 +11,5 @@ public class Test : String<caret> {
// EXIST: StringMy
// EXIST: String
// ABSENT: StringMethod
// ABSENT: StringMethod
// FIR_COMPARISON
@@ -0,0 +1,7 @@
class X {
init {
val y = 1
<caret>
}
}
// EXIST: y
@@ -0,0 +1,9 @@
class X {
val x: Int = 10
init {
<caret>
}
}
// EXIST: x
// FIR_COMPARISON
@@ -0,0 +1,5 @@
open class Base
class A : <caret>
// EXIST: Base
// FIR_COMPARISON
@@ -0,0 +1,5 @@
open class Base
class A : B<caret>
// EXIST: Base
// FIR_COMPARISON
@@ -0,0 +1,5 @@
open class Base<T>
class A<U> : Base<<caret>>
// EXIST: U
// FIR_COMPARISON
@@ -0,0 +1,8 @@
class X {
typealias Y = <caret>
}
class Z
// EXIST: Z
// FIR_COMPARISON
@@ -0,0 +1,5 @@
typealias Y = <caret>
class Z
// EXIST: Z
// FIR_COMPARISON
@@ -0,0 +1,6 @@
val y = 10
class X(val x: Int = <caret>)
// EXIST: y
// FIR_COMPARISON
@@ -0,0 +1,4 @@
class X(val y: Int, val x: Int = <caret>)
// EXIST: y
// FIR_COMPARISON
@@ -0,0 +1,6 @@
class Y
class X(val x: <caret>)
// EXIST: Y
// FIR_COMPARISON
@@ -4,4 +4,5 @@ annotation class SomeAnnotation
class Complete(@set:Some<caret> var field: Int)
// ELEMENT: SomeAnnotation
// ELEMENT: SomeAnnotation
// FIR_COMPARISON
@@ -4,4 +4,5 @@ annotation class SomeAnnotation
class Complete(@set:SomeAnnotation<caret> var field: Int)
// ELEMENT: SomeAnnotation
// ELEMENT: SomeAnnotation
// FIR_COMPARISON
@@ -1,3 +1,4 @@
package some
class Complete(@set:SomeAnn<caret> var field: Int)
// FIR_COMPARISON
@@ -3,3 +3,4 @@ package some
import other.SomeAnnotation
class Complete(@set:SomeAnnotation<caret> var field: Int)
// FIR_COMPARISON
@@ -1677,6 +1677,29 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inInitBlock")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InInitBlock extends AbstractJSBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInInitBlock() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inInitBlock"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("localVariable.kt")
public void testLocalVariable() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inInitBlock/localVariable.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inInitBlock/property.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inStringLiterals")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1720,6 +1743,57 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inSuperTypes")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InSuperTypes extends AbstractJSBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInSuperTypes() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inSuperTypes"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("baseClass.kt")
public void testBaseClass() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/baseClass.kt");
}
@TestMetadata("baseClassWithPrefix.kt")
public void testBaseClassWithPrefix() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/baseClassWithPrefix.kt");
}
@TestMetadata("typeParameter.kt")
public void testTypeParameter() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/typeParameter.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inTypeAlias")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InTypeAlias extends AbstractJSBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInTypeAlias() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inTypeAlias"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("member.kt")
public void testMember() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inTypeAlias/member.kt");
}
@TestMetadata("topLevel.kt")
public void testTopLevel() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inTypeAlias/topLevel.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/lambdaSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2231,6 +2305,34 @@ public class JSBasicCompletionTestGenerated extends AbstractJSBasicCompletionTes
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/primaryConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class PrimaryConstructor extends AbstractJSBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInPrimaryConstructor() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/primaryConstructor"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("parameterDefaultValue.kt")
public void testParameterDefaultValue() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/parameterDefaultValue.kt");
}
@TestMetadata("previousParameter.kt")
public void testPreviousParameter() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/previousParameter.kt");
}
@TestMetadata("propertyType.kt")
public void testPropertyType() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/propertyType.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/primitiveCompletion")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1677,6 +1677,29 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inInitBlock")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InInitBlock extends AbstractJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInInitBlock() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inInitBlock"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("localVariable.kt")
public void testLocalVariable() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inInitBlock/localVariable.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inInitBlock/property.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inStringLiterals")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1720,6 +1743,57 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inSuperTypes")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InSuperTypes extends AbstractJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInSuperTypes() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inSuperTypes"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("baseClass.kt")
public void testBaseClass() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/baseClass.kt");
}
@TestMetadata("baseClassWithPrefix.kt")
public void testBaseClassWithPrefix() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/baseClassWithPrefix.kt");
}
@TestMetadata("typeParameter.kt")
public void testTypeParameter() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/typeParameter.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inTypeAlias")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InTypeAlias extends AbstractJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInTypeAlias() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inTypeAlias"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("member.kt")
public void testMember() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inTypeAlias/member.kt");
}
@TestMetadata("topLevel.kt")
public void testTopLevel() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inTypeAlias/topLevel.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/lambdaSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2231,6 +2305,34 @@ public class JvmBasicCompletionTestGenerated extends AbstractJvmBasicCompletionT
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/primaryConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class PrimaryConstructor extends AbstractJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInPrimaryConstructor() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/primaryConstructor"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("parameterDefaultValue.kt")
public void testParameterDefaultValue() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/parameterDefaultValue.kt");
}
@TestMetadata("previousParameter.kt")
public void testPreviousParameter() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/previousParameter.kt");
}
@TestMetadata("propertyType.kt")
public void testPropertyType() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/propertyType.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/primitiveCompletion")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1677,6 +1677,29 @@ public class HighLevelJvmBasicCompletionTestGenerated extends AbstractHighLevelJ
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inInitBlock")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InInitBlock extends AbstractHighLevelJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInInitBlock() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inInitBlock"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("localVariable.kt")
public void testLocalVariable() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inInitBlock/localVariable.kt");
}
@TestMetadata("property.kt")
public void testProperty() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inInitBlock/property.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inStringLiterals")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1720,6 +1743,57 @@ public class HighLevelJvmBasicCompletionTestGenerated extends AbstractHighLevelJ
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inSuperTypes")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InSuperTypes extends AbstractHighLevelJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInSuperTypes() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inSuperTypes"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("baseClass.kt")
public void testBaseClass() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/baseClass.kt");
}
@TestMetadata("baseClassWithPrefix.kt")
public void testBaseClassWithPrefix() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/baseClassWithPrefix.kt");
}
@TestMetadata("typeParameter.kt")
public void testTypeParameter() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inSuperTypes/typeParameter.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/inTypeAlias")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class InTypeAlias extends AbstractHighLevelJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInInTypeAlias() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/inTypeAlias"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("member.kt")
public void testMember() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inTypeAlias/member.kt");
}
@TestMetadata("topLevel.kt")
public void testTopLevel() throws Exception {
runTest("idea/idea-completion/testData/basic/common/inTypeAlias/topLevel.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/lambdaSignature")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -2231,6 +2305,34 @@ public class HighLevelJvmBasicCompletionTestGenerated extends AbstractHighLevelJ
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/primaryConstructor")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class PrimaryConstructor extends AbstractHighLevelJvmBasicCompletionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
public void testAllFilesPresentInPrimaryConstructor() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/idea-completion/testData/basic/common/primaryConstructor"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("parameterDefaultValue.kt")
public void testParameterDefaultValue() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/parameterDefaultValue.kt");
}
@TestMetadata("previousParameter.kt")
public void testPreviousParameter() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/previousParameter.kt");
}
@TestMetadata("propertyType.kt")
public void testPropertyType() throws Exception {
runTest("idea/idea-completion/testData/basic/common/primaryConstructor/propertyType.kt");
}
}
@TestMetadata("idea/idea-completion/testData/basic/common/primitiveCompletion")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -8,14 +8,11 @@ package org.jetbrains.kotlin.idea.fir.low.level.api.api
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.builder.RawFirFragmentForLazyBodiesBuilder
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.builder.buildPropertyAccessorCopy
import org.jetbrains.kotlin.fir.declarations.builder.buildSimpleFunctionCopy
import org.jetbrains.kotlin.fir.declarations.builder.*
import org.jetbrains.kotlin.fir.expressions.FirReturnExpression
import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid
import org.jetbrains.kotlin.idea.fir.low.level.api.providers.firIdeProvider
import org.jetbrains.kotlin.psi.KtDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.psi.*
object DeclarationCopyBuilder {
fun createDeclarationCopy(
@@ -24,26 +21,36 @@ object DeclarationCopyBuilder {
state: FirModuleResolveState,
): FirDeclaration {
return when (fakeKtDeclaration) {
is KtNamedFunction -> buildFunctionCopy(
is KtNamedFunction -> createFunctionCopy(
fakeKtDeclaration,
originalFirDeclaration as FirSimpleFunction,
state
)
is KtProperty -> buildPropertyCopy(
is KtProperty -> createPropertyCopy(
fakeKtDeclaration,
originalFirDeclaration as FirProperty,
state
)
is KtClassOrObject -> createClassCopy(
fakeKtDeclaration,
originalFirDeclaration as FirRegularClass,
state
)
is KtTypeAlias -> createTypeAliasCopy(
fakeKtDeclaration,
originalFirDeclaration as FirTypeAlias,
state
)
else -> error("Unsupported declaration ${fakeKtDeclaration::class.simpleName}")
}
}
private fun buildFunctionCopy(
private fun createFunctionCopy(
element: KtNamedFunction,
originalFunction: FirSimpleFunction,
state: FirModuleResolveState,
): FirSimpleFunction {
val builtFunction = createCopy(element, originalFunction) as FirSimpleFunction
val builtFunction = createCopy(element, originalFunction)
// right now we can't resolve builtFunction header properly, as it built right in air,
// without file, which is now required for running stages other then body resolve, so we
@@ -57,12 +64,45 @@ object DeclarationCopyBuilder {
}.apply { reassignAllReturnTargets(builtFunction) }
}
private fun buildPropertyCopy(
private fun createClassCopy(
fakeKtClassOrObject: KtClassOrObject,
originalFirClass: FirRegularClass,
state: FirModuleResolveState,
): FirRegularClass {
val builtClass = createCopy(fakeKtClassOrObject, originalFirClass)
return buildRegularClassCopy(originalFirClass) {
declarations.clear()
declarations.addAll(builtClass.declarations)
symbol = builtClass.symbol
resolvePhase = minOf(originalFirClass.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtClass.source
session = state.rootModuleSession
}
}
private fun createTypeAliasCopy(
fakeKtTypeAlias: KtTypeAlias,
originalFirTypeAlias: FirTypeAlias,
state: FirModuleResolveState,
): FirTypeAlias {
val builtTypeAlias = createCopy(fakeKtTypeAlias, originalFirTypeAlias)
return buildTypeAliasCopy(originalFirTypeAlias) {
expandedTypeRef = builtTypeAlias.expandedTypeRef
symbol = builtTypeAlias.symbol
resolvePhase = minOf(originalFirTypeAlias.resolvePhase, FirResolvePhase.DECLARATIONS)
source = builtTypeAlias.source
session = state.rootModuleSession
}
}
private fun createPropertyCopy(
element: KtProperty,
originalProperty: FirProperty,
state: FirModuleResolveState
): FirProperty {
val builtProperty = createCopy(element, originalProperty) as FirProperty
val builtProperty = createCopy(element, originalProperty)
val originalSetter = originalProperty.setter
val builtSetter = builtProperty.setter
@@ -80,7 +120,7 @@ object DeclarationCopyBuilder {
builtSetter
}
return org.jetbrains.kotlin.fir.declarations.builder.buildPropertyCopy(originalProperty) {
return buildPropertyCopy(originalProperty) {
symbol = builtProperty.symbol
initializer = builtProperty.initializer
@@ -93,16 +133,16 @@ object DeclarationCopyBuilder {
}
}
fun createCopy(
internal inline fun <reified T : FirDeclaration> createCopy(
fakeKtDeclaration: KtDeclaration,
originalFirDeclaration: FirDeclaration,
): FirDeclaration {
originalFirDeclaration: T,
): T {
return RawFirFragmentForLazyBodiesBuilder.build(
session = originalFirDeclaration.session,
baseScopeProvider = originalFirDeclaration.session.firIdeProvider.kotlinScopeProvider,
designation = originalFirDeclaration.collectDesignation().fullDesignation,
declaration = fakeKtDeclaration
)
) as T
}
private fun FirFunction<*>.reassignAllReturnTargets(from: FirFunction<*>) {
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.fir.declarations.FirFile
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.resolve.FirTowerDataContext
import org.jetbrains.kotlin.idea.fir.low.level.api.util.originalDeclaration
import org.jetbrains.kotlin.psi.*
@ThreadSafeMutableState
@@ -36,11 +37,21 @@ class FirTowerDataContextCollector {
}
fun FirTowerDataContextCollector.getClosestAvailableParentContext(element: KtElement): FirTowerDataContext? {
var current: KtElement = element
while (true) {
getContext(current)?.let { return it }
current = current.parent as? KtElement ?: return null
var current: PsiElement? = element
while (current != null) {
if (current is KtElement) {
getContext(current)?.let { return it }
}
if (current is KtDeclaration) {
val originalDeclaration = current.originalDeclaration
originalDeclaration?.let { getContext(it) }?.let { return it }
}
if (current is KtFile) {
break
}
current = current.parent
}
return null
}
private tailrec fun PsiElement.closestBlockLevelOrInitializerExpression(): KtExpression? =
@@ -80,7 +80,7 @@ internal class ReanalyzableFunctionStructureElement(
towerDataContextCollector: FirTowerDataContextCollector,
): ReanalyzableFunctionStructureElement {
val originalFunction = firSymbol.fir as FirSimpleFunction
val newFunction = DeclarationCopyBuilder.createCopy(newKtDeclaration, originalFunction) as FirSimpleFunction
val newFunction = DeclarationCopyBuilder.createCopy(newKtDeclaration, originalFunction)
return FileStructureUtil.withDeclarationReplaced(firFile, cache, originalFunction, newFunction) {
firLazyDeclarationResolver.lazyResolveDeclaration(
@@ -122,7 +122,7 @@ internal class ReanalyzablePropertyStructureElement(
towerDataContextCollector: FirTowerDataContextCollector,
): ReanalyzablePropertyStructureElement {
val originalProperty = firSymbol.fir
val newProperty = DeclarationCopyBuilder.createCopy(newKtDeclaration, originalProperty) as FirProperty
val newProperty = DeclarationCopyBuilder.createCopy(newKtDeclaration, originalProperty)
return FileStructureUtil.withDeclarationReplaced(firFile, cache, originalProperty, newProperty) {
firLazyDeclarationResolver.lazyResolveDeclaration(
@@ -34,8 +34,8 @@ internal sealed class EnclosingDeclarationContext {
ktDeclaration.canBeEnclosingDeclaration()
}
if (fakeKtDeclaration != null) {
val originalDeclaration = originalFile.findDeclarationOfTypeAt(fakeKtDeclaration.textOffset, fakeKtDeclaration::class)
?: error("Cannot find original function matching to ${fakeKtDeclaration.getElementTextInContext()} in $originalFile")
val originalDeclaration = findMatchingElementInCopy(fakeKtDeclaration, originalFile)
?: error("Cannot find original declaration matching to ${fakeKtDeclaration.getElementTextInContext()} in $originalFile")
recordOriginalDeclaration(originalDeclaration, fakeKtDeclaration)
return EnclosingDeclarationContextImpl(fakeKtDeclaration, originalDeclaration)
}
@@ -62,7 +62,6 @@ internal class EnclosingDeclarationContextImpl(
) : EnclosingDeclarationContext()
internal fun EnclosingDeclarationContext.recordCompletionContext(originalFirFile: FirFile, firResolveState: FirModuleResolveState) {
LowLevelFirApiFacadeForCompletion.recordCompletionContextForDeclaration(
originalFirFile,
@@ -73,7 +72,8 @@ internal fun EnclosingDeclarationContext.recordCompletionContext(originalFirFile
)
}
private fun <T : KtElement> KtFile.findDeclarationOfTypeAt(offset: Int, declarationType: KClass<T>): T? {
val elementAtOffset = findElementAt(offset) ?: return null
return PsiTreeUtil.getParentOfType(elementAtOffset, declarationType.java, false)?.takeIf { it.textOffset == offset }
fun <T : KtElement> findMatchingElementInCopy(element: T, copy: KtFile): T? {
val elementOffset = element.textOffset
val elementAtOffset = copy.findElementAt(elementOffset) ?: return null
return PsiTreeUtil.getParentOfType(elementAtOffset, element::class.java, false)?.takeIf { it.textOffset == elementOffset }
}