Skip to content

Commit 90e9deb

Browse files
authored
Merge pull request #35 from evant/issue/types
Again, more robust type name resolution
2 parents 3b839b1 + cbc494f commit 90e9deb

File tree

6 files changed

+70
-125
lines changed
  • integration-tests/src/test/kotlin/me/tatarka/inject/test
  • kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler
  • kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt
  • kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp

6 files changed

+70
-125
lines changed

integration-tests/src/test/kotlin/me/tatarka/inject/test/TypeAliasTest.kt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package me.tatarka.inject.test
33
import assertk.assertAll
44
import assertk.assertThat
55
import assertk.assertions.isEqualTo
6-
import me.tatarka.inject.annotations.Inject
76
import me.tatarka.inject.annotations.Component
7+
import me.tatarka.inject.annotations.Inject
88
import me.tatarka.inject.annotations.Provides
99
import kotlin.test.Test
1010

@@ -29,6 +29,19 @@ typealias NamedFoo2 = NamedFoo
2929
@Provides fun foo2(): NamedFoo2 = NamedFoo("2")
3030
}
3131

32+
@Target(AnnotationTarget.TYPE)
33+
annotation class FooAnnotation
34+
35+
class GenericFoo<T>(val value: T)
36+
37+
typealias AnnotatedAliasedFoo<T> = @FooAnnotation GenericFoo<T>
38+
39+
@Component abstract class AliasedProvidesComponent {
40+
abstract val foo: AnnotatedAliasedFoo<String>
41+
42+
@Provides fun foo(): AnnotatedAliasedFoo<String> = GenericFoo("1")
43+
}
44+
3245
class QualifierTest {
3346

3447
@Test

kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/Ast.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ abstract class AstType : AstElement() {
141141

142142
abstract fun isUnit(): Boolean
143143

144+
abstract fun isFunction(): Boolean
145+
144146
@Suppress("NOTHING_TO_INLINE")
145147
inline fun isNotUnit() = !isUnit()
146148

kotlin-inject-compiler-core/src/main/kotlin/me/tatarka/inject/compiler/InjectGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ class InjectGenerator(provider: AstProvider, private val options: Options) :
420420
if (typeCreator != null) {
421421
return typeCreator.toResult(skipScoped)
422422
}
423-
if (key.type.name.matches(Regex("kotlin\\.Function[0-9]+.*"))) {
423+
if (key.type.isFunction()) {
424424
val typeAliasName = key.type.typeAliasName
425425
if (typeAliasName != null) {
426426
// Check to see if we have a function matching the type alias

kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/ModelAst.kt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,17 @@ interface ModelAstProvider :
5555
val type = elements.getTypeElement(klass.java.canonicalName)
5656
return ModelAstType(
5757
this,
58-
declaredType(type, astTypes.asList()),
59-
klass.toKmType()
58+
elementDeclaredType(type, astTypes.asList()),
59+
kmDeclaredType(klass.qualifiedName ?: klass.java.canonicalName, astTypes.asList())
6060
)
6161
}
62+
private fun kmDeclaredType(name: String, astTypes: List<AstType>): KmType = KmType(0).apply {
63+
classifier = KmClassifier.Class(name).apply {
64+
arguments.addAll(astTypes.map { KmTypeProjection(KmVariance.INVARIANT, kmDeclaredType(it.name, it.arguments)) })
65+
}
66+
}
6267

63-
private fun declaredType(type: TypeElement, astTypes: List<AstType>) = types.getDeclaredType(type, *astTypes.map {
68+
private fun elementDeclaredType(type: TypeElement, astTypes: List<AstType>) = types.getDeclaredType(type, *astTypes.map {
6469
(it as ModelAstType).type
6570
}.toTypedArray())
6671

@@ -452,6 +457,10 @@ private class ModelAstType(
452457

453458
override fun isUnit(): Boolean = type is NoType
454459

460+
override fun isFunction(): Boolean {
461+
return element.toString().matches(Regex("kotlin\\.jvm\\.functions\\.Function[0-9]+.*"))
462+
}
463+
455464
override fun asElement(): AstBasicElement =
456465
ModelBasicElement(this, element)
457466

@@ -534,8 +543,13 @@ private val KmAnnotation.type: KmType
534543
classifier = KmClassifier.Class(className)
535544
}
536545

537-
private fun KClass<*>.toKmType(): KmType = KmType(0).apply {
538-
classifier = KmClassifier.Class(java.canonicalName)
546+
private fun KClass<*>.toKmType(args: List<KmTypeProjection>): KmType =
547+
(qualifiedName ?: java.canonicalName).toKmType(args)
548+
549+
private fun String.toKmType(args: List<KmTypeProjection>): KmType = KmType(0).apply {
550+
classifier = KmClassifier.Class(this@toKmType).apply {
551+
arguments.addAll(args)
552+
}
539553
}
540554

541555
private fun collectModifiers(flags: Flags?): Set<AstModifier> {

kotlin-inject-compiler-kapt/src/main/kotlin/me/tatarka/inject/compiler/kapt/Util.kt

Lines changed: 30 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,6 @@ fun Element.typeAnnotatedWith(className: String) =
2121
it.annotationType.asElement().hasAnnotation(className)
2222
}?.annotationType?.asElement() as? TypeElement
2323

24-
fun TypeName.javaToKotlinType(): TypeName = if (this is ParameterizedTypeName) {
25-
(rawType.javaToKotlinType() as ClassName).parameterizedBy(
26-
*typeArguments.map { it.javaToKotlinType() }.toTypedArray()
27-
)
28-
} else if (this is WildcardTypeName) {
29-
if (inTypes.isNotEmpty()) {
30-
WildcardTypeName.consumerOf(inTypes.first().javaToKotlinType())
31-
} else {
32-
WildcardTypeName.producerOf(outTypes.first().javaToKotlinType())
33-
}
34-
} else {
35-
val s = toString()
36-
when (s) {
37-
"java.util.Map" -> ClassName.bestGuess("kotlin.collections.Map")
38-
"java.util.Set" -> ClassName.bestGuess("kotlin.collections.Set")
39-
"java.lang.String" -> ClassName.bestGuess("kotlin.String")
40-
else -> {
41-
val regex = Regex("kotlin\\.jvm\\.functions\\.(Function[0-9]+)")
42-
val result = regex.matchEntire(s)
43-
if (result != null) {
44-
ClassName.bestGuess("kotlin.${result.groupValues[1]}")
45-
} else {
46-
this
47-
}
48-
}
49-
}
50-
}
51-
5224
val TypeElement.metadata: KotlinClassMetadata?
5325
get() {
5426
val meta = getAnnotation(Metadata::class.java) ?: return null
@@ -168,101 +140,41 @@ private fun ExecutableElement.nameMatches(signature: JvmMemberSignature?): Boole
168140
return simpleName.contentEquals(signature.name)
169141
}
170142

171-
fun DeclaredType.rawTypeName(kmType: KmType?): ClassName {
172-
val className =
173-
(kmType?.classifier as? KmClassifier.Class)?.asClassName()
174-
?: (this.asElement() as TypeElement).asClassName()
175-
return className.kotlinClassName()
143+
fun KmClassifier.asClassName() = when (this) {
144+
is KmClassifier.Class -> name.asClassName()
145+
is KmClassifier.TypeAlias -> name.asClassName()
146+
is KmClassifier.TypeParameter -> null
176147
}
177148

178-
fun KmClassifier.Class.asClassName() = ClassName.bestGuess(name.replace('/', '.'))
179-
180-
fun ClassName.kotlinClassName(): ClassName {
181-
return when (canonicalName) {
182-
"java.util.Map" -> ClassName("kotlin.collections", "Map")
183-
"java.util.Set" -> ClassName("kotlin.collections", "Set")
184-
"java.lang.String" -> ClassName("kotlin", "String")
185-
else -> this
149+
fun kotlinx.metadata.ClassName.asClassName(): ClassName {
150+
val split = lastIndexOf('/')
151+
return if (split == -1) {
152+
ClassName("", this)
153+
} else {
154+
ClassName(substring(0, split).replace('/', '.'), substring(split + 1))
186155
}
187156
}
188157

189158
fun TypeMirror.asTypeName(kmType: KmType?): TypeName {
190-
return accept(object : SimpleTypeVisitor7<TypeName, Void?>() {
191-
override fun visitPrimitive(t: PrimitiveType, p: Void?): TypeName {
192-
return when (t.kind) {
193-
TypeKind.BOOLEAN -> BOOLEAN
194-
TypeKind.BYTE -> BYTE
195-
TypeKind.SHORT -> SHORT
196-
TypeKind.INT -> INT
197-
TypeKind.LONG -> LONG
198-
TypeKind.CHAR -> CHAR
199-
TypeKind.FLOAT -> FLOAT
200-
TypeKind.DOUBLE -> DOUBLE
201-
else -> throw AssertionError()
202-
}
203-
}
204-
205-
override fun visitDeclared(t: DeclaredType, p: Void?): TypeName {
206-
val isNullable = kmType?.let { Flag.Type.IS_NULLABLE(it.flags) } == true
207-
val rawType: ClassName = t.rawTypeName(kmType).copy(nullable = isNullable) as ClassName
208-
val enclosingType = t.enclosingType
209-
val enclosing = if (enclosingType.kind != TypeKind.NONE &&
210-
Modifier.STATIC !in t.asElement().modifiers
211-
)
212-
enclosingType.accept(this, null) else
213-
null
214-
if (t.typeArguments.isEmpty() && enclosing !is ParameterizedTypeName) {
215-
return rawType
216-
}
217-
218-
val typeArgumentNames = mutableListOf<TypeName>()
219-
for (i in 0 until t.typeArguments.size) {
220-
val typeArg = t.typeArguments[i]
221-
val kmArg = kmType?.arguments?.getOrNull(i)?.type
222-
typeArgumentNames += typeArg.asTypeName(kmArg)
223-
}
224-
225-
return if (enclosing is ParameterizedTypeName)
226-
enclosing.nestedClass(rawType.simpleName, typeArgumentNames) else
227-
rawType.parameterizedBy(typeArgumentNames)
159+
if (kmType != null) {
160+
val typeName = kmType.asTypeName()
161+
if (typeName != null) {
162+
return typeName
228163
}
229-
230-
override fun visitError(t: ErrorType, p: Void?): TypeName {
231-
return visitDeclared(t, p)
232-
}
233-
234-
override fun visitArray(t: ArrayType, p: Void?): TypeName {
235-
return when (t.componentType.kind) {
236-
TypeKind.BOOLEAN -> BOOLEAN_ARRAY
237-
TypeKind.BYTE -> BYTE_ARRAY
238-
TypeKind.SHORT -> SHORT_ARRAY
239-
TypeKind.INT -> INT_ARRAY
240-
TypeKind.LONG -> LONG_ARRAY
241-
TypeKind.CHAR -> CHAR_ARRAY
242-
TypeKind.FLOAT -> FLOAT_ARRAY
243-
TypeKind.DOUBLE -> DOUBLE_ARRAY
244-
else -> ARRAY.parameterizedBy(t.componentType.asTypeName(kmType?.arguments?.getOrNull(0)?.type))
245-
}
246-
}
247-
248-
override fun visitTypeVariable(
249-
t: TypeVariable,
250-
p: Void?
251-
): TypeName {
252-
return t.asTypeVariableName()
253-
}
254-
255-
override fun visitWildcard(t: WildcardType, p: Void?): TypeName {
256-
return t.asWildcardTypeName()
257-
}
258-
259-
override fun visitNoType(t: NoType, p: Void?): TypeName {
260-
if (t.kind == TypeKind.VOID) return UNIT
261-
return super.visitUnknown(t, p)
262-
}
263-
264-
override fun defaultAction(e: TypeMirror?, p: Void?): TypeName {
265-
throw IllegalArgumentException("Unexpected type mirror: " + e!!)
266-
}
267-
}, null)
164+
}
165+
return asTypeName()
268166
}
167+
168+
fun KmType.asTypeName(): TypeName? {
169+
val abbreviatedType = abbreviatedType
170+
if (abbreviatedType != null) {
171+
return abbreviatedType.asTypeName()
172+
}
173+
val isNullable = Flag.Type.IS_NULLABLE(flags)
174+
val className = classifier.asClassName() ?: return null
175+
return if (arguments.isEmpty()) {
176+
className
177+
} else {
178+
className.parameterizedBy(arguments.map { it.type!!.asTypeName()!! })
179+
}.copy(nullable = isNullable)
180+
}

kotlin-inject-compiler-ksp/src/main/kotlin/me/tatarka/inject/compiler/ksp/KSAst.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,10 @@ private class KSAstType(provider: KSAstProvider) : AstType(), KSAstAnnotated, KS
337337
return type == resolver.builtIns.unitType
338338
}
339339

340+
override fun isFunction(): Boolean {
341+
return type.declaration.qualifiedName!!.asString().matches(Regex("kotlin\\.Function[0-9]+.*"))
342+
}
343+
340344
override fun asElement(): AstBasicElement {
341345
return KSAstBasicElement(this, declaration)
342346
}

0 commit comments

Comments
 (0)