diff --git a/compiler-core/src/main/java/toothpick/compiler/common/ToothpickProcessor.kt b/compiler-core/src/main/java/toothpick/compiler/common/ToothpickProcessor.kt index 7476c434..f2c3e943 100644 --- a/compiler-core/src/main/java/toothpick/compiler/common/ToothpickProcessor.kt +++ b/compiler-core/src/main/java/toothpick/compiler/common/ToothpickProcessor.kt @@ -32,6 +32,7 @@ import com.google.devtools.ksp.symbol.KSDeclaration import com.google.devtools.ksp.symbol.KSFunctionDeclaration import com.google.devtools.ksp.symbol.KSNode import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeAlias import com.google.devtools.ksp.symbol.Variance import com.squareup.kotlinpoet.ksp.writeTo import toothpick.compiler.common.generators.TPCodeGenerator @@ -47,7 +48,7 @@ import javax.inject.Inject abstract class ToothpickProcessor( processorOptions: Map, private val codeGenerator: CodeGenerator, - protected val logger: KSPLogger + protected val logger: KSPLogger, ) : SymbolProcessor { protected val options = processorOptions.readOptions() @@ -65,13 +66,18 @@ abstract class ToothpickProcessor( } protected fun KSType.isValidInjectedType(node: KSNode, qualifiedName: String?): Boolean { - return if (!isValidInjectedClassKind()) false - else !isProviderOrLazy() || isValidProviderOrLazy(node, qualifiedName) + return when { + isError -> false + !isValidInjectedClassKind() && !isTypeAlias() -> false + else -> !isProviderOrLazy() || isValidProviderOrLazy(node, qualifiedName) + } } private fun KSType.isValidInjectedClassKind(): Boolean = (declaration as? KSClassDeclaration)?.classKind in validInjectableTypes + private fun KSType.isTypeAlias(): Boolean = declaration is KSTypeAlias + private fun KSType.isValidProviderOrLazy(node: KSNode, qualifiedName: String?): Boolean { // e.g. Provider> // -> Foo @@ -96,7 +102,8 @@ abstract class ToothpickProcessor( val firstArgumentArgumentType = firstArgumentArgument.type?.resolve()?.declaration?.qualifiedName?.asString() val isArgumentStar = firstArgumentArgument.variance == Variance.STAR - val isArgumentAny = firstArgumentArgument.variance == Variance.INVARIANT && firstArgumentArgumentType == Any::class.qualifiedName + val isArgumentAny = + firstArgumentArgument.variance == Variance.INVARIANT && firstArgumentArgumentType == Any::class.qualifiedName val areValidArguments = firstArgumentArguments.size <= 1 && (isArgumentStar || isArgumentAny) if (!areValidArguments) { diff --git a/compiler-core/src/main/java/toothpick/compiler/common/generators/targets/VariableInjectionTarget.kt b/compiler-core/src/main/java/toothpick/compiler/common/generators/targets/VariableInjectionTarget.kt index 374d8240..10c21dff 100644 --- a/compiler-core/src/main/java/toothpick/compiler/common/generators/targets/VariableInjectionTarget.kt +++ b/compiler-core/src/main/java/toothpick/compiler/common/generators/targets/VariableInjectionTarget.kt @@ -22,14 +22,18 @@ package toothpick.compiler.common.generators.targets import com.google.devtools.ksp.KspExperimental import com.google.devtools.ksp.getAnnotationsByType import com.google.devtools.ksp.isAnnotationPresent +import com.google.devtools.ksp.isLocal import com.google.devtools.ksp.processing.KSPLogger import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSDeclaration import com.google.devtools.ksp.symbol.KSName import com.google.devtools.ksp.symbol.KSPropertyDeclaration import com.google.devtools.ksp.symbol.KSType +import com.google.devtools.ksp.symbol.KSTypeAlias import com.google.devtools.ksp.symbol.KSValueParameter import com.squareup.kotlinpoet.ClassName import com.squareup.kotlinpoet.CodeBlock +import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.ksp.toClassName @@ -42,29 +46,31 @@ import javax.inject.Qualifier * Information necessary to identify the parameter of a method or a class's property. */ sealed class VariableInjectionTarget( - val memberType: KSType, + val className: ClassName, + val typeName: TypeName, val memberName: KSName, - val qualifierName: Any? + val qualifierName: Any?, ) { class Instance( - memberType: KSType, + className: ClassName, + typeName: TypeName, memberName: KSName, - qualifierName: Any? - ) : VariableInjectionTarget(memberType, memberName, qualifierName) + qualifierName: Any?, + ) : VariableInjectionTarget(className, typeName, memberName, qualifierName) class Lazy( - memberType: KSType, + className: ClassName, + typeName: TypeName, memberName: KSName, qualifierName: Any?, - val kindParamClass: KSType - ) : VariableInjectionTarget(memberType, memberName, qualifierName) + ) : VariableInjectionTarget(className, typeName, memberName, qualifierName) class Provider( - memberType: KSType, + className: ClassName, + typeName: TypeName, memberName: KSName, qualifierName: Any?, - val kindParamClass: KSType - ) : VariableInjectionTarget(memberType, memberName, qualifierName) + ) : VariableInjectionTarget(className, typeName, memberName, qualifierName) companion object { @@ -84,26 +90,58 @@ sealed class VariableInjectionTarget( private fun create(name: KSName, type: KSType, qualifierName: String?): VariableInjectionTarget = when (type.declaration.qualifiedName?.asString()) { - javax.inject.Provider::class.qualifiedName -> + javax.inject.Provider::class.qualifiedName -> { + val kindParamClass = type.getInjectedType() + Provider( - memberType = type, + className = kindParamClass.toClassName(), + typeName = type.toParameterizedTypeName(kindParamClass), memberName = name, - qualifierName = qualifierName, - kindParamClass = type.getInjectedType() + qualifierName = qualifierName ) - toothpick.Lazy::class.qualifiedName -> + } + toothpick.Lazy::class.qualifiedName -> { + val kindParamClass = type.getInjectedType() + Lazy( - memberType = type, + className = kindParamClass.toClassName(), + typeName = type.toParameterizedTypeName(kindParamClass), memberName = name, - qualifierName = qualifierName, - kindParamClass = type.getInjectedType() + qualifierName = qualifierName ) - else -> Instance( - memberType = type, + } + else -> createInstanceTarget(name, type, qualifierName) + } + + private fun createInstanceTarget(name: KSName, type: KSType, qualifierName: String?): Instance { + return if (type.declaration is KSTypeAlias) { + val actualTypeClassName = type.findActualType().toClassName() + val argumentsTypeNames = type.arguments.map { it.type!!.resolve().toTypeName() } + + val typeName = if (argumentsTypeNames.isNotEmpty()) { + type.declaration.toClassName().parameterizedBy(argumentsTypeNames) + } else { + type.toTypeName() + } + + Instance( + className = actualTypeClassName, + typeName = typeName, + memberName = name, + qualifierName = qualifierName + ) + } else { + Instance( + className = type.toClassName(), + typeName = type.toTypeName(), memberName = name, qualifierName = qualifierName ) } + } + + private fun KSType.toParameterizedTypeName(kindParamClass: KSType): ParameterizedTypeName = + toClassName().parameterizedBy(kindParamClass.toTypeName()) /** * Lookup both [javax.inject.Qualifier] and [javax.inject.Named] to provide the name @@ -145,6 +183,32 @@ sealed class VariableInjectionTarget( * (e.g. in `Lazy`, type is `B`, not `Lazy`). */ private fun KSType.getInjectedType(): KSType = arguments.first().type!!.resolve() + + private fun KSType.findActualType(): KSType { + val typeDeclaration = declaration + return if (typeDeclaration is KSTypeAlias) { + typeDeclaration.type.resolve().findActualType() + } else { + this + } + } + + /** + * Copied from ksp [com.squareup.kotlinpoet.ksp.toClassNameInternal] + * With it, we can create a correct type name for typealias (using [parameterizedBy]) + * Otherwise, we lose the generic parameters + */ + private fun KSDeclaration.toClassName(): ClassName { + require(!isLocal()) { + "Local/anonymous classes are not supported!" + } + val pkgName = packageName.asString() + val typesString = checkNotNull(qualifiedName).asString().removePrefix("$pkgName.") + + val simpleNames = typesString + .split(".") + return ClassName(pkgName, simpleNames) + } } } @@ -155,12 +219,6 @@ fun VariableInjectionTarget.getInvokeScopeGetMethodWithNameCodeBlock(): CodeBloc is VariableInjectionTarget.Lazy -> "getLazy" } - val className: ClassName = when (this) { - is VariableInjectionTarget.Instance -> memberType.toClassName() - is VariableInjectionTarget.Provider -> kindParamClass.toClassName() - is VariableInjectionTarget.Lazy -> kindParamClass.toClassName() - } - return CodeBlock.builder() .add("%N(%T::class.java", scopeGetMethodName, className) .apply { if (qualifierName != null) add(", %S", qualifierName) } @@ -168,10 +226,4 @@ fun VariableInjectionTarget.getInvokeScopeGetMethodWithNameCodeBlock(): CodeBloc .build() } -fun VariableInjectionTarget.getParamType(): TypeName = when (this) { - is VariableInjectionTarget.Instance -> memberType.toTypeName() - is VariableInjectionTarget.Provider -> memberType.toClassName() - .parameterizedBy(kindParamClass.toTypeName()) - is VariableInjectionTarget.Lazy -> memberType.toClassName() - .parameterizedBy(kindParamClass.toTypeName()) -} +fun VariableInjectionTarget.getParamType(): TypeName = typeName diff --git a/compiler-factory/src/main/java/toothpick/compiler/factory/FactoryProcessor.kt b/compiler-factory/src/main/java/toothpick/compiler/factory/FactoryProcessor.kt index f678608a..8daa546d 100644 --- a/compiler-factory/src/main/java/toothpick/compiler/factory/FactoryProcessor.kt +++ b/compiler-factory/src/main/java/toothpick/compiler/factory/FactoryProcessor.kt @@ -252,12 +252,21 @@ class FactoryProcessor( if (parentClass.isNonStaticInnerClass()) return false - return parameters.all { param -> + val invalidParams = parameters.filterNot { param -> param.type.resolve().isValidInjectedType( node = this, qualifiedName = param.name?.asString() ) } + + if (invalidParams.isNotEmpty()) { + logger.error( + this, + "Class ${parentClass.qualifiedName?.asString()} has invalid parameters: $invalidParams", + ) + } + + return invalidParams.isEmpty() } private fun KSFunctionDeclaration.createConstructorInjectionTarget(resolver: Resolver): ConstructorInjectionTarget { diff --git a/compiler-factory/src/test/java/toothpick/compiler/factory/FactoryTest.kt b/compiler-factory/src/test/java/toothpick/compiler/factory/FactoryTest.kt index 88aa6051..5d7716fb 100644 --- a/compiler-factory/src/test/java/toothpick/compiler/factory/FactoryTest.kt +++ b/compiler-factory/src/test/java/toothpick/compiler/factory/FactoryTest.kt @@ -2061,4 +2061,146 @@ class FactoryTest { } """ ) + + @Test + fun testNonEmptyConstructorWithTypeAlias_kt() { + val source = ktSource( + "TestNonEmptyConstructor", + """ + package test + import javax.inject.Inject + typealias TestTypeAlias = String + class TestNonEmptyConstructor @Inject constructor(testTypeAlias: TestTypeAlias) + """ + ) + + compilationAssert() + .that(source) + .processedWith(FactoryProcessorProvider()) + .compilesWithoutError() + .generatesSources(testNonEmptyConstructorWithTypeAlias_expected) + } + + private val testNonEmptyConstructorWithTypeAlias_expected = expectedKtSource( + "test/TestNonEmptyConstructor__Factory", + """ + package test + + import kotlin.Boolean + import kotlin.String + import kotlin.Suppress + import toothpick.Factory + import toothpick.Scope + + @Suppress( + "ClassName", + "RedundantVisibilityModifier", + ) + public class TestNonEmptyConstructor__Factory : Factory { + @Suppress( + "UNCHECKED_CAST", + "NAME_SHADOWING", + ) + public override fun createInstance(scope: Scope): TestNonEmptyConstructor { + val scope = getTargetScope(scope) + val param1 = scope.getInstance(String::class.java) as TestTypeAlias + return TestNonEmptyConstructor(param1) + } + + public override fun getTargetScope(scope: Scope): Scope = scope + + public override fun hasScopeAnnotation(): Boolean = false + + public override fun hasSingletonAnnotation(): Boolean = false + + public override fun hasReleasableAnnotation(): Boolean = false + + public override fun hasProvidesSingletonAnnotation(): Boolean = false + + public override fun hasProvidesReleasableAnnotation(): Boolean = false + } + """ + ) + + @Test + fun testNonEmptyConstructorWithTypeAliasAndGenerics_kt() { + val source = ktSource( + "TestNonEmptyConstructor", + """ + package test + import javax.inject.Inject + typealias TestTypeAlias = (param: Param) -> Entity + class TestNonEmptyConstructor @Inject constructor(testTypeAlias: TestTypeAlias) + """ + ) + + compilationAssert() + .that(source) + .processedWith(FactoryProcessorProvider()) + .compilesWithoutError() + .generatesSources(testNonEmptyConstructorWithTypeAliasAndGenerics_expected) + } + + private val testNonEmptyConstructorWithTypeAliasAndGenerics_expected = expectedKtSource( + "test/TestNonEmptyConstructor__Factory", + """ + package test + + import kotlin.Boolean + import kotlin.Function1 + import kotlin.Int + import kotlin.String + import kotlin.Suppress + import toothpick.Factory + import toothpick.Scope + + @Suppress( + "ClassName", + "RedundantVisibilityModifier", + ) + public class TestNonEmptyConstructor__Factory : Factory { + @Suppress( + "UNCHECKED_CAST", + "NAME_SHADOWING", + ) + public override fun createInstance(scope: Scope): TestNonEmptyConstructor { + val scope = getTargetScope(scope) + val param1 = scope.getInstance(Function1::class.java) as TestTypeAlias + return TestNonEmptyConstructor(param1) + } + + public override fun getTargetScope(scope: Scope): Scope = scope + + public override fun hasScopeAnnotation(): Boolean = false + + public override fun hasSingletonAnnotation(): Boolean = false + + public override fun hasReleasableAnnotation(): Boolean = false + + public override fun hasProvidesSingletonAnnotation(): Boolean = false + + public override fun hasProvidesReleasableAnnotation(): Boolean = false + } + """ + ) + + @Test + fun testEmptyConstructorWithIncorrectParameter_kt() { + val source = ktSource( + "TestNonEmptyConstructor", + """ + package test + import javax.inject.Inject + class TestNonEmptyConstructor @Inject constructor(invalid: Invalid) + """ + ) + + compilationAssert() + .that(source) + .processedWith(FactoryProcessorProvider()) + .failsToCompile() + .withLogContaining( + "Class test.TestNonEmptyConstructor has invalid parameters: [invalid]" + ) + } } diff --git a/compiler-memberinjector/src/main/java/toothpick/compiler/memberinjector/MemberInjectorProcessor.kt b/compiler-memberinjector/src/main/java/toothpick/compiler/memberinjector/MemberInjectorProcessor.kt index 0494c9a1..c1cc6dcc 100644 --- a/compiler-memberinjector/src/main/java/toothpick/compiler/memberinjector/MemberInjectorProcessor.kt +++ b/compiler-memberinjector/src/main/java/toothpick/compiler/memberinjector/MemberInjectorProcessor.kt @@ -58,7 +58,7 @@ import javax.inject.Inject class MemberInjectorProcessor( processorOptions: Map, codeGenerator: CodeGenerator, - logger: KSPLogger + logger: KSPLogger, ) : ToothpickProcessor( processorOptions, codeGenerator, logger ) { @@ -155,10 +155,20 @@ class MemberInjectorProcessor( return false } - return type.resolve().isValidInjectedType( + val isValidProperty = type.resolve().isValidInjectedType( node = this, qualifiedName = qualifiedName?.asString() ) + + if (!isValidProperty) { + val parentQualifiedName = parentDeclaration?.qualifiedName?.asString() + logger.error( + this, + "Class $parentQualifiedName has invalid property: ${simpleName.asString()}", + ) + } + + return isValidProperty } private fun KSFunctionDeclaration.isValidInjectAnnotatedMethod(): Boolean { @@ -181,17 +191,23 @@ class MemberInjectorProcessor( return false } - val areParametersValid = + val invalidParams = parameters - .map { param -> param.type.resolve() } - .all { type -> - type.isValidInjectedType( + .filterNot { param -> + param.type.resolve().isValidInjectedType( node = this, qualifiedName = qualifiedName?.asString() ) } - if (!areParametersValid) return false + if (invalidParams.isNotEmpty()) { + logger.error( + this, + "Class ${parentClass.qualifiedName?.asString()} has invalid parameters: $invalidParams", + ) + + return false + } if (!isJavaPackagePrivate() && !isInternal()) { if (!hasWarningSuppressed(SUPPRESS_WARNING_ANNOTATION_VISIBLE_VALUE)) { @@ -207,7 +223,11 @@ class MemberInjectorProcessor( } @Suppress("SameParameterValue") - private fun KSPLogger.crashOrWarnWhenMethodIsNotPackageOrInternal(node: KSNode, message: String, vararg args: Any?) { + private fun KSPLogger.crashOrWarnWhenMethodIsNotPackageOrInternal( + node: KSNode, + message: String, + vararg args: Any?, + ) { if (options.crashWhenInjectedMethodIsNotPackageVisible) error(node, message, *args) else warn(node, message, *args) } diff --git a/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.kt b/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.kt index 2c4a68c7..8b710641 100644 --- a/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.kt +++ b/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/FieldMemberInjectorTest.kt @@ -1505,4 +1505,121 @@ class FieldMemberInjectorTest { "Field test.TestFieldInjection.foo is of type int which is not supported by Toothpick." ) } + + @Test + fun testFieldInjectionWithTypeAlias_kt() { + val source = ktSource( + "TestFieldInjection", + """ + package test + import javax.inject.Inject + typealias TestTypeAlias = String + class TestFieldInjection { + @Inject lateinit var testTypeAlias: TestTypeAlias + } + class Foo + """ + ) + + compilationAssert() + .that(source) + .processedWith(MemberInjectorProcessorProvider()) + .compilesWithoutError() + .generatesSources(testFieldInjectionWithTypeAlias_expected) + } + + private val testFieldInjectionWithTypeAlias_expected = expectedKtSource( + "test/TestFieldInjection__MemberInjector", + """ + package test + + import kotlin.String + import kotlin.Suppress + import kotlin.Unit + import toothpick.MemberInjector + import toothpick.Scope + + @Suppress( + "ClassName", + "RedundantVisibilityModifier", + "UNCHECKED_CAST", + ) + public class TestFieldInjection__MemberInjector : MemberInjector { + public override fun inject(target: TestFieldInjection, scope: Scope): Unit { + target.testTypeAlias = scope.getInstance(String::class.java) as TestTypeAlias + } + } + """ + ) + + @Test + fun testFieldInjectionWithTypeAliasAndGeneric_kt() { + val source = ktSource( + "TestFieldInjection", + """ + package test + import javax.inject.Inject + typealias TestTypeAlias = (param: Param) -> Entity + class TestFieldInjection { + @Inject lateinit var testTypeAlias: TestTypeAlias + } + class Foo + """ + ) + + compilationAssert() + .that(source) + .processedWith(MemberInjectorProcessorProvider()) + .compilesWithoutError() + .generatesSources(testFieldInjectionWithTypeAliasAndGeneric_expected) + } + + private val testFieldInjectionWithTypeAliasAndGeneric_expected = expectedKtSource( + "test/TestFieldInjection__MemberInjector", + """ + package test + + import kotlin.Function1 + import kotlin.Int + import kotlin.String + import kotlin.Suppress + import kotlin.Unit + import toothpick.MemberInjector + import toothpick.Scope + + @Suppress( + "ClassName", + "RedundantVisibilityModifier", + "UNCHECKED_CAST", + ) + public class TestFieldInjection__MemberInjector : MemberInjector { + public override fun inject(target: TestFieldInjection, scope: Scope): Unit { + target.testTypeAlias = scope.getInstance(Function1::class.java) as TestTypeAlias + } + } + """ + ) + + @Test + fun testFieldInjectionWithIncorrectProperty_kt() { + val source = ktSource( + "TestFieldInjection", + """ + package test + import javax.inject.Inject + class TestFieldInjection { + @Inject lateinit var invalid: Invalid + } + class Foo + """ + ) + + compilationAssert() + .that(source) + .processedWith(MemberInjectorProcessorProvider()) + .failsToCompile() + .withLogContaining( + "Class test.TestFieldInjection has invalid property: invalid" + ) + } } diff --git a/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/MethodMemberInjectorTest.kt b/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/MethodMemberInjectorTest.kt index 6ca87780..96d16574 100644 --- a/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/MethodMemberInjectorTest.kt +++ b/compiler-memberinjector/src/test/java/toothpick/compiler/memberinjector/MethodMemberInjectorTest.kt @@ -812,4 +812,123 @@ class MethodMemberInjectorTest { } """ ) + + @Test + fun testMethodInjectionWithTypeAlias_kt() { + val source = ktSource( + "TestMethodInjection", + """ + package test + import javax.inject.Inject + typealias TestTypeAlias = String + class TestMethodInjection { + @Inject + fun m(testTypeAlias: TestTypeAlias) {} + } + """ + ) + + compilationAssert() + .that(source) + .processedWith(MemberInjectorProcessorProvider()) + .compilesWithoutError() + .generatesSources(testMethodInjectionWithTypeAlias_expected) + } + + private val testMethodInjectionWithTypeAlias_expected = expectedKtSource( + "test/TestMethodInjection__MemberInjector", + """ + package test + + import kotlin.String + import kotlin.Suppress + import kotlin.Unit + import toothpick.MemberInjector + import toothpick.Scope + + @Suppress( + "ClassName", + "RedundantVisibilityModifier", + "UNCHECKED_CAST", + ) + public class TestMethodInjection__MemberInjector : MemberInjector { + public override fun inject(target: TestMethodInjection, scope: Scope): Unit { + val param1 = scope.getInstance(String::class.java) as TestTypeAlias + target.m(param1) + } + } + """ + ) + + @Test + fun testMethodInjectionWithTypeAliasAndGeneric_kt() { + val source = ktSource( + "TestMethodInjection", + """ + package test + import javax.inject.Inject + typealias TestTypeAlias = (param: Param) -> Entity + class TestMethodInjection { + @Inject + fun m(testTypeAlias: TestTypeAlias) {} + } + """ + ) + + compilationAssert() + .that(source) + .processedWith(MemberInjectorProcessorProvider()) + .compilesWithoutError() + .generatesSources(testMethodInjectionWithTypeAliasAndGeneric_expected) + } + + private val testMethodInjectionWithTypeAliasAndGeneric_expected = expectedKtSource( + "test/TestMethodInjection__MemberInjector", + """ + package test + + import kotlin.Function1 + import kotlin.Int + import kotlin.String + import kotlin.Suppress + import kotlin.Unit + import toothpick.MemberInjector + import toothpick.Scope + + @Suppress( + "ClassName", + "RedundantVisibilityModifier", + "UNCHECKED_CAST", + ) + public class TestMethodInjection__MemberInjector : MemberInjector { + public override fun inject(target: TestMethodInjection, scope: Scope): Unit { + val param1 = scope.getInstance(Function1::class.java) as TestTypeAlias + target.m(param1) + } + } + """ + ) + + @Test + fun testMethodInjectionWithIncorrectParameter_kt() { + val source = ktSource( + "TestMethodInjection", + """ + package test + import javax.inject.Inject + class TestMethodInjection { + @Inject + fun m(invalid: Invalid) {} + } + """ + ) + + compilationAssert() + .that(source) + .processedWith(MemberInjectorProcessorProvider()) + .failsToCompile() + .withLogContaining( + "Class test.TestMethodInjection has invalid parameters: [invalid]" + ) + } } diff --git a/gradle.properties b/gradle.properties index 230c118b..a578a504 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP=fr.outadoc.toothpick-ksp -VERSION_NAME=1.0.1 +VERSION_NAME=1.0.2 POM_PACKAGING=JAR