From 051e1f7ca93f14d0a79f74adfb2cc59f69058e00 Mon Sep 17 00:00:00 2001 From: Jayson Minard Date: Sat, 22 Jul 2023 13:30:52 -0600 Subject: [PATCH 1/4] adding analysis for context receivers (experimental up thought Kotlin 1.9 but should be final in Kotlin 2.0) --- .../com/google/devtools/ksp/symbol/KSFunction.kt | 7 +++++++ .../devtools/ksp/symbol/KSFunctionDeclaration.kt | 5 +++++ .../impl/binary/KSFunctionDeclarationDescriptorImpl.kt | 6 ++++++ .../symbol/impl/java/KSFunctionDeclarationJavaImpl.kt | 2 ++ .../symbol/impl/kotlin/KSFunctionDeclarationImpl.kt | 7 +++++++ .../ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt | 5 +++++ .../devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt | 7 +++++++ .../impl/synthetic/KSConstructorSyntheticImpl.kt | 2 ++ .../impl/symbol/kotlin/KSFunctionDeclarationImpl.kt | 8 ++++++++ .../devtools/ksp/processor/AsMemberOfProcessor.kt | 7 ++++++- test-utils/testData/api/asMemberOf.kt | 10 +++++++++- 11 files changed, 64 insertions(+), 2 deletions(-) diff --git a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunction.kt b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunction.kt index d4139dbbe2..1e132a63f6 100644 --- a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunction.kt +++ b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunction.kt @@ -53,6 +53,13 @@ interface KSFunction { */ val extensionReceiverType: KSType? + /** + * The context receiver types of the function + * + * @see KSFunctionDeclaration.contextReceivers + */ + val contextReceiverTypes: List + /** * True if the compiler couldn't resolve the function. */ diff --git a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunctionDeclaration.kt b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunctionDeclaration.kt index 5731a10a35..0055719a74 100644 --- a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunctionDeclaration.kt +++ b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSFunctionDeclaration.kt @@ -44,6 +44,11 @@ interface KSFunctionDeclaration : KSDeclaration, KSDeclarationContainer { */ val extensionReceiver: KSTypeReference? + /** + * Context receivers of this function + */ + val contextReceivers: List + /** * Return type of this function. * Can be null if an error occurred during resolution. diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt index 0749a095b6..a379b4ffd8 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSFunctionDeclarationDescriptorImpl.kt @@ -58,6 +58,12 @@ class KSFunctionDeclarationDescriptorImpl private constructor(val descriptor: Fu } } + override val contextReceivers: List by lazy { + descriptor.contextReceiverParameters.map { + KSTypeReferenceDescriptorImpl.getCached(it.type, origin, this) + } + } + override val functionKind: FunctionKind by lazy { when { diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt index a1d60e1cb4..862243dfea 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSFunctionDeclarationJavaImpl.kt @@ -61,6 +61,8 @@ class KSFunctionDeclarationJavaImpl private constructor(val psi: PsiMethod) : override val extensionReceiver: KSTypeReference? = null + override val contextReceivers: List = emptyList() + override val functionKind: FunctionKind = when { psi.hasModifier(JvmModifier.STATIC) -> FunctionKind.STATIC else -> FunctionKind.MEMBER diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt index 75017fbb33..567d243bdc 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionDeclarationImpl.kt @@ -25,6 +25,7 @@ import com.google.devtools.ksp.processing.impl.KSPCompilationError import com.google.devtools.ksp.processing.impl.ResolverImpl import com.google.devtools.ksp.symbol.* import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.binary.KSTypeReferenceDescriptorImpl import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.KtConstructor @@ -78,6 +79,12 @@ class KSFunctionDeclarationImpl private constructor(val ktFunction: KtFunction) } } + override val contextReceivers: List by lazy { + ktFunction.contextReceivers.map { + KSTypeReferenceImpl.getCached(it.typeReference()!!) + } + } + override val functionKind: FunctionKind by lazy { if (parentDeclaration == null) { FunctionKind.TOP_LEVEL diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt index 5100e367e8..391e84c6a3 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionErrorImpl.kt @@ -43,6 +43,11 @@ class KSFunctionErrorImpl( KSErrorType } + override val contextReceiverTypes: List + get() = declaration.contextReceivers.let { + listOf(KSErrorType) + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt index 55eebfbfd8..3606851073 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSFunctionImpl.kt @@ -54,6 +54,12 @@ class KSFunctionImpl(val descriptor: CallableDescriptor) : KSFunction { descriptor.extensionReceiverParameter?.type?.let(::getKSTypeCached) } + override val contextReceiverTypes: List by lazy(LazyThreadSafetyMode.PUBLICATION) { + descriptor.contextReceiverParameters.map { + getKSTypeCached(it.type) + } + } + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false @@ -64,6 +70,7 @@ class KSFunctionImpl(val descriptor: CallableDescriptor) : KSFunction { if (parameterTypes != other.parameterTypes) return false if (typeParameters != other.typeParameters) return false if (extensionReceiverType != other.extensionReceiverType) return false + if (contextReceiverTypes != other.contextReceiverTypes) return false return true } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt index 8f2efd36f7..57da4b591e 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSConstructorSyntheticImpl.kt @@ -39,6 +39,8 @@ class KSConstructorSyntheticImpl private constructor(val ksClassDeclaration: KSC override val extensionReceiver: KSTypeReference? = null + override val contextReceivers: List = emptyList() + override val parameters: List = emptyList() override val functionKind: FunctionKind = FunctionKind.MEMBER diff --git a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSFunctionDeclarationImpl.kt b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSFunctionDeclarationImpl.kt index 2ea5eace19..23090513ca 100644 --- a/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSFunctionDeclarationImpl.kt +++ b/kotlin-analysis-api/src/main/kotlin/com/google/devtools/ksp/impl/symbol/kotlin/KSFunctionDeclarationImpl.kt @@ -62,6 +62,14 @@ class KSFunctionDeclarationImpl private constructor(internal val ktFunctionSymbo } } + override val contextReceivers: List by lazy { + analyze { + ktFunctionSymbol.contextReceivers.map { + KSTypeReferenceImpl.getCached(it.type, this@KSFunctionDeclarationImpl) + } + } + } + override val returnType: KSTypeReference? by lazy { analyze { // Constructors diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt index 7c260375df..07083680a3 100644 --- a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/AsMemberOfProcessor.kt @@ -224,7 +224,12 @@ class AsMemberOfProcessor : AbstractTestProcessor() { } else { "" } - return "$receiverSignature$paramTypesSignature($params) -> $returnType" + val contextSignature = if (contextReceiverTypes.isNotEmpty()) { + contextReceiverTypes.map { it.toSignature() }.joinToString(prefix = "context(", postfix = ") ", separator = ",") + } else { + "" + } + return "$contextSignature$receiverSignature$paramTypesSignature($params) -> $returnType" } private fun Nullability.toSignature() = when (this) { diff --git a/test-utils/testData/api/asMemberOf.kt b/test-utils/testData/api/asMemberOf.kt index d96346cccd..bb7d94cc68 100644 --- a/test-utils/testData/api/asMemberOf.kt +++ b/test-utils/testData/api/asMemberOf.kt @@ -26,6 +26,7 @@ // errorType: ? // extensionProperty: kotlin.String? // returnInt: () -> kotlin.Int!! +// returnInt2: context(kotlin.Int!!,kotlin.String!!) () -> kotlin.Int!! // returnArg1: () -> kotlin.Int!! // returnArg1Nullable: () -> kotlin.Int? // returnArg2: () -> kotlin.String? @@ -43,6 +44,7 @@ // errorType: ? // extensionProperty: kotlin.Any? // returnInt: () -> kotlin.Int!! +// returnInt2: context(kotlin.Int!!,kotlin.String!!) () -> kotlin.Int!! // returnArg1: () -> kotlin.Any? // returnArg1Nullable: () -> kotlin.Any? // returnArg2: () -> kotlin.Any? @@ -60,6 +62,7 @@ // errorType: ? // extensionProperty: kotlin.String? // returnInt: () -> kotlin.Int!! +// returnInt2: context(kotlin.Int!!,kotlin.String!!) () -> kotlin.Int!! // returnArg1: () -> kotlin.String!! // returnArg1Nullable: () -> kotlin.String? // returnArg2: () -> kotlin.String? @@ -77,6 +80,7 @@ // errorType: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `errorType` (Base) // extensionProperty: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `extensionProperty` (Base) // returnInt: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnInt` (Base) +// returnInt2: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnInt2` (Base) // returnArg1: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnArg1` (Base) // returnArg1Nullable: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnArg1Nullable` (Base) // returnArg2: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnArg2` (Base) @@ -104,7 +108,7 @@ // fileLevelFunction: java.lang.IllegalArgumentException: Cannot call asMemberOf with a function that is not declared in a class or an interface // fileLevelExtensionFunction: java.lang.IllegalArgumentException: Cannot call asMemberOf with a function that is not declared in a class or an interface // fileLevelProperty: java.lang.IllegalArgumentException: Cannot call asMemberOf with a property that is not declared in a class or an interface -// errorType: (?) -> ? +// errorType: context(?) (?) -> ? // expected comparison failures // (Base.functionArgType.BaseTypeArg1?) -> kotlin.String? // () -> kotlin.Int!! @@ -119,6 +123,10 @@ open class Base { val typePair: Pair = TODO() val errorType: NonExistType = TODO() fun returnInt():Int = TODO() + + context(Int, String) + fun returnInt2():Int = TODO() + fun returnArg1(): BaseTypeArg1 = TODO() fun returnArg1Nullable(): BaseTypeArg1? = TODO() fun returnArg2(): BaseTypeArg2 = TODO() From b4b4e6591820561bcfc9c6e29615bfb93121c18a Mon Sep 17 00:00:00 2001 From: Jayson Minard Date: Sat, 22 Jul 2023 13:40:57 -0600 Subject: [PATCH 2/4] improve AsMemberOf tests for extension and context receivers as members. --- test-utils/testData/api/asMemberOf.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test-utils/testData/api/asMemberOf.kt b/test-utils/testData/api/asMemberOf.kt index bb7d94cc68..2f8987cdba 100644 --- a/test-utils/testData/api/asMemberOf.kt +++ b/test-utils/testData/api/asMemberOf.kt @@ -27,6 +27,8 @@ // extensionProperty: kotlin.String? // returnInt: () -> kotlin.Int!! // returnInt2: context(kotlin.Int!!,kotlin.String!!) () -> kotlin.Int!! +// returnInt3: context(kotlin.String!!) kotlin.Int!!.() -> kotlin.Int!! +// returnInt4: kotlin.Int!!.() -> kotlin.Int!! // returnArg1: () -> kotlin.Int!! // returnArg1Nullable: () -> kotlin.Int? // returnArg2: () -> kotlin.String? @@ -45,6 +47,8 @@ // extensionProperty: kotlin.Any? // returnInt: () -> kotlin.Int!! // returnInt2: context(kotlin.Int!!,kotlin.String!!) () -> kotlin.Int!! +// returnInt3: context(kotlin.String!!) kotlin.Int!!.() -> kotlin.Int!! +// returnInt4: kotlin.Int!!.() -> kotlin.Int!! // returnArg1: () -> kotlin.Any? // returnArg1Nullable: () -> kotlin.Any? // returnArg2: () -> kotlin.Any? @@ -63,6 +67,8 @@ // extensionProperty: kotlin.String? // returnInt: () -> kotlin.Int!! // returnInt2: context(kotlin.Int!!,kotlin.String!!) () -> kotlin.Int!! +// returnInt3: context(kotlin.String!!) kotlin.Int!!.() -> kotlin.Int!! +// returnInt4: kotlin.Int!!.() -> kotlin.Int!! // returnArg1: () -> kotlin.String!! // returnArg1Nullable: () -> kotlin.String? // returnArg2: () -> kotlin.String? @@ -81,6 +87,8 @@ // extensionProperty: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `extensionProperty` (Base) // returnInt: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnInt` (Base) // returnInt2: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnInt2` (Base) +// returnInt3: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnInt3` (Base) +// returnInt4: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnInt4` (Base) // returnArg1: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnArg1` (Base) // returnArg1Nullable: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnArg1Nullable` (Base) // returnArg2: java.lang.IllegalArgumentException: NotAChild is not a sub type of the class/interface that contains `returnArg2` (Base) @@ -127,6 +135,11 @@ open class Base { context(Int, String) fun returnInt2():Int = TODO() + context(String) + fun Int.returnInt3():Int = TODO() + + fun Int.returnInt4():Int = TODO() + fun returnArg1(): BaseTypeArg1 = TODO() fun returnArg1Nullable(): BaseTypeArg1? = TODO() fun returnArg2(): BaseTypeArg2 = TODO() From b7dfe901ba927b0350d84819063c8ec28658ba8c Mon Sep 17 00:00:00 2001 From: Jayson Minard Date: Sat, 22 Jul 2023 16:23:59 -0600 Subject: [PATCH 3/4] enable context-receivers setting for compilation --- .../google/devtools/ksp/testutils/AbstractKSPTest.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/testutils/AbstractKSPTest.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/testutils/AbstractKSPTest.kt index b2d89bfb5d..8e5f62ae8f 100644 --- a/test-utils/src/main/kotlin/com/google/devtools/ksp/testutils/AbstractKSPTest.kt +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/testutils/AbstractKSPTest.kt @@ -22,11 +22,18 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.testFramework.TestDataFile +import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.setupCommonArguments import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.config.addJavaSourceRoot import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots +import org.jetbrains.kotlin.cli.jvm.setupJvmSpecificArguments import org.jetbrains.kotlin.codegen.GenerationUtils +import org.jetbrains.kotlin.config.CompilerConfigurationKey +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.test.ExecutionListenerBasedDisposableProvider @@ -111,6 +118,10 @@ abstract class AbstractKSPTest(frontend: FrontendKind<*>) : DisposableTest() { this@globalDefaults.frontend = frontend targetPlatform = JvmPlatforms.defaultJvmPlatform dependencyKind = DependencyKind.Source + languageSettings { + // TODO: when would this be removed after they become stable, as it is version specific? + this.enable(LanguageFeature.ContextReceivers) + } } useConfigurators( ::CommonEnvironmentConfigurator, From 2ac4bbb391985cc4c010fc9040016081b6ad4071 Mon Sep 17 00:00:00 2001 From: Jayson Minard Date: Sat, 22 Jul 2023 16:24:39 -0600 Subject: [PATCH 4/4] add analysis of context receivers at the class level, which then implicitly affect all constructors. --- .../devtools/ksp/symbol/KSClassDeclaration.kt | 5 ++ .../KSClassDeclarationDescriptorImpl.kt | 10 ++++ .../KSClassDeclarationJavaEnumEntryImpl.kt | 2 + .../impl/java/KSClassDeclarationJavaImpl.kt | 2 + .../impl/kotlin/KSClassDeclarationImpl.kt | 5 ++ .../synthetic/KSErrorTypeClassDeclaration.kt | 2 + .../ConstructorDeclarationsProcessor.kt | 11 +++- .../testData/api/constructorDeclarations.kt | 58 ++++++++++++++++++- 8 files changed, 92 insertions(+), 3 deletions(-) diff --git a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSClassDeclaration.kt b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSClassDeclaration.kt index d7de9eb723..41f3d58c1a 100644 --- a/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSClassDeclaration.kt +++ b/api/src/main/kotlin/com/google/devtools/ksp/symbol/KSClassDeclaration.kt @@ -74,4 +74,9 @@ interface KSClassDeclaration : KSDeclaration, KSDeclarationContainer { * @return A type with all type parameters applied with star projection. */ fun asStarProjectedType(): KSType + + /** + * The class may have context receivers at the class level, which affect all constructors + */ + val contextReceivers: List } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt index 94f788fb0a..e3eb9d030a 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/binary/KSClassDeclarationDescriptorImpl.kt @@ -72,6 +72,10 @@ class KSClassDeclarationDescriptorImpl private constructor(val descriptor: Class override fun getAllProperties(): Sequence = descriptor.getAllProperties() + override val contextReceivers: List by lazy { + descriptor.getAllContextReceivers(this) + } + override val primaryConstructor: KSFunctionDeclaration? by lazy { descriptor.unsubstitutedPrimaryConstructor?.let { KSFunctionDeclarationDescriptorImpl.getCached(it) } } @@ -190,6 +194,12 @@ internal fun ClassDescriptor.getAllProperties(): Sequence } } +internal fun ClassDescriptor.getAllContextReceivers(node: KSNode): List { + return contextReceivers.map { + KSTypeReferenceDescriptorImpl.getCached(it.type, origin, node) + } +} + internal fun ClassDescriptor.sealedSubclassesSequence(): Sequence { // TODO record incremental subclass lookups in Kotlin 1.5.x? return sealedSubclasses diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt index 05f017be10..3dbb8dc3b2 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaEnumEntryImpl.kt @@ -105,6 +105,8 @@ class KSClassDeclarationJavaEnumEntryImpl private constructor(val psi: PsiEnumCo return getKSTypeCached(descriptor!!.defaultType) } + override val contextReceivers: List = emptyList() + override fun accept(visitor: KSVisitor, data: D): R { return visitor.visitClassDeclaration(this, data) } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt index bc6c01c6af..50bcc9f3a8 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/java/KSClassDeclarationJavaImpl.kt @@ -166,6 +166,8 @@ class KSClassDeclarationJavaImpl private constructor(val psi: PsiClass) : } ?: KSErrorType } + override val contextReceivers: List = emptyList() + override fun accept(visitor: KSVisitor, data: D): R { return visitor.visitClassDeclaration(this, data) } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt index d4c1fb02ab..22ca3c3181 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/kotlin/KSClassDeclarationImpl.kt @@ -25,6 +25,7 @@ import com.google.devtools.ksp.processing.impl.KSTypeReferenceSyntheticImpl import com.google.devtools.ksp.processing.impl.ResolverImpl import com.google.devtools.ksp.symbol.* import com.google.devtools.ksp.symbol.impl.* +import com.google.devtools.ksp.symbol.impl.binary.getAllContextReceivers import com.google.devtools.ksp.symbol.impl.binary.getAllFunctions import com.google.devtools.ksp.symbol.impl.binary.getAllProperties import com.google.devtools.ksp.symbol.impl.binary.sealedSubclassesSequence @@ -132,6 +133,10 @@ class KSClassDeclarationImpl private constructor(val ktClassOrObject: KtClassOrO return getKSTypeCached(descriptor.defaultType.replaceArgumentsWithStarProjections()) } + override val contextReceivers: List by lazy { + descriptor.getAllContextReceivers(this) + } + override fun accept(visitor: KSVisitor, data: D): R { return visitor.visitClassDeclaration(this, data) } diff --git a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt index 2a71233bd7..1f66943b67 100644 --- a/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt +++ b/compiler-plugin/src/main/kotlin/com/google/devtools/ksp/symbol/impl/synthetic/KSErrorTypeClassDeclaration.kt @@ -64,6 +64,8 @@ object KSErrorTypeClassDeclaration : KSClassDeclaration { return ResolverImpl.instance!!.builtIns.nothingType } + override val contextReceivers: List = emptyList() + override fun asType(typeArguments: List): KSType { return ResolverImpl.instance!!.builtIns.nothingType } diff --git a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/ConstructorDeclarationsProcessor.kt b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/ConstructorDeclarationsProcessor.kt index ff51f80c63..ab655de2a3 100644 --- a/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/ConstructorDeclarationsProcessor.kt +++ b/test-utils/src/main/kotlin/com/google/devtools/ksp/processor/ConstructorDeclarationsProcessor.kt @@ -56,8 +56,15 @@ class ConstructorDeclarationsProcessor : AbstractTestProcessor() { listOf("class: " + it.key.qualifiedName!!.asString()) + it.value } } - fun KSFunctionDeclaration.toSignature(): String { + fun KSFunctionDeclaration.toSignature(classDeclaration: KSClassDeclaration): String { + val contextSignature = if (classDeclaration.contextReceivers.isNotEmpty()) { + classDeclaration.contextReceivers.map { it.resolve().declaration.qualifiedName?.asString() } + .joinToString(prefix = " context(", postfix = ") ", separator = ",") + } else { + "" + } return this.simpleName.asString() + + contextSignature + "(${this.parameters.map { buildString { append(it.type.resolve().declaration.qualifiedName?.asString()) @@ -74,7 +81,7 @@ class ConstructorDeclarationsProcessor : AbstractTestProcessor() { val declarations = mutableListOf() declarations.addAll( classDeclaration.getConstructors().map { - it.toSignature() + it.toSignature(classDeclaration) }.sorted() ) // TODO add some assertions that if we go through he path of getDeclarations diff --git a/test-utils/testData/api/constructorDeclarations.kt b/test-utils/testData/api/constructorDeclarations.kt index 438892713d..220ba224cd 100644 --- a/test-utils/testData/api/constructorDeclarations.kt +++ b/test-utils/testData/api/constructorDeclarations.kt @@ -114,6 +114,28 @@ // (): KotlinClassWithCompanion // class: lib.KotlinClassWithCompanion // (): lib.KotlinClassWithCompanion +// class: KotlinClassWithContextAndExplicitConstructor +// context(kotlin.Int,kotlin.String) (kotlin.Int): KotlinClassWithContextAndExplicitConstructor +// class: lib.KotlinClassWithContextAndExplicitConstructor +// context(kotlin.Int,kotlin.String) (kotlin.Int): lib.KotlinClassWithContextAndExplicitConstructor +// class: KotlinClassWithContextAndExplicitEmptyConstructor +// context(kotlin.Int,kotlin.String) (): KotlinClassWithContextAndExplicitEmptyConstructor +// class: lib.KotlinClassWithContextAndExplicitEmptyConstructor +// context(kotlin.Int,kotlin.String) (): lib.KotlinClassWithContextAndExplicitEmptyConstructor +// class: KotlinClassWithContextAndMultipleConstructors +// context(kotlin.Int,kotlin.String) (): KotlinClassWithContextAndMultipleConstructors +// context(kotlin.Int,kotlin.String) (kotlin.Int): KotlinClassWithContextAndMultipleConstructors +// class: lib.KotlinClassWithContextAndMultipleConstructors +// context(kotlin.Int,kotlin.String) (): lib.KotlinClassWithContextAndMultipleConstructors +// context(kotlin.Int,kotlin.String) (kotlin.Int): lib.KotlinClassWithContextAndMultipleConstructors +// class: KotlinClassWithContextAndPrimaryConstructor +// context(kotlin.Int,kotlin.String) (kotlin.Int): KotlinClassWithContextAndPrimaryConstructor +// class: lib.KotlinClassWithContextAndPrimaryConstructor +// context(kotlin.Int,kotlin.String) (kotlin.Int): lib.KotlinClassWithContextAndPrimaryConstructor +// class: KotlinClassWithContextWithoutExplicitConstructor +// context(kotlin.Int,kotlin.String) (): KotlinClassWithContextWithoutExplicitConstructor +// class: lib.KotlinClassWithContextWithoutExplicitConstructor +// context(kotlin.Int,kotlin.String) (): lib.KotlinClassWithContextWithoutExplicitConstructor // class: KotlinClassWithExplicitConstructor // (kotlin.Int): KotlinClassWithExplicitConstructor // class: lib.KotlinClassWithExplicitConstructor @@ -229,6 +251,23 @@ class KotlinClassWithMultipleConstructors2(z:Float) { constructor(y:Int): this(0f) {} constructor(x: String) : this(0f) {} } +context(Int, String) +class KotlinClassWithContextWithoutExplicitConstructor { +} +context(kotlin.Int, kotlin.String) +class KotlinClassWithContextAndExplicitEmptyConstructor() {} +context(Int, String) +class KotlinClassWithContextAndPrimaryConstructor(x:Int) { +} +context(Int, String) +class KotlinClassWithContextAndExplicitConstructor { + constructor(x:Int) {} +} +context(Int, String) +class KotlinClassWithContextAndMultipleConstructors { + constructor() {} + constructor(y:Int): this() {} +} abstract class AbstractKotlinClassWithoutExplicitConstructor { } abstract class AbstractKotlinClassWithPrimaryConstructor(x:Int) { @@ -318,6 +357,23 @@ class KotlinClassWithMultipleConstructors2(z:Float) { constructor(y:Int): this(0f) {} constructor(x: String) : this(0f) {} } +context(Int, String) +class KotlinClassWithContextWithoutExplicitConstructor { +} +context(Int, String) +class KotlinClassWithContextAndExplicitEmptyConstructor() {} +context(Int, String) +class KotlinClassWithContextAndPrimaryConstructor(x:Int) { +} +context(Int, String) +class KotlinClassWithContextAndExplicitConstructor { + constructor(x:Int) {} +} +context(Int, String) +class KotlinClassWithContextAndMultipleConstructors { + constructor() {} + constructor(y:Int): this() {} +} abstract class AbstractKotlinClassWithoutExplicitConstructor { } abstract class AbstractKotlinClassWithPrimaryConstructor(x:Int) { @@ -346,4 +402,4 @@ class KotlinClassWithNamedCompanion { data class DataClass(val x:Int, var y:String) data class DataClassWithSecondaryConstructor(val x:Int, val y:String) { constructor(x:Int) : this(x, "") -} \ No newline at end of file +}