Switch to spotless and format code (#1196)

* Add spotless configuration

* Reformat!

* Add copyright config for build.gradle.kts files

* Add toeholds for headers
This commit is contained in:
Zac Sweers
2020-08-27 23:40:15 -04:00
committed by GitHub
parent 701d6ba968
commit 538890e8c0
109 changed files with 6748 additions and 4972 deletions

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation
import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -28,8 +29,8 @@ tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "1.8"
freeCompilerArgs = listOf(
"-progressive",
"-Xopt-in=com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview"
"-progressive",
"-Xopt-in=com.squareup.kotlinpoet.metadata.KotlinPoetMetadataPreview"
)
}
}
@@ -80,8 +81,10 @@ val shadowJar = tasks.shadowJar.apply {
archiveClassifier.set("")
configurations = listOf(shade)
relocate("com.squareup.kotlinpoet.metadata", "com.squareup.moshi.kotlinpoet.metadata")
relocate("com.squareup.kotlinpoet.classinspector",
"com.squareup.moshi.kotlinpoet.classinspector")
relocate(
"com.squareup.kotlinpoet.classinspector",
"com.squareup.moshi.kotlinpoet.classinspector"
)
relocate("kotlinx.metadata", "com.squareup.moshi.kotlinx.metadata")
transformers.add(ServiceFileTransformer())
}

View File

@@ -60,8 +60,8 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
*/
const val OPTION_GENERATED = "moshi.generated"
private val POSSIBLE_GENERATED_NAMES = setOf(
"javax.annotation.processing.Generated",
"javax.annotation.Generated"
"javax.annotation.processing.Generated",
"javax.annotation.Generated"
)
}
@@ -84,7 +84,7 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
generatedType = processingEnv.options[OPTION_GENERATED]?.let {
require(it in POSSIBLE_GENERATED_NAMES) {
"Invalid option value for $OPTION_GENERATED. Found $it, " +
"allowable values are $POSSIBLE_GENERATED_NAMES."
"allowable values are $POSSIBLE_GENERATED_NAMES."
}
processingEnv.elementUtils.getTypeElement(it)
}
@@ -104,30 +104,34 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
for (type in roundEnv.getElementsAnnotatedWith(annotation)) {
if (type !is TypeElement) {
messager.printMessage(
Diagnostic.Kind.ERROR, "@JsonClass can't be applied to $type: must be a Kotlin class",
type)
Diagnostic.Kind.ERROR,
"@JsonClass can't be applied to $type: must be a Kotlin class",
type
)
continue
}
val jsonClass = type.getAnnotation(annotation)
if (jsonClass.generateAdapter && jsonClass.generator.isEmpty()) {
val generator = adapterGenerator(type, cachedClassInspector) ?: continue
val preparedAdapter = generator
.prepare { spec ->
spec.toBuilder()
.apply {
generatedType?.asClassName()?.let { generatedClassName ->
addAnnotation(
AnnotationSpec.builder(generatedClassName)
.addMember("value = [%S]",
JsonClassCodegenProcessor::class.java.canonicalName)
.addMember("comments = %S", "https://github.com/square/moshi")
.build()
.prepare { spec ->
spec.toBuilder()
.apply {
generatedType?.asClassName()?.let { generatedClassName ->
addAnnotation(
AnnotationSpec.builder(generatedClassName)
.addMember(
"value = [%S]",
JsonClassCodegenProcessor::class.java.canonicalName
)
}
}
.addOriginatingElement(type)
.build()
}
.addMember("comments = %S", "https://github.com/square/moshi")
.build()
)
}
}
.addOriginatingElement(type)
.build()
}
preparedAdapter.spec.writeTo(filer)
preparedAdapter.proguardConfig?.writeTo(filer, type)
@@ -138,8 +142,8 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
}
private fun adapterGenerator(
element: TypeElement,
cachedClassInspector: MoshiCachedClassInspector
element: TypeElement,
cachedClassInspector: MoshiCachedClassInspector
): AdapterGenerator? {
val type = targetType(messager, elements, types, element, cachedClassInspector) ?: return null
@@ -154,9 +158,10 @@ class JsonClassCodegenProcessor : AbstractProcessor() {
for ((name, parameter) in type.constructor.parameters) {
if (type.properties[parameter.name] == null && !parameter.hasDefault) {
messager.printMessage(
Diagnostic.Kind.ERROR,
"No property for required constructor parameter $name",
element)
Diagnostic.Kind.ERROR,
"No property for required constructor parameter $name",
element
)
return null
}
}

View File

@@ -34,4 +34,4 @@ internal class MoshiCachedClassInspector(private val classInspector: ClassInspec
toTypeSpec(toImmutableKmClass(element.metadata))
}
}
}
}

View File

