Repository: evant/kotlin-inject
Pull request #43 (2020-08-16 04:03:14)

fix too-strict requirement on ksp overrides check


Patch:
diff --git a/README.md b/README.md
index 32c4b94..b66c239 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@ A compile-time dependency injection library for kotlin.
     
     @Provides protected fun jsonParser() = JsonParser()
 
-    @Provides protected val RealHttp.http: Http get() = this
+    protected val RealHttp.http: Http
+        @Provides get() = this
 }
 
 interface Http
@@ -111,7 +112,8 @@ For external dependencies, you can declare a function or read-only property in t
 certain type. kotlin-inject will use the return type to provide this instance where it is requested.
 
 ```kotlin
-    @Provides protected val RealHttp.http: Http get() = this
+    protected val RealHttp.http: Http
+        @Provides get() = this
 ```
 You can declare arguments to a providing function/property to help you construct your instance. Here we are taking in an
 instance of `RealHttp` and providing it for the interface `Http`. You can see a little sugar with this as the receiver 
@@ -141,7 +143,7 @@ If you need to pass any instances into your component you can declare them as co
 the generated create function. You can optionally annotate it with `@Provides` to provide the value to the dependency graph.
 
 ```kotlin
-@Component abstract class MyComponent(@Provides protected val foo: Foo)
+@Component abstract class MyComponent(@get:Provides protected val foo: Foo)
 ```
 
 ```kotlin
@@ -223,7 +225,9 @@ instance you can scope it to a component. The instance will live as long as that
 
 First create your scope annotation.
 ```kotlin
