Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2026 Google LLC
* Copyright 2010-2026 JetBrains s.r.o. and Kotlin Programming Language contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.devtools.ksp.symbol

/**
* A backing field for a [KSPropertyDeclaration].
*/
interface KSBackingField : KSDeclaration {

/**
* The property that this backing field belongs to.
*/
val property: KSPropertyDeclaration

/**
* The type of the backing field.
* This is guaranteed to be a subtype of the type of [property].
*/
val type: KSTypeReference
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ interface KSPropertyDeclaration : KSDeclaration {
*/
val hasBackingField: Boolean

/**
* The property's backing field if it exists.
* Guaranteed to exist if [hasBackingField] holds.
*
* As with [hasBackingField], the backing field is specific to the current property,
* and does not check for overriding properties.
*/
val backingField: KSBackingField?

/**
* Indicates whether this is a delegated property.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ interface KSVisitor<D, R> {

fun visitPropertySetter(setter: KSPropertySetter, data: D): R

fun visitBackingField(backingField: KSBackingField, data: D): R

fun visitReferenceElement(element: KSReferenceElement, data: D): R

fun visitTypeAlias(typeAlias: KSTypeAlias, data: D): R
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ open class KSVisitorVoid : KSVisitor<Unit, Unit> {
override fun visitPropertySetter(setter: KSPropertySetter, data: Unit) {
}

override fun visitBackingField(backingField: KSBackingField, data: Unit) {
}

override fun visitClassifierReference(reference: KSClassifierReference, data: Unit) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ abstract class KSDefaultVisitor<D, R> : KSEmptyVisitor<D, R>() {
return super.visitPropertySetter(setter, data)
}

override fun visitBackingField(backingField: KSBackingField, data: D): R {
this.visitAnnotated(backingField, data)
return super.visitBackingField(backingField, data)
}

override fun visitTypeAlias(typeAlias: KSTypeAlias, data: D): R {
this.visitDeclaration(typeAlias, data)
return super.visitTypeAlias(typeAlias, data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ abstract class KSEmptyVisitor<D, R> : KSVisitor<D, R> {
return defaultHandler(setter, data)
}

override fun visitBackingField(backingField: KSBackingField, data: D): R {
return defaultHandler(backingField, data)
}

override fun visitClassifierReference(reference: KSClassifierReference, data: D): R {
return defaultHandler(reference, data)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ abstract class KSTopDownVisitor<D, R> : KSDefaultVisitor<D, R>() {
property.extensionReceiver?.accept(data)
property.getter?.accept(data)
property.setter?.accept(data)
property.backingField?.accept(data)
return super.visitPropertyDeclaration(property, data)
}

Expand Down Expand Up @@ -96,6 +97,11 @@ abstract class KSTopDownVisitor<D, R> : KSDefaultVisitor<D, R>() {
return super.visitPropertySetter(setter, data)
}

override fun visitBackingField(backingField: KSBackingField, data: D): R {
backingField.type.accept(data)
return super.visitBackingField(backingField, data)
}

override fun visitReferenceElement(element: KSReferenceElement, data: D): R {
element.typeArguments.accept(data)
return super.visitReferenceElement(element, data)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.google.devtools.ksp.symbol.*
open class KSValidateVisitor(
private val predicate: (KSNode?, KSNode) -> Boolean
) : KSDefaultVisitor<KSNode?, Boolean>() {
// TODO: Update this visitor
private fun validateType(type: KSType): Boolean {
return !type.isError && !type.arguments.any { it.type?.accept(this, null) == false }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2026 Google LLC
* Copyright 2010-2026 JetBrains s.r.o. and Kotlin Programming Language contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.devtools.ksp.impl.symbol.kotlin

import com.google.devtools.ksp.common.KSObjectCache
import com.google.devtools.ksp.common.impl.KSNameImpl
import com.google.devtools.ksp.impl.symbol.kotlin.resolved.KSTypeReferenceResolvedImpl
import com.google.devtools.ksp.symbol.KSBackingField
import com.google.devtools.ksp.symbol.KSExpectActual
import com.google.devtools.ksp.symbol.KSName
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.KSVisitor
import org.jetbrains.kotlin.analysis.api.symbols.KaBackingFieldSymbol
import org.jetbrains.kotlin.analysis.api.symbols.KaDeclarationSymbol
import org.jetbrains.kotlin.analysis.api.types.abbreviationOrSelf
import org.jetbrains.kotlin.psi.KtBackingField

class KSBackingFieldImpl private constructor(val kaBackingFieldSymbol: KaBackingFieldSymbol) :
KSBackingField,
AbstractKSDeclarationImpl(),
KSExpectActual by KSExpectActualImpl(kaBackingFieldSymbol) {
override val ktDeclarationSymbol: KaDeclarationSymbol = kaBackingFieldSymbol

companion object : KSObjectCache<KaBackingFieldSymbol, KSBackingFieldImpl>() {
fun getCached(kaBackingFieldSymbol: KaBackingFieldSymbol) =
cache.getOrPut(kaBackingFieldSymbol) { KSBackingFieldImpl(kaBackingFieldSymbol) }
}

override val type: KSTypeReference by lazy {
(kaBackingFieldSymbol.psiIfSource() as? KtBackingField
)?.typeReference?.let {
KSTypeReferenceImpl.getCached(it, this)
} ?: KSTypeReferenceResolvedImpl.getCached(
kaBackingFieldSymbol.returnType.abbreviationOrSelf,
this
)
}

override val property: KSPropertyDeclaration by lazy {
KSPropertyDeclarationImpl.getCached(kaBackingFieldSymbol.owningProperty)
}

override val qualifiedName: KSName? by lazy {
// N.B.: kaBackingFieldSymbol.callableId is always null
kaBackingFieldSymbol.owningProperty.callableId?.asSingleFqName()?.asString()?.let { propName ->
val suffix = ".field"
val length = propName.length + suffix.length
KSNameImpl.getCached(
buildString(length) {
append(propName)
append(suffix)
}
)
}
}

override fun defer(): Restorable =
kaBackingFieldSymbol.defer(::getCached)

override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R =
visitor.visitBackingField(this, data)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright 2026 Google LLC
* Copyright 2010-2026 JetBrains s.r.o. and Kotlin Programming Language contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.devtools.ksp.impl.symbol.kotlin

import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSBackingField
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFile
import com.google.devtools.ksp.symbol.KSName
import com.google.devtools.ksp.symbol.KSNode
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.KSTypeReference
import com.google.devtools.ksp.symbol.KSVisitor
import com.google.devtools.ksp.symbol.Location
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.symbol.Origin
import org.jetbrains.kotlin.analysis.api.symbols.KaJavaFieldSymbol

class KSBackingFieldJavaImpl(ktJavaFieldSymbol: KaJavaFieldSymbol) : KSBackingField {
override val property: KSPropertyDeclaration
get() = TODO("Not yet implemented")
override val type: KSTypeReference
get() = TODO("Not yet implemented")
override val simpleName: KSName
get() = TODO("Not yet implemented")
override val qualifiedName: KSName?
get() = TODO("Not yet implemented")
override val typeParameters: List<KSTypeParameter>
get() = TODO("Not yet implemented")
override val packageName: KSName
get() = TODO("Not yet implemented")
override val parentDeclaration: KSDeclaration?
get() = TODO("Not yet implemented")
override val containingFile: KSFile?
get() = TODO("Not yet implemented")
override val docString: String?
get() = TODO("Not yet implemented")
override val modifiers: Set<Modifier>
get() = TODO("Not yet implemented")
override val origin: Origin
get() = TODO("Not yet implemented")
override val location: Location
get() = TODO("Not yet implemented")
override val parent: KSNode?
get() = TODO("Not yet implemented")

override fun <D, R> accept(visitor: KSVisitor<D, R>, data: D): R {
TODO("Not yet implemented")
}

override val annotations: Sequence<KSAnnotation>
get() = TODO("Not yet implemented")
override val isActual: Boolean
get() = TODO("Not yet implemented")
override val isExpect: Boolean
get() = TODO("Not yet implemented")

override fun findActuals(): Sequence<KSDeclaration> {
TODO("Not yet implemented")
}

override fun findExpects(): Sequence<KSDeclaration> {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package com.google.devtools.ksp.impl.symbol.kotlin

import com.google.devtools.ksp.InternalKSPException
import com.google.devtools.ksp.closestClassDeclaration
import com.google.devtools.ksp.common.KSObjectCache
import com.google.devtools.ksp.common.impl.KSNameImpl
Expand Down Expand Up @@ -52,6 +53,7 @@ class KSPropertyDeclarationImpl private constructor(internal val ktPropertySymbo
AbstractKSDeclarationImpl(),
KSExpectActual by KSExpectActualImpl(ktPropertySymbol) {
override val ktDeclarationSymbol get() = ktPropertySymbol

companion object : KSObjectCache<KaPropertySymbol, KSPropertyDeclarationImpl>() {
fun getCached(ktPropertySymbol: KaPropertySymbol) =
cache.getOrPut(ktPropertySymbol) { KSPropertyDeclarationImpl(ktPropertySymbol) }
Expand Down Expand Up @@ -149,6 +151,21 @@ class KSPropertyDeclarationImpl private constructor(internal val ktPropertySymbo
ktPropertySymbol.hasBackingField
}
}
override val backingField: KSBackingField? by lazy {
if (hasBackingField) {
ktPropertySymbol.backingFieldSymbol?.let { KSBackingFieldImpl(it) }
?: throw InternalKSPException(
buildString {
append("Unexpected null backing field symbol for property ")
append(qualifiedName?.asString() ?: simpleName.asString())
},
location,
ktPropertySymbol.javaClass
)
} else {
null
}
}

override fun isDelegated(): Boolean {
return ktPropertySymbol.isDelegatedProperty
Expand Down Expand Up @@ -233,6 +250,6 @@ internal fun KSAnnotation.isValidOnProperty(): Boolean =
annotationType.resolve().declaration.annotations.none { metaAnnotation ->
metaAnnotation.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.annotation.Target" &&
(metaAnnotation.arguments.singleOrNull()?.value as? ArrayList<*>)?.none {
(it as? KSClassDeclaration)?.qualifiedName?.asString() == "kotlin.annotation.AnnotationTarget.PROPERTY"
} ?: false
(it as? KSClassDeclaration)?.qualifiedName?.asString() == "kotlin.annotation.AnnotationTarget.PROPERTY"
} ?: false
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/*
* Copyright 2022 Google LLC
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.devtools.ksp.impl.symbol.kotlin

import com.google.devtools.ksp.InternalKSPException
Expand Down Expand Up @@ -59,6 +76,10 @@ sealed class KSPropertyDeclarationJavaImpl : KSPropertyDeclaration, AbstractKSDe
override val hasBackingField: Boolean
get() = true

override val backingField: KSBackingField? by lazy {
KSBackingFieldJavaImpl(ktJavaFieldSymbol)
}

override fun isDelegated(): Boolean {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.google.devtools.ksp.impl.symbol.kotlin

import com.google.devtools.ksp.common.KSObjectCache
import com.google.devtools.ksp.impl.symbol.kotlin.resolved.KSTypeReferenceResolvedImpl
import com.google.devtools.ksp.symbol.KSBackingField
import com.google.devtools.ksp.symbol.KSExpectActual
import com.google.devtools.ksp.symbol.KSName
import com.google.devtools.ksp.symbol.KSPropertyDeclaration
Expand Down Expand Up @@ -41,6 +42,8 @@ class KSPropertyDeclarationLocalVariableImpl private constructor(

override val hasBackingField: Boolean = false

override val backingField: KSBackingField? = null

override fun isDelegated(): Boolean = false

override fun findOverridee() = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -829,13 +829,16 @@ internal fun fillInDeepSubstitutor(context: KaType, substitutorBuilder: KaSubsti
}
}

internal fun KaSymbol.psiIfSource(): PsiElement? {
return if (origin == KaSymbolOrigin.SOURCE || origin == KaSymbolOrigin.JAVA_SOURCE && toContainingFile() != null) {
internal val KaSymbol.isSource: Boolean
get() =
origin == KaSymbolOrigin.SOURCE || origin == KaSymbolOrigin.JAVA_SOURCE && toContainingFile() != null

internal fun KaSymbol.psiIfSource(): PsiElement? =
if (isSource) {
psi
} else {
null
}
}

fun interface Restorable {
fun restore(): KSAnnotated?
Expand Down
Loading