@@ -52,40 +52,42 @@ private const val TO_STRING_SIZE_BASE = TO_STRING_PREFIX.length + 1 // 1 is the
/** Generates a JSON adapter for a target type. */
internal class AdapterGenerator(
private val target: TargetType,
private val propertyList: List<PropertyGenerator>
private val target: TargetType,
private val propertyList: List<PropertyGenerator>
) {
companion object {
private val INT_TYPE_BLOCK = CodeBlock.of("%T::class.javaPrimitiveType", INT)
private val DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK = CodeBlock.of(
"%T.DEFAULT_CONSTRUCTOR_MARKER", Util::class)
"%T.DEFAULT_CONSTRUCTOR_MARKER",
Util::class
)
private val CN_MOSHI = Moshi::class.asClassName()
private val CN_TYPE = Type::class.asClassName()
private val COMMON_SUPPRESS = arrayOf(
// https://github.com/square/moshi/issues/1023
"DEPRECATION",
// Because we look it up reflectively
"unused",
// Because we include underscores
"ClassName",
// Because we generate redundant `out` variance for some generics and there's no way
// for us to know when it's redundant.
"REDUNDANT_PROJECTION",
// Because we may generate redundant explicit types for local vars with default values.
// Example: 'var fooSet: Boolean = false'
"RedundantExplicitType",
// NameAllocator will just add underscores to differentiate names, which Kotlin doesn't
// like for stylistic reasons.
"LocalVariableName"
// https://github.com/square/moshi/issues/1023
"DEPRECATION",
// Because we look it up reflectively
"unused",
// Because we include underscores
"ClassName",
// Because we generate redundant `out` variance for some generics and there's no way
// for us to know when it's redundant.
"REDUNDANT_PROJECTION",
// Because we may generate redundant explicit types for local vars with default values.
// Example: 'var fooSet: Boolean = false'
"RedundantExplicitType",
// NameAllocator will just add underscores to differentiate names, which Kotlin doesn't
// like for stylistic reasons.
"LocalVariableName"
).let { suppressions ->
AnnotationSpec.builder(Suppress::class)
.addMember(
suppressions.indices.joinToString { "%S" },
*suppressions
)
.build()
.addMember(
suppressions.indices.joinToString { "%S" },
*suppressions
)
.build()
}
}
@@ -94,7 +96,7 @@ internal class AdapterGenerator(
private val visibility = target.visibility
private val typeVariables = target.typeVariables
private val targetConstructorParams = target.constructor.parameters
.mapKeys { (_, param) -> param.index }
.mapKeys { (_, param) -> param.index }
private val nameAllocator = NameAllocator()
private val adapterName = "${className.simpleNames.joinToString(separator = "_")}JsonAdapter"
@@ -102,48 +104,57 @@ internal class AdapterGenerator(
private val originalRawTypeName = originalTypeName.rawType()
private val moshiParam = ParameterSpec.builder(
nameAllocator.newName("moshi"),
CN_MOSHI).build()
nameAllocator.newName("moshi"),
CN_MOSHI
).build()
private val typesParam = ParameterSpec.builder(
nameAllocator.newName("types"),
ARRAY.parameterizedBy(CN_TYPE))
.build()
nameAllocator.newName("types"),
ARRAY.parameterizedBy(CN_TYPE)
)
.build()
private val readerParam = ParameterSpec.builder(
nameAllocator.newName("reader"),
JsonReader::class)
.build()
nameAllocator.newName("reader"),
JsonReader::class
)
.build()
private val writerParam = ParameterSpec.builder(
nameAllocator.newName("writer"),
JsonWriter::class)
.build()
nameAllocator.newName("writer"),
JsonWriter::class
)
.build()
private val valueParam = ParameterSpec.builder(
nameAllocator.newName("value"),
originalTypeName.copy(nullable = true))
.build()
nameAllocator.newName("value"),
originalTypeName.copy(nullable = true)
)
.build()
private val jsonAdapterTypeName = JsonAdapter::class.asClassName().parameterizedBy(
originalTypeName)
originalTypeName
)
// selectName() API setup
private val optionsProperty = PropertySpec.builder(
nameAllocator.newName("options"), JsonReader.Options::class.asTypeName(),
KModifier.PRIVATE)
.initializer(
"%T.of(%L)",
JsonReader.Options::class.asTypeName(),
nonTransientProperties
.map { CodeBlock.of("%S", it.jsonName) }
.joinToCode(", ")
)
.build()
nameAllocator.newName("options"),
JsonReader.Options::class.asTypeName(),
KModifier.PRIVATE
)
.initializer(
"%T.of(%L)",
JsonReader.Options::class.asTypeName(),
nonTransientProperties
.map { CodeBlock.of("%S", it.jsonName) }
.joinToCode(", ")
)
.build()
private val constructorProperty = PropertySpec.builder(
nameAllocator.newName("constructorRef"),
Constructor::class.asClassName().parameterizedBy(originalTypeName).copy(nullable = true),
KModifier.PRIVATE)
.addAnnotation(Volatile::class)
.mutable(true)
.initializer("null")
.build()
nameAllocator.newName("constructorRef"),
Constructor::class.asClassName().parameterizedBy(originalTypeName).copy(nullable = true),
KModifier.PRIVATE
)
.addAnnotation(Volatile::class)
.mutable(true)
.initializer("null")
.build()
fun prepare(typeHook: (TypeSpec) -> TypeSpec = { it }): PreparedAdapter {
for (property in nonTransientProperties) {
@@ -159,17 +170,17 @@ internal class AdapterGenerator(
private fun TypeSpec.createProguardRule(): ProguardConfig {
val adapterProperties = propertySpecs
.asSequence()
.filter { prop ->
prop.type.rawType() == JsonAdapter::class.asClassName()
}
.filter { prop -> prop.annotations.isNotEmpty() }
.mapTo(mutableSetOf()) { prop ->
QualifierAdapterProperty(
name = prop.name,
qualifiers = prop.annotations.mapTo(mutableSetOf()) { it.className }
)
}
.asSequence()
.filter { prop ->
prop.type.rawType() == JsonAdapter::class.asClassName()
}
.filter { prop -> prop.annotations.isNotEmpty() }
.mapTo(mutableSetOf()) { prop ->
QualifierAdapterProperty(
name = prop.name,
qualifiers = prop.annotations.mapTo(mutableSetOf()) { it.className }
)
}
val adapterConstructorParams = when (requireNotNull(primaryConstructor).parameters.size) {
1 -> listOf(CN_MOSHI.reflectionName())
@@ -188,21 +199,21 @@ internal class AdapterGenerator(
}
hasDefaultProperties = propertyList.any { it.hasDefault }
parameterTypes = AsmType.getArgumentTypes(constructorSignature.removePrefix("<init>"))
.map { it.toReflectionString() }
.map { it.toReflectionString() }
}
return ProguardConfig(
targetClass = className,
adapterName = adapterName,
adapterConstructorParams = adapterConstructorParams,
targetConstructorHasDefaults = hasDefaultProperties,
targetConstructorParams = parameterTypes,
qualifierProperties = adapterProperties
targetClass = className,
adapterName = adapterName,
adapterConstructorParams = adapterConstructorParams,
targetConstructorHasDefaults = hasDefaultProperties,
targetConstructorParams = parameterTypes,
qualifierProperties = adapterProperties
)
}
private fun generateType(): TypeSpec {
val result = TypeSpec.classBuilder(adapterName)
.addAnnotation(COMMON_SUPPRESS)
.addAnnotation(COMMON_SUPPRESS)
result.superclass(jsonAdapterTypeName)
@@ -211,19 +222,21 @@ internal class AdapterGenerator(
// require(types.size == 1) {
// "TypeVariable mismatch: Expecting 1 type(s) for generic type variables [T], but received ${types.size} with values $types"
// }
result.addInitializerBlock(CodeBlock.builder()
result.addInitializerBlock(
CodeBlock.builder()
.beginControlFlow("require(types.size == %L)", typeVariables.size)
.addStatement(
"buildString·{·append(%S).append(%L).append(%S).append(%S).append(%S).append(%L)·}",
"TypeVariable mismatch: Expecting ",
typeVariables.size,
" ${if (typeVariables.size == 1) "type" else "types"} for generic type variables [",
typeVariables.joinToString(", ") { it.name },
"], but received ",
"${typesParam.name}.size"
"buildString·{·append(%S).append(%L).append(%S).append(%S).append(%S).append(%L)·}",
"TypeVariable mismatch: Expecting ",
typeVariables.size,
" ${if (typeVariables.size == 1) "type" else "types"} for generic type variables [",
typeVariables.joinToString(", ") { it.name },
"], but received ",
"${typesParam.name}.size"
)
.endControlFlow()
.build())
.build()
)
}
// TODO make this configurable. Right now it just matches the source model
@@ -243,8 +256,14 @@ internal class AdapterGenerator(
result.addProperty(optionsProperty)
for (uniqueAdapter in nonTransientProperties.distinctBy { it.delegateKey }) {
result.addProperty(uniqueAdapter.delegateKey.generateProperty(
nameAllocator, typeRenderer, moshiParam, uniqueAdapter.name))
result.addProperty(
uniqueAdapter.delegateKey.generateProperty(
nameAllocator,
typeRenderer,
moshiParam,
uniqueAdapter.name
)
)
}
result.addFunction(generateToStringFun())
@@ -269,24 +288,24 @@ internal class AdapterGenerator(
val name = originalRawTypeName.simpleNames.joinToString(".")
val size = TO_STRING_SIZE_BASE + name.length
return FunSpec.builder("toString")
.addModifiers(KModifier.OVERRIDE)
.returns(String::class)
.addStatement(
"return %M(%L)·{ append(%S).append(%S).append('%L') }",
MemberName("kotlin.text", "buildString"),
size,
TO_STRING_PREFIX,
name,
")"
)
.build()
.addModifiers(KModifier.OVERRIDE)
.returns(String::class)
.addStatement(
"return %M(%L)·{ append(%S).append(%S).append('%L') }",
MemberName("kotlin.text", "buildString"),
size,
TO_STRING_PREFIX,
name,
")"
)
.build()
}
private fun generateFromJsonFun(classBuilder: TypeSpec.Builder): FunSpec {
val result = FunSpec.builder("fromJson")
.addModifiers(KModifier.OVERRIDE)
.addParameter(readerParam)
.returns(originalTypeName)
.addModifiers(KModifier.OVERRIDE)
.addParameter(readerParam)
.returns(originalTypeName)
for (property in nonTransientProperties) {
result.addCode("%L", property.generateLocalProperty())
@@ -296,8 +315,8 @@ internal class AdapterGenerator(
}
val propertiesByIndex = propertyList.asSequence()
.filter { it.hasConstructorParameter }
.associateBy { it.target.parameterIndex }
.filter { it.hasConstructorParameter }
.associateBy { it.target.parameterIndex }
val components = mutableListOf<FromJsonComponent>()
// Add parameters (± properties) first, their index matters
@@ -333,7 +352,7 @@ internal class AdapterGenerator(
nameAllocator.newName("mask$index")
}
val useDefaultsConstructor = components.filterIsInstance<ParameterComponent>()
.any { it.parameter.hasDefault }
.any { it.parameter.hasDefault }
if (useDefaultsConstructor) {
// Initialize all our masks, defaulting to fully unset (-1)
for (maskName in maskNames) {
@@ -372,7 +391,8 @@ internal class AdapterGenerator(
for (input in components) {
if (input is ParameterOnly ||
(input is ParameterProperty && input.property.isTransient)) {
(input is ParameterProperty && input.property.isTransient)
) {
updateMaskIndexes()
constructorPropertyTypes += input.type.asTypeBlock()
continue
@@ -387,18 +407,30 @@ internal class AdapterGenerator(
if (property.hasLocalIsPresentName || property.hasConstructorDefault) {
result.beginControlFlow("%L ->", propertyIndex)
if (property.delegateKey.nullable) {
result.addStatement("%N = %N.fromJson(%N)",
property.localName, nameAllocator[property.delegateKey], readerParam)
result.addStatement(
"%N = %N.fromJson(%N)",
property.localName,
nameAllocator[property.delegateKey],
readerParam
)
} else {
val exception = unexpectedNull(property, readerParam)
result.addStatement("%N = %N.fromJson(%N) ?: throw·%L",
property.localName, nameAllocator[property.delegateKey], readerParam, exception)
result.addStatement(
"%N = %N.fromJson(%N) ?: throw·%L",
property.localName,
nameAllocator[property.delegateKey],
readerParam,
exception
)
}
if (property.hasConstructorDefault) {
val inverted = (1 shl maskIndex).inv()
result.addComment("\$mask = \$mask and (1 shl %L).inv()", maskIndex)
result.addStatement("%1L = %1L and 0x%2L.toInt()", maskNames[maskNameIndex],
Integer.toHexString(inverted))
result.addStatement(
"%1L = %1L and 0x%2L.toInt()",
maskNames[maskNameIndex],
Integer.toHexString(inverted)
)
} else {
// Presence tracker for a mutable property
result.addStatement("%N = true", property.localIsPresentName)
@@ -406,13 +438,23 @@ internal class AdapterGenerator(
result.endControlFlow()
} else {
if (property.delegateKey.nullable) {
result.addStatement("%L -> %N = %N.fromJson(%N)",
propertyIndex, property.localName, nameAllocator[property.delegateKey], readerParam)
result.addStatement(
"%L -> %N = %N.fromJson(%N)",
propertyIndex,
property.localName,
nameAllocator[property.delegateKey],
readerParam
)
} else {
val exception = unexpectedNull(property, readerParam)
result.addStatement("%L -> %N = %N.fromJson(%N) ?: throw·%L",
propertyIndex, property.localName, nameAllocator[property.delegateKey], readerParam,
exception)
result.addStatement(
"%L -> %N = %N.fromJson(%N) ?: throw·%L",
propertyIndex,
property.localName,
nameAllocator[property.delegateKey],
readerParam,
exception
)
}
}
if (property.hasConstructorParameter) {
@@ -447,13 +489,13 @@ internal class AdapterGenerator(
// Dynamic default constructor call
val nonNullConstructorType = constructorProperty.type.copy(nullable = false)
val args = constructorPropertyTypes
.plus(0.until(maskCount).map { INT_TYPE_BLOCK }) // Masks, one every 32 params
.plus(DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK) // Default constructor marker is always last
.joinToCode(", ")
.plus(0.until(maskCount).map { INT_TYPE_BLOCK }) // Masks, one every 32 params
.plus(DEFAULT_CONSTRUCTOR_MARKER_TYPE_BLOCK) // Default constructor marker is always last
.joinToCode(", ")
val coreLookupBlock = CodeBlock.of(
"%T::class.java.getDeclaredConstructor(%L)",
originalRawTypeName,
args
"%T::class.java.getDeclaredConstructor(%L)",
originalRawTypeName,
args
)
val lookupBlock = if (originalTypeName is ParameterizedTypeName) {
CodeBlock.of("(%L·as·%T)", coreLookupBlock, nonNullConstructorType)
@@ -461,23 +503,26 @@ internal class AdapterGenerator(
coreLookupBlock
}
val initializerBlock = CodeBlock.of(
"this.%1N·?: %2L.also·{ this.%1N·= it }",
constructorProperty,
lookupBlock
"this.%1N·?: %2L.also·{ this.%1N·= it }",
constructorProperty,
lookupBlock
)
val localConstructorProperty = PropertySpec.builder(
nameAllocator.newName("localConstructor"),
nonNullConstructorType)
.addAnnotation(AnnotationSpec.builder(Suppress::class)
.addMember("%S", "UNCHECKED_CAST")
.build())
.initializer(initializerBlock)
.build()
nameAllocator.newName("localConstructor"),
nonNullConstructorType
)
.addAnnotation(
AnnotationSpec.builder(Suppress::class)
.addMember("%S", "UNCHECKED_CAST")
.build()
)
.initializer(initializerBlock)
.build()
result.addCode("%L", localConstructorProperty)
result.addCode(
"«%L%N.newInstance(",
returnOrResultAssignment,
localConstructorProperty
"«%L%N.newInstance(",
returnOrResultAssignment,
localConstructorProperty
)
} else {
// Standard constructor call. Don't omit generics for parameterized types even if they can be
@@ -505,8 +550,13 @@ internal class AdapterGenerator(
val property = input.property
if (!property.isTransient && property.isRequired) {
val missingPropertyBlock =
CodeBlock.of("%T.missingProperty(%S, %S, %N)",
MOSHI_UTIL, property.localName, property.jsonName, readerParam)
CodeBlock.of(
"%T.missingProperty(%S, %S, %N)",
MOSHI_UTIL,
property.localName,
property.jsonName,
readerParam
)
result.addCode(" ?: throw·%L", missingPropertyBlock)
}
}
@@ -526,11 +576,20 @@ internal class AdapterGenerator(
continue // Property already handled.
}
if (property.hasLocalIsPresentName) {
result.addStatement("%1N.%2N = if (%3N) %4N else %1N.%2N",
resultName, property.name, property.localIsPresentName, property.localName)
result.addStatement(
"%1N.%2N = if (%3N) %4N else %1N.%2N",
resultName,
property.name,
property.localIsPresentName,
property.localName
)
} else {
result.addStatement("%1N.%2N = %3N ?: %1N.%2N",
resultName, property.name, property.localName)
result.addStatement(
"%1N.%2N = %3N ?: %1N.%2N",
resultName,
property.name,
property.localName
)
}
}
@@ -541,27 +600,40 @@ internal class AdapterGenerator(
}
private fun unexpectedNull(property: PropertyGenerator, reader: ParameterSpec): CodeBlock {
return CodeBlock.of("%T.unexpectedNull(%S, %S, %N)",
MOSHI_UTIL, property.localName, property.jsonName, reader)
return CodeBlock.of(
"%T.unexpectedNull(%S, %S, %N)",
MOSHI_UTIL,
property.localName,
property.jsonName,
reader
)
}
private fun generateToJsonFun(): FunSpec {
val result = FunSpec.builder("toJson")
.addModifiers(KModifier.OVERRIDE)
.addParameter(writerParam)
.addParameter(valueParam)
.addModifiers(KModifier.OVERRIDE)
.addParameter(writerParam)
.addParameter(valueParam)
result.beginControlFlow("if (%N == null)", valueParam)
result.addStatement("throw·%T(%S)", NullPointerException::class,
"${valueParam.name} was null! Wrap in .nullSafe() to write nullable values.")
result.addStatement(
"throw·%T(%S)",
NullPointerException::class,
"${valueParam.name} was null! Wrap in .nullSafe() to write nullable values."
)
result.endControlFlow()
result.addStatement("%N.beginObject()", writerParam)
nonTransientProperties.forEach { property ->
// We manually put in quotes because we know the jsonName is already escaped
result.addStatement("%N.name(%S)", writerParam, property.jsonName)
result.addStatement("%N.toJson(%N, %N.%N)",
nameAllocator[property.delegateKey], writerParam, valueParam, property.name)
result.addStatement(
"%N.toJson(%N, %N.%N)",
nameAllocator[property.delegateKey],
writerParam,
valueParam,
property.name
)
}
result.addStatement("%N.endObject()", writerParam)
@@ -610,20 +682,20 @@ private sealed class FromJsonComponent {
abstract val type: TypeName
data class ParameterOnly(
override val parameter: TargetParameter
override val parameter: TargetParameter
) : FromJsonComponent(), ParameterComponent {
override val type: TypeName = parameter.type
}
data class PropertyOnly(
override val property: PropertyGenerator
override val property: PropertyGenerator
) : FromJsonComponent(), PropertyComponent {
override val type: TypeName = property.target.type
}
data class ParameterProperty(
override val parameter: TargetParameter,
override val property: PropertyGenerator
override val parameter: TargetParameter,
override val property: PropertyGenerator
) : FromJsonComponent(), ParameterComponent, PropertyComponent {
override val type: TypeName = parameter.type
}

View File

@@ -50,24 +50,28 @@ internal data class DelegateKey(
"At${it.className.simpleName}"
}
val adapterName = nameAllocator.newName(
"${type.toVariableName().decapitalize()}${qualifierNames}Adapter", this)
"${type.toVariableName().decapitalize()}${qualifierNames}Adapter",
this
)
val adapterTypeName = JsonAdapter::class.asClassName().parameterizedBy(type)
val standardArgs = arrayOf(moshiParameter,
typeRenderer.render(type))
val standardArgs = arrayOf(
moshiParameter,
typeRenderer.render(type)
)
val (initializerString, args) = when {
jsonQualifiers.isEmpty() -> ", %M()" to arrayOf(MemberName("kotlin.collections", "emptySet"))
else -> {
", %T.getFieldJsonQualifierAnnotations(javaClass, " +
"%S)" to arrayOf(Types::class.asTypeName(), adapterName)
"%S)" to arrayOf(Types::class.asTypeName(), adapterName)
}
}
val finalArgs = arrayOf(*standardArgs, *args, propertyName)
return PropertySpec.builder(adapterName, adapterTypeName, KModifier.PRIVATE)
.addAnnotations(jsonQualifiers)
.initializer("%N.adapter(%L$initializerString, %S)", *finalArgs)
.build()
.addAnnotations(jsonQualifiers)
.initializer("%N.adapter(%L$initializerString, %S)", *finalArgs)
.build()
}
}

View File

@@ -20,20 +20,20 @@ import javax.tools.StandardLocation
* class with a deterministic name (see [outputFile]) with an appropriate originating element.
*/
internal data class ProguardConfig(
val targetClass: ClassName,
val adapterName: String,
val adapterConstructorParams: List<String>,
val targetConstructorHasDefaults: Boolean,
val targetConstructorParams: List<String>,
val qualifierProperties: Set<QualifierAdapterProperty>
val targetClass: ClassName,
val adapterName: String,
val adapterConstructorParams: List<String>,
val targetConstructorHasDefaults: Boolean,
val targetConstructorParams: List<String>,
val qualifierProperties: Set<QualifierAdapterProperty>
) {
private val outputFile = "META-INF/proguard/moshi-${targetClass.canonicalName}.pro"
/** Writes this to `filer`. */
fun writeTo(filer: Filer, vararg originatingElements: Element) {
filer.createResource(StandardLocation.CLASS_OUTPUT, "", outputFile, *originatingElements)
.openWriter()
.use(::writeTo)
.openWriter()
.use(::writeTo)
}
private fun writeTo(out: Appendable): Unit = out.run {
@@ -64,13 +64,13 @@ internal data class ProguardConfig(
appendln("}")
qualifierProperties.asSequence()
.flatMap { it.qualifiers.asSequence() }
.map(ClassName::reflectionName)
.sorted()
.forEach { qualifier ->
appendln("-if class $targetName")
appendln("-keep @interface $qualifier")
}
.flatMap { it.qualifiers.asSequence() }
.map(ClassName::reflectionName)
.sorted()
.forEach { qualifier ->
appendln("-if class $targetName")
appendln("-keep @interface $qualifier")
}
if (targetConstructorHasDefaults) {
// If the target class has default parameter values, keep its synthetic constructor

View File

@@ -56,24 +56,24 @@ internal class PropertyGenerator(
fun generateLocalProperty(): PropertySpec {
return PropertySpec.builder(localName, target.type.copy(nullable = true))
.mutable(true)
.apply {
if (hasConstructorDefault) {
// We default to the primitive default type, as reflectively invoking the constructor
// without this (even though it's a throwaway) will fail argument type resolution in
// the reflective invocation.
initializer(target.type.defaultPrimitiveValue())
} else {
initializer("null")
}
.mutable(true)
.apply {
if (hasConstructorDefault) {
// We default to the primitive default type, as reflectively invoking the constructor
// without this (even though it's a throwaway) will fail argument type resolution in
// the reflective invocation.
initializer(target.type.defaultPrimitiveValue())
} else {
initializer("null")
}
.build()
}
.build()
}
fun generateLocalIsPresentProperty(): PropertySpec {
return PropertySpec.builder(localIsPresentName, BOOLEAN)
.mutable(true)
.initializer("false")
.build()
.mutable(true)
.initializer("false")
.build()
}
}

View File

@@ -60,9 +60,11 @@ abstract class TypeRenderer {
is ParameterizedTypeName -> {
// If it's an Array type, we shortcut this to return Types.arrayOf()
if (typeName.rawType == ARRAY) {
CodeBlock.of("%T.arrayOf(%L)",
Types::class,
renderObjectType(typeName.typeArguments[0]))
CodeBlock.of(
"%T.arrayOf(%L)",
Types::class,
renderObjectType(typeName.typeArguments[0])
)
} else {
val builder = CodeBlock.builder().apply {
add("%T.", Types::class)
@@ -95,7 +97,8 @@ abstract class TypeRenderer {
method = "subtypeOf"
}
else -> throw IllegalArgumentException(
"Unrepresentable wildcard type. Cannot have more than one bound: $typeName")
"Unrepresentable wildcard type. Cannot have more than one bound: $typeName"
)
}
CodeBlock.of("%T.%L(%L)", Types::class, method, render(target, forceBox = true))
}

View File

@@ -48,18 +48,18 @@ internal fun TypeName.rawType(): ClassName {
}
internal fun TypeName.defaultPrimitiveValue(): CodeBlock =
when (this) {
BOOLEAN -> CodeBlock.of("false")
CHAR -> CodeBlock.of("0.toChar()")
BYTE -> CodeBlock.of("0.toByte()")
SHORT -> CodeBlock.of("0.toShort()")
INT -> CodeBlock.of("0")
FLOAT -> CodeBlock.of("0f")
LONG -> CodeBlock.of("0L")
DOUBLE -> CodeBlock.of("0.0")
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
else -> CodeBlock.of("null")
}
when (this) {
BOOLEAN -> CodeBlock.of("false")
CHAR -> CodeBlock.of("0.toChar()")
BYTE -> CodeBlock.of("0.toByte()")
SHORT -> CodeBlock.of("0.toShort()")
INT -> CodeBlock.of("0")
FLOAT -> CodeBlock.of("0f")
LONG -> CodeBlock.of("0L")
DOUBLE -> CodeBlock.of("0.0")
UNIT, Void::class.asTypeName(), NOTHING -> throw IllegalStateException("Parameter with void, Unit, or Nothing type is illegal")
else -> CodeBlock.of("null")
}
internal fun TypeName.asTypeBlock(): CodeBlock {
if (annotations.isNotEmpty()) {
@@ -103,12 +103,12 @@ internal fun KModifier.checkIsVisibility() {
}
}
internal inline fun <reified T: TypeName> TypeName.mapTypes(noinline transform: T.() -> TypeName?): TypeName {
internal inline fun <reified T : TypeName> TypeName.mapTypes(noinline transform: T.() -> TypeName?): TypeName {
return mapTypes(T::class, transform)
}
@Suppress("UNCHECKED_CAST")
internal fun <T: TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.() -> TypeName?): TypeName {
internal fun <T : TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.() -> TypeName?): TypeName {
if (target.java == javaClass) {
return (this as T).transform() ?: return this
}
@@ -116,7 +116,7 @@ internal fun <T: TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.()
is ClassName -> this
is ParameterizedTypeName -> {
(rawType.mapTypes(target, transform) as ClassName).parameterizedBy(typeArguments.map { it.mapTypes(target, transform) })
.copy(nullable = isNullable, annotations = annotations)
.copy(nullable = isNullable, annotations = annotations)
}
is TypeVariableName -> {
copy(bounds = bounds.map { it.mapTypes(target, transform) })
@@ -129,11 +129,11 @@ internal fun <T: TypeName> TypeName.mapTypes(target: KClass<T>, transform: T.()
this == STAR -> this
outTypes.isNotEmpty() && inTypes.isEmpty() -> {
WildcardTypeName.producerOf(outTypes[0].mapTypes(target, transform))
.copy(nullable = isNullable, annotations = annotations)
.copy(nullable = isNullable, annotations = annotations)
}
inTypes.isNotEmpty() -> {
WildcardTypeName.consumerOf(inTypes[0].mapTypes(target, transform))
.copy(nullable = isNullable, annotations = annotations)
.copy(nullable = isNullable, annotations = annotations)
}
else -> throw UnsupportedOperationException("Not possible.")
}

View File

@@ -65,10 +65,10 @@ private val JSON_QUALIFIER = JsonQualifier::class.java
private val JSON = Json::class.asClassName()
private val OBJECT_CLASS = ClassName("java.lang", "Object")
private val VISIBILITY_MODIFIERS = setOf(
KModifier.INTERNAL,
KModifier.PRIVATE,
KModifier.PROTECTED,
KModifier.PUBLIC
KModifier.INTERNAL,
KModifier.PRIVATE,
KModifier.PROTECTED,
KModifier.PUBLIC
)
private fun Collection<KModifier>.visibility(): KModifier {
@@ -77,10 +77,10 @@ private fun Collection<KModifier>.visibility(): KModifier {
@KotlinPoetMetadataPreview
internal fun primaryConstructor(
targetElement: TypeElement,
kotlinApi: TypeSpec,
elements: Elements,
messager: Messager
targetElement: TypeElement,
kotlinApi: TypeSpec,
elements: Elements,
messager: Messager
): TargetConstructor? {
val primaryConstructor = kotlinApi.primaryConstructor ?: return null
@@ -88,38 +88,47 @@ internal fun primaryConstructor(
for ((index, parameter) in primaryConstructor.parameters.withIndex()) {
val name = parameter.name
parameters[name] = TargetParameter(
name = name,
index = index,
type = parameter.type,
hasDefault = parameter.defaultValue != null,
qualifiers = parameter.annotations.qualifiers(elements),
jsonName = parameter.annotations.jsonName()
name = name,
index = index,
type = parameter.type,
hasDefault = parameter.defaultValue != null,
qualifiers = parameter.annotations.qualifiers(elements),
jsonName = parameter.annotations.jsonName()
)
}
val kmConstructorSignature = primaryConstructor.tag<ImmutableKmConstructor>()?.signature?.toString()
?: run {
messager.printMessage(ERROR, "No KmConstructor found for primary constructor.",
targetElement)
null
}
return TargetConstructor(parameters, primaryConstructor.modifiers.visibility(),
kmConstructorSignature)
?: run {
messager.printMessage(
ERROR,
"No KmConstructor found for primary constructor.",
targetElement
)
null
}
return TargetConstructor(
parameters,
primaryConstructor.modifiers.visibility(),
kmConstructorSignature
)
}
/** Returns a target type for `element`, or null if it cannot be used with code gen. */
@KotlinPoetMetadataPreview
internal fun targetType(messager: Messager,
elements: Elements,
types: Types,
element: TypeElement,
cachedClassInspector: MoshiCachedClassInspector
internal fun targetType(
messager: Messager,
elements: Elements,
types: Types,
element: TypeElement,
cachedClassInspector: MoshiCachedClassInspector
): TargetType? {
val typeMetadata = element.getAnnotation(Metadata::class.java)
if (typeMetadata == null) {
messager.printMessage(
ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class",
element)
ERROR,
"@JsonClass can't be applied to $element: must be a Kotlin class",
element
)
return null
}
@@ -127,54 +136,68 @@ internal fun targetType(messager: Messager,
cachedClassInspector.toImmutableKmClass(typeMetadata)
} catch (e: UnsupportedOperationException) {
messager.printMessage(
ERROR, "@JsonClass can't be applied to $element: must be a Class type",
element)
ERROR,
"@JsonClass can't be applied to $element: must be a Class type",
element
)
return null
}
when {
kmClass.isEnum -> {
messager.printMessage(
ERROR,
"@JsonClass with 'generateAdapter = \"true\"' can't be applied to $element: code gen for enums is not supported or necessary",
element)
ERROR,
"@JsonClass with 'generateAdapter = \"true\"' can't be applied to $element: code gen for enums is not supported or necessary",
element
)
return null
}
!kmClass.isClass -> {
messager.printMessage(
ERROR, "@JsonClass can't be applied to $element: must be a Kotlin class",
element)
ERROR,
"@JsonClass can't be applied to $element: must be a Kotlin class",
element
)
return null
}
kmClass.isInner -> {
messager.printMessage(
ERROR,
"@JsonClass can't be applied to $element: must not be an inner class", element)
ERROR,
"@JsonClass can't be applied to $element: must not be an inner class",
element
)
return null
}
kmClass.isSealed -> {
messager.printMessage(
ERROR, "@JsonClass can't be applied to $element: must not be sealed",
element)
ERROR,
"@JsonClass can't be applied to $element: must not be sealed",
element
)
return null
}
kmClass.isAbstract -> {
messager.printMessage(
ERROR, "@JsonClass can't be applied to $element: must not be abstract",
element)
ERROR,
"@JsonClass can't be applied to $element: must not be abstract",
element
)
return null
}
kmClass.isLocal -> {
messager.printMessage(
ERROR, "@JsonClass can't be applied to $element: must not be local",
element)
ERROR,
"@JsonClass can't be applied to $element: must not be local",
element
)
return null
}
!kmClass.isPublic && !kmClass.isInternal -> {
messager.printMessage(
ERROR,
"@JsonClass can't be applied to $element: must be internal or public",
element)
ERROR,
"@JsonClass can't be applied to $element: must be internal or public",
element
)
return null
}
}
@@ -185,13 +208,20 @@ internal fun targetType(messager: Messager,
val constructor = primaryConstructor(element, kotlinApi, elements, messager)
if (constructor == null) {
messager.printMessage(ERROR, "No primary constructor found on $element",
element)
messager.printMessage(
ERROR,
"No primary constructor found on $element",
element
)
return null
}
if (constructor.visibility != KModifier.INTERNAL && constructor.visibility != KModifier.PUBLIC) {
messager.printMessage(ERROR, "@JsonClass can't be applied to $element: " +
"primary constructor is not internal or public", element)
messager.printMessage(
ERROR,
"@JsonClass can't be applied to $element: " +
"primary constructor is not internal or public",
element
)
return null
}
@@ -199,66 +229,68 @@ internal fun targetType(messager: Messager,
val resolvedTypes = mutableListOf<ResolvedTypeMapping>()
val superTypes = appliedType.supertypes(types)
.filterNot { supertype ->
supertype.element.asClassName() == OBJECT_CLASS || // Don't load properties for java.lang.Object.
supertype.element.kind != ElementKind.CLASS // Don't load properties for interface types.
.filterNot { supertype ->
supertype.element.asClassName() == OBJECT_CLASS || // Don't load properties for java.lang.Object.
supertype.element.kind != ElementKind.CLASS // Don't load properties for interface types.
}
.onEach { supertype ->
if (supertype.element.getAnnotation(Metadata::class.java) == null) {
messager.printMessage(
ERROR,
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
element
)
return null
}
.onEach { supertype ->
if (supertype.element.getAnnotation(Metadata::class.java) == null) {
messager.printMessage(ERROR,
"@JsonClass can't be applied to $element: supertype $supertype is not a Kotlin type",
element)
return null
}
}
.associateWithTo(LinkedHashMap()) { supertype ->
// Load the kotlin API cache into memory eagerly so we can reuse the parsed APIs
val api = if (supertype.element == element) {
// We've already parsed this api above, reuse it
kotlinApi
} else {
cachedClassInspector.toTypeSpec(supertype.element)
}
.associateWithTo(LinkedHashMap()) { supertype ->
// Load the kotlin API cache into memory eagerly so we can reuse the parsed APIs
val api = if (supertype.element == element) {
// We've already parsed this api above, reuse it
kotlinApi
} else {
cachedClassInspector.toTypeSpec(supertype.element)
}
val apiSuperClass = api.superclass
if (apiSuperClass is ParameterizedTypeName) {
//
// This extends a typed generic superclass. We want to construct a mapping of the
// superclass typevar names to their materialized types here.
//
// class Foo extends Bar<String>
// class Bar<T>
//
// We will store {Foo : {T : [String]}}.
//
// Then when we look at Bar<T> later, we'll look up to the descendent Foo and extract its
// materialized type from there.
//
val superSuperClass = supertype.element.superclass as DeclaredType
val apiSuperClass = api.superclass
if (apiSuperClass is ParameterizedTypeName) {
//
// This extends a typed generic superclass. We want to construct a mapping of the
// superclass typevar names to their materialized types here.
//
// class Foo extends Bar<String>
// class Bar<T>
//
// We will store {Foo : {T : [String]}}.
//
// Then when we look at Bar<T> later, we'll look up to the descendent Foo and extract its
// materialized type from there.
//
val superSuperClass = supertype.element.superclass as DeclaredType
// Convert to an element and back to wipe the typed generics off of this
val untyped = superSuperClass.asElement().asType().asTypeName() as ParameterizedTypeName
resolvedTypes += ResolvedTypeMapping(
target = untyped.rawType,
args = untyped.typeArguments.asSequence()
.cast<TypeVariableName>()
.map(TypeVariableName::name)
.zip(apiSuperClass.typeArguments.asSequence())
.associate { it }
)
}
return@associateWithTo api
// Convert to an element and back to wipe the typed generics off of this
val untyped = superSuperClass.asElement().asType().asTypeName() as ParameterizedTypeName
resolvedTypes += ResolvedTypeMapping(
target = untyped.rawType,
args = untyped.typeArguments.asSequence()
.cast<TypeVariableName>()
.map(TypeVariableName::name)
.zip(apiSuperClass.typeArguments.asSequence())
.associate { it }
)
}
return@associateWithTo api
}
for ((localAppliedType, supertypeApi) in superTypes.entries) {
val appliedClassName = localAppliedType.element.asClassName()
val supertypeProperties = declaredProperties(
constructor = constructor,
kotlinApi = supertypeApi,
allowedTypeVars = typeVariables.toSet(),
currentClass = appliedClassName,
resolvedTypes = resolvedTypes
constructor = constructor,
kotlinApi = supertypeApi,
allowedTypeVars = typeVariables.toSet(),
currentClass = appliedClassName,
resolvedTypes = resolvedTypes
)
for ((name, property) in supertypeProperties) {
properties.putIfAbsent(name, property)
@@ -273,18 +305,19 @@ internal fun targetType(messager: Messager,
} else {
// Implicitly public, so now look up the hierarchy
val forceInternal = generateSequence<Element>(element) { it.enclosingElement }
.filterIsInstance<TypeElement>()
.map { cachedClassInspector.toImmutableKmClass(it.metadata) }
.any { it.isInternal }
.filterIsInstance<TypeElement>()
.map { cachedClassInspector.toImmutableKmClass(it.metadata) }
.any { it.isInternal }
if (forceInternal) KModifier.INTERNAL else visibility
}
return TargetType(
typeName = element.asType().asTypeName(),
constructor = constructor,
properties = properties,
typeVariables = typeVariables,
isDataClass = KModifier.DATA in kotlinApi.modifiers,
visibility = resolvedVisibility)
typeName = element.asType().asTypeName(),
constructor = constructor,
properties = properties,
typeVariables = typeVariables,
isDataClass = KModifier.DATA in kotlinApi.modifiers,
visibility = resolvedVisibility
)
}
/**
@@ -294,11 +327,11 @@ internal fun targetType(messager: Messager,
private data class ResolvedTypeMapping(val target: ClassName, val args: Map<String, TypeName>)
private fun resolveTypeArgs(
targetClass: ClassName,
propertyType: TypeName,
resolvedTypes: List<ResolvedTypeMapping>,
allowedTypeVars: Set<TypeVariableName>,
entryStartIndex: Int = resolvedTypes.indexOfLast { it.target == targetClass }
targetClass: ClassName,
propertyType: TypeName,
resolvedTypes: List<ResolvedTypeMapping>,
allowedTypeVars: Set<TypeVariableName>,
entryStartIndex: Int = resolvedTypes.indexOfLast { it.target == targetClass }
): TypeName {
val unwrappedType = propertyType.unwrapTypeAlias()
@@ -315,8 +348,8 @@ private fun resolveTypeArgs(
// We need to us a non-nullable version for mapping since we're just mapping based on raw java
// type vars, but then can re-copy nullability back if it is found.
val resolvedType = targetMappings[unwrappedType.name]
?.copy(nullable = unwrappedType.isNullable)
?: unwrappedType
?.copy(nullable = unwrappedType.isNullable)
?: unwrappedType
return when {
resolvedType !is TypeVariableName -> resolvedType
@@ -336,29 +369,29 @@ private fun resolveTypeArgs(
/** Returns the properties declared by `typeElement`. */
@KotlinPoetMetadataPreview
private fun declaredProperties(
constructor: TargetConstructor,
kotlinApi: TypeSpec,
allowedTypeVars: Set<TypeVariableName>,
currentClass: ClassName,
resolvedTypes: List<ResolvedTypeMapping>
constructor: TargetConstructor,
kotlinApi: TypeSpec,
allowedTypeVars: Set<TypeVariableName>,
currentClass: ClassName,
resolvedTypes: List<ResolvedTypeMapping>
): Map<String, TargetProperty> {
val result = mutableMapOf<String, TargetProperty>()
for (initialProperty in kotlinApi.propertySpecs) {
val resolvedType = resolveTypeArgs(
targetClass = currentClass,
propertyType = initialProperty.type,
resolvedTypes = resolvedTypes,
allowedTypeVars = allowedTypeVars
targetClass = currentClass,
propertyType = initialProperty.type,
resolvedTypes = resolvedTypes,
allowedTypeVars = allowedTypeVars
)
val property = initialProperty.toBuilder(type = resolvedType).build()
val name = property.name
val parameter = constructor.parameters[name]
result[name] = TargetProperty(
propertySpec = property,
parameter = parameter,
visibility = property.modifiers.visibility(),
jsonName = parameter?.jsonName ?: property.annotations.jsonName()
propertySpec = property,
parameter = parameter,
visibility = property.modifiers.visibility(),
jsonName = parameter?.jsonName ?: property.annotations.jsonName()
?: name.escapeDollarSigns()
)
}
@@ -370,9 +403,9 @@ private val TargetProperty.isTransient get() = propertySpec.annotations.any { it
private val TargetProperty.isSettable get() = propertySpec.mutable || parameter != null
private val TargetProperty.isVisible: Boolean
get() {
return visibility == KModifier.INTERNAL
|| visibility == KModifier.PROTECTED
|| visibility == KModifier.PUBLIC
return visibility == KModifier.INTERNAL ||
visibility == KModifier.PROTECTED ||
visibility == KModifier.PUBLIC
}
/**
@@ -380,23 +413,28 @@ private val TargetProperty.isVisible: Boolean
* cannot be used with code gen, or if no codegen is necessary for this property.
*/
internal fun TargetProperty.generator(
messager: Messager,
sourceElement: TypeElement,
elements: Elements
messager: Messager,
sourceElement: TypeElement,
elements: Elements
): PropertyGenerator? {
if (isTransient) {
if (!hasDefault) {
messager.printMessage(
ERROR, "No default value for transient property $name",
sourceElement)
ERROR,
"No default value for transient property $name",
sourceElement
)
return null
}
return PropertyGenerator(this, DelegateKey(type, emptyList()), true)
}
if (!isVisible) {
messager.printMessage(ERROR, "property $name is not visible",
sourceElement)
messager.printMessage(
ERROR,
"property $name is not visible",
sourceElement
)
return null
}
@@ -410,29 +448,35 @@ internal fun TargetProperty.generator(
val qualifierRawType = jsonQualifier.typeName.rawType()
// Check Java types since that covers both Java and Kotlin annotations.
val annotationElement = elements.getTypeElement(qualifierRawType.canonicalName)
?: continue
?: continue
annotationElement.getAnnotation(Retention::class.java)?.let {
if (it.value != RetentionPolicy.RUNTIME) {
messager.printMessage(ERROR,
"JsonQualifier @${qualifierRawType.simpleName} must have RUNTIME retention")
messager.printMessage(
ERROR,
"JsonQualifier @${qualifierRawType.simpleName} must have RUNTIME retention"
)
}
}
annotationElement.getAnnotation(Target::class.java)?.let {
if (ElementType.FIELD !in it.value) {
messager.printMessage(ERROR,
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target")
messager.printMessage(
ERROR,
"JsonQualifier @${qualifierRawType.simpleName} must support FIELD target"
)
}
}
}
val jsonQualifierSpecs = qualifiers.map {
it.toBuilder()
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
.build()
.useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD)
.build()
}
return PropertyGenerator(this,
DelegateKey(type, jsonQualifierSpecs))
return PropertyGenerator(
this,
DelegateKey(type, jsonQualifierSpecs)
)
}
private fun List<AnnotationSpec>?.qualifiers(elements: Elements): Set<AnnotationSpec> {
@@ -478,7 +522,7 @@ internal fun TypeName.unwrapTypeAlias(): TypeName {
internal val TypeElement.metadata: Metadata
get() {
return getAnnotation(Metadata::class.java)
?: throw IllegalStateException("Not a kotlin type! $this")
?: throw IllegalStateException("Not a kotlin type! $this")
}
private fun <E> Sequence<*>.cast(): Sequence<E> {

View File

@@ -41,10 +41,12 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun privateConstructor() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class PrivateConstructor private constructor(var a: Int, var b: Int) {
fun a() = a
@@ -54,7 +56,8 @@ class JsonClassCodegenProcessorTest {
}
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("constructor is not internal or public")
}
@@ -62,14 +65,17 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun privateConstructorParameter() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class PrivateConstructorParameter(private var a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("property a is not visible")
}
@@ -77,7 +83,9 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun privateProperties() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@@ -87,7 +95,8 @@ class JsonClassCodegenProcessorTest {
private var b: Int = -1
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("property a is not visible")
}
@@ -95,226 +104,279 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun interfacesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
interface Interface
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to Interface: must be a Kotlin class")
"error: @JsonClass can't be applied to Interface: must be a Kotlin class"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun interfacesDoNotErrorWhenGeneratorNotSet() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true, generator="customGenerator")
interface Interface
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun abstractClassesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
abstract class AbstractClass(val a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to AbstractClass: must not be abstract")
"error: @JsonClass can't be applied to AbstractClass: must not be abstract"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun sealedClassesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
sealed class SealedClass(val a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to SealedClass: must not be sealed")
"error: @JsonClass can't be applied to SealedClass: must not be sealed"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun innerClassesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
class Outer {
@JsonClass(generateAdapter = true)
inner class InnerClass(val a: Int)
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to Outer.InnerClass: must not be an inner class")
"error: @JsonClass can't be applied to Outer.InnerClass: must not be an inner class"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun enumClassesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
enum class KotlinEnum {
A, B
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass with 'generateAdapter = \"true\"' can't be applied to KotlinEnum: code gen for enums is not supported or necessary")
"error: @JsonClass with 'generateAdapter = \"true\"' can't be applied to KotlinEnum: code gen for enums is not supported or necessary"
)
}
// Annotation processors don't get called for local classes, so we don't have the opportunity to
@Ignore
@Test
fun localClassesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
fun outer() {
@JsonClass(generateAdapter = true)
class LocalClass(val a: Int)
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to LocalClass: must not be local")
"error: @JsonClass can't be applied to LocalClass: must not be local"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun privateClassesNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
private class PrivateClass(val a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to PrivateClass: must be internal or public")
"error: @JsonClass can't be applied to PrivateClass: must be internal or public"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun objectDeclarationsNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
object ObjectDeclaration {
var a = 5
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to ObjectDeclaration: must be a Kotlin class")
"error: @JsonClass can't be applied to ObjectDeclaration: must be a Kotlin class"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun objectExpressionsNotSupported() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
val expression = object : Any() {
var a = 5
}
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: @JsonClass can't be applied to getExpression\$annotations(): must be a Kotlin class")
"error: @JsonClass can't be applied to getExpression\$annotations(): must be a Kotlin class"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun requiredTransientConstructorParameterFails() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class RequiredTransientConstructorParameter(@Transient var a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: No default value for transient property a")
"error: No default value for transient property a"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun nonPropertyConstructorParameter() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
class NonPropertyConstructorParameter(a: Int, val b: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains(
"error: No property for required constructor parameter a")
"error: No property for required constructor parameter a"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun badGeneratedAnnotation() {
val result = prepareCompilation(kotlin("source.kt",
val result = prepareCompilation(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class Foo(val a: Int)
"""
)).apply {
)
).apply {
kaptArgs[JsonClassCodegenProcessor.OPTION_GENERATED] = "javax.annotation.GeneratedBlerg"
}.compile()
assertThat(result.messages).contains(
"Invalid option value for ${JsonClassCodegenProcessor.OPTION_GENERATED}")
"Invalid option value for ${JsonClassCodegenProcessor.OPTION_GENERATED}"
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun multipleErrors() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
@@ -324,7 +386,8 @@ class JsonClassCodegenProcessorTest {
@JsonClass(generateAdapter = true)
class Class2(private var c: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("property a is not visible")
assertThat(result.messages).contains("property c is not visible")
@@ -333,7 +396,9 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun extendPlatformType() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
import java.util.Date
@@ -341,31 +406,37 @@ class JsonClassCodegenProcessorTest {
@JsonClass(generateAdapter = true)
class ExtendsPlatformClass(var a: Int) : Date()
"""
))
)
)
assertThat(result.messages).contains("supertype java.util.Date is not a Kotlin type")
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun extendJavaType() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
import com.squareup.moshi.kotlin.codegen.JavaSuperclass
@JsonClass(generateAdapter = true)
class ExtendsJavaType(var b: Int) : JavaSuperclass()
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages)
.contains("supertype com.squareup.moshi.kotlin.codegen.JavaSuperclass is not a Kotlin type")
.contains("supertype com.squareup.moshi.kotlin.codegen.JavaSuperclass is not a Kotlin type")
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun nonFieldApplicableQualifier() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonQualifier
@@ -382,7 +453,8 @@ class JsonClassCodegenProcessorTest {
@JsonClass(generateAdapter = true)
class ClassWithQualifier(@UpperCase val a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("JsonQualifier @UpperCase must support FIELD target")
}
@@ -390,7 +462,9 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun nonRuntimeQualifier() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonQualifier
@@ -408,7 +482,8 @@ class JsonClassCodegenProcessorTest {
@JsonClass(generateAdapter = true)
class ClassWithQualifier(@UpperCase val a: Int)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.COMPILATION_ERROR)
assertThat(result.messages).contains("JsonQualifier @UpperCase must have RUNTIME retention")
}
@@ -416,37 +491,42 @@ class JsonClassCodegenProcessorTest {
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun `TypeAliases with the same backing type should share the same adapter`() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
import com.squareup.moshi.JsonClass
typealias FirstName = String
typealias LastName = String
@JsonClass(generateAdapter = true)
data class Person(val firstName: FirstName, val lastName: LastName, val hairColor: String)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
// We're checking here that we only generate one `stringAdapter` that's used for both the
// regular string properties as well as the the aliased ones.
val adapterClass = result.classLoader.loadClass("PersonJsonAdapter").kotlin
assertThat(adapterClass.declaredMemberProperties.map { it.returnType }).containsExactly(
JsonReader.Options::class.createType(),
JsonAdapter::class.parameterizedBy(String::class)
JsonReader.Options::class.createType(),
JsonAdapter::class.parameterizedBy(String::class)
)
}
@Ignore("Temporarily ignored pending a new KCT release https://github.com/tschuchortdev/kotlin-compile-testing/issues/51")
@Test
fun `Processor should generate comprehensive proguard rules`() {
val result = compile(kotlin("source.kt",
val result = compile(
kotlin(
"source.kt",
"""
package testPackage
import com.squareup.moshi.JsonClass
import com.squareup.moshi.JsonQualifier
typealias FirstName = String
typealias LastName = String
@@ -473,7 +553,7 @@ class JsonClassCodegenProcessorTest {
@JsonClass(generateAdapter = true)
data class Complex<T>(val firstName: FirstName = "", @MyQualifier val names: MutableList<String>, val genericProp: T)
object NestedType {
@JsonQualifier
annotation class NestedQualifier
@@ -552,36 +632,44 @@ class JsonClassCodegenProcessorTest {
val arg65: Long = 65
)
"""
))
)
)
assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
result.generatedFiles.filter { it.extension == "pro" }.forEach { generatedFile ->
when (generatedFile.nameWithoutExtension) {
"moshi-testPackage.Aliases" -> assertThat(generatedFile).hasContent("""
"moshi-testPackage.Aliases" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.Aliases
-keepnames class testPackage.Aliases
-if class testPackage.Aliases
-keep class testPackage.AliasesJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent())
"moshi-testPackage.Simple" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.Simple" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.Simple
-keepnames class testPackage.Simple
-if class testPackage.Simple
-keep class testPackage.SimpleJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent())
"moshi-testPackage.Generic" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.Generic" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.Generic
-keepnames class testPackage.Generic
-if class testPackage.Generic
-keep class testPackage.GenericJsonAdapter {
public <init>(com.squareup.moshi.Moshi,java.lang.reflect.Type[]);
}
""".trimIndent())
"moshi-testPackage.UsingQualifiers" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.UsingQualifiers" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.UsingQualifiers
-keepnames class testPackage.UsingQualifiers
-if class testPackage.UsingQualifiers
@@ -591,16 +679,20 @@ class JsonClassCodegenProcessorTest {
}
-if class testPackage.UsingQualifiers
-keep @interface testPackage.MyQualifier
""".trimIndent())
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.MixedTypes" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.MixedTypes
-keepnames class testPackage.MixedTypes
-if class testPackage.MixedTypes
-keep class testPackage.MixedTypesJsonAdapter {
public <init>(com.squareup.moshi.Moshi);
}
""".trimIndent())
"moshi-testPackage.DefaultParams" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.DefaultParams" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.DefaultParams
-keepnames class testPackage.DefaultParams
-if class testPackage.DefaultParams
@@ -613,8 +705,10 @@ class JsonClassCodegenProcessorTest {
-keepclassmembers class testPackage.DefaultParams {
public synthetic <init>(java.lang.String,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent())
"moshi-testPackage.Complex" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.Complex" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.Complex
-keepnames class testPackage.Complex
-if class testPackage.Complex
@@ -630,8 +724,10 @@ class JsonClassCodegenProcessorTest {
-keepclassmembers class testPackage.Complex {
public synthetic <init>(java.lang.String,java.util.List,java.lang.Object,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent())
"moshi-testPackage.MultipleMasks" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.MultipleMasks" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.MultipleMasks
-keepnames class testPackage.MultipleMasks
-if class testPackage.MultipleMasks
@@ -644,8 +740,10 @@ class JsonClassCodegenProcessorTest {
-keepclassmembers class testPackage.MultipleMasks {
public synthetic <init>(long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,long,int,int,int,kotlin.jvm.internal.DefaultConstructorMarker);
}
""".trimIndent())
"moshi-testPackage.NestedType.NestedSimple" -> assertThat(generatedFile).hasContent("""
""".trimIndent()
)
"moshi-testPackage.NestedType.NestedSimple" -> assertThat(generatedFile).hasContent(
"""
-if class testPackage.NestedType${'$'}NestedSimple
-keepnames class testPackage.NestedType${'$'}NestedSimple
-if class testPackage.NestedType${'$'}NestedSimple
@@ -655,7 +753,8 @@ class JsonClassCodegenProcessorTest {
}
-if class testPackage.NestedType${'$'}NestedSimple
-keep @interface testPackage.NestedType${'$'}NestedQualifier
""".trimIndent())
""".trimIndent()
)
else -> error("Unexpected proguard file! ${generatedFile.name}")
}
}
@@ -663,13 +762,13 @@ class JsonClassCodegenProcessorTest {
private fun prepareCompilation(vararg sourceFiles: SourceFile): KotlinCompilation {
return KotlinCompilation()
.apply {
workingDir = temporaryFolder.root
annotationProcessors = listOf(JsonClassCodegenProcessor())
inheritClassPath = true
sources = sourceFiles.asList()
verbose = false
}
.apply {
workingDir = temporaryFolder.root
annotationProcessors = listOf(JsonClassCodegenProcessor())
inheritClassPath = true
sources = sourceFiles.asList()
verbose = false
}
}
private fun compile(vararg sourceFiles: SourceFile): KotlinCompilation.Result {
@@ -682,12 +781,11 @@ class JsonClassCodegenProcessorTest {
private fun KClassifier.parameterizedBy(vararg types: KType): KType {
return createType(
types.map { it.asProjection() }
types.map { it.asProjection() }
)
}
private fun KType.asProjection(variance: KVariance? = INVARIANT): KTypeProjection {
return KTypeProjection(variance, this)
}
}