/* * Copyright (C) 2015 The Dagger Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package dagger.internal.codegen; import static dagger.internal.codegen.CompilerMode.DEFAULT_MODE; import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_BUILDER; import static dagger.internal.codegen.base.ComponentCreatorAnnotation.SUBCOMPONENT_FACTORY; import static dagger.internal.codegen.base.ComponentCreatorKind.BUILDER; import static dagger.internal.codegen.base.ComponentCreatorKind.FACTORY; import static dagger.internal.codegen.base.ComponentKind.SUBCOMPONENT; import static dagger.internal.codegen.binding.ErrorMessages.ComponentCreatorMessages.moreThanOneRefToSubcomponent; import static dagger.internal.codegen.binding.ErrorMessages.componentMessagesFor; import androidx.room.compiler.processing.util.Source; import com.google.common.collect.ImmutableList; import dagger.internal.codegen.base.ComponentCreatorAnnotation; import dagger.testing.compile.CompilerTests; import java.util.Collection; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; /** Tests for {@link dagger.Subcomponent.Builder} validation. */ @RunWith(Parameterized.class) public class SubcomponentCreatorValidationTest extends ComponentCreatorTestHelper { @Parameters(name = "creatorKind={0}") public static Collection parameters() { return ImmutableList.copyOf(new Object[][] {{SUBCOMPONENT_BUILDER}, {SUBCOMPONENT_FACTORY}}); } public SubcomponentCreatorValidationTest(ComponentCreatorAnnotation componentCreatorAnnotation) { super(DEFAULT_MODE, componentCreatorAnnotation); } @Test public void testRefSubcomponentAndSubCreatorFails() { Source componentFile = preprocessedJavaSource( "test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent child();", " ChildComponent.Builder childComponentBuilder();", "}"); Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface ChildComponent {", " @Subcomponent.Builder", " static interface Builder {", " ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( moreThanOneRefToSubcomponent(), "test.ChildComponent", process("[" + "test.ParentComponent.child(), " + "test.ParentComponent.childComponentBuilder()" + "]"))) .onSource(componentFile); }); } @Test public void testRefSubCreatorTwiceFails() { Source componentFile = preprocessedJavaSource("test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder builder1();", " ChildComponent.Builder builder2();", "}"); Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface ChildComponent {", " @Subcomponent.Builder", " static interface Builder {", " ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( moreThanOneRefToSubcomponent(), "test.ChildComponent", process("[" + "test.ParentComponent.builder1(), " + "test.ParentComponent.builder2()" + "]"))) .onSource(componentFile); }); } @Test public void testMoreThanOneCreatorFails() { Source componentFile = preprocessedJavaSource("test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder1 build();", "}"); Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface ChildComponent {", " @Subcomponent.Builder", " static interface Builder1 {", " ChildComponent build();", " }", "", " @Subcomponent.Builder", " static interface Builder2 {", " ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( componentMessagesFor(SUBCOMPONENT).moreThanOne(), process("[test.ChildComponent.Builder1, test.ChildComponent.Builder2]"))) .onSource(childComponentFile); }); } @Test public void testMoreThanOneCreatorFails_differentTypes() { Source componentFile = preprocessedJavaSource("test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder build();", "}"); Source childComponentFile = CompilerTests.javaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface ChildComponent {", " @Subcomponent.Builder", " static interface Builder {", " ChildComponent build();", " }", "", " @Subcomponent.Factory", " static interface Factory {", " ChildComponent create();", " }", "}"); CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( componentMessagesFor(SUBCOMPONENT).moreThanOne(), "[test.ChildComponent.Builder, test.ChildComponent.Factory]")) .onSource(childComponentFile); }); } @Test public void testCreatorGenericsFails() { Source componentFile = preprocessedJavaSource("test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder build();", "}"); Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface ChildComponent {", " @Subcomponent.Builder", " interface Builder {", " ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.generics()).onSource(childComponentFile); }); } @Test public void testCreatorNotInComponentFails() { Source builder = preprocessedJavaSource("test.Builder", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent.Builder", "interface Builder {}"); CompilerTests.daggerCompiler(builder) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.mustBeInComponent()).onSource(builder); }); } @Test public void testCreatorMissingFactoryMethodFails() { Source componentFile = preprocessedJavaSource( "test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder childComponentBuilder();", "}"); Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface ChildComponent {", " @Subcomponent.Builder", " interface Builder {}", "}"); CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.missingFactoryMethod()) .onSource(childComponentFile); }); } @Test public void testPrivateCreatorFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " private interface Builder {}", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.isPrivate()).onSource(childComponentFile); }); } @Test public void testNonStaticCreatorFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " abstract class Builder {}", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.mustBeStatic()).onSource(childComponentFile); }); } @Test public void testNonAbstractCreatorFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " static class Builder {}", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.mustBeAbstract()) .onSource(childComponentFile); }); } @Test public void testCreatorOneConstructorWithArgsFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " static abstract class Builder {", " Builder(String unused) {}", " abstract ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.invalidConstructor()) .onSource(childComponentFile); }); } @Test public void testCreatorMoreThanOneConstructorFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " static abstract class Builder {", " Builder() {}", " Builder(String unused) {}", " abstract ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.invalidConstructor()) .onSource(childComponentFile); }); } @Test public void testCreatorEnumFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " enum Builder {}", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.mustBeClassOrInterface()) .onSource(childComponentFile); }); } @Test public void testCreatorFactoryMethodReturnsWrongTypeFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " interface Builder {", " String build();", " }", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(messages.factoryMethodMustReturnComponentType()) .onSource(childComponentFile) .onLine(9); }); } @Test public void testInheritedCreatorFactoryMethodReturnsWrongTypeFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " interface Parent {", " String build();", " }", "", " @Subcomponent.Builder", " interface Builder extends Parent {}", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( messages.inheritedFactoryMethodMustReturnComponentType(), process("String test.ChildComponent.Parent.build()"))) .onSource(childComponentFile) .onLine(12); }); } @Test public void testTwoFactoryMethodsFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " @Subcomponent.Builder", " interface Builder {", " ChildComponent build();", " ChildComponent build1();", " }", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( messages.twoFactoryMethods(), process("test.ChildComponent test.ChildComponent.Builder.build()"))) .onSource(childComponentFile) .onLine(10); }); } @Test public void testInheritedTwoFactoryMethodsFails() { Source childComponentFile = preprocessedJavaSource("test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " interface Parent {", " ChildComponent build();", " ChildComponent build1();", " }", "", " @Subcomponent.Builder", " interface Builder extends Parent {}", "}"); CompilerTests.daggerCompiler(childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( messages.inheritedTwoFactoryMethods(), process("test.ChildComponent test.ChildComponent.Parent.build()"), process("test.ChildComponent test.ChildComponent.Parent.build1()"))) .onSource(childComponentFile) .onLine(13); }); } @Test public void testMultipleSettersPerTypeFails() { Source moduleFile = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " @Provides String s() { return \"\"; }", "}"); Source componentFile = preprocessedJavaSource( "test.ParentComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder childComponentBuilder();", "}"); Source childComponentFile = javaFileBuilder("test.ChildComponent") .addLines( "package test;", "", "import dagger.Subcomponent;", "import javax.inject.Provider;", "", "@Subcomponent(modules = TestModule.class)", "abstract class ChildComponent {", " abstract String s();", "") .addLinesIf( BUILDER, " @Subcomponent.Builder", " interface Builder {", " ChildComponent build();", " void set1(TestModule s);", " void set2(TestModule s);", " }") .addLinesIf( FACTORY, " @Subcomponent.Factory", " interface Factory {", " ChildComponent create(TestModule m1, TestModule m2);", " }") .addLines( // "}") .buildSource(); String elements = creatorKind.equals(BUILDER) ? "[void test.ChildComponent.Builder.set1(test.TestModule), " + "void test.ChildComponent.Builder.set2(test.TestModule)]" : "[test.TestModule m1, test.TestModule m2]"; CompilerTests.daggerCompiler(moduleFile, componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) .onSource(childComponentFile) .onLine(11); }); } @Test public void testMultipleSettersPerTypeIncludingResolvedGenericsFails() { Source moduleFile = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " @Provides String s() { return \"\"; }", "}"); Source componentFile = preprocessedJavaSource( "test.ParentComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder childComponentBuilder();", "}"); Source childComponentFile = javaFileBuilder("test.ChildComponent") .addLines( "package test;", "", "import dagger.Subcomponent;", "import javax.inject.Provider;", "", "@Subcomponent(modules = TestModule.class)", "abstract class ChildComponent {", " abstract String s();", "") .addLinesIf( BUILDER, " interface Parent {", " void set1(T t);", " }", "", " @Subcomponent.Builder", " interface Builder extends Parent {", " ChildComponent build();", " void set2(TestModule s);", " }") .addLinesIf( FACTORY, " interface Parent {", " C create(TestModule m1, T t);", " }", "", " @Subcomponent.Factory", " interface Factory extends Parent {}") .addLines( // "}") .buildSource(); String elements = creatorKind.equals(BUILDER) ? "[void test.ChildComponent.Builder.set1(test.TestModule), " + "void test.ChildComponent.Builder.set2(test.TestModule)]" : "[test.TestModule m1, test.TestModule t]"; CompilerTests.daggerCompiler(moduleFile, componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( String.format( messages.multipleSettersForModuleOrDependencyType(), "test.TestModule", elements)) .onSource(childComponentFile) .onLine(15); }); } @Test public void testMultipleSettersPerBoundInstanceTypeFails() { Source componentFile = preprocessedJavaSource( "test.ParentComponent", "package test;", "", "import dagger.Component;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder childComponentBuilder();", "}"); Source childComponentFile = javaFileBuilder("test.ChildComponent") .addLines( "package test;", "", "import dagger.BindsInstance;", "import dagger.Subcomponent;", "", "@Subcomponent", "abstract class ChildComponent {", " abstract String s();", "") .addLinesIf( BUILDER, " @Subcomponent.Builder", " interface Builder {", " ChildComponent build();", " @BindsInstance void set1(String s);", " @BindsInstance void set2(String s);", " }") .addLinesIf( FACTORY, " @Subcomponent.Factory", " interface Factory {", " ChildComponent create(", " @BindsInstance String s1, @BindsInstance String s2);", " }") .addLines( // "}") .buildSource(); String firstBinding = creatorKind.equals(FACTORY) ? "ChildComponent.Factory.create(s1, …)" : "@BindsInstance void ChildComponent.Builder.set1(String)"; String secondBinding = creatorKind.equals(FACTORY) ? "ChildComponent.Factory.create(…, s2)" : "@BindsInstance void ChildComponent.Builder.set2(String)"; CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining("String is bound multiple times:"); subject.hasErrorContaining(" " + firstBinding); subject.hasErrorContaining(" " + secondBinding); subject.hasErrorContaining(" in component: [ParentComponent → ChildComponent]") .onSource(componentFile) .onLineContaining("interface ParentComponent {"); }); } @Test public void testExtraSettersFails() { Source componentFile = preprocessedJavaSource("test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder build();", "}"); Source childComponentFile = javaFileBuilder("test.ChildComponent") .addLines( "package test;", "", "import dagger.Subcomponent;", "import javax.inject.Provider;", "", "@Subcomponent", "abstract class ChildComponent {") .addLinesIf( BUILDER, " @Subcomponent.Builder", " interface Builder {", " ChildComponent build();", " void set1(String s);", " void set2(Integer s);", " }") .addLinesIf( FACTORY, " @Subcomponent.Factory", " interface Factory {", " ChildComponent create(String s, Integer i);", " }") .addLines("}") .buildSource(); String elements = creatorKind.equals(FACTORY) ? "[String s, Integer i]" : "[void test.ChildComponent.Builder.set1(String)," + " void test.ChildComponent.Builder.set2(Integer)]"; CompilerTests.daggerCompiler(componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining(String.format(messages.extraSetters(), elements)) .onSource(childComponentFile) .onLine(9); }); } @Test public void testMissingSettersFail() { Source moduleFile = CompilerTests.javaSource( "test.TestModule", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class TestModule {", " TestModule(String unused) {}", " @Provides String s() { return null; }", "}"); Source module2File = CompilerTests.javaSource( "test.Test2Module", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class Test2Module {", " @Provides Integer i() { return null; }", "}"); Source module3File = CompilerTests.javaSource( "test.Test3Module", "package test;", "", "import dagger.Module;", "import dagger.Provides;", "", "@Module", "final class Test3Module {", " Test3Module(String unused) {}", " @Provides Double d() { return null; }", "}"); Source componentFile = preprocessedJavaSource( "test.ParentComponent", "package test;", "", "import dagger.Component;", "import javax.inject.Provider;", "", "@Component", "interface ParentComponent {", " ChildComponent.Builder build();", "}"); Source childComponentFile = preprocessedJavaSource( "test.ChildComponent", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent(modules = {TestModule.class, Test2Module.class, Test3Module.class})", "interface ChildComponent {", " String string();", " Integer integer();", "", " @Subcomponent.Builder", " interface Builder {", " ChildComponent build();", " }", "}"); CompilerTests.daggerCompiler( moduleFile, module2File, module3File, componentFile, childComponentFile) .compile( subject -> { subject.hasErrorCount(1); subject.hasErrorContaining( // Ignores Test2Module because we can construct it ourselves. // TODO(sameb): Ignore Test3Module because it's not used within transitive // dependencies. String.format( messages.missingSetters(), "[test.TestModule, test.Test3Module]")) .onSource(childComponentFile) .onLine(11); }); } @Test public void covariantFactoryMethodReturnType() { Source foo = CompilerTests.javaSource( "test.Foo", "package test;", "", "import javax.inject.Inject;", "", "class Foo {", " @Inject Foo() {}", "}"); Source supertype = CompilerTests.javaSource( "test.Supertype", "package test;", "", "interface Supertype {", " Foo foo();", "}"); Source subcomponent = preprocessedJavaSource( "test.HasSupertype", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface HasSupertype extends Supertype {", " @Subcomponent.Builder", " interface Builder {", " Supertype build();", " }", "}"); CompilerTests.daggerCompiler(foo, supertype, subcomponent) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(0); }); } @Test public void covariantFactoryMethodReturnType_hasNewMethod() { Source foo = CompilerTests.javaSource( "test.Foo", "package test;", "", "import javax.inject.Inject;", "", "class Foo {", " @Inject Foo() {}", "}"); Source bar = CompilerTests.javaSource( "test.Bar", "package test;", "", "import javax.inject.Inject;", "", "class Bar {", " @Inject Bar() {}", "}"); Source supertype = CompilerTests.javaSource( "test.Supertype", "package test;", "", "interface Supertype {", " Foo foo();", "}"); Source subcomponent = preprocessedJavaSource( "test.HasSupertype", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface HasSupertype extends Supertype {", " Bar bar();", "", " @Subcomponent.Builder", " interface Builder {", " Supertype build();", " }", "}"); CompilerTests.daggerCompiler(foo, bar, supertype, subcomponent) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(1); subject.hasWarningContaining( process( "test.HasSupertype.Builder.build() returns test.Supertype, but " + "test.HasSupertype declares additional component method(s): bar(). " + "In order to provide type-safe access to these methods, override " + "build() to return test.HasSupertype")) .onSource(subcomponent) .onLine(11); }); } @Test public void covariantFactoryMethodReturnType_hasNewMethod_buildMethodInherited() { Source foo = CompilerTests.javaSource( "test.Foo", "package test;", "", "import javax.inject.Inject;", "", "class Foo {", " @Inject Foo() {}", "}"); Source bar = CompilerTests.javaSource( "test.Bar", "package test;", "", "import javax.inject.Inject;", "", "class Bar {", " @Inject Bar() {}", "}"); Source supertype = CompilerTests.javaSource( "test.Supertype", "package test;", "", "interface Supertype {", " Foo foo();", "}"); Source creatorSupertype = preprocessedJavaSource( "test.CreatorSupertype", "package test;", "", "interface CreatorSupertype {", " Supertype build();", "}"); Source subcomponent = preprocessedJavaSource( "test.HasSupertype", "package test;", "", "import dagger.Subcomponent;", "", "@Subcomponent", "interface HasSupertype extends Supertype {", " Bar bar();", "", " @Subcomponent.Builder", " interface Builder extends CreatorSupertype {}", "}"); CompilerTests.daggerCompiler(foo, bar, supertype, creatorSupertype, subcomponent) .compile( subject -> { subject.hasErrorCount(0); subject.hasWarningCount(1); subject.hasWarningContaining( process( "[test.CreatorSupertype.build()] test.HasSupertype.Builder.build() returns " + "test.Supertype, but test.HasSupertype declares additional component " + "method(s): bar(). In order to provide type-safe access to these " + "methods, override build() to return test.HasSupertype")); }); } }