-@Scope annotation class MyScope
+@Scope
+@Target(CLASS, FUNCTION, PROPERTY_GETTER)
+annotation class MyScope
 ```
 
 Then annotate your component with that scope annotation.
diff --git a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt
index e778ebd..2e21b7d 100644
--- a/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt
+++ b/kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/TypeCollector.kt
@@ -95,9 +95,14 @@ class TypeCollector private constructor(private val provider: AstProvider, priva
             val scopedComponent = if (scopeType != null) astClass else null
             if (method.hasAnnotation<IntoMap>()) {
                 // Pair<A, B> -> Map<A, B>
-                val typeArgs = method.returnTypeFor(astClass).arguments
-                val mapType = TypeKey(declaredTypeOf(Map::class, typeArgs[0], typeArgs[1]))
-                addContainerType(mapType, mapOf, method, accessor, scopedComponent)
+                val type = method.returnTypeFor(astClass)
+                if (type.packageName == "kotlin" && type.simpleName == "Pair") {
+                    val typeArgs = method.returnTypeFor(astClass).arguments
+                    val mapType = TypeKey(declaredTypeOf(Map::class, typeArgs[0], typeArgs[1]))
+                    addContainerType(mapType, mapOf, method, accessor, scopedComponent)
+                } else {
+                    error("@IntoMap must have return type of type Pair", method)
+                }
             } else if (method.hasAnnotation<IntoSet>()) {
                 // A -> Set<A>
                 val setType = TypeKey(declaredTypeOf(Set::class, method.returnTypeFor(astClass)))
diff --git a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
index 9d9c052..3d9406a 100644
--- a/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
+++ b/kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt
@@ -411,29 +411,11 @@ private class ModelAstProperty(
             ModelAstType(this, element.parameters[0].asType(), it)
         }
 
-    private val annotatedElement: Element? by lazy {
-        val signature = kmProperty.syntheticMethodForAnnotations ?: return@lazy null
-        for (method in ElementFilter.methodsIn(element.enclosingElement.enclosedElements)) {
-            if (method.matches(signature)) {
-                return@lazy method
-            }
-        }
-        null
-    }
-
     override fun overrides(other: AstMethod): Boolean {
         require(other is ModelAstMethod)
         return elements.overrides(element, other.element, parent)
     }
 
-    override fun hasAnnotation(className: String): Boolean {
-        return annotatedElement?.hasAnnotation(className) == true
-    }
-
-    override fun typeAnnotatedWith(className: String): AstClass? {
-        return annotatedElement?.typeAnnotatedWith(className)?.toAstClass()
-    }
-
     override fun asMemberName(): MemberName {
         return MemberName(elements.getPackageOf(element).qualifiedName.toString(), name)
     }
diff --git a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
index 00bab06..5bcda13 100644
--- a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
+++ b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt
@@ -211,7 +211,7 @@ private class KSAstFunction(provider: KSAstProvider, override val declaration: K
         get() = declaration.extensionReceiver?.let { KSAstType(this, it) }
 
     override fun overrides(other: AstMethod): Boolean {
-        require(other is KSAstFunction)
+        if (other !is KSAstFunction) return false
         return declaration.overrides(other.declaration)
     }
 
@@ -249,8 +249,8 @@ private class KSAstProperty(provider: KSAstProvider, override val declaration: K
         get() = declaration.extensionReceiver?.let { KSAstType(this, it) }
 
     override fun overrides(other: AstMethod): Boolean {
-        require(other is KSAstProperty)
-        return false //TODO?
+        if (other !is KSAstProperty) return false
+        return declaration.overrides(other.declaration)
     }
 
     override val returnType: AstType
@@ -266,6 +266,18 @@ private class KSAstProperty(provider: KSAstProvider, override val declaration: K
         return MemberName(packageName, declaration.simpleName.toString())
     }
 
+    override fun hasAnnotation(className: String): Boolean {
+        // also check the declaration itself https://github.com/android/kotlin/issues/108
+        return declaration.getter?.hasAnnotation(className) == true
+                || declaration.hasAnnotation(className)
+    }
+
+    override fun typeAnnotatedWith(className: String): AstClass? {
+        // also check the declaration itself https://github.com/android/kotlin/issues/108
+        return (declaration.getter?.typeAnnotatedWith(className)?.declaration as? KSClassDeclaration)?.toAstClass()
+            ?: (declaration.typeAnnotatedWith(className)?.declaration as? KSClassDeclaration)?.toAstClass()
+    }
+
     override fun equals(other: Any?): Boolean {
         return other is KSAstProperty && declaration == other.declaration
     }
diff --git a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt
index 9a41783..f5ddc61 100644
--- a/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt
+++ b/kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/Util.kt
@@ -8,7 +8,6 @@ import com.squareup.kotlinpoet.TypeVariableName
 import org.jetbrains.kotlin.ksp.isAbstract
 import org.jetbrains.kotlin.ksp.symbol.*
 import org.jetbrains.kotlin.ksp.visitor.KSDefaultVisitor
-import kotlin.reflect.KClass
 
 fun KSAnnotated.typeAnnotatedWith(className: String): KSType? {
     for (annotation in annotations) {
@@ -33,11 +32,17 @@ fun KSDeclaration.asClassName(): ClassName {
 
 fun KSDeclaration.isAbstract() = when (this) {
     is KSFunctionDeclaration -> isAbstract
-    is KSPropertyDeclaration -> isAbstract()
+    is KSPropertyDeclaration -> {
+        isAbstractWorkaround()
+    }
     is KSClassDeclaration -> isAbstract()
     else -> false
 }
 
+// https://github.com/android/kotlin/issues/107
+fun KSPropertyDeclaration.isAbstractWorkaround() = this.modifiers.contains(Modifier.ABSTRACT) ||
+        ((this.parentDeclaration as? KSClassDeclaration)?.classKind == ClassKind.INTERFACE && getter?.origin == Origin.SYNTHETIC)
+
 fun KSTypeReference.memberOf(enclosingClass: KSClassDeclaration): KSTypeReference {
     val declaration = resolve()!!.declaration
     return if (declaration is KSTypeParameter) {
@@ -63,6 +68,7 @@ fun KSType.asTypeName(): TypeName {
         override fun visitTypeAlias(typeAlias: KSTypeAlias, data: Unit): TypeName {
             return fromDeclaration(typeAlias)
         }
+
         override fun visitTypeParameter(typeParameter: KSTypeParameter, data: Unit): TypeName {
             return TypeVariableName(
                 name = typeParameter.name.asString(),
diff --git a/kotlin-inject-runtime/src/main/kotlin/me/tatarka/inject/annotations/Annotations.kt b/kotlin-inject-runtime/src/main/kotlin/me/tatarka/inject/annotations/Annotations.kt
index a9eec33..a2b58b2 100644
--- a/kotlin-inject-runtime/src/main/kotlin/me/tatarka/inject/annotations/Annotations.kt
+++ b/kotlin-inject-runtime/src/main/kotlin/me/tatarka/inject/annotations/Annotations.kt
@@ -12,7 +12,7 @@ annotation class Inject
 annotation class Component
 
 @Retention(RUNTIME)
-@Target(FUNCTION, PROPERTY)
+@Target(FUNCTION, PROPERTY_GETTER)
 annotation class Provides
 
 @Retention(RUNTIME)
@@ -20,10 +20,10 @@ annotation class Provides
 annotation class Scope
 
 @Retention(RUNTIME)
-@Target(FUNCTION, PROPERTY)
+@Target(FUNCTION, PROPERTY_GETTER)
 annotation class IntoSet
 
 @Retention(RUNTIME)
-@Target(FUNCTION, PROPERTY)
+@Target(FUNCTION, PROPERTY_GETTER)
 annotation class IntoMap
 


Test patch:
diff --git a/integration-tests/src/test/kotlin/me/tatarka/inject/test/InheritanceTest.kt b/integration-tests/src/test/kotlin/me/tatarka/inject/test/InheritanceTest.kt
index a8ddbc6..c23146b 100644
--- a/integration-tests/src/test/kotlin/me/tatarka/inject/test/InheritanceTest.kt
+++ b/integration-tests/src/test/kotlin/me/tatarka/inject/test/InheritanceTest.kt
@@ -4,6 +4,7 @@ import assertk.assertThat
 import assertk.assertions.hasClass
 import assertk.assertions.isNotNull
 import me.tatarka.inject.annotations.Component
+import me.tatarka.inject.annotations.Provides
 import kotlin.test.Test
 
 interface ComponentInterface {
@@ -18,6 +19,15 @@ interface GenericComponentInterface<T> {
 
 @Component abstract class GenericInterfaceComponent : GenericComponentInterface<Foo>
 
+interface ProvidesComponentInterface {
+    val iFoo: IFoo
+        @Provides get() = Foo()
+}
+
+@Component abstract class ProvidesInterfaceComponent : ProvidesComponentInterface {
+    abstract val foo: IFoo
+}
+
 class InheritanceTest {
     @Test fun generates_a_component_that_provides_a_dep_defined_in_an_implemented_interface() {
         val component = InterfaceComponent::class.create()
diff --git a/integration-tests/src/test/kotlin/me/tatarka/inject/test/Models.kt b/integration-tests/src/test/kotlin/me/tatarka/inject/test/Models.kt
index 0918ec6..a5655a6 100644
--- a/integration-tests/src/test/kotlin/me/tatarka/inject/test/Models.kt
+++ b/integration-tests/src/test/kotlin/me/tatarka/inject/test/Models.kt
@@ -2,6 +2,7 @@ package me.tatarka.inject.test
 
 import me.tatarka.inject.annotations.Inject
 import me.tatarka.inject.annotations.Scope
+import kotlin.annotation.AnnotationTarget.*
 
 interface IFoo
 
@@ -34,6 +35,7 @@ interface INamedBar {
 class NamedBar(override val name: String) : INamedBar
 
 @Scope
+@Target(CLASS, FUNCTION, PROPERTY_GETTER)
 annotation class CustomScope
 
 var customScopeBarConstructorCount = 0
diff --git a/integration-tests/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt b/integration-tests/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt
index 6169393..0ad3d0f 100644
--- a/integration-tests/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt
+++ b/integration-tests/src/test/kotlin/me/tatarka/inject/test/MultibindsTest.kt
@@ -13,9 +13,8 @@ data class FooValue(val name: String)
     @Provides @IntoSet
     fun fooValue1() = FooValue("1")
 
-    @Provides @IntoSet
     val fooValue2
-        get() = FooValue("2")
+        @Provides @IntoSet get() = FooValue("2")
 }
 
 @Component abstract class DynamicKeyComponent {
@@ -25,23 +24,19 @@ data class FooValue(val name: String)
     @Provides @IntoMap
     fun fooValue1() = "1" to FooValue("1")
 
-    @Provides @IntoMap
     val fooValue2
-        get() = "2" to FooValue("2") }
+        @Provides @IntoMap get() = "2" to FooValue("2") }
 
 @Component abstract class ParentSetComponent {
-    @Provides @IntoSet
     val Foo.bind: IFoo
-        get() = this
+        @Provides @IntoSet get() = this
 
-    @Provides @IntoSet
     val Bar.bind: IFoo
-        get() = this
+        @Provides @IntoSet get() = this
 
     // Should not be in the set
-    @Provides
     val Baz.bind: IFoo
-        get() = this
+        @Provides get() = this
 }
 
 @Component abstract class ChildSetComponent(@Component val parent: ParentSetComponent) {
diff --git a/integration-tests/src/test/kotlin/me/tatarka/inject/test/NestedComponentTest.kt b/integration-tests/src/test/kotlin/me/tatarka/inject/test/NestedComponentTest.kt
index eeba361..b170df5 100644
--- a/integration-tests/src/test/kotlin/me/tatarka/inject/test/NestedComponentTest.kt
+++ b/integration-tests/src/test/kotlin/me/tatarka/inject/test/NestedComponentTest.kt
@@ -11,17 +11,15 @@ import kotlin.test.Test
     @Provides
     fun foo() = NamedFoo("parent")
 
-    @Provides
     val Foo.binds: IFoo
-        get() = this
+        @Provides get() = this
 }
 
 @Component abstract class SimpleChildComponent1(@Component val parent: ParentComponent) {
     abstract val namedFoo: NamedFoo
 
-    @Provides
     val BarImpl.binds: IBar
-        get() = this
+        @Provides get() = this
 
     abstract val foo: IFoo
 }
diff --git a/integration-tests/src/test/kotlin/me/tatarka/inject/test/ProvidesTest.kt b/integration-tests/src/test/kotlin/me/tatarka/inject/test/ProvidesTest.kt
index 951d774..a43aabb 100644
--- a/integration-tests/src/test/kotlin/me/tatarka/inject/test/ProvidesTest.kt
+++ b/integration-tests/src/test/kotlin/me/tatarka/inject/test/ProvidesTest.kt
@@ -34,9 +34,8 @@ class ProvidesFoo(val bar: ProvidesBar? = null)
 
     abstract val foo: ProvidesFoo
 
-    @Provides
     val provideFoo
-        get() = ProvidesFoo().also { providesCalled = true }
+        @Provides get() = ProvidesFoo().also { providesCalled = true }
 }
 
 @Component abstract class ProvidesExtensionFunctionComponent {
@@ -53,12 +52,11 @@ class ProvidesFoo(val bar: ProvidesBar? = null)
 
     abstract val foo: ProvidesFoo
 
-    @Provides
     val ProvidesBar.provideFoo
-        get() = ProvidesFoo(this).also { providesCalled = true }
+        @Provides get() = ProvidesFoo(this).also { providesCalled = true }
 }
 
-@Component abstract class ProvidesValConstructorComponent(@Provides val provideFoo: ProvidesFoo) {
+@Component abstract class ProvidesValConstructorComponent(@get:Provides val provideFoo: ProvidesFoo) {
     abstract val foo: ProvidesFoo
 }
 
@@ -79,13 +77,11 @@ class Foo2
     @Provides
     fun foo(bar: ProvidesBar) = Foo2()
 
-    @Provides
     val foo
-        get() = Foo3()
+        @Provides get() = Foo3()
 
-    @Provides
     val Foo3.foo: IFoo
-        get() = this
+        @Provides get() = this
 }
 
 class IntFoo(val int: Int)
@@ -114,65 +110,50 @@ class StringArrayFoo(val stringArray: Array<String>)
 @Component abstract class ProvidesBasicTypes {
     abstract val basicType: BasicTypes
 
-    @Provides
     val boolean
-        get() = true
+        @Provides get() = true
 
-    @Provides
     val byte
-        get() = 1.toByte()
+        @Provides get() = 1.toByte()
 
-    @Provides
     val char
-        get() = 'a'
+        @Provides get() = 'a'
 
-    @Provides
     val short
-        get() = 2.toShort()
+        @Provides get() = 2.toShort()
 
-    @Provides
     val int
-        get() = 3
+        @Provides get() = 3
 
-    @Provides
     val long
-        get() = 4L
+        @Provides get() = 4L
 
-    @Provides
     val float
-        get() = 5f
+        @Provides get() = 5f
 
-    @Provides
     val double
-        get() = 6.0
+        @Provides get() = 6.0
 
-    @Provides
     val string
-        get() = "b"
+        @Provides get() = "b"
 
-    @Provides
     val booleanArray
-        get() = booleanArrayOf(true)
+        @Provides get() = booleanArrayOf(true)
 
-    @Provides
     val stringArray
-        get() = arrayOf("c")
+        @Provides get() = arrayOf("c")
 
-    @Provides
     val intArray
-        get() = intArrayOf(7)
+        @Provides get() = intArrayOf(7)
 
-    @Provides
     val Int.bind
-        get() = IntFoo(this)
+        @Provides get() = IntFoo(this)
 
-    @Provides
     val IntArray.bind
-        get() = IntArrayFoo(this)
+        @Provides get() = IntArrayFoo(this)
 
-    @Provides
     val Array<String>.bind
-        get() = StringArrayFoo(this)
+        @Provides get() = StringArrayFoo(this)
 }
 
 class ProvidesTest {
diff --git a/integration-tests/src/test/kotlin/me/tatarka/inject/test/ScopeTest.kt b/integration-tests/src/test/kotlin/me/tatarka/inject/test/ScopeTest.kt
index 0a2f652..7496cbb 100644
--- a/integration-tests/src/test/kotlin/me/tatarka/inject/test/ScopeTest.kt
+++ b/integration-tests/src/test/kotlin/me/tatarka/inject/test/ScopeTest.kt
@@ -18,10 +18,8 @@ import kotlin.test.Test
 
     abstract val foo: IFoo
 
-    @Provides
-    @CustomScope
     val Foo.binds: IFoo
-        get() = this
+        @Provides @CustomScope get() = this
 }