Skip to content

Commit

Permalink
ksp-plugin: generate accessors only for public properties (#11 #15)
Browse files Browse the repository at this point in the history
  • Loading branch information
nesk committed Oct 9, 2023
1 parent 4e9cad9 commit 92119a3
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### ⚠️ Breaking changes

- Validation accessors are generated only for public properties. ([#11](https://github.com/nesk/akkurate/issues/11) [#15](https://github.com/nesk/akkurate/issues/15))

### Added

- Support generating accessors for generic types ([#10](https://github.com/nesk/akkurate/issues/10))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.nesk.validation.accessors

import dev.nesk.PublicClass
import dev.nesk.akkurate.validatables.Validatable
import dev.nesk.akkurate.validatables.validatableOf
import kotlin.Any
import kotlin.Suppress
import kotlin.jvm.JvmName
import kotlin.reflect.KProperty1

/**
* [Validatable] accessor of [PublicClass.publicPropInPublicScope]
*/
public val Validatable<PublicClass>.publicPropInPublicScope: Validatable<Any>
@JvmName(name = "validatablePublicClassPublicPropInPublicScope")
get() = validatableOf(PublicClass::publicPropInPublicScope)

/**
* [Validatable] accessor of [PublicClass.publicPropInPublicScope]
*/
public val Validatable<PublicClass?>.publicPropInPublicScope: Validatable<Any?>
@JvmName(name = "validatableNullablePublicClassPublicPropInPublicScope")
@Suppress("USELESS_CAST")
get() = validatableOf(PublicClass::publicPropInPublicScope as KProperty1)
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package dev.nesk.akkurate.ksp

import com.google.devtools.ksp.getVisibility
import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.symbol.*
import com.squareup.kotlinpoet.*
Expand Down Expand Up @@ -84,6 +85,9 @@ public class ValidateAnnotationProcessor(
for (validatable in validatables) {
logger.info("Processing class '${validatable.qualifiedName!!.asString()}'.")
for (property in validatable.getAllProperties()) {
// Until issue #15 is fixed, we handle only public properties to avoid unexpected bugs (https://github.com/nesk/akkurate/issues/15)
if (!property.isPublic) continue

logger.info("Processing property '${property.simpleName.asString()}'.")
add(property.toValidatablePropertySpec(validatable))
add(property.toValidatablePropertySpec(validatable, withNullableReceiver = true))
Expand Down Expand Up @@ -211,4 +215,29 @@ public class ValidateAnnotationProcessor(
}
}
}

/**
* Determines if a declaration is public or not.
*
* Handles [a bug in ksp](https://github.com/google/ksp/issues/1515) where the visibility of some properties can't be resolved.
*/
private val KSDeclaration.isPublic: Boolean
get() {
if (parentDeclaration?.isPublic == false) {
return false
}

val visibility: Visibility = try {
getVisibility()
} catch (e: IllegalStateException) {
// Rethrow the exception if it's not about an unhandled visibility.
if (e.message?.startsWith("unhandled visibility") != true) {
throw e
}
// If the visibility can't be resolved, default to private visibility.
Visibility.PRIVATE
}

return visibility == Visibility.PUBLIC
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,32 @@ class ValidateAnnotationProcessorTest {
.matchWithSnapshot("processing annotated classes across multiple packages generates a file per package (user)")
}

@Test
fun `accessors are generated only for public properties`() {
// Arrange
val source = SourceFile.kotlin(
"Examples.kt", """
package dev.nesk
import dev.nesk.akkurate.annotations.Validate
private class PrivateParent {
public class PublicParent {
@Validate public class PublicClass(public val publicPropInPrivateScope: Any)
}
}
@Validate public class PublicClass(public val publicPropInPublicScope: Any, private val privatePropInPublicScope: Any)
"""
)

// Act
val (result, compiler) = compile(source)

// Assert
assertCompilationIsSuccessful(result)
assertCountOfFilesGeneratedByTheProcessor(1, compiler)
Path("${compiler.kotlinFilesDir}/dev/nesk/validation/accessors/ValidationAccessors.kt").readText()
.matchWithSnapshot("accessors are generated only for public properties")
}

@Test
fun `the option 'appendPackagesWith' appends its value to the original package name`() {
// Arrange
Expand Down

0 comments on commit 92119a3

Please sign in to comment.