Skip to content
Open
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
Expand Up @@ -452,11 +452,18 @@ class ResolverAAImpl(
return it
}

if (accessor.receiver.closestClassDeclaration()?.classKind == ClassKind.ANNOTATION_CLASS) {
return accessor.receiver.simpleName.asString()
}

val name = accessor.receiver.simpleName.asString()
val containingClass = accessor.receiver.closestClassDeclaration()

// Annotation classes and @JvmRecord data classes both use bare property names
// as accessor names (no get/set prefix).
if (containingClass?.classKind == ClassKind.ANNOTATION_CLASS ||
containingClass != null && containingClass.annotations.any {
it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.jvm.JvmRecord"
}
) {
return name
}
Comment on lines +460 to +466
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can improve the readability of this by extracting some variables so it reads

if (isAnnotationClass || isJvmRecord) {
    return name
}

Additionally, we can shortcircuit the expensive type resolution by checking for the short name first in the lambda:

any {
    it.shortName.asString() == "JvmRecord" ||
        it.annotationType.resolve().declaration.qualifiedName?.asString() == "kotlin.jvm.Record
}

Lastly, maybe we can rename name to propertyName so it's clearer what the name. I know you didn't change it, but let's improve it.

// https://kotlinlang.org/docs/java-to-kotlin-interop.html#properties
val prefixedName = when (accessor) {
is KSPropertyGetter -> JvmAbi.getterName(name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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.test

import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.test.TestMetadata
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.directives.JvmEnvironmentConfigurationDirectives
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode

@Execution(ExecutionMode.SAME_THREAD)
class KSPAA17Test : AbstractKSPAATest() {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you try adding the test to the existing test suite?


override fun configureTest(builder: TestConfigurationBuilder) {
builder.defaultDirectives {
-JvmEnvironmentConfigurationDirectives.JVM_TARGET
JvmEnvironmentConfigurationDirectives.JVM_TARGET with JvmTarget.JVM_17
}
}

@TestMetadata("jvmNameRecord.kt")
@Test
fun testJvmNameRecord() {
runTest("../kotlin-analysis-api/testData/jvmNameRecord.kt")
}
}
12 changes: 12 additions & 0 deletions kotlin-analysis-api/testData/jvmNameRecord.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// TEST PROCESSOR: JvmNameRecordProcessor
// EXPECTED:
// (x, null), (y, null)
// (x, null), (y, null)
// END
// MODULE: main
// FILE: TestRecordClass.kt
@JvmRecord
data class TestRecordClass(val x: Int, val y: String)
// FILE: TestLibRecordClass.kt
@JvmRecord
data class TestLibRecordClass(val x: Int, val y: String)
Comment on lines +8 to +12
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these classes distinct except for the name? It's better if the classes are a bit more diverse. I'm thinking an approximation of a matrix of the following:

  • Classes with different number of parameters
  • Different property names
  • Classes extending other types
  • Classes with type parameters
  • Classes marked as JvmRecords that override a val declared in a non-annotation interface, e.g., interface Example { val x: Int }
  • Classes with different names (like you already have)
  • Classes with var and val properties

If you have type parameters and or normal parameters and a class that extends another type, then it's also a good idea to override some parameters and add some locally, e.g.,

interface Example<A> {
    val x: A
}

data class Ext<A, B>(override val x: A, val y: B) : Example<A>

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.google.devtools.ksp.processor

import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSAnnotated

class JvmNameRecordProcessor : AbstractTestProcessor() {
val results = mutableListOf<String>()
override fun toResult(): List<String> {
return results
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be sorted to ensure results are reproducible.

}

@OptIn(KspExperimental::class)
override fun process(resolver: Resolver): List<KSAnnotated> {
listOf("TestRecordClass", "TestLibRecordClass").forEach { clsName ->
resolver.getClassDeclarationByName(clsName)?.let { cls ->
results.add(
cls.getAllProperties().map {
"(${it.getter?.let { resolver.getJvmName(it) }}, " +
"${it.setter?.let { resolver.getJvmName(it) }})"
}.toList().joinToString()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's more stable if you use resolver.getSymbolsWithAnnotation("kotlin.jvm.JvmRecord") and filter for class declaration instances.
It might also be a bit cleaner if you flatmap over getAllProperties and then return a list of size at most two, that contains the getter and setter names which you can the call joinToString on, e.g.,

getAllProperties().flatMap {
    // create a list that contains the jvm getter name if it exists and the setter name if it exists
}

The names should probably also be qualified / prefixed with the class names.

)
}
}
return emptyList()
}
}