Skip to content

Commit

Permalink
[square#150] Use guessed class only for non-ambiguous results
Browse files Browse the repository at this point in the history
  - fall back to manual resultion when class is potentially from the sentence case package
  • Loading branch information
vlad-kasatkin committed Oct 27, 2020
1 parent 151d3a4 commit 9649d92
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,15 @@ internal fun ClassDescriptor.asClassName(): ClassName =
)

internal fun FqName.asClassName(module: ModuleDescriptor): ClassName {
try {
val segments = pathSegments().map { it.asString() }

// If the first sentence case is not the last segment of the path it becomes ambiguous,
// for example, com.Foo.Bar could be a inner class Bar or an unconventional package com.Foo.
val canGuessClassName = segments.indexOfFirst { it[0].isUpperCase() } == segments.size - 1
if (canGuessClassName) {
return ClassName.bestGuess(asString())
} catch (ignored: IllegalArgumentException) {
// Probably lowercase class. Try to resolve the class below.
}

val segments = pathSegments().map { it.asString() }
for (index in (segments.size - 1) downTo 1) {
val packageSegments = segments.subList(0, index)
val classSegments = segments.subList(index, segments.size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package com.squareup.anvil.compiler.dagger

import com.google.common.truth.Truth.assertThat
import com.squareup.anvil.compiler.componentInterfaceAnvilModule
import com.squareup.anvil.compiler.dagger.UppercasePackage.OuterClass.InnerClass
import com.squareup.anvil.compiler.dagger.UppercasePackage.TestClassInUppercasePackage
import com.squareup.anvil.compiler.dagger.UppercasePackage.lowerCaseClassInUppercasePackage
import com.squareup.anvil.compiler.daggerModule1
import com.squareup.anvil.compiler.innerModule
import com.squareup.anvil.compiler.isStatic
Expand Down Expand Up @@ -2231,7 +2234,7 @@ public final class DaggerComponentInterface implements ComponentInterface {
@Test
fun `a factory class is generated for a method returning a class with a named import`() {
compile(
"""
"""
package com.squareup.test
import dagger.Module
Expand All @@ -2254,7 +2257,7 @@ public final class DaggerComponentInterface implements ComponentInterface {
@Test
fun `a factory class is generated ignoring the named import original path`() {
compile(
"""
"""
package com.squareup.test
import dagger.Module
Expand Down Expand Up @@ -2309,6 +2312,93 @@ public final class DaggerComponentInterface implements ComponentInterface {
}
}

@Test
fun `a factory class is generated for a capital case package name`() {
compile(
"""
package com.squareup.test
import dagger.Module
import dagger.Provides
import com.squareup.anvil.compiler.dagger.UppercasePackage.TestClassInUppercasePackage
@Module
object DaggerModule1 {
@Provides fun provideThing(): TestClassInUppercasePackage = TestClassInUppercasePackage()
}
"""
) {
val factoryClass = daggerModule1.moduleFactoryClass("provideThing")

val constructor = factoryClass.declaredConstructors.single()
assertThat(constructor.parameterTypes.toList()).isEmpty()

val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }

val thingProvider = staticMethods.single { it.name == "provideThing" }
assertThat(thingProvider.invoke(null)).isInstanceOf(TestClassInUppercasePackage::class.java)
}
}

@Test
fun `a factory class is generated for a capital case package name and lower class name`() {
compile(
"""
package com.squareup.test
import dagger.Module
import dagger.Provides
import com.squareup.anvil.compiler.dagger.UppercasePackage.lowerCaseClassInUppercasePackage
@Module
object DaggerModule1 {
@Provides fun provideThing(): lowerCaseClassInUppercasePackage {
return lowerCaseClassInUppercasePackage()
}
}
"""
) {
val factoryClass = daggerModule1.moduleFactoryClass("provideThing")

val constructor = factoryClass.declaredConstructors.single()
assertThat(constructor.parameterTypes.toList()).isEmpty()

val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }

val thingProvider = staticMethods.single { it.name == "provideThing" }
assertThat(thingProvider.invoke(null))
.isInstanceOf(lowerCaseClassInUppercasePackage::class.java)
}
}

@Test
fun `a factory class is generated for a capital case package name and inner class`() {
compile(
"""
package com.squareup.test
import dagger.Module
import dagger.Provides
import com.squareup.anvil.compiler.dagger.UppercasePackage.OuterClass.InnerClass
@Module
object DaggerModule1 {
@Provides fun provideThing(): InnerClass = InnerClass()
}
"""
) {
val factoryClass = daggerModule1.moduleFactoryClass("provideThing")

val constructor = factoryClass.declaredConstructors.single()
assertThat(constructor.parameterTypes.toList()).isEmpty()

val staticMethods = factoryClass.declaredMethods.filter { it.isStatic }

val thingProvider = staticMethods.single { it.name == "provideThing" }
assertThat(thingProvider.invoke(null)).isInstanceOf(InnerClass::class.java)
}
}

private fun compile(
source: String,
block: Result.() -> Unit = { }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@file:Suppress("PackageName")

package com.squareup.anvil.compiler.dagger.UppercasePackage

// These classes are used in unit tests. Don't rename or move them!
class TestClassInUppercasePackage

@Suppress("ClassName")
class lowerCaseClassInUppercasePackage

class OuterClass {
class InnerClass
}

0 comments on commit 9649d92

Please sign in to comment.