From 83dbd5aad77695201b4f2bf505fad7e65b28bee0 Mon Sep 17 00:00:00 2001 From: Ralf Wondratschek Date: Wed, 11 Nov 2020 14:40:33 -0800 Subject: [PATCH] If a classname matches two imports, then try to resolve the type to find the correct one. Fixes #154 --- .../anvil/compiler/codegen/PsiUtils.kt | 41 +++++++++++------ .../com/squareup/anvil/compiler/TestUtils.kt | 17 +++++-- .../dagger/ComponentDetectorCheckTest.kt | 4 +- .../InjectConstructorFactoryGeneratorTest.kt | 4 +- .../dagger/MembersInjectorGeneratorTest.kt | 4 +- .../ProvidesMethodFactoryGeneratorTest.kt | 45 ++++++++++++++++++- 6 files changed, 89 insertions(+), 26 deletions(-) diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt index 48e790aa7..84d4ffa49 100644 --- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt +++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/PsiUtils.kt @@ -239,18 +239,31 @@ internal fun PsiElement.requireFqName( // First look in the imports for the reference name. If the class is imported, then we know the // fully qualified name. importPaths - .filter { it.alias == null } - .firstOrNull { - it.fqName.shortName().asString() == classReference + .filter { it.alias == null && it.fqName.shortName().asString() == classReference } + .also { matchingImportPaths -> + when { + matchingImportPaths.size == 1 -> + return matchingImportPaths[0].fqName + matchingImportPaths.size > 1 -> + return matchingImportPaths.first { importPath -> + module.resolveClassByFqName(importPath.fqName, FROM_BACKEND) != null + }.fqName + } } - ?.let { return it.fqName } importPaths - .filter { it.alias == null } - .firstOrNull { - it.fqName.shortName().asString() == classReferenceOuter + .filter { it.alias == null && it.fqName.shortName().asString() == classReferenceOuter } + .also { matchingImportPaths -> + when { + matchingImportPaths.size == 1 -> + return FqName("${matchingImportPaths[0].fqName.parent()}.$classReference") + matchingImportPaths.size > 1 -> + return matchingImportPaths.first { importPath -> + val fqName = FqName("${importPath.fqName.parent()}.$classReference") + module.resolveClassByFqName(fqName, FROM_BACKEND) != null + }.fqName + } } - ?.let { return FqName("${it.fqName.parent()}.$classReference") } // If there is no import, then try to resolve the class with the same package as this file. module.findClassOrTypeAlias(containingKtFile.packageFqName, classReference) @@ -289,9 +302,9 @@ internal fun PsiElement.requireFqName( // Check if it's a named import. containingKtFile.importDirectives - .firstOrNull { classReference == it.importPath?.importedName?.asString() } - ?.importedFqName - ?.let { return it } + .firstOrNull { classReference == it.importPath?.importedName?.asString() } + ?.importedFqName + ?.let { return it } // Everything else isn't supported. throw AnvilCompilationException( @@ -390,7 +403,7 @@ fun KtUserType.isTypeParameter(): Boolean { fun KtUserType.findExtendsBound(): List { return parents.filterIsInstance() - .first() - .typeParameters - .mapNotNull { it.fqName } + .first() + .typeParameters + .mapNotNull { it.fqName } } diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt index 4c36a0763..946a966df 100644 --- a/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt +++ b/compiler/src/test/java/com/squareup/anvil/compiler/TestUtils.kt @@ -18,7 +18,7 @@ import java.util.Locale.US import kotlin.reflect.KClass internal fun compile( - source: String, + vararg sources: String, enableDaggerAnnotationProcessor: Boolean = false, generateDaggerFactories: Boolean = false, block: Result.() -> Unit = { } @@ -50,10 +50,19 @@ internal fun compile( ) ) - val name = "${workingDir.absolutePath}/sources/src/main/java/com/squareup/test/Source.kt" - check(File(name).parentFile.mkdirs()) + this.sources = sources.map { content -> + val packageDir = content.lines() + .first { it.trim().startsWith("package ") } + .substringAfter("package ") + .replace('.', '/') - sources = listOf(SourceFile.kotlin(name, contents = source, trimIndent = true)) + val name = "${workingDir.absolutePath}/sources/src/main/java/$packageDir/Source.kt" + with(File(name).parentFile) { + check(exists() || mkdirs()) + } + + SourceFile.kotlin(name, contents = content, trimIndent = true) + } } .compile() .also(block) diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt index 0ea5248c9..850db76c8 100644 --- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt +++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ComponentDetectorCheckTest.kt @@ -89,10 +89,10 @@ class ComponentDetectorCheckTest { } private fun compile( - source: String, + vararg sources: String, block: Result.() -> Unit = { } ): Result = com.squareup.anvil.compiler.compile( - source = source, + sources = *sources, generateDaggerFactories = true, block = block ) diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt index eeca7ab8b..17f7334cf 100644 --- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt +++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/InjectConstructorFactoryGeneratorTest.kt @@ -1304,10 +1304,10 @@ public final class InjectClass_Factory implements Factor } private fun compile( - source: String, + vararg sources: String, block: Result.() -> Unit = { } ): Result = com.squareup.anvil.compiler.compile( - source = source, + sources = *sources, enableDaggerAnnotationProcessor = useDagger, generateDaggerFactories = !useDagger, block = block diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt index 580a8c8f3..8c89b9eaa 100644 --- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt +++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/MembersInjectorGeneratorTest.kt @@ -685,10 +685,10 @@ public final class InjectClass_MembersInjector implements MembersInject } private fun compile( - source: String, + vararg sources: String, block: Result.() -> Unit = { } ): Result = com.squareup.anvil.compiler.compile( - source = source, + sources = *sources, enableDaggerAnnotationProcessor = useDagger, generateDaggerFactories = !useDagger, block = block diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt index 1bea16671..6dd4d6db6 100644 --- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt +++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/ProvidesMethodFactoryGeneratorTest.kt @@ -2399,11 +2399,52 @@ public final class DaggerComponentInterface implements ComponentInterface { } } + @Test + fun `a factory class is generated for an uppercase factory function`() { + compile( + """ + package com.squareup.test.a + + import com.squareup.test.b.User + + fun User(): User = User(42) + """, + """ + package com.squareup.test.b + + data class User(val age: Int) + """, + """ + package com.squareup.test + + import com.squareup.test.a.User + import com.squareup.test.b.User + import dagger.Module + import dagger.Provides + + @Module + object DaggerModule1 { + @Provides fun user(): User = User() + } + """ + ) { + val factoryClass = daggerModule1.moduleFactoryClass("user") + + val constructor = factoryClass.declaredConstructors.single() + assertThat(constructor.parameterTypes.toList()).isEmpty() + + val staticMethods = factoryClass.declaredMethods.filter { it.isStatic } + + val userProvider = staticMethods.single { it.name == "user" } + assertThat(userProvider.invoke(null)).isNotNull() + } + } + private fun compile( - source: String, + vararg sources: String, block: Result.() -> Unit = { } ): Result = com.squareup.anvil.compiler.compile( - source, + sources = *sources, enableDaggerAnnotationProcessor = useDagger, generateDaggerFactories = !useDagger, block = block