diff --git a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/BindsMethodValidator.kt b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/BindsMethodValidator.kt index d0025a047..67db5c504 100644 --- a/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/BindsMethodValidator.kt +++ b/compiler/src/main/java/com/squareup/anvil/compiler/codegen/dagger/BindsMethodValidator.kt @@ -7,6 +7,7 @@ import com.squareup.anvil.compiler.codegen.PrivateCodeGenerator import com.squareup.anvil.compiler.daggerBindsFqName import com.squareup.anvil.compiler.daggerModuleFqName import com.squareup.anvil.compiler.internal.reference.AnvilCompilationExceptionFunctionReference +import com.squareup.anvil.compiler.internal.reference.ClassReference import com.squareup.anvil.compiler.internal.reference.FunctionReference import com.squareup.anvil.compiler.internal.reference.allSuperTypeClassReferences import com.squareup.anvil.compiler.internal.reference.classAndInnerClassReferences @@ -75,28 +76,48 @@ internal class BindsMethodValidator : PrivateCodeGenerator() { ) if (!function.parameterMatchesReturnType() && !function.receiverMatchesReturnType()) { + val returnType = function.returnType().asClassReference().shortName + val paramSuperTypes = (function.parameterSuperTypes() ?: function.receiverSuperTypes())!! + .map { it.shortName } + .toList() + + val superTypesMessage = if (paramSuperTypes.size == 1) { + "has no supertypes." + } else { + "only has the following supertypes: ${paramSuperTypes.drop(1)}" + } throw AnvilCompilationExceptionFunctionReference( - message = "@Binds methods' parameter type must be assignable to the return type", + message = "@Binds methods' parameter type must be assignable to the return type. " + + "Expected binding of type $returnType but impl parameter of type " + + "${paramSuperTypes.first()} $superTypesMessage", functionReference = function ) } } private fun FunctionReference.Psi.parameterMatchesReturnType(): Boolean { + return parameterSuperTypes() + ?.contains(returnType().asClassReference()) + ?: false + } + + private fun FunctionReference.Psi.parameterSuperTypes(): Sequence? { return parameters.singleOrNull() ?.type() ?.asClassReference() ?.allSuperTypeClassReferences(includeSelf = true) + } + + private fun FunctionReference.Psi.receiverMatchesReturnType(): Boolean { + return receiverSuperTypes() ?.contains(returnType().asClassReference()) ?: false } - private fun FunctionReference.Psi.receiverMatchesReturnType(): Boolean { + private fun FunctionReference.Psi.receiverSuperTypes(): Sequence? { return function.receiverTypeReference ?.toTypeReference(declaringClass) ?.asClassReference() ?.allSuperTypeClassReferences(includeSelf = true) - ?.contains(returnType().asClassReference()) - ?: false } } diff --git a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/BindsMethodValidatorTest.kt b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/BindsMethodValidatorTest.kt index 0d38bf67d..f64a20c41 100644 --- a/compiler/src/test/java/com/squareup/anvil/compiler/dagger/BindsMethodValidatorTest.kt +++ b/compiler/src/test/java/com/squareup/anvil/compiler/dagger/BindsMethodValidatorTest.kt @@ -29,6 +29,41 @@ class BindsMethodValidatorTest( @Test fun `a binding with an incompatible parameter type fails to compile`() { + compile( + """ + package com.squareup.test + + import dagger.Binds + import dagger.Module + import javax.inject.Inject + + interface Lorem + open class Ipsum + class Foo @Inject constructor() : Ipsum(), Lorem + interface Bar + + @Module + abstract class BarModule { + @Binds + abstract fun bindsBar(impl: Foo): Bar + } + """ + ) { + assertThat(exitCode).isError() + assertThat(messages).contains( + "@Binds methods' parameter type must be assignable to the return type" + ) + if (!useDagger) { + assertThat(messages).contains( + "Expected binding of type Bar but impl parameter of type Foo only has the following " + + "supertypes: [Ipsum, Lorem]" + ) + } + } + } + + @Test + fun `a binding with an incompatible parameter type with no supertypes fails to compile`() { compile( """ package com.squareup.test @@ -51,6 +86,11 @@ class BindsMethodValidatorTest( assertThat(messages).contains( "@Binds methods' parameter type must be assignable to the return type" ) + if (!useDagger) { + assertThat(messages).contains( + "Expected binding of type Bar but impl parameter of type Foo has no supertypes." + ) + } } }