From 64b3e2acf885d148f2b50c3cd6b3aa33e368b83e Mon Sep 17 00:00:00 2001 From: Zac Sweers Date: Fri, 22 Oct 2021 17:12:55 -0400 Subject: [PATCH] Make more experimental APIs public + overridable properties (#5) --- .../value/kotlin/AutoValueKotlinExtension.kt | 18 ++----- .../slack/auto/value/kotlin/KotlinClass.kt | 3 ++ .../auto/value/kotlin/PropertyContext.kt | 2 + .../com/slack/auto/value/kotlin/utils.kt | 47 +++++++++++++++---- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/main/kotlin/com/slack/auto/value/kotlin/AutoValueKotlinExtension.kt b/src/main/kotlin/com/slack/auto/value/kotlin/AutoValueKotlinExtension.kt index 859b1d4..5b59d2a 100644 --- a/src/main/kotlin/com/slack/auto/value/kotlin/AutoValueKotlinExtension.kt +++ b/src/main/kotlin/com/slack/auto/value/kotlin/AutoValueKotlinExtension.kt @@ -38,9 +38,7 @@ import javax.annotation.processing.ProcessingEnvironment import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement import javax.lang.model.element.Modifier -import javax.lang.model.element.Modifier.PUBLIC import javax.lang.model.element.NestingKind -import javax.lang.model.type.TypeMirror import javax.lang.model.util.ElementFilter import javax.lang.model.util.Elements import javax.lang.model.util.Types @@ -190,7 +188,7 @@ public class AutoValueKotlinExtension : AutoValueExtension() { annotations = annotations, isOverride = isAnOverride, isRedacted = isRedacted, - visibility = if (PUBLIC in method.modifiers) KModifier.PUBLIC else KModifier.INTERNAL, + visibility = if (Modifier.PUBLIC in method.modifiers) KModifier.PUBLIC else KModifier.INTERNAL, doc = method.parseDocs() ) } @@ -200,12 +198,7 @@ public class AutoValueKotlinExtension : AutoValueExtension() { // If all the properties are redacted, redacted the class? properties.values.all { it.isRedacted } - val isParcelable = elements - .getTypeElement("android.os.Parcelable") - ?.asType() - ?.let { parcelableClass -> - avClass.interfaces.any { it.isClassOfType(types, parcelableClass) } - } ?: false + val isParcelable = context.processingEnvironment().isParcelable(avClass) val propertyMethods = context.properties().values.toSet() @@ -409,7 +402,7 @@ public class AutoValueKotlinExtension : AutoValueExtension() { packageName = context.packageName(), doc = classDoc, name = avClass.simpleName.toString(), - visibility = if (PUBLIC in avClass.modifiers) KModifier.PUBLIC else KModifier.INTERNAL, + visibility = if (Modifier.PUBLIC in avClass.modifiers) KModifier.PUBLIC else KModifier.INTERNAL, isRedacted = isClassRedacted, isParcelable = isParcelable, superClass = superclass, @@ -429,9 +422,6 @@ public class AutoValueKotlinExtension : AutoValueExtension() { return null } - - private fun TypeMirror.isClassOfType(types: Types, other: TypeMirror?) = - types.isAssignable(this, other) } private fun AvkBuilder.Companion.from( @@ -474,7 +464,7 @@ private fun AvkBuilder.Companion.from( return AvkBuilder( name = builderContext.builderType().simpleName.toString(), doc = builderContext.builderType().parseDocs(), - visibility = if (PUBLIC in builderContext.builderType().modifiers) { + visibility = if (Modifier.PUBLIC in builderContext.builderType().modifiers) { KModifier.PUBLIC } else { KModifier.INTERNAL diff --git a/src/main/kotlin/com/slack/auto/value/kotlin/KotlinClass.kt b/src/main/kotlin/com/slack/auto/value/kotlin/KotlinClass.kt index 29d6a26..03649be 100644 --- a/src/main/kotlin/com/slack/auto/value/kotlin/KotlinClass.kt +++ b/src/main/kotlin/com/slack/auto/value/kotlin/KotlinClass.kt @@ -107,6 +107,9 @@ public data class KotlinClass( if (!isRedacted && prop.isRedacted && redactedClassName != null) { addAnnotation(redactedClassName) } + if (prop.forcePropertyOverride) { + addModifiers(OVERRIDE) + } prop.doc?.let { addKdoc(it) } } .build() diff --git a/src/main/kotlin/com/slack/auto/value/kotlin/PropertyContext.kt b/src/main/kotlin/com/slack/auto/value/kotlin/PropertyContext.kt index 3a97c58..8bb997b 100644 --- a/src/main/kotlin/com/slack/auto/value/kotlin/PropertyContext.kt +++ b/src/main/kotlin/com/slack/auto/value/kotlin/PropertyContext.kt @@ -26,6 +26,8 @@ public data class PropertyContext( val type: TypeName, val annotations: List, val isOverride: Boolean, + /** Forces the property to be marked with the override modifier too. */ + val forcePropertyOverride: Boolean = false, val isRedacted: Boolean, val visibility: KModifier, val doc: String? diff --git a/src/main/kotlin/com/slack/auto/value/kotlin/utils.kt b/src/main/kotlin/com/slack/auto/value/kotlin/utils.kt index 2944c9a..df55ac8 100644 --- a/src/main/kotlin/com/slack/auto/value/kotlin/utils.kt +++ b/src/main/kotlin/com/slack/auto/value/kotlin/utils.kt @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +@file:Suppress("TooManyFunctions") @file:OptIn(DelicateKotlinPoetApi::class) package com.slack.auto.value.kotlin @@ -42,6 +43,7 @@ import com.squareup.kotlinpoet.asClassName import com.squareup.kotlinpoet.asTypeName import com.squareup.kotlinpoet.asTypeVariableName import com.squareup.moshi.Json +import javax.annotation.processing.ProcessingEnvironment import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement import javax.lang.model.element.Modifier @@ -49,6 +51,7 @@ import javax.lang.model.element.TypeElement import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeMirror import javax.lang.model.type.TypeVariable +import javax.lang.model.util.Types internal val NONNULL_ANNOTATIONS = setOf( "NonNull", @@ -57,6 +60,7 @@ internal val NONNULL_ANNOTATIONS = setOf( ) internal val PARCELIZE = ClassName("kotlinx.parcelize", "Parcelize") + internal val INTRINSIC_IMPORTS = setOf( "import java.lang.String", "import java.lang.CharSequence", @@ -81,12 +85,14 @@ internal const val MAX_PARAMS = 7 internal val JSON_CN = Json::class.asClassName() -internal fun TypeMirror.asSafeTypeName(): TypeName { +@ExperimentalAvkApi +public fun TypeMirror.asSafeTypeName(): TypeName { return asTypeName().copy(nullable = false).normalize() } @Suppress("ComplexMethod") -internal fun TypeName.normalize(): TypeName { +@ExperimentalAvkApi +public fun TypeName.normalize(): TypeName { return when (this) { is ClassName -> { when (this) { @@ -110,7 +116,8 @@ internal fun TypeName.normalize(): TypeName { } } -internal fun TypeElement.classAnnotations(): List { +@ExperimentalAvkApi +public fun TypeElement.classAnnotations(): List { return annotationMirrors .map { @Suppress("DEPRECATION") @@ -132,7 +139,8 @@ internal fun TypeElement.classAnnotations(): List { } } -internal fun TypeName.defaultPrimitiveValue(): CodeBlock = +@ExperimentalAvkApi +public fun TypeName.defaultPrimitiveValue(): CodeBlock = when (this) { BOOLEAN -> CodeBlock.of("false") CHAR -> CodeBlock.of("0.toChar()") @@ -148,7 +156,8 @@ internal fun TypeName.defaultPrimitiveValue(): CodeBlock = else -> CodeBlock.of("null") } -internal fun deprecatedAnnotation(message: String, replaceWith: String): AnnotationSpec { +@ExperimentalAvkApi +public fun deprecatedAnnotation(message: String, replaceWith: String): AnnotationSpec { return AnnotationSpec.builder(Deprecated::class) .addMember("message = %S", message) .addMember("replaceWith = %T(%S)", ReplaceWith::class, replaceWith) @@ -156,7 +165,8 @@ internal fun deprecatedAnnotation(message: String, replaceWith: String): Annotat } @Suppress("DEPRECATION", "SpreadOperator") -internal fun FunSpec.Companion.copyOf(method: ExecutableElement): FunSpec.Builder { +@ExperimentalAvkApi +public fun FunSpec.Companion.copyOf(method: ExecutableElement): FunSpec.Builder { var modifiers: Set = method.modifiers val methodName = method.simpleName.toString() @@ -192,11 +202,13 @@ internal fun FunSpec.Companion.copyOf(method: ExecutableElement): FunSpec.Builde return funBuilder } -internal fun ParameterSpec.Companion.parametersWithNullabilityOf(method: ExecutableElement): List = +@ExperimentalAvkApi +public fun ParameterSpec.Companion.parametersWithNullabilityOf(method: ExecutableElement): List = method.parameters.map(ParameterSpec.Companion::getWithNullability) @Suppress("DEPRECATION") -internal fun ParameterSpec.Companion.getWithNullability(element: VariableElement): ParameterSpec { +@ExperimentalAvkApi +public fun ParameterSpec.Companion.getWithNullability(element: VariableElement): ParameterSpec { val name = element.simpleName.toString() val isNullable = element.annotationMirrors.any { (it.annotationType.asElement() as TypeElement).simpleName.toString() == "Nullable" } @@ -207,7 +219,8 @@ internal fun ParameterSpec.Companion.getWithNullability(element: VariableElement } /** Cleans up the generated doc and translates some html to equivalent markdown for Kotlin docs. */ -internal fun cleanUpDoc(doc: String): String { +@ExperimentalAvkApi +public fun cleanUpDoc(doc: String): String { // TODO not covered yet // {@link TimeFormatter#getDateTimeString(SlackDateTime)} return doc.replace("", "*") @@ -247,10 +260,24 @@ internal fun cleanUpDoc(doc: String): String { .trim() } -internal fun FunSpec.Builder.withDocsFrom( +@ExperimentalAvkApi +public fun FunSpec.Builder.withDocsFrom( e: Element, parseDocs: Element.() -> String? ): FunSpec.Builder { val doc = e.parseDocs() ?: return this return addKdoc(doc) } + +@ExperimentalAvkApi +public fun ProcessingEnvironment.isParcelable(element: TypeElement): Boolean { + return elementUtils + .getTypeElement("android.os.Parcelable") + ?.asType() + ?.let { parcelableClass -> + element.interfaces.any { it.isClassOfType(typeUtils, parcelableClass) } + } ?: false +} + +private fun TypeMirror.isClassOfType(types: Types, other: TypeMirror?) = + types.isAssignable(this, other)