diff options
Diffstat (limited to 'java')
158 files changed, 1557 insertions, 3503 deletions
diff --git a/java/dagger/android/support/BUILD b/java/dagger/android/support/BUILD index 8afa703d4..03846b577 100644 --- a/java/dagger/android/support/BUILD +++ b/java/dagger/android/support/BUILD @@ -41,13 +41,9 @@ android_library( "//:dagger_with_compiler", "//java/dagger/android", "@google_bazel_common//third_party/java/error_prone:annotations", - "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", "@maven//:androidx_appcompat_appcompat", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], ) diff --git a/java/dagger/android/support/DaggerAppCompatActivity.java b/java/dagger/android/support/DaggerAppCompatActivity.java index 3f0469ed6..da5b17313 100644 --- a/java/dagger/android/support/DaggerAppCompatActivity.java +++ b/java/dagger/android/support/DaggerAppCompatActivity.java @@ -17,10 +17,10 @@ package dagger.android.support; import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; import androidx.annotation.ContentView; import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; import dagger.android.AndroidInjection; import dagger.android.AndroidInjector; import dagger.android.DispatchingAndroidInjector; diff --git a/java/dagger/android/support/DaggerFragment.java b/java/dagger/android/support/DaggerFragment.java index e705b3c69..887451154 100644 --- a/java/dagger/android/support/DaggerFragment.java +++ b/java/dagger/android/support/DaggerFragment.java @@ -17,9 +17,9 @@ package dagger.android.support; import android.content.Context; -import androidx.fragment.app.Fragment; import androidx.annotation.ContentView; import androidx.annotation.LayoutRes; +import androidx.fragment.app.Fragment; import dagger.android.AndroidInjector; import dagger.android.DispatchingAndroidInjector; import dagger.android.HasAndroidInjector; diff --git a/java/dagger/hilt/BUILD b/java/dagger/hilt/BUILD index 3d12c91e7..94af18a91 100644 --- a/java/dagger/hilt/BUILD +++ b/java/dagger/hilt/BUILD @@ -13,7 +13,7 @@ # limitations under the License. load("//tools:maven.bzl", "gen_maven_artifact") -load("//:build_defs.bzl", "POM_VERSION") +load("//:build_defs.bzl", "POM_VERSION_ALPHA") # Description: # A library that wraps the Dagger API to make DI usage and testing easier. @@ -46,14 +46,11 @@ java_library( # TODO(bcorso): Consider using a separate processor to validate @EntryPoint. "//java/dagger/hilt/processor/internal/aggregateddeps:plugin", ], - proguard_specs = ["proguard-rules.pro"], deps = [ ":generates_root_input", ":package_info", "//java/dagger/hilt/internal:component_manager", "//java/dagger/hilt/internal:generated_component", - "//java/dagger/hilt/internal:preconditions", - "//java/dagger/hilt/internal:test_singleton_component", "@google_bazel_common//third_party/java/jsr305_annotations", ], ) @@ -165,6 +162,7 @@ filegroup( "//java/dagger/hilt/android/processor/internal/androidentrypoint:srcs_filegroup", "//java/dagger/hilt/android/processor/internal/bindvalue:srcs_filegroup", "//java/dagger/hilt/android/processor/internal/customtestapplication:srcs_filegroup", + "//java/dagger/hilt/android/processor/internal/uninstallmodules:srcs_filegroup", "//java/dagger/hilt/android/processor/internal/viewmodel:srcs_filegroup", "//java/dagger/hilt/processor:srcs_filegroup", "//java/dagger/hilt/processor/internal:srcs_filegroup", @@ -175,13 +173,12 @@ filegroup( "//java/dagger/hilt/processor/internal/generatesrootinput:srcs_filegroup", "//java/dagger/hilt/processor/internal/originatingelement:srcs_filegroup", "//java/dagger/hilt/processor/internal/root:srcs_filegroup", - "//java/dagger/hilt/processor/internal/uninstallmodules:srcs_filegroup", ], ) java_library( name = "artifact-core-lib", - tags = ["maven_coordinates=com.google.dagger:hilt-core:" + POM_VERSION], + tags = ["maven_coordinates=com.google.dagger:hilt-core:" + POM_VERSION_ALPHA], exports = [ ":define_component", ":entry_point", @@ -197,7 +194,7 @@ java_library( gen_maven_artifact( name = "artifact-core", - artifact_coordinates = "com.google.dagger:hilt-core:" + POM_VERSION, + artifact_coordinates = "com.google.dagger:hilt-core:" + POM_VERSION_ALPHA, artifact_name = "Hilt Core", artifact_target = ":artifact-core-lib", artifact_target_libs = [ @@ -213,7 +210,6 @@ gen_maven_artifact( "//java/dagger/hilt/internal:component_manager", "//java/dagger/hilt/internal:generated_component", "//java/dagger/hilt/internal:preconditions", - "//java/dagger/hilt/internal:test_singleton_component", "//java/dagger/hilt/internal:unsafe_casts", "//java/dagger/hilt/internal/aliasof", "//java/dagger/hilt/internal/definecomponent", diff --git a/java/dagger/hilt/EntryPoints.java b/java/dagger/hilt/EntryPoints.java index 3db77f903..32e231212 100644 --- a/java/dagger/hilt/EntryPoints.java +++ b/java/dagger/hilt/EntryPoints.java @@ -18,14 +18,10 @@ package dagger.hilt; import dagger.hilt.internal.GeneratedComponent; import dagger.hilt.internal.GeneratedComponentManager; -import dagger.hilt.internal.Preconditions; -import dagger.hilt.internal.TestSingletonComponent; -import java.lang.annotation.Annotation; import javax.annotation.Nonnull; /** Static utility methods for accessing objects through entry points. */ public final class EntryPoints { - private static final String EARLY_ENTRY_POINT = "dagger.hilt.android.EarlyEntryPoint"; /** * Returns the entry point interface given a component or component manager. Note that this @@ -43,20 +39,11 @@ public final class EntryPoints { @Nonnull public static <T> T get(Object component, Class<T> entryPoint) { if (component instanceof GeneratedComponent) { - if (component instanceof TestSingletonComponent) { - // @EarlyEntryPoint only has an effect in test environment, so we shouldn't fail in - // non-test cases. In addition, some of the validation requires the use of reflection, which - // we don't want to do in non-test cases anyway. - Preconditions.checkState( - !hasAnnotationReflection(entryPoint, EARLY_ENTRY_POINT), - "Interface, %s, annotated with @EarlyEntryPoint should be called with " - + "EarlyEntryPoints.get() rather than EntryPoints.get()", - entryPoint.getCanonicalName()); - } // Unsafe cast. There is no way for this method to know that the correct component was used. return entryPoint.cast(component); } else if (component instanceof GeneratedComponentManager) { - return get(((GeneratedComponentManager<?>) component).generatedComponent(), entryPoint); + // Unsafe cast. There is no way for this method to know that the correct component was used. + return entryPoint.cast(((GeneratedComponentManager<?>) component).generatedComponent()); } else { throw new IllegalStateException( String.format( @@ -65,15 +52,5 @@ public final class EntryPoints { } } - // Note: This method uses reflection but it should only be called in test environments. - private static boolean hasAnnotationReflection(Class<?> clazz, String annotationName) { - for (Annotation annotation : clazz.getAnnotations()) { - if (annotation.annotationType().getCanonicalName().contentEquals(annotationName)) { - return true; - } - } - return false; - } - private EntryPoints() {} } diff --git a/java/dagger/hilt/android/BUILD b/java/dagger/hilt/android/BUILD index d8833e77c..47d7e678c 100644 --- a/java/dagger/hilt/android/BUILD +++ b/java/dagger/hilt/android/BUILD @@ -14,7 +14,7 @@ # Description: # A library based on Hilt that provides standard components and automated injection for Android. -load("//:build_defs.bzl", "POM_VERSION") +load("//:build_defs.bzl", "POM_VERSION_ALPHA") load("//tools:maven.bzl", "gen_maven_artifact") package(default_visibility = ["//:src"]) @@ -41,12 +41,8 @@ android_library( "//java/dagger/hilt/internal:component_manager", "//java/dagger/hilt/internal:generated_entry_point", "//java/dagger/hilt/internal:preconditions", - "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], deps = [ ":package_info", @@ -63,28 +59,18 @@ android_library( "//java/dagger/hilt/processor/internal/root:plugin", ], exports = [ - ":activity_retained_lifecycle", "//:dagger_with_compiler", "//java/dagger/hilt:install_in", "//java/dagger/hilt/android/components", - "//java/dagger/hilt/android/internal/builders", "//java/dagger/hilt/android/internal/managers", "//java/dagger/hilt/android/internal/managers:component_supplier", "//java/dagger/hilt/android/internal/modules", - "//java/dagger/hilt/android/scopes", "//java/dagger/hilt/codegen:originating_element", "//java/dagger/hilt/internal:component_manager", "//java/dagger/hilt/internal:generated_component", "//java/dagger/hilt/internal:generated_entry_point", - "//java/dagger/hilt/internal/aggregatedroot", - "//java/dagger/hilt/internal/processedrootsentinel", "//java/dagger/hilt/migration:disable_install_in_check", - "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", - "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], deps = [ ":package_info", @@ -99,11 +85,7 @@ android_library( ":package_info", "//java/dagger/hilt:entry_point", "@google_bazel_common//third_party/java/jsr305_annotations", - "@maven//:androidx_activity_activity", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], ) @@ -117,27 +99,16 @@ android_library( ) android_library( - name = "early_entry_point", - srcs = [ - "EarlyEntryPoint.java", - "EarlyEntryPoints.java", - ], - exported_plugins = [ - "//java/dagger/hilt/processor/internal/aggregateddeps:plugin", - "//java/dagger/hilt/processor/internal/earlyentrypoint:processor", - ], - proguard_specs = ["proguard-rules.pro"], + name = "artifact-lib", + tags = ["maven_coordinates=com.google.dagger:hilt-android:" + POM_VERSION_ALPHA], exports = [ - "//java/dagger/hilt/android/internal/earlyentrypoint", - ], - deps = [ + ":android_entry_point", + ":entry_point_accessors", + ":hilt_android_app", ":package_info", - "//:dagger_with_compiler", - "//java/dagger/hilt:entry_point", - "//java/dagger/hilt/internal:component_manager", - "//java/dagger/hilt/internal:preconditions", - "//java/dagger/hilt/internal:test_singleton_component_manager", - "@google_bazel_common//third_party/java/jsr305_annotations", + "//java/dagger/hilt:artifact-core-lib", + "//java/dagger/hilt/android/migration:optional_inject", + "//java/dagger/lint:lint-android-artifact-lib", ], ) @@ -149,24 +120,9 @@ java_library( ], ) -android_library( - name = "artifact-lib", - tags = ["maven_coordinates=com.google.dagger:hilt-android:" + POM_VERSION], - exports = [ - ":android_entry_point", - ":early_entry_point", - ":entry_point_accessors", - ":hilt_android_app", - ":package_info", - "//java/dagger/hilt:artifact-core-lib", - "//java/dagger/hilt/android/migration:optional_inject", - "//java/dagger/lint:lint-android-artifact-lib", - ], -) - gen_maven_artifact( name = "artifact", - artifact_coordinates = "com.google.dagger:hilt-android:" + POM_VERSION, + artifact_coordinates = "com.google.dagger:hilt-android:" + POM_VERSION_ALPHA, artifact_name = "Hilt Android", artifact_target = ":artifact-lib", artifact_target_libs = [ @@ -174,13 +130,12 @@ gen_maven_artifact( "//java/dagger/hilt/android:activity_retained_lifecycle", "//java/dagger/hilt/android:android_entry_point", "//java/dagger/hilt/android:hilt_android_app", - "//java/dagger/hilt/android:early_entry_point", "//java/dagger/hilt/android:package_info", "//java/dagger/hilt/android/components", + "//java/dagger/hilt/android/components:view_model_component", "//java/dagger/hilt/android/components:package_info", "//java/dagger/hilt/android/internal", "//java/dagger/hilt/android/internal/builders", - "//java/dagger/hilt/android/internal/earlyentrypoint", "//java/dagger/hilt/android/internal/lifecycle", "//java/dagger/hilt/android/internal/managers", "//java/dagger/hilt/android/internal/managers:component_supplier", @@ -192,18 +147,16 @@ gen_maven_artifact( "//java/dagger/hilt/android/qualifiers", "//java/dagger/hilt/android/qualifiers:package_info", "//java/dagger/hilt/android/scopes", + "//java/dagger/hilt/android/scopes:activity_retained_scoped", + "//java/dagger/hilt/android/scopes:view_model_scoped", "//java/dagger/hilt/android/scopes:package_info", "//java/dagger/hilt/internal:component_entry_point", "//java/dagger/hilt/internal:generated_entry_point", - "//java/dagger/hilt/internal:test_singleton_component_manager", - "//java/dagger/hilt/internal/aggregatedroot:aggregatedroot", - "//java/dagger/hilt/internal/processedrootsentinel:processedrootsentinel", ], artifact_target_maven_deps = [ "androidx.activity:activity", "androidx.annotation:annotation", "androidx.fragment:fragment", - "androidx.lifecycle:lifecycle-common", "androidx.lifecycle:lifecycle-viewmodel", "androidx.lifecycle:lifecycle-viewmodel-savedstate", "androidx.savedstate:savedstate", @@ -230,10 +183,7 @@ gen_maven_artifact( manifest = "AndroidManifest.xml", packaging = "aar", proguard_specs = [ - "//java/dagger/hilt:proguard-rules.pro", - ":proguard-rules.pro", "//java/dagger/hilt/android/lifecycle:proguard-rules.pro", - "//java/dagger/hilt/internal:proguard-rules.pro", ], ) diff --git a/java/dagger/hilt/android/EarlyEntryPoint.java b/java/dagger/hilt/android/EarlyEntryPoint.java deleted file mode 100644 index 2a9845e2a..000000000 --- a/java/dagger/hilt/android/EarlyEntryPoint.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.android; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import dagger.internal.Beta; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * An escape hatch for when a Hilt entry point usage needs to be called before the singleton - * component is available in a Hilt test. - * - * <p>Warning: Please see documentation for more details: - * https://dagger.dev/hilt/early-entry-point - * - * <p>Usage: - * - * <p>To enable an existing entry point to be called early in a Hilt test, replace its - * {@link dagger.hilt.EntryPoint} annotation with {@link EarlyEntryPoint}. (Note that, - * {@link EarlyEntryPoint} is only allowed on entry points installed in the - * {@link dagger.hilt.components.SingletonComponent}). - * - * <pre><code> - * @EarlyEntryPoint // <- This replaces @EntryPoint - * @InstallIn(SingletonComponent.class) - * interface FooEntryPoint { - * Foo getFoo(); - * } - * </code></pre> - * - * <p>Then, replace any of the corresponding usages of {@link dagger.hilt.EntryPoints} with - * {@link EarlyEntryPoints}, as shown below: - * - * <pre><code> - * // EarlyEntryPoints.get() must be used with entry points annotated with @EarlyEntryPoint - * // This entry point can now be called at any point during a test, e.g. in Application.onCreate(). - * Foo foo = EarlyEntryPoints.get(appContext, FooEntryPoint.class).getFoo(); - * </code></pre> - */ -@Beta -@Retention(RUNTIME) // Needs to be runtime for checks in EntryPoints and EarlyEntryPoints. -@Target(ElementType.TYPE) -public @interface EarlyEntryPoint {} diff --git a/java/dagger/hilt/android/EarlyEntryPoints.java b/java/dagger/hilt/android/EarlyEntryPoints.java deleted file mode 100644 index e71401995..000000000 --- a/java/dagger/hilt/android/EarlyEntryPoints.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.android; - -import android.content.Context; -import dagger.hilt.EntryPoints; -import dagger.hilt.internal.GeneratedComponentManagerHolder; -import dagger.hilt.internal.Preconditions; -import dagger.hilt.internal.TestSingletonComponentManager; -import dagger.internal.Beta; -import java.lang.annotation.Annotation; -import javax.annotation.Nonnull; - -/** Static utility methods for accessing entry points annotated with {@link EarlyEntryPoint}. */ -@Beta -public final class EarlyEntryPoints { - - /** - * Returns the early entry point interface given a component manager holder. Note that this - * performs an unsafe cast and so callers should be sure that the given component/component - * manager matches the early entry point interface that is given. - * - * @param applicationContext The application context. - * @param entryPoint The interface marked with {@link EarlyEntryPoint}. The {@link - * dagger.hilt.InstallIn} annotation on this entry point should match the component argument - * above. - */ - // Note that the input is not statically declared to be a Component or ComponentManager to make - // this method easier to use, since most code will use this with an Application or Context type. - @Nonnull - public static <T> T get(Context applicationContext, Class<T> entryPoint) { - applicationContext = applicationContext.getApplicationContext(); - Preconditions.checkState( - applicationContext instanceof GeneratedComponentManagerHolder, - "Expected application context to implement GeneratedComponentManagerHolder. " - + "Check that you're passing in an application context that uses Hilt."); - Object componentManager = - ((GeneratedComponentManagerHolder) applicationContext).componentManager(); - if (componentManager instanceof TestSingletonComponentManager) { - Preconditions.checkState( - hasAnnotationReflection(entryPoint, EarlyEntryPoint.class), - "%s should be called with EntryPoints.get() rather than EarlyEntryPoints.get()", - entryPoint.getCanonicalName()); - Object earlyComponent = - ((TestSingletonComponentManager) componentManager).earlySingletonComponent(); - return entryPoint.cast(earlyComponent); - } - - // @EarlyEntryPoint only has an effect in test environment, so if this is not a test we - // delegate to EntryPoints. - return EntryPoints.get(applicationContext, entryPoint); - } - - // Note: This method uses reflection but it should only be called in test environments. - private static boolean hasAnnotationReflection( - Class<?> clazz, Class<? extends Annotation> annotationClazz) { - for (Annotation annotation : clazz.getAnnotations()) { - if (annotation.annotationType().equals(annotationClazz)) { - return true; - } - } - return false; - } - - private EarlyEntryPoints() {} -} diff --git a/java/dagger/hilt/android/components/BUILD b/java/dagger/hilt/android/components/BUILD index 655ccd199..3a307d145 100644 --- a/java/dagger/hilt/android/components/BUILD +++ b/java/dagger/hilt/android/components/BUILD @@ -25,7 +25,6 @@ android_library( "FragmentComponent.java", "ServiceComponent.java", "ViewComponent.java", - "ViewModelComponent.java", "ViewWithFragmentComponent.java", ], exports = [ @@ -35,7 +34,20 @@ android_library( ":package_info", "//java/dagger/hilt:define_component", "//java/dagger/hilt/android/scopes", + "//java/dagger/hilt/android/scopes:activity_retained_scoped", "//java/dagger/hilt/components", + "@google_bazel_common//third_party/java/jsr330_inject", + ], +) + +android_library( + name = "view_model_component", + srcs = ["ViewModelComponent.java"], + deps = [ + ":components", + ":package_info", + "//java/dagger/hilt:define_component", + "//java/dagger/hilt/android/scopes:view_model_scoped", ], ) diff --git a/java/dagger/hilt/android/internal/builders/BUILD b/java/dagger/hilt/android/internal/builders/BUILD index 9693f79c5..0e282be5f 100644 --- a/java/dagger/hilt/android/internal/builders/BUILD +++ b/java/dagger/hilt/android/internal/builders/BUILD @@ -24,10 +24,9 @@ android_library( "//:dagger_with_compiler", "//java/dagger/hilt:define_component", "//java/dagger/hilt/android/components", + "//java/dagger/hilt/android/components:view_model_component", "@maven//:androidx_activity_activity", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], ) diff --git a/java/dagger/hilt/android/internal/earlyentrypoint/AggregatedEarlyEntryPoint.java b/java/dagger/hilt/android/internal/earlyentrypoint/AggregatedEarlyEntryPoint.java deleted file mode 100644 index 124f831f3..000000000 --- a/java/dagger/hilt/android/internal/earlyentrypoint/AggregatedEarlyEntryPoint.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.android.internal.earlyentrypoint; - -import static java.lang.annotation.RetentionPolicy.CLASS; - -import java.lang.annotation.Retention; - -/** Holds aggregated data about {@link dagger.hilt.android.EarlyEntryPoint} elements. */ -@Retention(CLASS) -public @interface AggregatedEarlyEntryPoint { - - /** Returns the entry point annotated with {@link dagger.hilt.android.EarlyEntryPoint}. */ - String earlyEntryPoint(); -} diff --git a/java/dagger/hilt/android/internal/earlyentrypoint/BUILD b/java/dagger/hilt/android/internal/earlyentrypoint/BUILD deleted file mode 100644 index ab1478afe..000000000 --- a/java/dagger/hilt/android/internal/earlyentrypoint/BUILD +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2021 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. - -# Description: -# A processor that aggregates metadata about Hilt @EarlyEntryPoint annotations - -package(default_visibility = ["//:src"]) - -java_library( - name = "earlyentrypoint", - srcs = ["AggregatedEarlyEntryPoint.java"], -) diff --git a/java/dagger/hilt/android/internal/lifecycle/BUILD b/java/dagger/hilt/android/internal/lifecycle/BUILD index 17812c904..b3ecfc22e 100644 --- a/java/dagger/hilt/android/internal/lifecycle/BUILD +++ b/java/dagger/hilt/android/internal/lifecycle/BUILD @@ -25,12 +25,12 @@ android_library( "//java/dagger/hilt:entry_point", "//java/dagger/hilt:install_in", "//java/dagger/hilt/android/components", + "//java/dagger/hilt/android/components:view_model_component", "//java/dagger/hilt/android/internal/builders", "//java/dagger/hilt/android/qualifiers", "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", "@maven//:androidx_lifecycle_lifecycle_viewmodel", "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", "@maven//:androidx_savedstate_savedstate", diff --git a/java/dagger/hilt/internal/processedrootsentinel/ProcessedRootSentinel.java b/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java index e5e0b1b08..becce8cdb 100644 --- a/java/dagger/hilt/internal/processedrootsentinel/ProcessedRootSentinel.java +++ b/java/dagger/hilt/android/internal/lifecycle/DefaultActivityViewModelFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Dagger Authors. + * Copyright (C) 2020 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. @@ -14,17 +14,16 @@ * limitations under the License. */ -package dagger.hilt.internal.processedrootsentinel; +package dagger.hilt.android.internal.lifecycle; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.inject.Qualifier; -/** An annotation used to aggregate sentinels for processed roots. */ -@Target(ElementType.TYPE) +/** Qualifier for the default view model factory used by @AndroidEntryPoint annotated activities. */ +@Qualifier @Retention(RetentionPolicy.CLASS) -public @interface ProcessedRootSentinel { - /** Returns the set of roots processed in a previous build. */ - String[] roots(); -} +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) +public @interface DefaultActivityViewModelFactory {} diff --git a/java/dagger/hilt/internal/aggregatedroot/AggregatedRoot.java b/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java index b53ee7258..996705376 100644 --- a/java/dagger/hilt/internal/aggregatedroot/AggregatedRoot.java +++ b/java/dagger/hilt/android/internal/lifecycle/DefaultFragmentViewModelFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Dagger Authors. + * Copyright (C) 2020 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. @@ -14,21 +14,16 @@ * limitations under the License. */ -package dagger.hilt.internal.aggregatedroot; +package dagger.hilt.android.internal.lifecycle; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import javax.inject.Qualifier; -/** - * An annotation used to aggregate {@link dagger.hilt.android.HiltAndroidApp} and {@link - * dagger.hilt.android.testing.HiltAndroidTest} roots. - */ -@Target(ElementType.TYPE) +/** Qualifier for the default view model factory used by @AndroidEntryPoint annotated fragments. */ +@Qualifier @Retention(RetentionPolicy.CLASS) -public @interface AggregatedRoot { - String root(); - - Class<?> rootAnnotation(); -} +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) +public @interface DefaultFragmentViewModelFactory {} diff --git a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java index 448847cd3..427822dbb 100644 --- a/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java +++ b/java/dagger/hilt/android/internal/lifecycle/DefaultViewModelFactories.java @@ -50,11 +50,10 @@ public final class DefaultViewModelFactories { * * <p>Do not use except in Hilt generated code! */ - public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity, - ViewModelProvider.Factory delegateFactory) { + public static ViewModelProvider.Factory getActivityFactory(ComponentActivity activity) { return EntryPoints.get(activity, ActivityEntryPoint.class) .getHiltInternalFactoryFactory() - .fromActivity(activity, delegateFactory); + .fromActivity(activity); } /** @@ -62,11 +61,10 @@ public final class DefaultViewModelFactories { * * <p>Do not use except in Hilt generated code! */ - public static ViewModelProvider.Factory getFragmentFactory( - Fragment fragment, ViewModelProvider.Factory delegateFactory) { + public static ViewModelProvider.Factory getFragmentFactory(Fragment fragment) { return EntryPoints.get(fragment, FragmentEntryPoint.class) .getHiltInternalFactoryFactory() - .fromFragment(fragment, delegateFactory); + .fromFragment(fragment); } /** Internal factory for the Hilt ViewModel Factory. */ @@ -75,28 +73,33 @@ public final class DefaultViewModelFactories { private final Application application; private final Set<String> keySet; private final ViewModelComponentBuilder viewModelComponentBuilder; + @Nullable private final ViewModelProvider.Factory defaultActivityFactory; + @Nullable private final ViewModelProvider.Factory defaultFragmentFactory; @Inject InternalFactoryFactory( Application application, @HiltViewModelMap.KeySet Set<String> keySet, - ViewModelComponentBuilder viewModelComponentBuilder) { + ViewModelComponentBuilder viewModelComponentBuilder, + // These default factory bindings are temporary for the transition of deprecating + // the Hilt ViewModel extension for the built-in support + @DefaultActivityViewModelFactory Set<ViewModelProvider.Factory> defaultActivityFactorySet, + @DefaultFragmentViewModelFactory Set<ViewModelProvider.Factory> defaultFragmentFactorySet) { this.application = application; this.keySet = keySet; this.viewModelComponentBuilder = viewModelComponentBuilder; + this.defaultActivityFactory = getFactoryFromSet(defaultActivityFactorySet); + this.defaultFragmentFactory = getFactoryFromSet(defaultFragmentFactorySet); } - ViewModelProvider.Factory fromActivity( - ComponentActivity activity, ViewModelProvider.Factory delegateFactory) { - return getHiltViewModelFactory( - activity, + ViewModelProvider.Factory fromActivity(ComponentActivity activity) { + return getHiltViewModelFactory(activity, activity.getIntent() != null ? activity.getIntent().getExtras() : null, - delegateFactory); + defaultActivityFactory); } - ViewModelProvider.Factory fromFragment( - Fragment fragment, ViewModelProvider.Factory delegateFactory) { - return getHiltViewModelFactory(fragment, fragment.getArguments(), delegateFactory); + ViewModelProvider.Factory fromFragment(Fragment fragment) { + return getHiltViewModelFactory(fragment, fragment.getArguments(), defaultFragmentFactory); } private ViewModelProvider.Factory getHiltViewModelFactory( @@ -109,6 +112,24 @@ public final class DefaultViewModelFactories { return new HiltViewModelFactory( owner, defaultArgs, keySet, delegate, viewModelComponentBuilder); } + + @Nullable + private static ViewModelProvider.Factory getFactoryFromSet(Set<ViewModelProvider.Factory> set) { + // A multibinding set is used instead of BindsOptionalOf because Optional is not available in + // Android until API 24 and we don't want to have Guava as a transitive dependency. + if (set.isEmpty()) { + return null; + } + if (set.size() > 1) { + throw new IllegalStateException( + "At most one default view model factory is expected. Found " + set); + } + ViewModelProvider.Factory factory = set.iterator().next(); + if (factory == null) { + throw new IllegalStateException("Default view model factory must not be null."); + } + return factory; + } } /** The activity module to declare the optional factories. */ @@ -118,6 +139,14 @@ public final class DefaultViewModelFactories { @Multibinds @HiltViewModelMap.KeySet abstract Set<String> viewModelKeys(); + + @Multibinds + @DefaultActivityViewModelFactory + Set<ViewModelProvider.Factory> defaultActivityViewModelFactory(); + + @Multibinds + @DefaultFragmentViewModelFactory + Set<ViewModelProvider.Factory> defaultFragmentViewModelFactory(); } /** The activity entry point to retrieve the factory. */ diff --git a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java index 443894127..3b0d3bc43 100644 --- a/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java +++ b/java/dagger/hilt/android/internal/lifecycle/HiltViewModelFactory.java @@ -16,7 +16,6 @@ package dagger.hilt.android.internal.lifecycle; -import android.app.Activity; import androidx.lifecycle.AbstractSavedStateViewModelFactory; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; @@ -29,7 +28,6 @@ import dagger.Module; import dagger.hilt.EntryPoint; import dagger.hilt.EntryPoints; import dagger.hilt.InstallIn; -import dagger.hilt.android.components.ActivityComponent; import dagger.hilt.android.components.ViewModelComponent; import dagger.hilt.android.internal.builders.ViewModelComponentBuilder; import dagger.multibindings.Multibinds; @@ -111,28 +109,4 @@ public final class HiltViewModelFactory implements ViewModelProvider.Factory { return delegateFactory.create(modelClass); } } - - @EntryPoint - @InstallIn(ActivityComponent.class) - interface ActivityCreatorEntryPoint { - @HiltViewModelMap.KeySet - Set<String> getViewModelKeys(); - ViewModelComponentBuilder getViewModelComponentBuilder(); - } - - public static ViewModelProvider.Factory createInternal( - @NonNull Activity activity, - @NonNull SavedStateRegistryOwner owner, - @Nullable Bundle defaultArgs, - @NonNull ViewModelProvider.Factory delegateFactory) { - ActivityCreatorEntryPoint entryPoint = - EntryPoints.get(activity, ActivityCreatorEntryPoint.class); - return new HiltViewModelFactory( - owner, - defaultArgs, - entryPoint.getViewModelKeys(), - delegateFactory, - entryPoint.getViewModelComponentBuilder() - ); - } } diff --git a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java index 0af3dcd11..2d4a72bdd 100644 --- a/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java +++ b/java/dagger/hilt/android/internal/managers/ActivityRetainedComponentManager.java @@ -18,8 +18,6 @@ package dagger.hilt.android.internal.managers; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; -import androidx.lifecycle.ViewModelStoreOwner; -import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.activity.ComponentActivity; @@ -84,26 +82,23 @@ final class ActivityRetainedComponentManager private final Object componentLock = new Object(); ActivityRetainedComponentManager(ComponentActivity activity) { - this.viewModelProvider = getViewModelProvider(activity, activity.getApplication()); - } - - private ViewModelProvider getViewModelProvider( - ViewModelStoreOwner owner, Context applicationContext) { - return new ViewModelProvider( - owner, - new ViewModelProvider.Factory() { - @NonNull - @Override - @SuppressWarnings("unchecked") - public <T extends ViewModel> T create(@NonNull Class<T> aClass) { - ActivityRetainedComponent component = - EntryPoints.get( - applicationContext, ActivityRetainedComponentBuilderEntryPoint.class) - .retainedComponentBuilder() - .build(); - return (T) new ActivityRetainedComponentViewModel(component); - } - }); + this.viewModelProvider = + new ViewModelProvider( + activity, + new ViewModelProvider.Factory() { + @NonNull + @Override + @SuppressWarnings("unchecked") + public <T extends ViewModel> T create(@NonNull Class<T> aClass) { + ActivityRetainedComponent component = + EntryPoints.get( + activity.getApplication(), + ActivityRetainedComponentBuilderEntryPoint.class) + .retainedComponentBuilder() + .build(); + return (T) new ActivityRetainedComponentViewModel(component); + } + }); } @Override diff --git a/java/dagger/hilt/android/internal/managers/BUILD b/java/dagger/hilt/android/internal/managers/BUILD index 76613101e..3bc8df144 100644 --- a/java/dagger/hilt/android/internal/managers/BUILD +++ b/java/dagger/hilt/android/internal/managers/BUILD @@ -40,17 +40,17 @@ android_library( "//java/dagger/hilt:install_in", "//java/dagger/hilt/android:activity_retained_lifecycle", "//java/dagger/hilt/android/components", + "//java/dagger/hilt/android/components:view_model_component", "//java/dagger/hilt/android/internal", "//java/dagger/hilt/android/internal/builders", - "//java/dagger/hilt/android/scopes", + "//java/dagger/hilt/android/scopes:activity_retained_scoped", + "//java/dagger/hilt/android/scopes:view_model_scoped", "//java/dagger/hilt/internal:component_manager", "//java/dagger/hilt/internal:preconditions", "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], ) diff --git a/java/dagger/hilt/android/internal/managers/ViewComponentManager.java b/java/dagger/hilt/android/internal/managers/ViewComponentManager.java index 78614950a..cb2ece0c8 100644 --- a/java/dagger/hilt/android/internal/managers/ViewComponentManager.java +++ b/java/dagger/hilt/android/internal/managers/ViewComponentManager.java @@ -16,9 +16,6 @@ package dagger.hilt.android.internal.managers; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleEventObserver; -import androidx.lifecycle.LifecycleOwner; import android.content.Context; import android.content.ContextWrapper; import androidx.fragment.app.Fragment; @@ -107,7 +104,7 @@ public final class ViewComponentManager implements GeneratedComponentManager<Obj if (context instanceof FragmentContextWrapper) { FragmentContextWrapper fragmentContextWrapper = (FragmentContextWrapper) context; - return (GeneratedComponentManager<?>) fragmentContextWrapper.getFragment(); + return (GeneratedComponentManager<?>) fragmentContextWrapper.fragment; } else if (allowMissing) { // We didn't find anything, so return null if we're not supposed to fail. // The rest of the logic is just about getting a good error message. @@ -168,41 +165,22 @@ public final class ViewComponentManager implements GeneratedComponentManager<Obj * * <p>A wrapper class to expose the {@link Fragment} to the views they're inflating. */ + // This is only non-final for the account override public static final class FragmentContextWrapper extends ContextWrapper { - private Fragment fragment; private LayoutInflater baseInflater; private LayoutInflater inflater; - private final LifecycleEventObserver fragmentLifecycleObserver = - new LifecycleEventObserver() { - @Override - public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { - if (event == Lifecycle.Event.ON_DESTROY) { - // Prevent the fragment from leaking if the view outlives the fragment. - // See https://github.com/google/dagger/issues/2070 - FragmentContextWrapper.this.fragment = null; - FragmentContextWrapper.this.baseInflater = null; - FragmentContextWrapper.this.inflater = null; - } - } - }; - - FragmentContextWrapper(Context base, Fragment fragment) { + public final Fragment fragment; + + public FragmentContextWrapper(Context base, Fragment fragment) { super(Preconditions.checkNotNull(base)); this.baseInflater = null; this.fragment = Preconditions.checkNotNull(fragment); - this.fragment.getLifecycle().addObserver(fragmentLifecycleObserver); } - FragmentContextWrapper(LayoutInflater baseInflater, Fragment fragment) { + public FragmentContextWrapper(LayoutInflater baseInflater, Fragment fragment) { super(Preconditions.checkNotNull(Preconditions.checkNotNull(baseInflater).getContext())); this.baseInflater = baseInflater; this.fragment = Preconditions.checkNotNull(fragment); - this.fragment.getLifecycle().addObserver(fragmentLifecycleObserver); - } - - Fragment getFragment() { - Preconditions.checkNotNull(fragment, "The fragment has already been destroyed."); - return fragment; } @Override diff --git a/java/dagger/hilt/android/internal/modules/BUILD b/java/dagger/hilt/android/internal/modules/BUILD index c1b639dda..a36e23746 100644 --- a/java/dagger/hilt/android/internal/modules/BUILD +++ b/java/dagger/hilt/android/internal/modules/BUILD @@ -28,9 +28,6 @@ android_library( "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], ) diff --git a/java/dagger/hilt/android/internal/testing/BUILD b/java/dagger/hilt/android/internal/testing/BUILD index 98a273d12..b0bc361c7 100644 --- a/java/dagger/hilt/android/internal/testing/BUILD +++ b/java/dagger/hilt/android/internal/testing/BUILD @@ -21,6 +21,7 @@ java_library( name = "test_injector", testonly = 1, srcs = [ + "TestApplicationInjector.java", "TestInjector.java", ], ) @@ -36,23 +37,15 @@ android_library( ) android_library( - name = "early_test_singleton_component_creator", - testonly = 1, - srcs = ["EarlySingletonComponentCreator.java"], -) - -android_library( name = "test_application_component_manager", testonly = 1, srcs = ["TestApplicationComponentManager.java"], deps = [ - ":early_test_singleton_component_creator", ":test_component_data", ":test_injector", "//java/dagger/hilt/android/testing:on_component_ready_runner", "//java/dagger/hilt/internal:component_manager", "//java/dagger/hilt/internal:preconditions", - "//java/dagger/hilt/internal:test_singleton_component_manager", "@maven//:junit_junit", ], ) @@ -74,9 +67,6 @@ android_library( name = "test_application_component_manager_holder", testonly = 1, srcs = ["TestApplicationComponentManagerHolder.java"], - deps = [ - "//java/dagger/hilt/internal:component_manager", - ], ) android_library( diff --git a/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java b/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java deleted file mode 100644 index 8e61e4aff..000000000 --- a/java/dagger/hilt/android/internal/testing/EarlySingletonComponentCreator.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.android.internal.testing; - -import java.lang.reflect.InvocationTargetException; - -/** Creates a test's early component. */ -public abstract class EarlySingletonComponentCreator { - private static final String EARLY_SINGLETON_COMPONENT_CREATOR_IMPL = - "dagger.hilt.android.internal.testing.EarlySingletonComponentCreatorImpl"; - - static Object createComponent() { - try { - return Class.forName(EARLY_SINGLETON_COMPONENT_CREATOR_IMPL) - .asSubclass(EarlySingletonComponentCreator.class) - .getDeclaredConstructor() - .newInstance() - .create(); - } catch (ClassNotFoundException - | NoSuchMethodException - | IllegalAccessException - | InstantiationException - | InvocationTargetException e) { - throw new RuntimeException( - "The EarlyComponent was requested but does not exist. Check that you have annotated " - + "your test class with @HiltAndroidTest and that the processor is running over your " - + "test.", - e); - } - } - - /** Creates the early test component. */ - abstract Object create(); -} diff --git a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java index c2051170d..187887022 100644 --- a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java +++ b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManager.java @@ -21,7 +21,7 @@ import dagger.hilt.android.testing.OnComponentReadyRunner; import dagger.hilt.android.testing.OnComponentReadyRunner.OnComponentReadyRunnerHolder; import dagger.hilt.internal.GeneratedComponentManager; import dagger.hilt.internal.Preconditions; -import dagger.hilt.internal.TestSingletonComponentManager; +import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -35,18 +35,17 @@ import org.junit.runner.Description; * <p>A manager for the creation of components that live in the test Application. */ public final class TestApplicationComponentManager - implements TestSingletonComponentManager, OnComponentReadyRunnerHolder { + implements GeneratedComponentManager<Object>, OnComponentReadyRunnerHolder { - private final Object earlyComponentLock = new Object(); - private volatile Object earlyComponent = null; - - private final Object testComponentDataLock = new Object(); - private volatile TestComponentData testComponentData; + // This is a generated class that we always generate in a known location. + private static final String TEST_COMPONENT_DATA_SUPPLIER_IMPL = + "dagger.hilt.android.internal.testing.TestComponentDataSupplierImpl"; private final Application application; + private final Map<Class<?>, TestComponentData> testComponentDataSupplier; + private final AtomicReference<Object> component = new AtomicReference<>(); private final AtomicReference<Description> hasHiltTestRule = new AtomicReference<>(); - // TODO(bcorso): Consider using a lock here rather than ConcurrentHashMap to avoid b/37042460. private final Map<Class<?>, Object> registeredModules = new ConcurrentHashMap<>(); private final AtomicReference<Boolean> autoAddModuleEnabled = new AtomicReference<>(); private final AtomicReference<DelayedComponentState> delayedComponentState = @@ -76,18 +75,24 @@ public final class TestApplicationComponentManager public TestApplicationComponentManager(Application application) { this.application = application; - } - - @Override - public Object earlySingletonComponent() { - if (earlyComponent == null) { - synchronized (earlyComponentLock) { - if (earlyComponent == null) { - earlyComponent = EarlySingletonComponentCreator.createComponent(); - } - } + try { + this.testComponentDataSupplier = + Class.forName(TEST_COMPONENT_DATA_SUPPLIER_IMPL) + .asSubclass(TestComponentDataSupplier.class) + .getDeclaredConstructor() + .newInstance() + .get(); + } catch (ClassNotFoundException + | NoSuchMethodException + | IllegalAccessException + | InstantiationException + | InvocationTargetException e) { + throw new RuntimeException( + "Hilt classes generated from @HiltAndroidTest are missing. Check that you have annotated " + + "your test class with @HiltAndroidTest and that the processor is running over your " + + "test", + e); } - return earlyComponent; } @Override @@ -144,9 +149,6 @@ public final class TestApplicationComponentManager testInstance == null, "The Hilt BindValue instance cannot be set before Hilt's test rule has run."); Preconditions.checkState( - testComponentData == null, - "The testComponentData instance cannot be set before Hilt's test rule has run."); - Preconditions.checkState( registeredModules.isEmpty(), "The Hilt registered modules cannot be set before Hilt's test rule has run."); Preconditions.checkState( @@ -169,7 +171,6 @@ public final class TestApplicationComponentManager component.set(null); hasHiltTestRule.set(null); testInstance = null; - testComponentData = null; registeredModules.clear(); autoAddModuleEnabled.set(null); delayedComponentState.set(DelayedComponentState.NOT_DELAYED); @@ -307,14 +308,7 @@ public final class TestApplicationComponentManager } private TestComponentData testComponentData() { - if (testComponentData == null) { - synchronized (testComponentDataLock) { - if (testComponentData == null) { - testComponentData = TestComponentDataSupplier.get(testClass()); - } - } - } - return testComponentData; + return testComponentDataSupplier.get(testClass()); } private Class<?> testClass() { diff --git a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java index 4be5829eb..a8695c4fe 100644 --- a/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java +++ b/java/dagger/hilt/android/internal/testing/TestApplicationComponentManagerHolder.java @@ -16,8 +16,9 @@ package dagger.hilt.android.internal.testing; -import dagger.hilt.internal.GeneratedComponentManagerHolder; - /** For use by Hilt internally only! Returns the component manager. */ -// TODO(bcorso):Consider deleting this interface and just using GeneratedComponentManagerHolder -public interface TestApplicationComponentManagerHolder extends GeneratedComponentManagerHolder {} +public interface TestApplicationComponentManagerHolder { + // Returns {@link Object} so that we do not expose {@code TestApplicationComponentManager} to + // clients. Framework code should explicitly cast to {@code TestApplicationComponentManager}. + Object componentManager(); +} diff --git a/java/dagger/hilt/internal/TestSingletonComponent.java b/java/dagger/hilt/android/internal/testing/TestApplicationInjector.java index 730b70bef..c7ff5c9aa 100644 --- a/java/dagger/hilt/internal/TestSingletonComponent.java +++ b/java/dagger/hilt/android/internal/testing/TestApplicationInjector.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Dagger Authors. + * Copyright (C) 2020 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. @@ -14,7 +14,11 @@ * limitations under the License. */ -package dagger.hilt.internal; +package dagger.hilt.android.internal.testing; -/** A marker that the given component is a test {@code SingletonComponent}. */ -public interface TestSingletonComponent extends GeneratedComponent {} +/** + * Interface to expose a method for members injection for use in tests. + */ +public interface TestApplicationInjector<T> { + void injectApp(T t); +} diff --git a/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java b/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java index b6f8b0b45..e39073f9a 100644 --- a/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java +++ b/java/dagger/hilt/android/internal/testing/TestComponentDataSupplier.java @@ -16,50 +16,11 @@ package dagger.hilt.android.internal.testing; -import java.lang.reflect.InvocationTargetException; +import java.util.Map; -/** Stores the {@link TestComponentData} for a Hilt test class. */ +/** Stores the {@link TestComponentData} for all Hilt test classes. */ public abstract class TestComponentDataSupplier { - /** Returns a {@link TestComponentData}. */ - protected abstract TestComponentData get(); - - static TestComponentData get(Class<?> testClass) { - String generatedClassName = getEnclosedClassName(testClass) + "_TestComponentDataSupplier"; - try { - return Class.forName(generatedClassName) - .asSubclass(TestComponentDataSupplier.class) - .getDeclaredConstructor() - .newInstance() - .get(); - } catch (ClassNotFoundException - | NoSuchMethodException - | IllegalAccessException - | InstantiationException - | InvocationTargetException e) { - throw new RuntimeException( - String.format( - "Hilt test, %s, is missing generated file: %s. Check that the test class is " - + " annotated with @HiltAndroidTest and that the processor is running over your" - + " test.", - testClass.getSimpleName(), - generatedClassName), - e); - } - } - - private static String getEnclosedClassName(Class<?> testClass) { - StringBuilder sb = new StringBuilder(); - Class<?> currClass = testClass; - while (currClass != null) { - Class<?> enclosingClass = currClass.getEnclosingClass(); - if (enclosingClass != null) { - sb.insert(0, "_" + currClass.getSimpleName()); - } else { - sb.insert(0, currClass.getCanonicalName()); - } - currClass = enclosingClass; - } - return sb.toString(); - } + /** Returns a map of {@link TestComponentData} keyed by test class. */ + protected abstract Map<Class<?>, TestComponentData> get(); } diff --git a/java/dagger/hilt/android/internal/testing/root/BUILD b/java/dagger/hilt/android/internal/testing/root/BUILD deleted file mode 100644 index 96b161e32..000000000 --- a/java/dagger/hilt/android/internal/testing/root/BUILD +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (C) 2021 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. - -# Description: -# Internal Hilt android testing root - -package(default_visibility = ["//:src"]) - -android_library( - name = "default", - srcs = ["Default.java"], -) diff --git a/java/dagger/hilt/android/internal/testing/root/Default.java b/java/dagger/hilt/android/internal/testing/root/Default.java deleted file mode 100644 index 0aa1b16bc..000000000 --- a/java/dagger/hilt/android/internal/testing/root/Default.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.android.internal.testing.root; - -/** - * This is internal code. Do not depend on this class directly. - * - * <p>This is a "default" root (used only in tests) that generates a component without any test - * specific dependencies. - */ -final class Default {} diff --git a/java/dagger/hilt/android/internal/uninstallmodules/AggregatedUninstallModules.java b/java/dagger/hilt/android/internal/uninstallmodules/AggregatedUninstallModules.java deleted file mode 100644 index 0ab1f316f..000000000 --- a/java/dagger/hilt/android/internal/uninstallmodules/AggregatedUninstallModules.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.android.internal.uninstallmodules; - -import static java.lang.annotation.RetentionPolicy.CLASS; - -import java.lang.annotation.Retention; - -/** Holds aggregated data about {@link dagger.hilt.android.testing.UninstallModules} elements. */ -@Retention(CLASS) -public @interface AggregatedUninstallModules { - - /** Returns the test annotated with {@link dagger.hilt.android.testing.UninstallModules}. */ - String test(); - - /** Returns the list of modules to uninstall. */ - String[] uninstallModules(); -} diff --git a/java/dagger/hilt/android/internal/uninstallmodules/BUILD b/java/dagger/hilt/android/internal/uninstallmodules/BUILD deleted file mode 100644 index 583964b53..000000000 --- a/java/dagger/hilt/android/internal/uninstallmodules/BUILD +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (C) 2021 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. - -# Description: -# A processor that aggregates metadata about Hilt @UninstallModules annotations - -package(default_visibility = ["//:src"]) - -java_library( - name = "uninstallmodules", - testonly = 1, - srcs = ["AggregatedUninstallModules.java"], -) diff --git a/java/dagger/hilt/android/lifecycle/BUILD b/java/dagger/hilt/android/lifecycle/BUILD index 25b3e5e53..fd805102c 100644 --- a/java/dagger/hilt/android/lifecycle/BUILD +++ b/java/dagger/hilt/android/lifecycle/BUILD @@ -25,7 +25,7 @@ android_library( ], proguard_specs = ["proguard-rules.pro"], exports = [ - "//java/dagger/hilt/android/components", + "//java/dagger/hilt/android/components:view_model_component", "//java/dagger/hilt/android/internal/lifecycle", ], deps = [ diff --git a/java/dagger/hilt/android/migration/BUILD b/java/dagger/hilt/android/migration/BUILD index a42f0ac65..ade47c37f 100644 --- a/java/dagger/hilt/android/migration/BUILD +++ b/java/dagger/hilt/android/migration/BUILD @@ -33,9 +33,6 @@ android_library( "@maven//:androidx_activity_activity", "@maven//:androidx_annotation_annotation", "@maven//:androidx_fragment_fragment", - "@maven//:androidx_lifecycle_lifecycle_common", - "@maven//:androidx_lifecycle_lifecycle_viewmodel", - "@maven//:androidx_lifecycle_lifecycle_viewmodel_savedstate", ], ) diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle index 480c6f0a4..274ecbead 100644 --- a/java/dagger/hilt/android/plugin/build.gradle +++ b/java/dagger/hilt/android/plugin/build.gradle @@ -17,7 +17,6 @@ buildscript { repositories { google() - mavenCentral() jcenter() } } @@ -30,7 +29,6 @@ plugins { repositories { google() - mavenCentral() jcenter() } diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt index 3bbf1e190..e066f48a1 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt @@ -81,7 +81,6 @@ internal class AndroidEntryPointClassTransformer( if (entry.isClassFile()) { val clazz = classPool.makeClass(input, false) transformed = transformClassToOutput(clazz) || transformed - clazz.detach() } entry = input.nextEntry } @@ -100,9 +99,7 @@ internal class AndroidEntryPointClassTransformer( "Invalid file, '$inputFile' is not a class." } val clazz = inputFile.inputStream().use { classPool.makeClass(it, false) } - val transformed = transformClassToOutput(clazz) - clazz.detach() - return transformed + return transformClassToOutput(clazz) } private fun transformClassToOutput(clazz: CtClass): Boolean { diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt index a82826ed8..e26edb5fd 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt @@ -32,12 +32,10 @@ import com.android.build.gradle.api.UnitTestVariant import dagger.hilt.android.plugin.util.CopyTransform import dagger.hilt.android.plugin.util.SimpleAGPVersion import java.io.File -import javax.inject.Inject import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.attributes.Attribute -import org.gradle.api.provider.ProviderFactory /** * A Gradle plugin that checks if the project is an Android project and if so, registers a @@ -47,9 +45,7 @@ import org.gradle.api.provider.ProviderFactory * classes annotated with `@AndroidEntryPoint` since the registered transform by this plugin will * update the superclass. */ -class HiltGradlePlugin @Inject constructor( - val providers: ProviderFactory -) : Plugin<Project> { +class HiltGradlePlugin : Plugin<Project> { override fun apply(project: Project) { var configured = false project.plugins.withType(AndroidBasePlugin::class.java) { @@ -131,7 +127,6 @@ class HiltGradlePlugin @Inject constructor( } } - @Suppress("UnstableApiUsage") private fun configureVariantCompileClasspath( project: Project, hiltExtension: HiltExtension, @@ -165,7 +160,7 @@ class HiltGradlePlugin @Inject constructor( "android.injected.build.model.only.versioned", // Sent by AS 2.4+ "android.injected.build.model.feature.full.dependencies", // Sent by AS 2.4+ "android.injected.build.model.v2", // Sent by AS 4.2+ - ).any { providers.gradleProperty(it).forUseAtConfigurationTime().isPresent } + ).any { project.properties.containsKey(it) } ) { // Do not configure compile classpath when AndroidStudio is building the model (syncing) // otherwise it will cause a freeze. diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt index 339c83e1a..158043124 100644 --- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt +++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/SimpleAGPVersion.kt @@ -1,5 +1,7 @@ package dagger.hilt.android.plugin.util +import com.android.Version + /** * Simple Android Gradle Plugin version class since there is no public API one. b/175816217 */ @@ -18,18 +20,7 @@ internal data class SimpleAGPVersion( companion object { - // TODO(danysantiago): Migrate to AndroidPluginVersion once it is available (b/175816217) - val ANDROID_GRADLE_PLUGIN_VERSION by lazy { - val clazz = - findClass("com.android.Version") - ?: findClass("com.android.builder.model.Version") - if (clazz != null) { - return@lazy parse(clazz.getField("ANDROID_GRADLE_PLUGIN_VERSION").get(null) as String) - } - error( - "Unable to obtain AGP version. It is likely that the AGP version being used is too old." - ) - } + val ANDROID_GRADLE_PLUGIN_VERSION by lazy { parse(Version.ANDROID_GRADLE_PLUGIN_VERSION) } fun parse(version: String?) = tryParse(version) ?: error("Unable to parse AGP version: $version") @@ -46,11 +37,5 @@ internal data class SimpleAGPVersion( return SimpleAGPVersion(parts[0].toInt(), parts[1].toInt()) } - - private fun findClass(fqName: String) = try { - Class.forName(fqName) - } catch (ex: ClassNotFoundException) { - null - } } } diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt index cab50aab0..a003ab516 100644 --- a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt +++ b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt @@ -157,22 +157,28 @@ class IncrementalProcessorTest { genActivityInjector2 = File(projectRoot, "$GEN_SRC_DIR/simple/Activity2_GeneratedInjector.java") genAppInjectorDeps = File( projectRoot, - "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.java" + "$GEN_SRC_DIR/hilt_aggregated_deps/simple_SimpleApp_GeneratedInjectorModuleDeps.java" ) genActivityInjectorDeps1 = File( projectRoot, - "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.java" + "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Activity1_GeneratedInjectorModuleDeps.java" ) genActivityInjectorDeps2 = File( projectRoot, - "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.java" + "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Activity2_GeneratedInjectorModuleDeps.java" ) genModuleDeps1 = File( projectRoot, - "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Module1.java" + "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Module1ModuleDeps.java" + ) + genModuleDeps2 = File( + projectRoot, + "$GEN_SRC_DIR/hilt_aggregated_deps/simple_Module2ModuleDeps.java" + ) + genHiltComponents = File( + projectRoot, + "$GEN_SRC_DIR/simple/SimpleApp_HiltComponents.java" ) - genModuleDeps2 = File(projectRoot, "$GEN_SRC_DIR/hilt_aggregated_deps/_simple_Module2.java") - genHiltComponents = File(projectRoot, "$GEN_SRC_DIR/simple/SimpleApp_HiltComponents.java") genDaggerHiltApplicationComponent = File( projectRoot, "$GEN_SRC_DIR/simple/DaggerSimpleApp_HiltComponents_SingletonC.java" @@ -197,18 +203,24 @@ class IncrementalProcessorTest { ) classGenAppInjectorDeps = File( projectRoot, - "$CLASS_DIR/hilt_aggregated_deps/_simple_SimpleApp_GeneratedInjector.class" + "$CLASS_DIR/hilt_aggregated_deps/simple_SimpleApp_GeneratedInjectorModuleDeps.class" ) classGenActivityInjectorDeps1 = File( projectRoot, - "$CLASS_DIR/hilt_aggregated_deps/_simple_Activity1_GeneratedInjector.class" + "$CLASS_DIR/hilt_aggregated_deps/simple_Activity1_GeneratedInjectorModuleDeps.class" ) classGenActivityInjectorDeps2 = File( projectRoot, - "$CLASS_DIR/hilt_aggregated_deps/_simple_Activity2_GeneratedInjector.class" + "$CLASS_DIR/hilt_aggregated_deps/simple_Activity2_GeneratedInjectorModuleDeps.class" + ) + classGenModuleDeps1 = File( + projectRoot, + "$CLASS_DIR/hilt_aggregated_deps/simple_Module1ModuleDeps.class" + ) + classGenModuleDeps2 = File( + projectRoot, + "$CLASS_DIR/hilt_aggregated_deps/simple_Module2ModuleDeps.class" ) - classGenModuleDeps1 = File(projectRoot, "$CLASS_DIR/hilt_aggregated_deps/_simple_Module1.class") - classGenModuleDeps2 = File(projectRoot, "$CLASS_DIR/hilt_aggregated_deps/_simple_Module2.class") classGenHiltComponents = File( projectRoot, "$CLASS_DIR/simple/SimpleApp_HiltComponents.class" diff --git a/java/dagger/hilt/android/processor/BUILD b/java/dagger/hilt/android/processor/BUILD index ccf21033d..e116c4443 100644 --- a/java/dagger/hilt/android/processor/BUILD +++ b/java/dagger/hilt/android/processor/BUILD @@ -15,14 +15,14 @@ # Description: # Hilt android processors. -load("//:build_defs.bzl", "POM_VERSION") +load("//:build_defs.bzl", "POM_VERSION_ALPHA") load("//tools:maven.bzl", "gen_maven_artifact") package(default_visibility = ["//:src"]) java_library( name = "artifact-lib", - tags = ["maven_coordinates=com.google.dagger:hilt-android-compiler:" + POM_VERSION], + tags = ["maven_coordinates=com.google.dagger:hilt-android-compiler:" + POM_VERSION_ALPHA], visibility = ["//visibility:private"], exports = [ "//java/dagger/hilt/processor:artifact-lib-shared", @@ -31,23 +31,23 @@ java_library( gen_maven_artifact( name = "artifact", - artifact_coordinates = "com.google.dagger:hilt-android-compiler:" + POM_VERSION, + artifact_coordinates = "com.google.dagger:hilt-android-compiler:" + POM_VERSION_ALPHA, artifact_name = "Hilt Android Processor", artifact_target = ":artifact-lib", artifact_target_libs = [ "//java/dagger/hilt/android/processor/internal:android_classnames", "//java/dagger/hilt/android/processor/internal:utils", "//java/dagger/hilt/android/processor/internal/androidentrypoint:android_generators", + "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options", "//java/dagger/hilt/android/processor/internal/androidentrypoint:metadata", "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib", "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib", "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib", "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib", + "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib", "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib", - "//java/dagger/hilt/processor/internal:aggregated_elements", "//java/dagger/hilt/processor/internal:base_processor", "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:compiler_options", "//java/dagger/hilt/processor/internal:component_descriptor", "//java/dagger/hilt/processor/internal:component_names", "//java/dagger/hilt/processor/internal:components", @@ -55,22 +55,17 @@ gen_maven_artifact( "//java/dagger/hilt/processor/internal:processor_errors", "//java/dagger/hilt/processor/internal:processors", "//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies", - "//java/dagger/hilt/processor/internal/aggregateddeps:pkg_private_metadata", "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib", "//java/dagger/hilt/processor/internal/aliasof:alias_ofs", "//java/dagger/hilt/processor/internal/aliasof:processor_lib", "//java/dagger/hilt/processor/internal/definecomponent:define_components", "//java/dagger/hilt/processor/internal/definecomponent:processor_lib", - "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata", - "//java/dagger/hilt/processor/internal/earlyentrypoint:processor_lib", "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs", "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib", "//java/dagger/hilt/processor/internal/originatingelement:processor_lib", "//java/dagger/hilt/processor/internal/root:processor_lib", "//java/dagger/hilt/processor/internal/root:root_metadata", "//java/dagger/hilt/processor/internal/root:root_type", - "//java/dagger/hilt/processor/internal/uninstallmodules:processor_lib", - "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata", ], artifact_target_maven_deps = [ "com.google.auto:auto-common", diff --git a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java index d37092003..915ae519f 100644 --- a/java/dagger/hilt/android/processor/internal/AndroidClassNames.java +++ b/java/dagger/hilt/android/processor/internal/AndroidClassNames.java @@ -113,10 +113,5 @@ public final class AndroidClassNames { public static final ClassName SAVED_STATE_HANDLE = get("androidx.lifecycle", "SavedStateHandle"); - public static final ClassName ON_CONTEXT_AVAILABLE_LISTENER = - get("androidx.activity.contextaware", "OnContextAvailableListener"); - public static final ClassName INJECT_VIA_ON_CONTEXT_AVAILABLE_LISTENER = - get("dagger.hilt.android", "InjectViaOnContextAvailableListener"); - private AndroidClassNames() {} } diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java index 86fbaa712..ce9ad14c5 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ActivityGenerator.java @@ -17,9 +17,9 @@ package dagger.hilt.android.processor.internal.androidentrypoint; import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.TypeSpec; import com.squareup.javapoet.TypeVariableName; import dagger.hilt.android.processor.internal.AndroidClassNames; @@ -30,6 +30,7 @@ import javax.lang.model.element.Modifier; /** Generates an Hilt Activity class for the @AndroidEntryPoint annotated class. */ public final class ActivityGenerator { + private final ProcessingEnvironment env; private final AndroidEntryPointMetadata metadata; private final ClassName generatedClassName; @@ -55,11 +56,9 @@ public final class ActivityGenerator { Generators.addGeneratedBaseClassJavadoc(builder, AndroidClassNames.ANDROID_ENTRY_POINT); Processors.addGeneratedAnnotation(builder, env, getClass()); - Generators.copyConstructors( - metadata.baseElement(), - CodeBlock.builder().addStatement("_initHiltInternal()").build(), - builder); - builder.addMethod(init()); + Generators.copyConstructors(metadata.baseElement(), builder); + builder.addMethod(onCreate()); + metadata.baseElement().getTypeParameters().stream() .map(TypeVariableName::get) @@ -80,36 +79,29 @@ public final class ActivityGenerator { .writeTo(env.getFiler()); } - // private void init() { - // addOnContextAvailableListener(new OnContextAvailableListener() { - // @Override - // public void onContextAvailable(Context context) { - // inject(); - // } - // }); + // @CallSuper + // @Override + // protected void onCreate(@Nullable Bundle savedInstanceState) { + // inject(); + // super.onCreate(savedInstanceState); // } - private MethodSpec init() { - return MethodSpec.methodBuilder("_initHiltInternal") - .addModifiers(Modifier.PRIVATE) - .addStatement( - "addOnContextAvailableListener($L)", - TypeSpec.anonymousClassBuilder("") - .addSuperinterface(AndroidClassNames.ON_CONTEXT_AVAILABLE_LISTENER) - .addMethod( - MethodSpec.methodBuilder("onContextAvailable") - .addAnnotation(Override.class) - .addModifiers(Modifier.PUBLIC) - .addParameter(AndroidClassNames.CONTEXT, "context") - .addStatement("inject()") - .build()) + private MethodSpec onCreate() { + return MethodSpec.methodBuilder("onCreate") + .addAnnotation(AndroidClassNames.CALL_SUPER) + .addAnnotation(Override.class) + .addModifiers(Modifier.PROTECTED) + .addParameter( + ParameterSpec.builder(AndroidClassNames.BUNDLE, "savedInstanceState") + .addAnnotation(AndroidClassNames.NULLABLE) .build()) + .addStatement("inject()") + .addStatement("super.onCreate(savedInstanceState)") .build(); } // @Override // public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { - // return DefaultViewModelFactories.getActivityFactory( - // this, super.getDefaultViewModelProviderFactory()); + // return DefaultViewModelFactories.getActivityFactory(this); // } private MethodSpec getDefaultViewModelProviderFactory() { return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory") @@ -117,7 +109,7 @@ public final class ActivityGenerator { .addModifiers(Modifier.PUBLIC) .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY) .addStatement( - "return $T.getActivityFactory(this, super.getDefaultViewModelProviderFactory())", + "return $T.getActivityFactory(this)", AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES) .build(); } diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java index c94f6a977..e5868f861 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java @@ -16,7 +16,7 @@ package dagger.hilt.android.processor.internal.androidentrypoint; -import static dagger.hilt.processor.internal.HiltCompilerOptions.isAndroidSuperclassValidationDisabled; +import static dagger.hilt.android.processor.internal.androidentrypoint.HiltCompilerOptions.BooleanOption.DISABLE_ANDROID_SUPERCLASS_VALIDATION; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import com.google.auto.common.MoreElements; @@ -245,7 +245,7 @@ public abstract class AndroidEntryPointMetadata { final TypeElement baseElement; final ClassName generatedClassName; boolean requiresBytecodeInjection = - isAndroidSuperclassValidationDisabled(androidEntryPointElement, env) + DISABLE_ANDROID_SUPERCLASS_VALIDATION.get(env) && MoreTypes.isTypeOf(Void.class, androidEntryPointClassValue.asType()); if (requiresBytecodeInjection) { baseElement = MoreElements.asType(env.getTypeUtils().asElement(androidEntryPointElement.getSuperclass())); diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java index cd3290b48..7bb9b9afe 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointProcessor.java @@ -44,6 +44,11 @@ public final class AndroidEntryPointProcessor extends BaseProcessor { } @Override + public Set<String> getSupportedOptions() { + return HiltCompilerOptions.getProcessorOptions(); + } + + @Override public boolean delayErrors() { return true; } diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java index 8d63cdbea..f16e06dee 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/ApplicationGenerator.java @@ -38,13 +38,11 @@ public final class ApplicationGenerator { private final ProcessingEnvironment env; private final AndroidEntryPointMetadata metadata; private final ClassName wrapperClassName; - private final ComponentNames componentNames; public ApplicationGenerator(ProcessingEnvironment env, AndroidEntryPointMetadata metadata) { this.env = env; this.metadata = metadata; - this.wrapperClassName = metadata.generatedClassName(); - this.componentNames = ComponentNames.withoutRenaming(); + wrapperClassName = metadata.generatedClassName(); } // @Generated("ApplicationGenerator") @@ -109,7 +107,7 @@ public final class ApplicationGenerator { // } private TypeSpec creatorType() { ClassName component = - componentNames.generatedComponent( + ComponentNames.generatedComponent( metadata.elementClassName(), AndroidClassNames.SINGLETON_COMPONENT); return TypeSpec.anonymousClassBuilder("") .addSuperinterface(AndroidClassNames.COMPONENT_SUPPLIER) diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD index efbf9e47a..55e9ddc02 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/BUILD @@ -35,9 +35,11 @@ java_library( srcs = ["AndroidEntryPointProcessor.java"], deps = [ ":android_generators", + ":compiler_options", ":metadata", "//java/dagger/hilt/android/processor/internal:android_classnames", "//java/dagger/hilt/processor/internal:base_processor", + "//java/dagger/hilt/processor/internal:processors", "//java/dagger/internal/guava:collect", "@google_bazel_common//third_party/java/auto:service", "@google_bazel_common//third_party/java/incap", @@ -79,9 +81,9 @@ java_library( "AndroidEntryPointMetadata.java", ], deps = [ + ":compiler_options", "//java/dagger/hilt/android/processor/internal:android_classnames", "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:compiler_options", "//java/dagger/hilt/processor/internal:components", "//java/dagger/hilt/processor/internal:kotlin", "//java/dagger/hilt/processor/internal:processor_errors", @@ -96,6 +98,11 @@ java_library( ], ) +java_library( + name = "compiler_options", + srcs = ["HiltCompilerOptions.java"], +) + filegroup( name = "srcs_filegroup", srcs = glob(["*"]), diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java index 81b2b6156..4ef479f46 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/FragmentGenerator.java @@ -202,8 +202,7 @@ public final class FragmentGenerator { // @Override // public ViewModelProvider.Factory getDefaultViewModelProviderFactory() { - // return DefaultViewModelFactories.getFragmentFactory( - // this, super.getDefaultViewModelProviderFactory()); + // return DefaultViewModelFactories.getFragmentFactory(this); // } private MethodSpec getDefaultViewModelProviderFactory() { return MethodSpec.methodBuilder("getDefaultViewModelProviderFactory") @@ -211,7 +210,7 @@ public final class FragmentGenerator { .addModifiers(Modifier.PUBLIC) .returns(AndroidClassNames.VIEW_MODEL_PROVIDER_FACTORY) .addStatement( - "return $T.getFragmentFactory(this, super.getDefaultViewModelProviderFactory())", + "return $T.getFragmentFactory(this)", AndroidClassNames.DEFAULT_VIEW_MODEL_FACTORIES) .build(); } diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java index 91df537c9..daadd032d 100644 --- a/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/Generators.java @@ -319,12 +319,6 @@ final class Generators { .endControlFlow(); } - // Only add @Override if an ancestor extends a generated Hilt class. - // When using bytecode injection, this isn't always guaranteed. - if (metadata.overridesAndroidEntryPointClass() - && ancestorExtendsGeneratedHiltClass(metadata)) { - methodSpecBuilder.addAnnotation(Override.class); - } typeSpecBuilder.addField(injectedField(metadata)); switch (metadata.androidType()) { @@ -332,6 +326,12 @@ final class Generators { case FRAGMENT: case VIEW: case SERVICE: + // Only add @Override if an ancestor extends a generated Hilt class. + // When using bytecode injection, this isn't always guaranteed. + if (metadata.overridesAndroidEntryPointClass() + && ancestorExtendsGeneratedHiltClass(metadata)) { + methodSpecBuilder.addAnnotation(Override.class); + } methodSpecBuilder .beginControlFlow("if (!injected)") .addStatement("injected = true") diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java new file mode 100644 index 000000000..577410d81 --- /dev/null +++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/HiltCompilerOptions.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 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.hilt.android.processor.internal.androidentrypoint; + +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.processing.ProcessingEnvironment; + +/** Hilt annotation processor options. */ +// TODO(danysantiago): Consider consolidating with Dagger compiler options logic. +// TODO(user): Move this class to dagger/hilt/processor/internal +public final class HiltCompilerOptions { + + /** Processor options which can have true or false values. */ + public enum BooleanOption { + /** + * Flag that disables validating the superclass of @AndroidEntryPoint are Hilt_ generated, + * classes. This flag is to be used internally by the Gradle plugin, enabling the bytecode + * transformation to change the superclass. + */ + DISABLE_ANDROID_SUPERCLASS_VALIDATION( + "android.internal.disableAndroidSuperclassValidation", false), + + /** Flag that disables check on modules to be annotated with @InstallIn. */ + DISABLE_MODULES_HAVE_INSTALL_IN_CHECK("disableModulesHaveInstallInCheck", false); + + private final String name; + private final boolean defaultValue; + + BooleanOption(String name, boolean defaultValue) { + this.name = name; + this.defaultValue = defaultValue; + } + + public boolean get(ProcessingEnvironment env) { + String value = env.getOptions().get(getQualifiedName()); + if (value == null) { + return defaultValue; + } + // TODO(danysantiago): Strictly verify input, either 'true' or 'false' and nothing else. + return Boolean.parseBoolean(value); + } + + public String getQualifiedName() { + return "dagger.hilt." + name; + } + } + + public static Set<String> getProcessorOptions() { + return Arrays.stream(BooleanOption.values()) + .map(BooleanOption::getQualifiedName) + .collect(Collectors.toSet()); + } + + private HiltCompilerOptions() {} +} diff --git a/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java index 4f7f1bd6f..51b7ef466 100644 --- a/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java +++ b/java/dagger/hilt/android/processor/internal/customtestapplication/CustomTestApplicationGenerator.java @@ -109,7 +109,7 @@ final class CustomTestApplicationGenerator { return MethodSpec.methodBuilder("componentManager") .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) - .returns(ParameterizedTypeName.get(ClassNames.GENERATED_COMPONENT_MANAGER, TypeName.OBJECT)) + .returns(TypeName.OBJECT) .addStatement("return $N", COMPONENT_MANAGER) .build(); } diff --git a/java/dagger/hilt/processor/internal/uninstallmodules/BUILD b/java/dagger/hilt/android/processor/internal/uninstallmodules/BUILD index 4f944be48..73c46061e 100644 --- a/java/dagger/hilt/processor/internal/uninstallmodules/BUILD +++ b/java/dagger/hilt/android/processor/internal/uninstallmodules/BUILD @@ -20,14 +20,13 @@ package(default_visibility = ["//:src"]) java_plugin( name = "processor", generates_api = 1, - processor_class = "dagger.hilt.processor.internal.uninstallmodules.UninstallModulesProcessor", + processor_class = "dagger.hilt.android.processor.internal.uninstallmodules.UninstallModulesProcessor", deps = [":processor_lib"], ) java_library( name = "processor_lib", srcs = [ - "AggregatedUninstallModulesGenerator.java", "UninstallModulesProcessor.java", ], deps = [ @@ -39,26 +38,10 @@ java_library( "//java/dagger/internal/guava:collect", "@google_bazel_common//third_party/java/auto:service", "@google_bazel_common//third_party/java/incap", - "@google_bazel_common//third_party/java/javapoet", "@maven//:com_google_auto_auto_common", ], ) -java_library( - name = "aggregated_uninstall_modules_metadata", - srcs = [ - "AggregatedUninstallModulesMetadata.java", - ], - deps = [ - "//java/dagger/hilt/processor/internal:aggregated_elements", - "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:processors", - "//java/dagger/internal/codegen/extension", - "//java/dagger/internal/guava:collect", - "@google_bazel_common//third_party/java/auto:value", - ], -) - filegroup( name = "srcs_filegroup", srcs = glob(["*"]), diff --git a/java/dagger/hilt/processor/internal/uninstallmodules/UninstallModulesProcessor.java b/java/dagger/hilt/android/processor/internal/uninstallmodules/UninstallModulesProcessor.java index c7e528d9a..e92f0f0a4 100644 --- a/java/dagger/hilt/processor/internal/uninstallmodules/UninstallModulesProcessor.java +++ b/java/dagger/hilt/android/processor/internal/uninstallmodules/UninstallModulesProcessor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package dagger.hilt.processor.internal.uninstallmodules; +package dagger.hilt.android.processor.internal.uninstallmodules; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING; @@ -23,7 +23,6 @@ import com.google.auto.common.MoreElements; import com.google.auto.service.AutoService; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; -import com.squareup.javapoet.ClassName; import dagger.hilt.processor.internal.BaseProcessor; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.ProcessorErrors; @@ -41,7 +40,7 @@ public final class UninstallModulesProcessor extends BaseProcessor { @Override public Set<String> getSupportedAnnotationTypes() { - return ImmutableSet.of(ClassNames.UNINSTALL_MODULES.toString()); + return ImmutableSet.of(ClassNames.IGNORE_MODULES.toString()); } @Override @@ -57,24 +56,12 @@ public final class UninstallModulesProcessor extends BaseProcessor { ClassNames.HILT_ANDROID_TEST.simpleName(), element); - TypeElement testElement = MoreElements.asType(element); - ImmutableList<TypeElement> uninstallModules = - Processors.getAnnotationClassValues( - getElementUtils(), - Processors.getAnnotationMirror(testElement, ClassNames.UNINSTALL_MODULES), - "value"); - - checkModulesHaveInstallIn(testElement, uninstallModules); - checkModulesDontOriginateFromTest(testElement, uninstallModules); - - new AggregatedUninstallModulesGenerator(testElement, uninstallModules, getProcessingEnv()) - .generate(); - } - - private void checkModulesHaveInstallIn( - TypeElement testElement, ImmutableList<TypeElement> uninstallModules) { ImmutableList<TypeElement> invalidModules = - uninstallModules.stream() + Processors.getAnnotationClassValues( + getElementUtils(), + Processors.getAnnotationMirror(element, ClassNames.IGNORE_MODULES), + "value") + .stream() .filter( module -> !(Processors.hasAnnotation(module, ClassNames.MODULE) @@ -84,27 +71,10 @@ public final class UninstallModulesProcessor extends BaseProcessor { ProcessorErrors.checkState( invalidModules.isEmpty(), // TODO(b/152801981): Point to the annotation value rather than the annotated element. - testElement, - "@UninstallModules should only include modules annotated with both @Module and @InstallIn, " - + "but found: %s.", - invalidModules); - } - - private void checkModulesDontOriginateFromTest( - TypeElement testElement, ImmutableList<TypeElement> uninstallModules) { - ImmutableList<ClassName> invalidModules = - uninstallModules.stream() - .filter( - module -> - Processors.getOriginatingTestElement(module, getElementUtils()).isPresent()) - .map(ClassName::get) - .collect(toImmutableList()); - - ProcessorErrors.checkState( - invalidModules.isEmpty(), - // TODO(b/152801981): Point to the annotation value rather than the annotated element. - testElement, - "@UninstallModules should not contain test modules, but found: %s", + element, + "@%s should only include modules annotated with both @Module and @InstallIn, but found: " + + "%s.", + annotation.getSimpleName(), invalidModules); } } diff --git a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt index 1bc2e93bf..846f7d261 100644 --- a/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt +++ b/java/dagger/hilt/android/processor/internal/viewmodel/ViewModelModuleGenerator.kt @@ -93,11 +93,6 @@ internal class ViewModelModuleGenerator( component = AndroidClassNames.VIEW_MODEL_COMPONENT ) .addModifiers(Modifier.ABSTRACT) - .addMethod( - MethodSpec.constructorBuilder() - .addModifiers(Modifier.PRIVATE) - .build() - ) .addMethod(getViewModelBindsMethod()) .build() diff --git a/java/dagger/hilt/android/proguard-rules.pro b/java/dagger/hilt/android/proguard-rules.pro deleted file mode 100644 index 6fd3a82ea..000000000 --- a/java/dagger/hilt/android/proguard-rules.pro +++ /dev/null @@ -1,3 +0,0 @@ -# Keep for the reflective cast done in EntryPoints. -# See b/183070411#comment4 for more info. --keep,allowobfuscation,allowshrinking @dagger.hilt.android.EarlyEntryPoint class *
\ No newline at end of file diff --git a/java/dagger/hilt/android/scopes/BUILD b/java/dagger/hilt/android/scopes/BUILD index 5abc27e09..e74ac9e38 100644 --- a/java/dagger/hilt/android/scopes/BUILD +++ b/java/dagger/hilt/android/scopes/BUILD @@ -20,11 +20,9 @@ package(default_visibility = ["//:src"]) android_library( name = "scopes", srcs = [ - "ActivityRetainedScoped.java", "ActivityScoped.java", "FragmentScoped.java", "ServiceScoped.java", - "ViewModelScoped.java", "ViewScoped.java", ], deps = [ @@ -33,6 +31,24 @@ android_library( ], ) +android_library( + name = "activity_retained_scoped", + srcs = ["ActivityRetainedScoped.java"], + deps = [ + ":package_info", + "@google_bazel_common//third_party/java/jsr330_inject", + ], +) + +android_library( + name = "view_model_scoped", + srcs = ["ViewModelScoped.java"], + deps = [ + ":package_info", + "@google_bazel_common//third_party/java/jsr330_inject", + ], +) + java_library( name = "package_info", srcs = ["package-info.java"], diff --git a/java/dagger/hilt/android/testing/BUILD b/java/dagger/hilt/android/testing/BUILD index 93d4ceb94..47db287e5 100644 --- a/java/dagger/hilt/android/testing/BUILD +++ b/java/dagger/hilt/android/testing/BUILD @@ -14,7 +14,7 @@ # Description: # Testing libraries for Hilt Android. -load("//:build_defs.bzl", "POM_VERSION") +load("//:build_defs.bzl", "POM_VERSION_ALPHA") load("//tools:maven.bzl", "gen_maven_artifact") package(default_visibility = ["//:src"]) @@ -57,21 +57,14 @@ android_library( "//java/dagger/hilt/android/internal/builders", "//java/dagger/hilt/android/internal/managers", "//java/dagger/hilt/android/internal/modules", - "//java/dagger/hilt/android/internal/testing:early_test_singleton_component_creator", "//java/dagger/hilt/android/internal/testing:test_application_component_manager", "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder", - "//java/dagger/hilt/android/internal/testing:test_component_data", "//java/dagger/hilt/android/internal/testing:test_injector", - "//java/dagger/hilt/android/internal/testing/root:default", "//java/dagger/hilt/android/scopes", "//java/dagger/hilt/internal:component_entry_point", "//java/dagger/hilt/internal:component_manager", - "//java/dagger/hilt/internal:generated_component", "//java/dagger/hilt/internal:generated_entry_point", "//java/dagger/hilt/internal:preconditions", - "//java/dagger/hilt/internal:test_singleton_component", - "//java/dagger/hilt/internal/aggregatedroot", - "//java/dagger/hilt/internal/processedrootsentinel", "//java/dagger/hilt/migration:disable_install_in_check", "@maven//:androidx_annotation_annotation", "@maven//:androidx_multidex_multidex", @@ -130,14 +123,10 @@ android_library( testonly = 1, srcs = ["UninstallModules.java"], exported_plugins = [ - "//java/dagger/hilt/processor/internal/uninstallmodules:processor", - ], - exports = [ - "//java/dagger/hilt/android/internal/uninstallmodules", + "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor", ], deps = [ ":package_info", - "//java/dagger/hilt:generates_root_input", ], ) @@ -174,7 +163,7 @@ java_library( android_library( name = "artifact-lib", testonly = 1, - tags = ["maven_coordinates=com.google.dagger:hilt-android-testing:" + POM_VERSION], + tags = ["maven_coordinates=com.google.dagger:hilt-android-testing:" + POM_VERSION_ALPHA], exports = [ ":bind_value", ":custom_test_application", @@ -189,18 +178,15 @@ android_library( gen_maven_artifact( name = "artifact", testonly = 1, - artifact_coordinates = "com.google.dagger:hilt-android-testing:" + POM_VERSION, + artifact_coordinates = "com.google.dagger:hilt-android-testing:" + POM_VERSION_ALPHA, artifact_name = "Hilt Android Testing", artifact_target = ":artifact-lib", artifact_target_libs = [ - "//java/dagger/hilt/android/internal/testing:early_test_singleton_component_creator", "//java/dagger/hilt/android/internal/testing:mark_that_rules_ran_rule", "//java/dagger/hilt/android/internal/testing:test_application_component_manager", "//java/dagger/hilt/android/internal/testing:test_application_component_manager_holder", "//java/dagger/hilt/android/internal/testing:test_component_data", "//java/dagger/hilt/android/internal/testing:test_injector", - "//java/dagger/hilt/android/internal/testing/root:default", - "//java/dagger/hilt/android/internal/uninstallmodules:uninstallmodules", "//java/dagger/hilt/android/testing:bind_value", "//java/dagger/hilt/android/testing:custom_test_application", "//java/dagger/hilt/android/testing:hilt_android_rule", @@ -216,7 +202,6 @@ gen_maven_artifact( "androidx.activity:activity", "androidx.annotation:annotation", "androidx.fragment:fragment", - "androidx.lifecycle:lifecycle-common", "androidx.lifecycle:lifecycle-viewmodel", "androidx.lifecycle:lifecycle-viewmodel-savedstate", "androidx.multidex:multidex", diff --git a/java/dagger/hilt/android/testing/HiltTestApplication.java b/java/dagger/hilt/android/testing/HiltTestApplication.java index 293bfda4a..97eb4cbb2 100644 --- a/java/dagger/hilt/android/testing/HiltTestApplication.java +++ b/java/dagger/hilt/android/testing/HiltTestApplication.java @@ -40,7 +40,7 @@ public final class HiltTestApplication extends MultiDexApplication } @Override - public final GeneratedComponentManager<Object> componentManager() { + public final Object componentManager() { return componentManager; } diff --git a/java/dagger/hilt/android/testing/UninstallModules.java b/java/dagger/hilt/android/testing/UninstallModules.java index 607d76a68..6480c10f5 100644 --- a/java/dagger/hilt/android/testing/UninstallModules.java +++ b/java/dagger/hilt/android/testing/UninstallModules.java @@ -16,7 +16,6 @@ package dagger.hilt.android.testing; -import dagger.hilt.GeneratesRootInput; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @@ -44,7 +43,6 @@ import java.lang.annotation.Target; * } * </code></pre> */ -@GeneratesRootInput @Target({ElementType.TYPE}) public @interface UninstallModules { diff --git a/java/dagger/hilt/internal/BUILD b/java/dagger/hilt/internal/BUILD index ad4dbb5d4..dc245d1d7 100644 --- a/java/dagger/hilt/internal/BUILD +++ b/java/dagger/hilt/internal/BUILD @@ -18,20 +18,10 @@ package(default_visibility = ["//:src"]) java_library( - name = "test_singleton_component", - srcs = ["TestSingletonComponent.java"], - deps = [":generated_component"], -) - -java_library( name = "generated_component", - srcs = ["GeneratedComponent.java"], -) - -java_library( - name = "test_singleton_component_manager", - srcs = ["TestSingletonComponentManager.java"], - deps = [":component_manager"], + srcs = [ + "GeneratedComponent.java", + ], ) java_library( @@ -63,14 +53,12 @@ java_library( java_library( name = "component_entry_point", srcs = ["ComponentEntryPoint.java"], - proguard_specs = ["proguard-rules.pro"], deps = ["//java/dagger/hilt:generates_root_input"], ) java_library( name = "generated_entry_point", srcs = ["GeneratedEntryPoint.java"], - proguard_specs = ["proguard-rules.pro"], deps = ["//java/dagger/hilt:generates_root_input"], ) diff --git a/java/dagger/hilt/internal/TestSingletonComponentManager.java b/java/dagger/hilt/internal/TestSingletonComponentManager.java deleted file mode 100644 index 316a008cb..000000000 --- a/java/dagger/hilt/internal/TestSingletonComponentManager.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.internal; - -/** A marker that the given component manager is for an {@link TestSingletonComponent}. */ -public interface TestSingletonComponentManager extends GeneratedComponentManager<Object> { - Object earlySingletonComponent(); -} diff --git a/java/dagger/hilt/internal/aggregatedroot/BUILD b/java/dagger/hilt/internal/aggregatedroot/BUILD deleted file mode 100644 index 0a7263a28..000000000 --- a/java/dagger/hilt/internal/aggregatedroot/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2021 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. - -# Description: -# The annotation for aggregating information about Hilt roots. - -package(default_visibility = ["//:src"]) - -java_library( - name = "aggregatedroot", - srcs = ["AggregatedRoot.java"], -) - -filegroup( - name = "srcs_filegroup", - srcs = glob(["*"]), -) diff --git a/java/dagger/hilt/internal/aliasof/BUILD b/java/dagger/hilt/internal/aliasof/BUILD index 13d4364c3..3e96ed4d9 100644 --- a/java/dagger/hilt/internal/aliasof/BUILD +++ b/java/dagger/hilt/internal/aliasof/BUILD @@ -17,7 +17,7 @@ package(default_visibility = ["//:src"]) -java_library( +android_library( name = "aliasof", srcs = ["AliasOfPropagatedData.java"], ) diff --git a/java/dagger/hilt/internal/processedrootsentinel/BUILD b/java/dagger/hilt/internal/processedrootsentinel/BUILD deleted file mode 100644 index 70b72a6ab..000000000 --- a/java/dagger/hilt/internal/processedrootsentinel/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2021 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. - -# Description: -# The annotation for aggregating information about processed Hilt roots. - -package(default_visibility = ["//:src"]) - -java_library( - name = "processedrootsentinel", - srcs = ["ProcessedRootSentinel.java"], -) - -filegroup( - name = "srcs_filegroup", - srcs = glob(["*"]), -) diff --git a/java/dagger/hilt/internal/proguard-rules.pro b/java/dagger/hilt/internal/proguard-rules.pro deleted file mode 100644 index 2607ba1f3..000000000 --- a/java/dagger/hilt/internal/proguard-rules.pro +++ /dev/null @@ -1,4 +0,0 @@ -# Keep for the reflective cast done in EntryPoints. -# See b/183070411#comment4 for more info. --keep,allowobfuscation,allowshrinking @dagger.hilt.internal.ComponentEntryPoint class * --keep,allowobfuscation,allowshrinking @dagger.hilt.internal.GeneratedEntryPoint class *
\ No newline at end of file diff --git a/java/dagger/hilt/processor/BUILD b/java/dagger/hilt/processor/BUILD index 1f75d0a34..87adcf34f 100644 --- a/java/dagger/hilt/processor/BUILD +++ b/java/dagger/hilt/processor/BUILD @@ -15,7 +15,7 @@ # Description: # Hilt android processors. -load("//:build_defs.bzl", "POM_VERSION") +load("//:build_defs.bzl", "POM_VERSION_ALPHA") load("//tools:maven.bzl", "gen_maven_artifact") package(default_visibility = ["//:src"]) @@ -27,23 +27,22 @@ java_library( "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib", "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib", "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib", + "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib", "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib", "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib", "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib", "//java/dagger/hilt/processor/internal/aliasof:processor_lib", "//java/dagger/hilt/processor/internal/definecomponent:processor_lib", - "//java/dagger/hilt/processor/internal/earlyentrypoint:processor_lib", "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib", "//java/dagger/hilt/processor/internal/originatingelement:processor_lib", "//java/dagger/hilt/processor/internal/root:processor_lib", - "//java/dagger/hilt/processor/internal/uninstallmodules:processor_lib", "//java/dagger/internal/codegen:processor", ], ) java_library( name = "artifact-lib", - tags = ["maven_coordinates=com.google.dagger:hilt-compiler:" + POM_VERSION], + tags = ["maven_coordinates=com.google.dagger:hilt-compiler:" + POM_VERSION_ALPHA], visibility = ["//visibility:private"], exports = [ ":artifact-lib-shared", @@ -52,23 +51,23 @@ java_library( gen_maven_artifact( name = "artifact", - artifact_coordinates = "com.google.dagger:hilt-compiler:" + POM_VERSION, + artifact_coordinates = "com.google.dagger:hilt-compiler:" + POM_VERSION_ALPHA, artifact_name = "Hilt Processor", artifact_target = ":artifact-lib", artifact_target_libs = [ "//java/dagger/hilt/android/processor/internal:android_classnames", "//java/dagger/hilt/android/processor/internal:utils", "//java/dagger/hilt/android/processor/internal/androidentrypoint:android_generators", + "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options", "//java/dagger/hilt/android/processor/internal/androidentrypoint:metadata", "//java/dagger/hilt/android/processor/internal/androidentrypoint:processor_lib", "//java/dagger/hilt/android/processor/internal/bindvalue:bind_value_processor_lib", "//java/dagger/hilt/android/processor/internal/customtestapplication:processor_lib", "//java/dagger/hilt/android/processor/internal/viewmodel:validation_plugin_lib", + "//java/dagger/hilt/android/processor/internal/uninstallmodules:processor_lib", "//java/dagger/hilt/android/processor/internal/viewmodel:processor_lib", - "//java/dagger/hilt/processor/internal:aggregated_elements", "//java/dagger/hilt/processor/internal:base_processor", "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:compiler_options", "//java/dagger/hilt/processor/internal:component_descriptor", "//java/dagger/hilt/processor/internal:component_names", "//java/dagger/hilt/processor/internal:components", @@ -76,22 +75,17 @@ gen_maven_artifact( "//java/dagger/hilt/processor/internal:processor_errors", "//java/dagger/hilt/processor/internal:processors", "//java/dagger/hilt/processor/internal/aggregateddeps:component_dependencies", - "//java/dagger/hilt/processor/internal/aggregateddeps:pkg_private_metadata", "//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib", "//java/dagger/hilt/processor/internal/aliasof:alias_ofs", "//java/dagger/hilt/processor/internal/aliasof:processor_lib", "//java/dagger/hilt/processor/internal/definecomponent:define_components", "//java/dagger/hilt/processor/internal/definecomponent:processor_lib", - "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata", - "//java/dagger/hilt/processor/internal/earlyentrypoint:processor_lib", "//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs", "//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib", "//java/dagger/hilt/processor/internal/originatingelement:processor_lib", "//java/dagger/hilt/processor/internal/root:processor_lib", "//java/dagger/hilt/processor/internal/root:root_metadata", "//java/dagger/hilt/processor/internal/root:root_type", - "//java/dagger/hilt/processor/internal/uninstallmodules:processor_lib", - "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata", ], artifact_target_maven_deps = [ "com.google.auto:auto-common", diff --git a/java/dagger/hilt/processor/internal/AggregatedElements.java b/java/dagger/hilt/processor/internal/AggregatedElements.java deleted file mode 100644 index 8ee820fbd..000000000 --- a/java/dagger/hilt/processor/internal/AggregatedElements.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.common.MoreElements; -import com.google.common.collect.ImmutableSet; -import com.squareup.javapoet.ClassName; -import javax.lang.model.element.PackageElement; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** Utility class for aggregating metadata. */ -public final class AggregatedElements { - - /** Returns all aggregated elements in the aggregating package after validating them. */ - public static ImmutableSet<TypeElement> from( - String aggregatingPackage, ClassName aggregatingAnnotation, Elements elements) { - PackageElement packageElement = elements.getPackageElement(aggregatingPackage); - - if (packageElement == null) { - return ImmutableSet.of(); - } - - ImmutableSet<TypeElement> aggregatedElements = - packageElement.getEnclosedElements().stream() - .map(MoreElements::asType) - .collect(toImmutableSet()); - - ProcessorErrors.checkState( - !aggregatedElements.isEmpty(), - packageElement, - "No dependencies found. Did you remove code in package %s?", - packageElement); - - for (TypeElement aggregatedElement : aggregatedElements) { - ProcessorErrors.checkState( - Processors.hasAnnotation(aggregatedElement, aggregatingAnnotation), - aggregatedElement, - "Expected element, %s, to be annotated with @%s, but only found: %s.", - aggregatedElement.getSimpleName(), - aggregatingAnnotation, - aggregatedElement.getAnnotationMirrors()); - } - - return aggregatedElements; - } - - private AggregatedElements() {} -} diff --git a/java/dagger/hilt/processor/internal/AnnotationValues.java b/java/dagger/hilt/processor/internal/AnnotationValues.java index 9ebeeebca..584d8f950 100644 --- a/java/dagger/hilt/processor/internal/AnnotationValues.java +++ b/java/dagger/hilt/processor/internal/AnnotationValues.java @@ -18,19 +18,15 @@ package dagger.hilt.processor.internal; import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.AnnotationMirrors.getAnnotationValuesWithDefaults; -import static com.google.auto.common.MoreTypes.asTypeElement; import static com.google.common.base.Preconditions.checkNotNull; -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableSet; import java.util.List; import java.util.Optional; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; -import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; @@ -91,18 +87,6 @@ public final class AnnotationValues { } } - /** Returns a class array value as a set of {@link TypeElement}. */ - public static ImmutableSet<TypeElement> getTypeElements(AnnotationValue value) { - return getAnnotationValues(value).stream() - .map(AnnotationValues::getTypeElement) - .collect(toImmutableSet()); - } - - /** Returns a class value as a {@link TypeElement}. */ - public static TypeElement getTypeElement(AnnotationValue value) { - return asTypeElement(getTypeMirror(value)); - } - /** * Returns the value as a VariableElement. * @@ -112,13 +96,6 @@ public final class AnnotationValues { return EnumVisitor.INSTANCE.visit(value); } - /** Returns a string array value as a set of strings. */ - public static ImmutableSet<String> getStrings(AnnotationValue value) { - return getAnnotationValues(value).stream() - .map(AnnotationValues::getString) - .collect(toImmutableSet()); - } - /** * Returns the value as a string. * @@ -128,15 +105,6 @@ public final class AnnotationValues { return valueOfType(value, String.class); } - /** - * Returns the value as a boolean. - * - * @throws IllegalArgumentException if the value is not a boolean. - */ - public static boolean getBoolean(AnnotationValue value) { - return valueOfType(value, Boolean.class); - } - private static <T> T valueOfType(AnnotationValue annotationValue, Class<T> type) { Object value = annotationValue.getValue(); if (!type.isInstance(value)) { diff --git a/java/dagger/hilt/processor/internal/BUILD b/java/dagger/hilt/processor/internal/BUILD index 978655dea..baff1d823 100644 --- a/java/dagger/hilt/processor/internal/BUILD +++ b/java/dagger/hilt/processor/internal/BUILD @@ -24,7 +24,6 @@ java_library( "ProcessorErrorHandler.java", ], deps = [ - ":compiler_options", ":processor_errors", ":processors", "//java/dagger/internal/guava:base", @@ -63,9 +62,11 @@ java_library( ":processor_errors", "//java/dagger/internal/codegen/extension", "//java/dagger/internal/codegen/kotlin", + "//java/dagger/internal/codegen/langmodel", "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", "@google_bazel_common//third_party/java/javapoet", + "@google_bazel_common//third_party/java/jsr305_annotations", "@google_bazel_common//third_party/java/jsr330_inject", "@maven//:com_google_auto_auto_common", "@maven//:org_jetbrains_kotlin_kotlin_stdlib", @@ -89,34 +90,25 @@ java_library( "ComponentNames.java", ], deps = [ - ":classnames", ":processors", - "//java/dagger/internal/guava:base", - "//java/dagger/internal/guava:collect", "@google_bazel_common//third_party/java/javapoet", ], ) java_library( - name = "aggregated_elements", + name = "component_descriptor", srcs = [ - "AggregatedElements.java", + "ComponentDescriptor.java", + "ComponentGenerator.java", + "ComponentTree.java", ], deps = [ - ":processor_errors", + ":classnames", ":processors", "//java/dagger/internal/codegen/extension", + "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", - "@google_bazel_common//third_party/java/javapoet", - "@maven//:com_google_auto_auto_common", - ], -) - -java_library( - name = "component_descriptor", - srcs = ["ComponentDescriptor.java"], - deps = [ - "//java/dagger/internal/guava:collect", + "//java/dagger/internal/guava:graph", "@google_bazel_common//third_party/java/auto:value", "@google_bazel_common//third_party/java/javapoet", ], @@ -151,16 +143,6 @@ java_library( ], ) -java_library( - name = "compiler_options", - srcs = ["HiltCompilerOptions.java"], - deps = [ - ":processor_errors", - "//java/dagger/internal/guava:collect", - "@google_bazel_common//third_party/java/javapoet", - ], -) - filegroup( name = "srcs_filegroup", srcs = glob(["*"]), diff --git a/java/dagger/hilt/processor/internal/BadInputException.java b/java/dagger/hilt/processor/internal/BadInputException.java index f57a34a5e..d9617688e 100644 --- a/java/dagger/hilt/processor/internal/BadInputException.java +++ b/java/dagger/hilt/processor/internal/BadInputException.java @@ -36,11 +36,6 @@ public final class BadInputException extends RuntimeException { this.badElements = ImmutableList.copyOf(badElements); } - public BadInputException(String message) { - super(message); - this.badElements = ImmutableList.of(); - } - public ImmutableList<Element> getBadElements() { return badElements; } diff --git a/java/dagger/hilt/processor/internal/BaseProcessor.java b/java/dagger/hilt/processor/internal/BaseProcessor.java index 1a63f8b47..4961cd570 100644 --- a/java/dagger/hilt/processor/internal/BaseProcessor.java +++ b/java/dagger/hilt/processor/internal/BaseProcessor.java @@ -96,15 +96,6 @@ public abstract class BaseProcessor extends AbstractProcessor { private Messager messager; private ProcessorErrorHandler errorHandler; - @Override - public final Set<String> getSupportedOptions() { - // This is declared here rather than in the actual processors because KAPT will issue a - // warning if any used option is not unsupported. This can happen when there is a module - // which uses Hilt but lacks any @AndroidEntryPoint annotations. - // See: https://github.com/google/dagger/issues/2040 - return HiltCompilerOptions.getProcessorOptions(); - } - /** Used to perform initialization before each round of processing. */ protected void preRoundProcess(RoundEnvironment roundEnv) {}; diff --git a/java/dagger/hilt/processor/internal/ClassNames.java b/java/dagger/hilt/processor/internal/ClassNames.java index 093e1b3d5..234ea7b45 100644 --- a/java/dagger/hilt/processor/internal/ClassNames.java +++ b/java/dagger/hilt/processor/internal/ClassNames.java @@ -22,26 +22,6 @@ import com.squareup.javapoet.ClassName; /** Holder for commonly used class names. */ public final class ClassNames { - public static final String AGGREGATED_ROOT_PACKAGE = - "dagger.hilt.internal.aggregatedroot.codegen"; - public static final ClassName AGGREGATED_ROOT = - get("dagger.hilt.internal.aggregatedroot", "AggregatedRoot"); - public static final String PROCESSED_ROOT_SENTINEL_PACKAGE = - "dagger.hilt.internal.processedrootsentinel.codegen"; - public static final ClassName PROCESSED_ROOT_SENTINEL = - get("dagger.hilt.internal.processedrootsentinel", "ProcessedRootSentinel"); - - public static final String AGGREGATED_EARLY_ENTRY_POINT_PACKAGE = - "dagger.hilt.android.internal.earlyentrypoint.codegen"; - public static final ClassName AGGREGATED_EARLY_ENTRY_POINT = - get("dagger.hilt.android.internal.earlyentrypoint", "AggregatedEarlyEntryPoint"); - public static final ClassName EARLY_ENTRY_POINT = get("dagger.hilt.android", "EarlyEntryPoint"); - - public static final String AGGREGATED_UNINSTALL_MODULES_PACKAGE = - "dagger.hilt.android.internal.uninstallmodules.codegen"; - public static final ClassName AGGREGATED_UNINSTALL_MODULES = - get("dagger.hilt.android.internal.uninstallmodules", "AggregatedUninstallModules"); - public static final ClassName ORIGINATING_ELEMENT = get("dagger.hilt.codegen", "OriginatingElement"); public static final ClassName AGGREGATED_DEPS = @@ -52,11 +32,9 @@ public final class ClassNames { get("dagger.hilt.internal", "GeneratedComponentManager"); public static final ClassName GENERATED_COMPONENT_MANAGER_HOLDER = get("dagger.hilt.internal", "GeneratedComponentManagerHolder"); - public static final ClassName UNINSTALL_MODULES = + public static final ClassName IGNORE_MODULES = get("dagger.hilt.android.testing", "UninstallModules"); - public static final String DEFINE_COMPONENT_CLASSES_PACKAGE = - "dagger.hilt.processor.internal.definecomponent.codegen"; public static final ClassName DEFINE_COMPONENT = get("dagger.hilt", "DefineComponent"); public static final ClassName DEFINE_COMPONENT_BUILDER = get("dagger.hilt", "DefineComponent", "Builder"); @@ -101,8 +79,6 @@ public final class ClassNames { public static final ClassName ALIAS_OF = get("dagger.hilt.migration", "AliasOf"); public static final ClassName ALIAS_OF_PROPAGATED_DATA = get("dagger.hilt.internal.aliasof", "AliasOfPropagatedData"); - public static final String ALIAS_OF_PROPAGATED_DATA_PACKAGE = - "dagger.hilt.processor.internal.aliasof.codegen"; public static final ClassName GENERATES_ROOT_INPUT = get("dagger.hilt", "GeneratesRootInput"); public static final ClassName GENERATES_ROOT_INPUT_PROPAGATED_DATA = @@ -146,12 +122,12 @@ public final class ClassNames { get("dagger.hilt.android.internal.managers", "ComponentSupplier"); public static final ClassName APPLICATION_CONTEXT_MODULE = get("dagger.hilt.android.internal.modules", "ApplicationContextModule"); - public static final ClassName DEFAULT_ROOT = - ClassName.get("dagger.hilt.android.internal.testing.root", "Default"); public static final ClassName INTERNAL_TEST_ROOT = get("dagger.hilt.android.internal.testing", "InternalTestRoot"); public static final ClassName TEST_INJECTOR = get("dagger.hilt.android.internal.testing", "TestInjector"); + public static final ClassName TEST_APPLICATION_INJECTOR = + get("dagger.hilt.android.internal.testing", "TestApplicationInjector"); public static final ClassName TEST_APPLICATION_COMPONENT_MANAGER = get("dagger.hilt.android.internal.testing", "TestApplicationComponentManager"); public static final ClassName TEST_APPLICATION_COMPONENT_MANAGER_HOLDER = @@ -176,8 +152,6 @@ public final class ClassNames { get("dagger.hilt.android.testing", "BindValueIntoSet"); public static final ClassName APPLICATION_CONTEXT = get("dagger.hilt.android.qualifiers", "ApplicationContext"); - public static final ClassName TEST_SINGLETON_COMPONENT = - get("dagger.hilt.internal", "TestSingletonComponent"); public static final ClassName TEST_COMPONENT_DATA = get("dagger.hilt.android.internal.testing", "TestComponentData"); public static final ClassName TEST_COMPONENT_DATA_SUPPLIER = diff --git a/java/dagger/hilt/processor/internal/root/ComponentGenerator.java b/java/dagger/hilt/processor/internal/ComponentGenerator.java index 0e8d0ad80..3a4bf1e78 100644 --- a/java/dagger/hilt/processor/internal/root/ComponentGenerator.java +++ b/java/dagger/hilt/processor/internal/ComponentGenerator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package dagger.hilt.processor.internal.root; +package dagger.hilt.processor.internal; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static java.util.Comparator.comparing; @@ -28,8 +28,6 @@ import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; import java.io.IOException; import java.util.ArrayList; import java.util.Comparator; @@ -38,9 +36,11 @@ import java.util.Optional; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; /** Generates a Dagger component or subcomponent interface. */ -final class ComponentGenerator { +// TODO(bcorso): Make this non-public +public final class ComponentGenerator { private static final Joiner JOINER = Joiner.on("."); private static final Comparator<ClassName> SIMPLE_NAME_SORTER = Comparator.comparing((ClassName c) -> JOINER.join(c.simpleNames())) @@ -49,6 +49,7 @@ final class ComponentGenerator { private final ProcessingEnvironment processingEnv; private final ClassName name; + private final TypeElement rootElement; private final Optional<ClassName> superclass; private final ImmutableList<ClassName> modules; private final ImmutableList<TypeName> entryPoints; @@ -60,6 +61,7 @@ final class ComponentGenerator { public ComponentGenerator( ProcessingEnvironment processingEnv, ClassName name, + TypeElement rootElement, Optional<ClassName> superclass, Set<? extends ClassName> modules, Set<? extends TypeName> entryPoints, @@ -69,6 +71,7 @@ final class ComponentGenerator { Optional<TypeSpec> componentBuilder) { this.processingEnv = processingEnv; this.name = name; + this.rootElement = rootElement; this.superclass = superclass; this.modules = modules.stream().sorted(SIMPLE_NAME_SORTER).collect(toImmutableList()); this.entryPoints = entryPoints.stream().sorted(TYPE_NAME_SORTER).collect(toImmutableList()); @@ -78,24 +81,25 @@ final class ComponentGenerator { this.componentBuilder = componentBuilder; } - public TypeSpec.Builder typeSpecBuilder() throws IOException { - TypeSpec.Builder builder = + public TypeSpec generate() throws IOException { + TypeSpec.Builder generator = TypeSpec.classBuilder(name) // Public because components from a scope below must reference to create .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addOriginatingElement(rootElement) .addAnnotation(getComponentAnnotation()); - componentBuilder.ifPresent(builder::addType); + componentBuilder.ifPresent(generator::addType); - scopes.forEach(builder::addAnnotation); + scopes.forEach(generator::addAnnotation); - addEntryPoints(builder); + addEntryPoints(generator); - superclass.ifPresent(builder::superclass); + superclass.ifPresent(generator::superclass); - builder.addAnnotations(extraAnnotations); + generator.addAnnotations(extraAnnotations); - return builder; + return generator.build(); } /** Returns the component annotation with the list of modules to install for the component. */ @@ -156,6 +160,7 @@ final class ComponentGenerator { Processors.getEnclosedClassName(name), "_EntryPointPartition" + partitionIndex); TypeSpec.Builder builder = TypeSpec.interfaceBuilder(partitionName) + .addOriginatingElement(rootElement) .addModifiers(Modifier.ABSTRACT) .addSuperinterfaces(partition); diff --git a/java/dagger/hilt/processor/internal/ComponentNames.java b/java/dagger/hilt/processor/internal/ComponentNames.java index eeaa5f44b..fab4d1995 100644 --- a/java/dagger/hilt/processor/internal/ComponentNames.java +++ b/java/dagger/hilt/processor/internal/ComponentNames.java @@ -16,25 +16,7 @@ package dagger.hilt.processor.internal; -import static java.lang.Character.isUpperCase; -import static java.lang.String.format; -import static java.util.Comparator.comparing; - -import com.google.common.base.CharMatcher; -import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; -import com.google.common.collect.Multimaps; import com.squareup.javapoet.ClassName; -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import javax.lang.model.element.Name; -import javax.lang.model.element.TypeElement; /** * Utility class for getting the generated component name. @@ -42,45 +24,15 @@ import javax.lang.model.element.TypeElement; * <p>This should not be used externally. */ public final class ComponentNames { - private static final Splitter QUALIFIED_NAME_SPLITTER = Splitter.on('.'); - - private final boolean renameTestComponents; - private final String destinationPackage; - private final ImmutableMap<ClassName, String> simpleNameByClassName; - - public static ComponentNames withoutRenaming() { - return new ComponentNames( - /*renameTestComponents=*/ false, /*destinationPackage=*/ null, ImmutableMap.of()); - } - - public static ComponentNames withRenamingIntoPackage( - String destinationPackage, ImmutableList<TypeElement> roots) { - ImmutableMap.Builder<ClassName, String> builder = ImmutableMap.builder(); - ImmutableListMultimap<String, TypeElement> rootsBySimpleName = - Multimaps.index(roots, typeElement -> typeElement.getSimpleName().toString()); - rootsBySimpleName.asMap().values().stream() - .map(ComponentNames::disambiguateConflictingSimpleNames) - .forEach(builder::putAll); - return new ComponentNames(/*renameTestComponents=*/ true, destinationPackage, builder.build()); - } - - private ComponentNames( - boolean renameTestComponents, - String destinationPackage, - ImmutableMap<ClassName, String> simpleNameByClassName) { - this.renameTestComponents = renameTestComponents; - this.destinationPackage = destinationPackage; - this.simpleNameByClassName = simpleNameByClassName; - } + private ComponentNames() {} /** Returns the name of the generated component wrapper. */ - public ClassName generatedComponentsWrapper(ClassName root) { - return Processors.append( - Processors.getEnclosedClassName(maybeRenameComponent(root)), "_HiltComponents"); + public static ClassName generatedComponentsWrapper(ClassName root) { + return Processors.append(Processors.getEnclosedClassName(root), "_HiltComponents"); } /** Returns the name of the generated component. */ - public ClassName generatedComponent(ClassName root, ClassName component) { + public static ClassName generatedComponent(ClassName root, ClassName component) { return generatedComponentsWrapper(root).nestedClass(componentName(component)); } @@ -98,82 +50,4 @@ public final class ComponentNames { // Note: This uses regex matching so we only match if the name ends in "Component" return Processors.getEnclosedName(component).replaceAll("Component$", "C"); } - - /** - * Rewrites the provided HiltAndroidTest-annotated class name using the shared component - * directory. - */ - private ClassName maybeRenameComponent(ClassName className) { - return (renameTestComponents && !className.equals(ClassNames.DEFAULT_ROOT)) - ? ClassName.get(destinationPackage, dedupeSimpleName(className)) - : className; - } - - /** - * Derives a new generated component base name, should the simple names of two roots have - * conflicting simple names. - * - * <p>This is lifted nearly verbatim (albeit with new different struct types) from {@link - * dagger.internal.codegen.writing.SubcomponentNames}. - */ - private String dedupeSimpleName(ClassName className) { - Preconditions.checkState( - simpleNameByClassName.containsKey(className), - "Class name %s not found in simple name map", - className.canonicalName()); - return simpleNameByClassName.get(className); - } - - private static ImmutableMap<ClassName, String> disambiguateConflictingSimpleNames( - Collection<TypeElement> rootsWithConflictingNames) { - // If there's only 1 root there's nothing to disambiguate so return the simple name. - if (rootsWithConflictingNames.size() == 1) { - TypeElement root = Iterables.getOnlyElement(rootsWithConflictingNames); - return ImmutableMap.of(ClassName.get(root), root.getSimpleName().toString()); - } - - // There are conflicting simple names, so disambiguate them with a unique prefix. - // We keep them small to fix https://github.com/google/dagger/issues/421. - // Sorted in order to guarantee determinism if this is invoked by different processors. - ImmutableList<TypeElement> sortedRootsWithConflictingNames = - ImmutableList.sortedCopyOf( - comparing(typeElement -> typeElement.getQualifiedName().toString()), - rootsWithConflictingNames); - Set<String> usedNames = new HashSet<>(); - ImmutableMap.Builder<ClassName, String> uniqueNames = ImmutableMap.builder(); - for (TypeElement root : sortedRootsWithConflictingNames) { - String basePrefix = uniquingPrefix(root); - String uniqueName = basePrefix; - for (int differentiator = 2; !usedNames.add(uniqueName); differentiator++) { - uniqueName = basePrefix + differentiator; - } - uniqueNames.put(ClassName.get(root), format("%s_%s", uniqueName, root.getSimpleName())); - } - return uniqueNames.build(); - } - - /** Returns a prefix that could make the component's simple name more unique. */ - private static String uniquingPrefix(TypeElement typeElement) { - String containerName = typeElement.getEnclosingElement().getSimpleName().toString(); - - // If parent element looks like a class, use its initials as a prefix. - if (!containerName.isEmpty() && isUpperCase(containerName.charAt(0))) { - return CharMatcher.javaLowerCase().removeFrom(containerName); - } - - // Not in a normally named class. Prefix with the initials of the elements leading here. - Name qualifiedName = typeElement.getQualifiedName(); - Iterator<String> pieces = QUALIFIED_NAME_SPLITTER.split(qualifiedName).iterator(); - StringBuilder b = new StringBuilder(); - - while (pieces.hasNext()) { - String next = pieces.next(); - if (pieces.hasNext()) { - b.append(next.charAt(0)); - } - } - - // Note that a top level class in the root package will be prefixed "$_". - return b.length() > 0 ? b.toString() : "$"; - } } diff --git a/java/dagger/hilt/processor/internal/root/ComponentTree.java b/java/dagger/hilt/processor/internal/ComponentTree.java index fe3d4b133..6d2137a02 100644 --- a/java/dagger/hilt/processor/internal/root/ComponentTree.java +++ b/java/dagger/hilt/processor/internal/ComponentTree.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package dagger.hilt.processor.internal.root; +package dagger.hilt.processor.internal; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; @@ -27,18 +27,17 @@ import com.google.common.graph.Graphs; import com.google.common.graph.ImmutableGraph; import com.google.common.graph.MutableGraph; import com.squareup.javapoet.ClassName; -import dagger.hilt.processor.internal.ComponentDescriptor; import java.util.HashMap; import java.util.Map; import java.util.Set; /** A representation of the full tree of scopes. */ -final class ComponentTree { +public final class ComponentTree { private final ImmutableGraph<ComponentDescriptor> graph; private final ComponentDescriptor root; /** Creates a new tree from a set of descriptors. */ - static ComponentTree from(Set<ComponentDescriptor> descriptors) { + public static ComponentTree from(Set<ComponentDescriptor> descriptors) { MutableGraph<ComponentDescriptor> graph = GraphBuilder.directed().allowsSelfLoops(false).build(); @@ -89,19 +88,19 @@ final class ComponentTree { root = Iterables.getOnlyElement(roots); } - ImmutableSet<ComponentDescriptor> getComponentDescriptors() { + public ImmutableSet<ComponentDescriptor> getComponentDescriptors() { return ImmutableSet.copyOf(graph.nodes()); } - ImmutableSet<ComponentDescriptor> childrenOf(ComponentDescriptor componentDescriptor) { + public ImmutableSet<ComponentDescriptor> childrenOf(ComponentDescriptor componentDescriptor) { return ImmutableSet.copyOf(graph.successors(componentDescriptor)); } - ImmutableGraph<ComponentDescriptor> graph() { + public ImmutableGraph<ComponentDescriptor> graph() { return graph; } - ComponentDescriptor root() { + public ComponentDescriptor root() { return root; } } diff --git a/java/dagger/hilt/processor/internal/HiltCompilerOptions.java b/java/dagger/hilt/processor/internal/HiltCompilerOptions.java deleted file mode 100644 index 0d248239b..000000000 --- a/java/dagger/hilt/processor/internal/HiltCompilerOptions.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2020 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.hilt.processor.internal; - -import com.google.common.collect.ImmutableSet; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.TypeElement; - -/** Hilt annotation processor options. */ -// TODO(danysantiago): Consider consolidating with Dagger compiler options logic. -public final class HiltCompilerOptions { - - /** - * Returns {@code true} if the superclass validation is disabled for - * {@link dagger.hilt.android.AndroidEntryPoint}-annotated classes. - * - * This flag is for internal use only! The superclass validation checks that the super class is a - * generated {@code Hilt_} class. This flag is disabled by the Hilt Gradle plugin to enable - * bytecode transformation to change the superclass. - */ - public static boolean isAndroidSuperclassValidationDisabled( - TypeElement element, ProcessingEnvironment env) { - BooleanOption option = BooleanOption.DISABLE_ANDROID_SUPERCLASS_VALIDATION; - return option.get(env); - } - - /** - * Returns {@code true} if cross-compilation root validation is disabled. - * - * <p>This flag should rarely be needed, but may be used for legacy/migration purposes if - * tests require the use of {@link dagger.hilt.android.HiltAndroidApp} rather than - * {@link dagger.hilt.android.testing.HiltAndroidTest}. - * - * <p>Note that Hilt still does validation within a single compilation unit. In particular, - * a compilation unit that contains a {@code HiltAndroidApp} usage cannot have other - * {@code HiltAndroidApp} or {@code HiltAndroidTest} usages in the same compilation unit. - */ - public static boolean isCrossCompilationRootValidationDisabled( - ImmutableSet<TypeElement> rootElements, ProcessingEnvironment env) { - BooleanOption option = BooleanOption.DISABLE_CROSS_COMPILATION_ROOT_VALIDATION; - return option.get(env); - } - - /** Returns {@code true} if the check for {@link dagger.hilt.InstallIn} is disabled. */ - public static boolean isModuleInstallInCheckDisabled(ProcessingEnvironment env) { - return BooleanOption.DISABLE_MODULES_HAVE_INSTALL_IN_CHECK.get(env); - } - - /** - * Returns {@code true} of unit tests should try to share generated components, rather than using - * separate generated components per Hilt test root. - * - * <p>Tests that provide their own test bindings (e.g. using {@link - * dagger.hilt.android.testing.BindValue} or a test {@link dagger.Module}) cannot use the shared - * component. In these cases, a component will be generated for the test. - */ - public static boolean isSharedTestComponentsEnabled(ProcessingEnvironment env) { - return BooleanOption.SHARE_TEST_COMPONENTS.get(env); - } - - /** Processor options which can have true or false values. */ - private enum BooleanOption { - DISABLE_ANDROID_SUPERCLASS_VALIDATION( - "android.internal.disableAndroidSuperclassValidation", false), - - DISABLE_CROSS_COMPILATION_ROOT_VALIDATION("disableCrossCompilationRootValidation", false), - - DISABLE_MODULES_HAVE_INSTALL_IN_CHECK("disableModulesHaveInstallInCheck", false), - - SHARE_TEST_COMPONENTS("shareTestComponents", false); - - private final String name; - private final boolean defaultValue; - - BooleanOption(String name, boolean defaultValue) { - this.name = name; - this.defaultValue = defaultValue; - } - - boolean get(ProcessingEnvironment env) { - String value = env.getOptions().get(getQualifiedName()); - if (value == null) { - return defaultValue; - } - // TODO(danysantiago): Strictly verify input, either 'true' or 'false' and nothing else. - return Boolean.parseBoolean(value); - } - - String getQualifiedName() { - return "dagger.hilt." + name; - } - } - - public static Set<String> getProcessorOptions() { - return Arrays.stream(BooleanOption.values()) - .map(BooleanOption::getQualifiedName) - .collect(Collectors.toSet()); - } -} diff --git a/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java b/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java index 64d5892c4..ec5ddf871 100644 --- a/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java +++ b/java/dagger/hilt/processor/internal/KotlinMetadataUtils.java @@ -20,11 +20,7 @@ import dagger.Component; import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import javax.inject.Singleton; -/** - * A single-use provider of {@link KotlinMetadataUtil}. Since the returned util has a cache, it is - * better to reuse the same instance as much as possible, except for going across processor rounds - * because the cache contains elements. - */ +/** A single-use provider of {@link KotlinMetadataUtil}. */ // TODO(erichang): Revert this, should be wrapped with a Dagger module. public final class KotlinMetadataUtils { diff --git a/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java b/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java index 2ecc0f098..460e8a946 100644 --- a/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java +++ b/java/dagger/hilt/processor/internal/ProcessorErrorHandler.java @@ -62,9 +62,6 @@ final class ProcessorErrorHandler { if (t instanceof BadInputException) { BadInputException badInput = (BadInputException) t; - if (badInput.getBadElements().isEmpty()) { - hiltErrors.add(HiltError.of(badInput.getMessage())); - } for (Element element : badInput.getBadElements()) { hiltErrors.add(HiltError.of(badInput.getMessage(), element)); } diff --git a/java/dagger/hilt/processor/internal/ProcessorErrors.java b/java/dagger/hilt/processor/internal/ProcessorErrors.java index d75bb625e..b1578daa3 100644 --- a/java/dagger/hilt/processor/internal/ProcessorErrors.java +++ b/java/dagger/hilt/processor/internal/ProcessorErrors.java @@ -36,49 +36,6 @@ public final class ProcessorErrors { * involving any parameters to the calling method. * * @param expression a boolean expression - * @param errorMessage the exception message to use if the check fails; will be converted to a - * string using {@link String#valueOf(Object)} - * @throws BadInputException if {@code expression} is false - */ - public static void checkState( - boolean expression, - @Nullable Object errorMessage) { - if (!expression) { - throw new BadInputException(String.valueOf(errorMessage)); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling instance, but not - * involving any parameters to the calling method. - * - * @param expression a boolean expression - * @param errorMessageTemplate a template for the exception message should the check fail. The - * message is formed by replacing each {@code %s} placeholder in the template with an - * argument. These are matched by position - the first {@code %s} gets {@code - * errorMessageArgs[0]}, etc. Unmatched arguments will be appended to the formatted message in - * square braces. Unmatched placeholders will be left as-is. - * @param errorMessageArgs the arguments to be substituted into the message template. Arguments - * are converted to strings using {@link String#valueOf(Object)}. - * @throws BadInputException if {@code expression} is false - * @throws NullPointerException if the check fails and either {@code errorMessageTemplate} or - * {@code errorMessageArgs} is null (don't let this happen) - */ - @FormatMethod - public static void checkState( - boolean expression, - @Nullable @FormatString String errorMessageTemplate, - @Nullable Object... errorMessageArgs) { - if (!expression) { - throw new BadInputException(String.format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures the truth of an expression involving the state of the calling instance, but not - * involving any parameters to the calling method. - * - * @param expression a boolean expression * @param badElement the element that was at fault * @param errorMessage the exception message to use if the check fails; will be converted to a * string using {@link String#valueOf(Object)} diff --git a/java/dagger/hilt/processor/internal/Processors.java b/java/dagger/hilt/processor/internal/Processors.java index 8740bd6a0..b33c19d67 100644 --- a/java/dagger/hilt/processor/internal/Processors.java +++ b/java/dagger/hilt/processor/internal/Processors.java @@ -17,12 +17,10 @@ package dagger.hilt.processor.internal; import static com.google.auto.common.MoreElements.asPackage; -import static com.google.auto.common.MoreElements.asType; import static com.google.auto.common.MoreElements.asVariable; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.extension.DaggerCollectors.toOptional; import static javax.lang.model.element.Modifier.ABSTRACT; -import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import com.google.auto.common.AnnotationMirrors; @@ -43,7 +41,6 @@ import com.google.common.collect.Multimap; import com.google.common.collect.SetMultimap; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterSpec; import com.squareup.javapoet.ParameterizedTypeName; @@ -51,7 +48,6 @@ import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import dagger.internal.codegen.extension.DaggerStreams; import dagger.internal.codegen.kotlin.KotlinMetadataUtil; -import java.io.IOException; import java.lang.annotation.Annotation; import java.util.LinkedHashSet; import java.util.List; @@ -92,26 +88,6 @@ public final class Processors { private static final String JAVA_CLASS = "java.lang.Class"; - public static void generateAggregatingClass( - String aggregatingPackage, - AnnotationSpec aggregatingAnnotation, - TypeElement element, - Class<?> generatedAnnotationClass, - ProcessingEnvironment env) throws IOException { - ClassName name = ClassName.get(aggregatingPackage, "_" + getFullEnclosedName(element)); - TypeSpec.Builder builder = - TypeSpec.classBuilder(name) - .addModifiers(PUBLIC) - .addOriginatingElement(element) - .addAnnotation(aggregatingAnnotation) - .addJavadoc("This class should only be referenced by generated code!") - .addJavadoc("This class aggregates information across multiple compilations.\n");; - - addGeneratedAnnotation(builder, env, generatedAnnotationClass); - - JavaFile.builder(name.packageName(), builder.build()).build().writeTo(env.getFiler()); - } - /** Returns a map from {@link AnnotationMirror} attribute name to {@link AnnotationValue}s */ public static ImmutableMap<String, AnnotationValue> getAnnotationValues(Elements elements, AnnotationMirror annotation) { @@ -535,14 +511,6 @@ public final class Processors { return ClassName.get(className.packageName(), getEnclosedName(className)); } - /** - * Returns an equivalent class name with the {@code .} (dots) used for inner classes replaced with - * {@code _}. - */ - public static ClassName getEnclosedClassName(TypeElement typeElement) { - return getEnclosedClassName(ClassName.get(typeElement)); - } - /** Returns the fully qualified class name, with _ instead of . */ public static String getFullyQualifiedEnclosedClassName(ClassName className) { return className.packageName().replace('.', '_') + getEnclosedName(className); @@ -925,10 +893,7 @@ public final class Processors { return ElementFilter.methodsIn(elements.getAllMembers(module)).stream() .filter(Processors::isBindingMethod) .map(ExecutableElement::getModifiers) - .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC)) - // TODO(erichang): Getting a new KotlinMetadataUtil each time isn't great here, but until - // we have some sort of dependency management it will be difficult to share the instance. - && !KotlinMetadataUtils.getMetadataUtil().isObjectOrCompanionObjectClass(module); + .anyMatch(modifiers -> !modifiers.contains(ABSTRACT) && !modifiers.contains(STATIC)); } private static boolean isBindingMethod(ExecutableElement method) { @@ -971,27 +936,5 @@ public final class Processors { : typeName; } - public static Optional<TypeElement> getOriginatingTestElement( - Element element, Elements elements) { - TypeElement topLevelType = getOriginatingTopLevelType(element, elements); - return hasAnnotation(topLevelType, ClassNames.HILT_ANDROID_TEST) - ? Optional.of(asType(topLevelType)) - : Optional.empty(); - } - - private static TypeElement getOriginatingTopLevelType(Element element, Elements elements) { - TypeElement topLevelType = getTopLevelType(element); - if (hasAnnotation(topLevelType, ClassNames.ORIGINATING_ELEMENT)) { - return getOriginatingTopLevelType( - getAnnotationClassValue( - elements, - getAnnotationMirror(topLevelType, ClassNames.ORIGINATING_ELEMENT), - "topLevelClass"), - elements); - } - - return topLevelType; - } - private Processors() {} } diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java index 84fb5a1ee..bcc2dfe04 100644 --- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java +++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java @@ -19,6 +19,8 @@ package dagger.hilt.processor.internal.aggregateddeps; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.TypeSpec; import dagger.hilt.processor.internal.Processors; import java.io.IOException; import java.util.Optional; @@ -57,8 +59,20 @@ final class AggregatedDepsGenerator { } void generate() throws IOException { - Processors.generateAggregatingClass( - AGGREGATING_PACKAGE, aggregatedDepsAnnotation(), dependency, getClass(), processingEnv); + ClassName name = + ClassName.get( + AGGREGATING_PACKAGE, Processors.getFullEnclosedName(dependency) + "ModuleDeps"); + TypeSpec.Builder generator = + TypeSpec.classBuilder(name.simpleName()) + .addOriginatingElement(dependency) + .addAnnotation(aggregatedDepsAnnotation()) + .addJavadoc("Generated class to pass information through multiple javac runs.\n"); + + Processors.addGeneratedAnnotation(generator, processingEnv, getClass()); + + JavaFile.builder(name.packageName(), generator.build()) + .build() + .writeTo(processingEnv.getFiler()); } private AnnotationSpec aggregatedDepsAnnotation() { diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java deleted file mode 100644 index 09e1651fe..000000000 --- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.aggregateddeps; - -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.collect.Iterables.getOnlyElement; -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import java.util.Optional; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * A class that represents the values stored in an {@link - * dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation. - */ -@AutoValue -abstract class AggregatedDepsMetadata { - private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps"; - - enum DependencyType { - MODULE, - ENTRY_POINT, - COMPONENT_ENTRY_POINT - } - - abstract Optional<TypeElement> testElement(); - - abstract ImmutableSet<TypeElement> componentElements(); - - abstract DependencyType dependencyType(); - - abstract TypeElement dependency(); - - abstract ImmutableSet<TypeElement> replacedDependencies(); - - /** Returns all aggregated deps in the aggregating package. */ - public static ImmutableSet<AggregatedDepsMetadata> from(Elements elements) { - return AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static AggregatedDepsMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - return new AutoValue_AggregatedDepsMetadata( - getTestElement(values.get("test"), elements), - getComponents(values.get("components"), elements), - getDependencyType( - values.get("modules"), - values.get("entryPoints"), - values.get("componentEntryPoints")), - getDependency( - values.get("modules"), - values.get("entryPoints"), - values.get("componentEntryPoints"), - elements), - getReplacedDependencies(values.get("replaces"), elements)); - } - - private static Optional<TypeElement> getTestElement( - AnnotationValue testValue, Elements elements) { - checkNotNull(testValue); - String test = AnnotationValues.getString(testValue); - return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test)); - } - - private static ImmutableSet<TypeElement> getComponents( - AnnotationValue componentsValue, Elements elements) { - checkNotNull(componentsValue); - ImmutableSet<TypeElement> componentNames = - AnnotationValues.getAnnotationValues(componentsValue).stream() - .map(AnnotationValues::getString) - .map( - // This is a temporary hack to map the old ApplicationComponent to the new - // SingletonComponent. Technically, this is only needed for backwards compatibility - // with libraries using the old processor since new processors should convert to the - // new SingletonComponent when generating the metadata class. - componentName -> - componentName.contentEquals( - "dagger.hilt.android.components.ApplicationComponent") - ? ClassNames.SINGLETON_COMPONENT.canonicalName() - : componentName) - .map(elements::getTypeElement) - .collect(toImmutableSet()); - checkState(!componentNames.isEmpty()); - return componentNames; - } - - private static DependencyType getDependencyType( - AnnotationValue modulesValue, - AnnotationValue entryPointsValue, - AnnotationValue componentEntryPointsValue) { - checkNotNull(modulesValue); - checkNotNull(entryPointsValue); - checkNotNull(componentEntryPointsValue); - - ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder(); - if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) { - dependencyTypes.add(DependencyType.MODULE); - } - if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) { - dependencyTypes.add(DependencyType.ENTRY_POINT); - } - if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) { - dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT); - } - return getOnlyElement(dependencyTypes.build()); - } - - private static TypeElement getDependency( - AnnotationValue modulesValue, - AnnotationValue entryPointsValue, - AnnotationValue componentEntryPointsValue, - Elements elements) { - checkNotNull(modulesValue); - checkNotNull(entryPointsValue); - checkNotNull(componentEntryPointsValue); - - return elements.getTypeElement( - AnnotationValues.getString( - getOnlyElement( - ImmutableSet.<AnnotationValue>builder() - .addAll(AnnotationValues.getAnnotationValues(modulesValue)) - .addAll(AnnotationValues.getAnnotationValues(entryPointsValue)) - .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue)) - .build()))); - } - - private static ImmutableSet<TypeElement> getReplacedDependencies( - AnnotationValue replacedDependenciesValue, Elements elements) { - // Allow null values to support libraries using a Hilt version before @TestInstallIn was added - return replacedDependenciesValue == null - ? ImmutableSet.of() - : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream() - .map(AnnotationValues::getString) - .map(elements::getTypeElement) - .map(replacedDep -> getPublicDependency(replacedDep, elements)) - .collect(toImmutableSet()); - } - - /** Returns the public Hilt wrapper module, or the module itself if its already public. */ - private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) { - return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE) - .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString())) - .orElse(dependency); - } -} diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java index 151401e60..58c938f88 100644 --- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java +++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java @@ -20,7 +20,7 @@ import static com.google.auto.common.AnnotationMirrors.getAnnotationValue; import static com.google.auto.common.MoreElements.asType; import static com.google.auto.common.MoreElements.getPackage; import static com.google.common.collect.Iterables.getOnlyElement; -import static dagger.hilt.processor.internal.HiltCompilerOptions.isModuleInstallInCheckDisabled; +import static dagger.hilt.android.processor.internal.androidentrypoint.HiltCompilerOptions.BooleanOption.DISABLE_MODULES_HAVE_INSTALL_IN_CHECK; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static javax.lang.model.element.ElementKind.CLASS; @@ -37,8 +37,10 @@ import com.squareup.javapoet.ClassName; import dagger.hilt.processor.internal.BaseProcessor; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.Components; +import dagger.hilt.processor.internal.KotlinMetadataUtils; import dagger.hilt.processor.internal.ProcessorErrors; import dagger.hilt.processor.internal.Processors; +import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import java.util.HashSet; import java.util.List; import java.util.Optional; @@ -63,7 +65,6 @@ public final class AggregatedDepsProcessor extends BaseProcessor { private static final ImmutableSet<ClassName> ENTRY_POINT_ANNOTATIONS = ImmutableSet.of( ClassNames.ENTRY_POINT, - ClassNames.EARLY_ENTRY_POINT, ClassNames.GENERATED_ENTRY_POINT, ClassNames.COMPONENT_ENTRY_POINT); @@ -165,10 +166,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor { // Check that if Dagger needs an instance of the module, Hilt can provide it automatically by // calling a visible empty constructor. ProcessorErrors.checkState( - // Skip ApplicationContextModule, since Hilt manages this module internally. - ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module)) - || !Processors.requiresModuleInstance(getElementUtils(), module) - || hasVisibleEmptyConstructor(module), + !daggerRequiresModuleInstance(module) || hasVisibleEmptyConstructor(module), module, "Modules that need to be instantiated by Hilt must have a visible, empty constructor."); @@ -188,14 +186,13 @@ public final class AggregatedDepsProcessor extends BaseProcessor { ImmutableList<TypeElement> replacedModules = ImmutableList.of(); if (Processors.hasAnnotation(module, ClassNames.TEST_INSTALL_IN)) { - Optional<TypeElement> originatingTestElement = - Processors.getOriginatingTestElement(module, getElementUtils()); + Optional<TypeElement> originatingTestElement = getOriginatingTestElement(module); ProcessorErrors.checkState( !originatingTestElement.isPresent(), // TODO(b/152801981): this should really error on the annotation value module, "@TestInstallIn modules cannot be nested in (or originate from) a " - + "@HiltAndroidTest-annotated class: %s", + + "@HiltAndroidTest-annotated class: %s", originatingTestElement .map(testElement -> testElement.getQualifiedName().toString()) .orElse("")); @@ -265,10 +262,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor { // Prevent users from uninstalling test-specific @InstallIn modules. ImmutableList<TypeElement> replacedTestSpecificInstallIn = replacedModules.stream() - .filter( - replacedModule -> - Processors.getOriginatingTestElement(replacedModule, getElementUtils()) - .isPresent()) + .filter(replacedModule -> getOriginatingTestElement(replacedModule).isPresent()) .collect(toImmutableList()); ProcessorErrors.checkState( @@ -310,28 +304,6 @@ public final class AggregatedDepsProcessor extends BaseProcessor { element); TypeElement entryPoint = asType(element); - if (entryPointAnnotation.equals(ClassNames.EARLY_ENTRY_POINT)) { - ImmutableSet<ClassName> components = Components.getComponents(getElementUtils(), element); - ProcessorErrors.checkState( - components.equals(ImmutableSet.of(ClassNames.SINGLETON_COMPONENT)), - element, - "@EarlyEntryPoint can only be installed into the SingletonComponent. Found: %s", - components); - - Optional<TypeElement> optionalTestElement = - Processors.getOriginatingTestElement(element, getElementUtils()); - ProcessorErrors.checkState( - !optionalTestElement.isPresent(), - element, - "@EarlyEntryPoint-annotated entry point, %s, cannot be nested in (or originate from) " - + "a @HiltAndroidTest-annotated class, %s. This requirement is to avoid confusion " - + "with other, test-specific entry points.", - asType(element).getQualifiedName().toString(), - optionalTestElement - .map(testElement -> testElement.getQualifiedName().toString()) - .orElse("")); - } - generateAggregatedDeps( entryPointAnnotation.equals(ClassNames.COMPONENT_ENTRY_POINT) ? "componentEntryPoints" @@ -361,8 +333,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor { .generate(); } } else { - Optional<ClassName> testName = - Processors.getOriginatingTestElement(element, getElementUtils()).map(ClassName::get); + Optional<ClassName> testName = getOriginatingTestElement(element).map(ClassName::get); new AggregatedDepsGenerator( key, element, testName, components, replacedModules, getProcessingEnv()) .generate(); @@ -391,6 +362,25 @@ public final class AggregatedDepsProcessor extends BaseProcessor { return Optional.of(getOnlyElement(usedAnnotations)); } + private Optional<TypeElement> getOriginatingTestElement(Element element) { + TypeElement topLevelType = getOriginatingTopLevelType(element); + return Processors.hasAnnotation(topLevelType, ClassNames.HILT_ANDROID_TEST) + ? Optional.of(asType(topLevelType)) + : Optional.empty(); + } + + private TypeElement getOriginatingTopLevelType(Element element) { + TypeElement topLevelType = Processors.getTopLevelType(element); + if (Processors.hasAnnotation(topLevelType, ClassNames.ORIGINATING_ELEMENT)) { + return getOriginatingTopLevelType( + Processors.getAnnotationClassValue( + getElementUtils(), + Processors.getAnnotationMirror(topLevelType, ClassNames.ORIGINATING_ELEMENT), + "topLevelClass")); + } + return topLevelType; + } + private static boolean isValidKind(Element element) { // don't go down the rabbit hole of analyzing undefined types. N.B. we don't issue // an error here because javac already has and we don't want to spam the user. @@ -398,7 +388,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor { } private boolean installInCheckDisabled(Element element) { - return isModuleInstallInCheckDisabled(getProcessingEnv()) + return DISABLE_MODULES_HAVE_INSTALL_IN_CHECK.get(getProcessingEnv()) || Processors.hasAnnotation(element, ClassNames.DISABLE_INSTALL_IN_CHECK); } @@ -445,6 +435,27 @@ public final class AggregatedDepsProcessor extends BaseProcessor { || name.contentEquals("javax.annotation.processing.Generated"); } + private static boolean daggerRequiresModuleInstance(TypeElement module) { + return !module.getModifiers().contains(ABSTRACT) + && !hasOnlyStaticProvides(module) + // Skip ApplicationContextModule, since Hilt manages this module internally. + && !ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module)) + // Skip Kotlin object modules since all their provision methods are static + && !isKotlinObject(module); + } + + private static boolean isKotlinObject(TypeElement type) { + KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil(); + return metadataUtil.isObjectClass(type) || metadataUtil.isCompanionObjectClass(type); + } + + private static boolean hasOnlyStaticProvides(TypeElement module) { + // TODO(erichang): Check for @Produces too when we have a producers story + return ElementFilter.methodsIn(module.getEnclosedElements()).stream() + .filter(method -> Processors.hasAnnotation(method, ClassNames.PROVIDES)) + .allMatch(method -> method.getModifiers().contains(STATIC)); + } + private static boolean hasVisibleEmptyConstructor(TypeElement type) { List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements()); return constructors.isEmpty() diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/BUILD b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD index 5da2241f5..ebbc941bc 100644 --- a/java/dagger/hilt/processor/internal/aggregateddeps/BUILD +++ b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD @@ -39,13 +39,14 @@ java_library( "AggregatedDepsGenerator.java", "AggregatedDepsProcessor.java", "PkgPrivateEntryPointGenerator.java", + "PkgPrivateMetadata.java", "PkgPrivateModuleGenerator.java", ], deps = [ - ":pkg_private_metadata", + "//:dagger_with_compiler", + "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options", "//java/dagger/hilt/processor/internal:base_processor", "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:compiler_options", "//java/dagger/hilt/processor/internal:components", "//java/dagger/hilt/processor/internal:kotlin", "//java/dagger/hilt/processor/internal:processor_errors", @@ -54,21 +55,10 @@ java_library( "//java/dagger/internal/codegen/kotlin", "//java/dagger/internal/guava:collect", "@google_bazel_common//third_party/java/auto:service", - "@google_bazel_common//third_party/java/incap", - "@google_bazel_common//third_party/java/javapoet", - "@maven//:com_google_auto_auto_common", - ], -) - -java_library( - name = "pkg_private_metadata", - srcs = ["PkgPrivateMetadata.java"], - deps = [ - "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:kotlin", - "//java/dagger/hilt/processor/internal:processors", "@google_bazel_common//third_party/java/auto:value", + "@google_bazel_common//third_party/java/incap", "@google_bazel_common//third_party/java/javapoet", + "@google_bazel_common//third_party/java/jsr250_annotations", "@maven//:com_google_auto_auto_common", ], ) @@ -76,17 +66,14 @@ java_library( java_library( name = "component_dependencies", srcs = [ - "AggregatedDepsMetadata.java", "ComponentDependencies.java", ], deps = [ - ":pkg_private_metadata", - "//java/dagger/hilt/processor/internal:aggregated_elements", + ":processor_lib", "//java/dagger/hilt/processor/internal:classnames", "//java/dagger/hilt/processor/internal:component_descriptor", + "//java/dagger/hilt/processor/internal:processor_errors", "//java/dagger/hilt/processor/internal:processors", - "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata", - "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata", "//java/dagger/internal/codegen/extension", "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java index 897ffd144..23ed148c0 100644 --- a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java +++ b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java @@ -16,17 +16,36 @@ package dagger.hilt.processor.internal.aggregateddeps; +import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.getOnlyElement; +import static dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsGenerator.AGGREGATING_PACKAGE; +import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import com.google.auto.value.AutoValue; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; +import com.google.common.collect.SetMultimap; import com.squareup.javapoet.ClassName; +import dagger.hilt.processor.internal.AnnotationValues; +import dagger.hilt.processor.internal.BadInputException; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.ComponentDescriptor; -import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata; -import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata; +import dagger.hilt.processor.internal.ProcessorErrors; +import dagger.hilt.processor.internal.Processors; +import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies.AggregatedDepMetadata.DependencyType; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; @@ -46,23 +65,6 @@ public abstract class ComponentDependencies { /** Returns the component entry point associated with the given a component. */ public abstract Dependencies componentEntryPoints(); - /** Returns the set of early entry points */ - public abstract ImmutableSet<ClassName> earlyEntryPoints(); - - /** Returns {@code true} if any entry points are annotated with {@code EarlyEntryPoints}. */ - public boolean hasEarlyEntryPoints() { - return !earlyEntryPoints().isEmpty(); - } - - /** - * Returns {@code true} if the test binds or uninstalls test-specific bindings that would prevent - * it from sharing components with other test roots. - */ - public final boolean includesTestDeps(ClassName root) { - return modules().testDeps().keySet().stream().anyMatch((key) -> key.test().equals(root)) - || modules().uninstalledTestDeps().containsKey(root); - } - @AutoValue.Builder abstract static class Builder { abstract Dependencies.Builder modulesBuilder(); @@ -71,9 +73,12 @@ public abstract class ComponentDependencies { abstract Dependencies.Builder componentEntryPointsBuilder(); - abstract ImmutableSet.Builder<ClassName> earlyEntryPointsBuilder(); + abstract ComponentDependencies autoBuild(); - abstract ComponentDependencies build(); + ComponentDependencies build(Elements elements) { + validateModules(modulesBuilder().build(), elements); + return autoBuild(); + } } /** A key used for grouping a test dependency by both its component and test name. */ @@ -118,17 +123,6 @@ public abstract class ComponentDependencies { /** Returns the global uninstalled test deps. */ abstract ImmutableSet<TypeElement> globalUninstalledTestDeps(); - /** Returns the dependencies to be installed in the global singleton component. */ - ImmutableSet<TypeElement> getGlobalSingletonDeps() { - return ImmutableSet.<TypeElement>builder() - .addAll( - globalDeps().get(ClassNames.SINGLETON_COMPONENT).stream() - .filter(dep -> !globalUninstalledTestDeps().contains(dep)) - .collect(toImmutableSet())) - .addAll(globalTestDeps().get(ClassNames.SINGLETON_COMPONENT)) - .build(); - } - /** Returns the dependencies to be installed in the given component for the given root. */ public ImmutableSet<TypeElement> get(ClassName component, ClassName root, boolean isTestRoot) { if (!isTestRoot) { @@ -181,10 +175,14 @@ public abstract class ComponentDependencies { */ public static ComponentDependencies from( ImmutableSet<ComponentDescriptor> descriptors, Elements elements) { - ImmutableSet<ClassName> componentNames = - descriptors.stream().map(ComponentDescriptor::component).collect(toImmutableSet()); + Map<String, ComponentDescriptor> descriptorLookup = descriptorLookupMap(descriptors); + ImmutableList<AggregatedDepMetadata> metadatas = + getAggregatedDeps(elements).stream() + .map(deps -> AggregatedDepMetadata.create(deps, descriptorLookup, elements)) + .collect(toImmutableList()); + ComponentDependencies.Builder componentDependencies = ComponentDependencies.builder(); - for (AggregatedDepsMetadata metadata : AggregatedDepsMetadata.from(elements)) { + for (AggregatedDepMetadata metadata : metadatas) { Dependencies.Builder builder = null; switch (metadata.dependencyType()) { case MODULE: @@ -197,46 +195,265 @@ public abstract class ComponentDependencies { builder = componentDependencies.componentEntryPointsBuilder(); break; } - for (TypeElement componentElement : metadata.componentElements()) { - ClassName componentName = ClassName.get(componentElement); - checkState( - componentNames.contains(componentName), "%s is not a valid Component.", componentName); + + for (ComponentDescriptor componentDescriptor : metadata.componentDescriptors()) { + ClassName component = componentDescriptor.component(); if (metadata.testElement().isPresent()) { // In this case the @InstallIn or @TestInstallIn applies to only the given test root. ClassName test = ClassName.get(metadata.testElement().get()); - builder.testDepsBuilder().put(TestDepKey.of(componentName, test), metadata.dependency()); + builder.testDepsBuilder().put(TestDepKey.of(component, test), metadata.dependency()); builder.uninstalledTestDepsBuilder().putAll(test, metadata.replacedDependencies()); } else { // In this case the @InstallIn or @TestInstallIn applies to all roots if (!metadata.replacedDependencies().isEmpty()) { // If there are replacedDependencies() it means this is a @TestInstallIn - builder.globalTestDepsBuilder().put(componentName, metadata.dependency()); + builder.globalTestDepsBuilder().put(component, metadata.dependency()); builder.globalUninstalledTestDepsBuilder().addAll(metadata.replacedDependencies()); } else { - builder.globalDepsBuilder().put(componentName, metadata.dependency()); + builder.globalDepsBuilder().put(component, metadata.dependency()); } } } } - AggregatedUninstallModulesMetadata.from(elements) + // Collect all @UninstallModules. + // TODO(b/176438516): Filter @UninstallModules at the root. + metadatas.stream() + .filter(metadata -> metadata.testElement().isPresent()) + .map(metadata -> metadata.testElement().get()) + .distinct() + .filter(testElement -> Processors.hasAnnotation(testElement, ClassNames.IGNORE_MODULES)) .forEach( - metadata -> + testElement -> componentDependencies .modulesBuilder() .uninstalledTestDepsBuilder() .putAll( - ClassName.get(metadata.testElement()), - metadata.uninstallModuleElements().stream() - .map(module -> PkgPrivateMetadata.publicModule(module, elements)) - .collect(toImmutableSet()))); - - AggregatedEarlyEntryPointMetadata.from(elements).stream() - .map(AggregatedEarlyEntryPointMetadata::earlyEntryPoint) - .map(entryPoint -> PkgPrivateMetadata.publicEarlyEntryPoint(entryPoint, elements)) - .map(ClassName::get) - .forEach(componentDependencies.earlyEntryPointsBuilder()::add); - - return componentDependencies.build(); + ClassName.get(testElement), getUninstalledModules(testElement, elements))); + + return componentDependencies.build(elements); + } + + private static ImmutableMap<String, ComponentDescriptor> descriptorLookupMap( + ImmutableSet<ComponentDescriptor> descriptors) { + ImmutableMap.Builder<String, ComponentDescriptor> builder = ImmutableMap.builder(); + for (ComponentDescriptor descriptor : descriptors) { + // This is a temporary hack to map the old ApplicationComponent to the new SingletonComponent. + // Technically, this is only needed for backwards compatibility with libraries using the old + // processor since new processors should convert to the new SingletonComponent when generating + // the metadata class. + if (descriptor.component().equals(ClassNames.SINGLETON_COMPONENT)) { + builder.put("dagger.hilt.android.components.ApplicationComponent", descriptor); + } + builder.put(descriptor.component().toString(), descriptor); + } + return builder.build(); + } + + // Validate that the @UninstallModules doesn't contain any test modules. + private static Dependencies validateModules(Dependencies moduleDeps, Elements elements) { + SetMultimap<ClassName, TypeElement> invalidTestModules = HashMultimap.create(); + moduleDeps.testDeps().entries().stream() + .filter( + e -> moduleDeps.uninstalledTestDeps().containsEntry(e.getKey().test(), e.getValue())) + .forEach(e -> invalidTestModules.put(e.getKey().test(), e.getValue())); + + // Currently we don't have a good way to throw an error for all tests, so we sort (to keep the + // error reporting order stable) and then choose the first test. + // TODO(bcorso): Consider using ProcessorErrorHandler directly to report all errors at once? + Optional<ClassName> invalidTest = + invalidTestModules.keySet().stream() + .min((test1, test2) -> test1.toString().compareTo(test2.toString())); + if (invalidTest.isPresent()) { + throw new BadInputException( + String.format( + "@UninstallModules on test, %s, should not containing test modules, " + + "but found: %s", + invalidTest.get(), + invalidTestModules.get(invalidTest.get()).stream() + // Sort modules to keep stable error messages. + .sorted((test1, test2) -> test1.toString().compareTo(test2.toString())) + .collect(toImmutableList())), + elements.getTypeElement(invalidTest.get().toString())); + } + return moduleDeps; + } + + private static ImmutableSet<TypeElement> getUninstalledModules( + TypeElement testElement, Elements elements) { + ImmutableList<TypeElement> userUninstallModules = + Processors.getAnnotationClassValues( + elements, + Processors.getAnnotationMirror(testElement, ClassNames.IGNORE_MODULES), + "value"); + + // For pkg-private modules, find the generated wrapper class and uninstall that instead. + return userUninstallModules.stream() + .map(uninstallModule -> getPublicDependency(uninstallModule, elements)) + .collect(toImmutableSet()); + } + + /** Returns the public Hilt wrapper module, or the module itself if its already public. */ + private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) { + return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE) + .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString())) + .orElse(dependency); + } + + /** Returns the top-level elements of the aggregated deps package. */ + private static ImmutableList<AnnotationMirror> getAggregatedDeps(Elements elements) { + PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE); + checkState( + packageElement != null, + "Couldn't find package %s. Did you mark your @Module classes with @InstallIn annotations?", + AGGREGATING_PACKAGE); + + List<? extends Element> aggregatedDepsElements = packageElement.getEnclosedElements(); + checkState( + !aggregatedDepsElements.isEmpty(), + "No dependencies found. Did you mark your @Module classes with @InstallIn annotations?"); + + ImmutableList.Builder<AnnotationMirror> builder = ImmutableList.builder(); + for (Element element : aggregatedDepsElements) { + ProcessorErrors.checkState( + element.getKind() == ElementKind.CLASS, + element, + "Only classes may be in package %s. Did you add custom code in the package?", + AGGREGATING_PACKAGE); + + AnnotationMirror aggregatedDeps = + Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS); + ProcessorErrors.checkState( + aggregatedDeps != null, + element, + "Classes in package %s must be annotated with @AggregatedDeps: %s. Found: %s.", + AGGREGATING_PACKAGE, + element.getSimpleName(), + element.getAnnotationMirrors()); + + builder.add(aggregatedDeps); + } + return builder.build(); + } + + @AutoValue + abstract static class AggregatedDepMetadata { + static AggregatedDepMetadata create( + AnnotationMirror aggregatedDeps, + Map<String, ComponentDescriptor> descriptorLookup, + Elements elements) { + ImmutableMap<String, AnnotationValue> aggregatedDepsValues = + Processors.getAnnotationValues(elements, aggregatedDeps); + + return new AutoValue_ComponentDependencies_AggregatedDepMetadata( + getTestElement(aggregatedDepsValues.get("test"), elements), + getComponents(aggregatedDepsValues.get("components"), descriptorLookup), + getDependencyType( + aggregatedDepsValues.get("modules"), + aggregatedDepsValues.get("entryPoints"), + aggregatedDepsValues.get("componentEntryPoints")), + getDependency( + aggregatedDepsValues.get("modules"), + aggregatedDepsValues.get("entryPoints"), + aggregatedDepsValues.get("componentEntryPoints"), + elements), + getReplacedDependencies(aggregatedDepsValues.get("replaces"), elements)); + } + + enum DependencyType { + MODULE, + ENTRY_POINT, + COMPONENT_ENTRY_POINT + } + + abstract Optional<TypeElement> testElement(); + + abstract ImmutableList<ComponentDescriptor> componentDescriptors(); + + abstract DependencyType dependencyType(); + + abstract TypeElement dependency(); + + abstract ImmutableSet<TypeElement> replacedDependencies(); + + private static Optional<TypeElement> getTestElement( + AnnotationValue testValue, Elements elements) { + checkNotNull(testValue); + String test = AnnotationValues.getString(testValue); + return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test)); + } + + private static ImmutableList<ComponentDescriptor> getComponents( + AnnotationValue componentsValue, Map<String, ComponentDescriptor> descriptorLookup) { + checkNotNull(componentsValue); + ImmutableList<String> componentNames = + AnnotationValues.getAnnotationValues(componentsValue).stream() + .map(AnnotationValues::getString) + .collect(toImmutableList()); + + checkState(!componentNames.isEmpty()); + ImmutableList.Builder<ComponentDescriptor> components = ImmutableList.builder(); + for (String componentName : componentNames) { + checkState( + descriptorLookup.containsKey(componentName), + "%s is not a valid Component. Did you add or remove code in package %s?", + componentName, + AGGREGATING_PACKAGE); + components.add(descriptorLookup.get(componentName)); + } + return components.build(); + } + + private static DependencyType getDependencyType( + AnnotationValue modulesValue, + AnnotationValue entryPointsValue, + AnnotationValue componentEntryPointsValue) { + checkNotNull(modulesValue); + checkNotNull(entryPointsValue); + checkNotNull(componentEntryPointsValue); + + ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder(); + if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) { + dependencyTypes.add(DependencyType.MODULE); + } + if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) { + dependencyTypes.add(DependencyType.ENTRY_POINT); + } + if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) { + dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT); + } + return getOnlyElement(dependencyTypes.build()); + } + + private static TypeElement getDependency( + AnnotationValue modulesValue, + AnnotationValue entryPointsValue, + AnnotationValue componentEntryPointsValue, + Elements elements) { + checkNotNull(modulesValue); + checkNotNull(entryPointsValue); + checkNotNull(componentEntryPointsValue); + + return elements.getTypeElement( + AnnotationValues.getString( + getOnlyElement( + ImmutableList.<AnnotationValue>builder() + .addAll(AnnotationValues.getAnnotationValues(modulesValue)) + .addAll(AnnotationValues.getAnnotationValues(entryPointsValue)) + .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue)) + .build()))); + } + + private static ImmutableSet<TypeElement> getReplacedDependencies( + AnnotationValue replacedDependenciesValue, Elements elements) { + // Allow null values to support libraries using a Hilt version before @TestInstallIn was added + return replacedDependenciesValue == null + ? ImmutableSet.of() + : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream() + .map(AnnotationValues::getString) + .map(elements::getTypeElement) + .map(replacedDep -> getPublicDependency(replacedDep, elements)) + .collect(toImmutableSet()); + } } } diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java index ff344a651..66f175151 100644 --- a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java +++ b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java @@ -24,35 +24,16 @@ import com.google.auto.value.AutoValue; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.KotlinMetadataUtils; import dagger.hilt.processor.internal.Processors; import java.util.Optional; import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; /** PkgPrivateModuleMetadata contains a set of utilities for processing package private modules. */ @AutoValue -public abstract class PkgPrivateMetadata { - /** Returns the public Hilt wrapped type or the type itself if it is already public. */ - public static TypeElement publicModule(TypeElement element, Elements elements) { - return publicDep(element, elements, ClassNames.MODULE); - } - - /** Returns the public Hilt wrapped type or the type itself if it is already public. */ - public static TypeElement publicEarlyEntryPoint(TypeElement element, Elements elements) { - return publicDep(element, elements, ClassNames.EARLY_ENTRY_POINT); - } - - private static TypeElement publicDep( - TypeElement element, Elements elements, ClassName annotation) { - return of(elements, element, annotation) - .map(PkgPrivateMetadata::generatedClassName) - .map(ClassName::canonicalName) - .map(elements::getTypeElement) - .orElse(element); - } - +abstract class PkgPrivateMetadata { private static final String PREFIX = "HiltWrapper_"; /** Returns the base class name of the elemenet. */ @@ -82,11 +63,9 @@ public abstract class PkgPrivateMetadata { * Returns an Optional PkgPrivateMetadata requiring Hilt processing, otherwise returns an empty * Optional. */ - static Optional<PkgPrivateMetadata> of( - Elements elements, TypeElement element, ClassName annotation) { + static Optional<PkgPrivateMetadata> of(Elements elements, Element element, ClassName annotation) { // If this is a public element no wrapping is needed - if (effectiveVisibilityOfElement(element) == Visibility.PUBLIC - && !KotlinMetadataUtils.getMetadataUtil().isVisibilityInternal(element)) { + if (effectiveVisibilityOfElement(element) == Visibility.PUBLIC) { return Optional.empty(); } diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java index 02e7f5d56..6efd6431e 100644 --- a/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java +++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfProcessor.java @@ -16,7 +16,6 @@ package dagger.hilt.processor.internal.aliasof; -import static com.google.auto.common.MoreElements.asType; import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING; import com.google.auto.service.AutoService; @@ -49,14 +48,13 @@ public final class AliasOfProcessor extends BaseProcessor { "%s should only be used on scopes." + " However, it was found annotating %s", annotation, element); - AnnotationMirror annotationMirror = Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF); - TypeElement defineComponentScope = + Element defineComponentScope = Processors.getAnnotationClassValue(getElementUtils(), annotationMirror, "value"); - new AliasOfPropagatedDataGenerator(getProcessingEnv(), asType(element), defineComponentScope) + new AliasOfPropagatedDataGenerator(getProcessingEnv(), element, defineComponentScope) .generate(); } } diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java index 1d7edf285..75c4c15ac 100644 --- a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java +++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataGenerator.java @@ -17,37 +17,43 @@ package dagger.hilt.processor.internal.aliasof; import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.TypeSpec; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.Processors; import java.io.IOException; import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.TypeElement; +import javax.lang.model.element.Element; /** Generates resource files for {@link dagger.hilt.migration.AliasOf}. */ final class AliasOfPropagatedDataGenerator { private final ProcessingEnvironment processingEnv; - private final TypeElement aliasScope; - private final TypeElement defineComponentScope; + private final Element aliasScope; + private final Element defineComponentScope; AliasOfPropagatedDataGenerator( - ProcessingEnvironment processingEnv, - TypeElement aliasScope, - TypeElement defineComponentScope) { + ProcessingEnvironment processingEnv, Element aliasScope, Element defineComponentScope) { this.processingEnv = processingEnv; this.aliasScope = aliasScope; this.defineComponentScope = defineComponentScope; } void generate() throws IOException { - Processors.generateAggregatingClass( - ClassNames.ALIAS_OF_PROPAGATED_DATA_PACKAGE, - AnnotationSpec.builder(ClassNames.ALIAS_OF_PROPAGATED_DATA) + TypeSpec.Builder generator = + TypeSpec.classBuilder(Processors.getFullEnclosedName(aliasScope)) + .addOriginatingElement(aliasScope) + .addAnnotation( + AnnotationSpec.builder(ClassNames.ALIAS_OF_PROPAGATED_DATA) .addMember("defineComponentScope", "$T.class", defineComponentScope) .addMember("alias", "$T.class", aliasScope) - .build(), - aliasScope, - getClass(), - processingEnv); + .build()) + .addJavadoc("Generated class for aggregating scope aliases. \n"); + + Processors.addGeneratedAnnotation(generator, processingEnv, getClass()); + + JavaFile.builder(AliasOfs.AGGREGATING_PACKAGE, generator.build()) + .build() + .writeTo(processingEnv.getFiler()); } } diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java deleted file mode 100644 index 30a2c70c6..000000000 --- a/java/dagger/hilt/processor/internal/aliasof/AliasOfPropagatedDataMetadata.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.aliasof; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * A class that represents the values stored in an {@link - * dagger.hilt.internal.aliasof.AliasOfPropagatedData} annotation. - */ -@AutoValue -abstract class AliasOfPropagatedDataMetadata { - - abstract TypeElement defineComponentScopeElement(); - - abstract TypeElement aliasElement(); - - static ImmutableSet<AliasOfPropagatedDataMetadata> from(Elements elements) { - return AggregatedElements.from( - ClassNames.ALIAS_OF_PROPAGATED_DATA_PACKAGE, - ClassNames.ALIAS_OF_PROPAGATED_DATA, - elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static AliasOfPropagatedDataMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF_PROPAGATED_DATA); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - return new AutoValue_AliasOfPropagatedDataMetadata( - AnnotationValues.getTypeElement(values.get("defineComponentScope")), - AnnotationValues.getTypeElement(values.get("alias"))); - } -} diff --git a/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java b/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java index 18951bdf9..930a72e27 100644 --- a/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java +++ b/java/dagger/hilt/processor/internal/aliasof/AliasOfs.java @@ -16,11 +16,23 @@ package dagger.hilt.processor.internal.aliasof; +import static com.google.common.base.Suppliers.memoize; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; -import com.google.common.collect.ImmutableSetMultimap; import com.squareup.javapoet.ClassName; +import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.ProcessorErrors; +import dagger.hilt.processor.internal.Processors; +import java.util.List; +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.PackageElement; +import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; /** @@ -28,33 +40,69 @@ import javax.lang.model.util.Elements; * to scopes they are alias of. */ public final class AliasOfs { - public static AliasOfs create(Elements elements, ImmutableSet<ClassName> defineComponentScopes) { - ImmutableSetMultimap.Builder<ClassName, ClassName> builder = ImmutableSetMultimap.builder(); - AliasOfPropagatedDataMetadata.from(elements) - .forEach( - metadata -> { - ClassName defineComponentScopeName = - ClassName.get(metadata.defineComponentScopeElement()); - ClassName aliasScopeName = ClassName.get(metadata.aliasElement()); - ProcessorErrors.checkState( - defineComponentScopes.contains(defineComponentScopeName), - metadata.aliasElement(), - "The scope %s cannot be an alias for %s. You can only have aliases of a scope" - + " defined directly on a @DefineComponent type.", - aliasScopeName, - defineComponentScopeName); - builder.put(defineComponentScopeName, aliasScopeName); - }); - return new AliasOfs(builder.build()); - } + static final String AGGREGATING_PACKAGE = AliasOfs.class.getPackage().getName() + ".codegen"; - private final ImmutableSetMultimap<ClassName, ClassName> defineComponentScopeToAliases; + private final ProcessingEnvironment processingEnvironment; + private final ImmutableSet<ClassName> defineComponentScopes; + private final Supplier<ImmutableMultimap<ClassName, ClassName>> aliases = + memoize(() -> getAliases()); - private AliasOfs(ImmutableSetMultimap<ClassName, ClassName> defineComponentScopeToAliases) { - this.defineComponentScopeToAliases = defineComponentScopeToAliases; + public AliasOfs( + ProcessingEnvironment processingEnvironment, ImmutableSet<ClassName> defineComponentScopes) { + this.defineComponentScopes = defineComponentScopes; + this.processingEnvironment = processingEnvironment; } public ImmutableSet<ClassName> getAliasesFor(ClassName defineComponentScope) { - return defineComponentScopeToAliases.get(defineComponentScope); + return ImmutableSet.copyOf(aliases.get().get(defineComponentScope)); + } + + private ImmutableMultimap<ClassName, ClassName> getAliases() { + Elements elements = processingEnvironment.getElementUtils(); + PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE); + if (packageElement == null) { + return ImmutableMultimap.of(); + } + List<? extends Element> scopeAliasElements = packageElement.getEnclosedElements(); + Preconditions.checkState( + !scopeAliasElements.isEmpty(), "No scope aliases Found in package %s.", packageElement); + + ImmutableMultimap.Builder<ClassName, ClassName> builder = ImmutableMultimap.builder(); + for (Element element : scopeAliasElements) { + ProcessorErrors.checkState( + element.getKind() == ElementKind.CLASS, + element, + "Only classes may be in package %s. Did you add custom code in the package?", + packageElement); + + AnnotationMirror annotationMirror = + Processors.getAnnotationMirror(element, ClassNames.ALIAS_OF_PROPAGATED_DATA); + + ProcessorErrors.checkState( + annotationMirror != null, + element, + "Classes in package %s must be annotated with @%s: %s." + + " Found: %s. Files in this package are generated, did you add custom code in the" + + " package? ", + packageElement, + ClassNames.ALIAS_OF_PROPAGATED_DATA, + element.getSimpleName(), + element.getAnnotationMirrors()); + + TypeElement defineComponentScope = + Processors.getAnnotationClassValue(elements, annotationMirror, "defineComponentScope"); + TypeElement alias = Processors.getAnnotationClassValue(elements, annotationMirror, "alias"); + + Preconditions.checkState( + defineComponentScopes.contains(ClassName.get(defineComponentScope)), + "The scope %s cannot be an alias for %s. You can only have aliases of a scope defined" + + " directly on a @DefineComponent type.", + ClassName.get(alias), + ClassName.get(defineComponentScope)); + + builder.put(ClassName.get(defineComponentScope), ClassName.get(alias)); + } + + return builder.build(); } } diff --git a/java/dagger/hilt/processor/internal/aliasof/BUILD b/java/dagger/hilt/processor/internal/aliasof/BUILD index ffd0c9ae8..d3f90f2fa 100644 --- a/java/dagger/hilt/processor/internal/aliasof/BUILD +++ b/java/dagger/hilt/processor/internal/aliasof/BUILD @@ -31,6 +31,7 @@ java_library( "AliasOfPropagatedDataGenerator.java", ], deps = [ + ":alias_ofs", "//java/dagger/hilt/processor/internal:base_processor", "//java/dagger/hilt/processor/internal:classnames", "//java/dagger/hilt/processor/internal:processor_errors", @@ -39,24 +40,20 @@ java_library( "@google_bazel_common//third_party/java/auto:service", "@google_bazel_common//third_party/java/incap", "@google_bazel_common//third_party/java/javapoet", - "@maven//:com_google_auto_auto_common", ], ) java_library( name = "alias_ofs", srcs = [ - "AliasOfPropagatedDataMetadata.java", "AliasOfs.java", ], deps = [ - "//java/dagger/hilt/processor/internal:aggregated_elements", "//java/dagger/hilt/processor/internal:classnames", "//java/dagger/hilt/processor/internal:processor_errors", "//java/dagger/hilt/processor/internal:processors", - "//java/dagger/internal/codegen/extension", + "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", - "@google_bazel_common//third_party/java/auto:value", "@google_bazel_common//third_party/java/javapoet", ], ) diff --git a/java/dagger/hilt/processor/internal/definecomponent/BUILD b/java/dagger/hilt/processor/internal/definecomponent/BUILD index 43f4dfda0..7edec1cba 100644 --- a/java/dagger/hilt/processor/internal/definecomponent/BUILD +++ b/java/dagger/hilt/processor/internal/definecomponent/BUILD @@ -45,18 +45,18 @@ java_library( name = "define_components", srcs = [ "DefineComponentBuilderMetadatas.java", - "DefineComponentClassesMetadata.java", "DefineComponentMetadatas.java", "DefineComponents.java", ], deps = [ - "//java/dagger/hilt/processor/internal:aggregated_elements", "//java/dagger/hilt/processor/internal:classnames", "//java/dagger/hilt/processor/internal:component_descriptor", "//java/dagger/hilt/processor/internal:processor_errors", "//java/dagger/hilt/processor/internal:processors", "//java/dagger/internal/codegen/extension", + "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", + "//java/dagger/internal/guava:graph", "@google_bazel_common//third_party/java/auto:value", "@google_bazel_common//third_party/java/javapoet", "@maven//:com_google_auto_auto_common", diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java deleted file mode 100644 index 36ac28fb3..000000000 --- a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentClassesMetadata.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.definecomponent; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.ProcessorErrors; -import dagger.hilt.processor.internal.Processors; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * A class that represents the values stored in an {@link - * dagger.hilt.internal.definecomponent.DefineComponentClasses} annotation. - */ -@AutoValue -abstract class DefineComponentClassesMetadata { - - /** - * Returns the element annotated with {@code dagger.hilt.internal.definecomponent.DefineComponent} - * or {@code dagger.hilt.internal.definecomponent.DefineComponent.Builder}. - */ - abstract TypeElement element(); - - /** Returns {@code true} if this element represents a component. */ - abstract boolean isComponent(); - - /** Returns {@code true} if this element represents a component builder. */ - boolean isComponentBuilder() { - return !isComponent(); - } - - static ImmutableSet<DefineComponentClassesMetadata> from(Elements elements) { - return AggregatedElements.from( - ClassNames.DEFINE_COMPONENT_CLASSES_PACKAGE, - ClassNames.DEFINE_COMPONENT_CLASSES, - elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static DefineComponentClassesMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.DEFINE_COMPONENT_CLASSES); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - String componentName = AnnotationValues.getString(values.get("component")); - String builderName = AnnotationValues.getString(values.get("builder")); - - ProcessorErrors.checkState( - !(componentName.isEmpty() && builderName.isEmpty()), - element, - "@DefineComponentClasses missing both `component` and `builder` members."); - - ProcessorErrors.checkState( - componentName.isEmpty() || builderName.isEmpty(), - element, - "@DefineComponentClasses should not include both `component` and `builder` members."); - - boolean isComponent = !componentName.isEmpty(); - String componentOrBuilderName = isComponent ? componentName : builderName; - TypeElement componentOrBuilderElement = elements.getTypeElement(componentOrBuilderName); - ProcessorErrors.checkState( - componentOrBuilderElement != null, - componentOrBuilderElement, - "%s.%s(), has invalid value: `%s`.", - ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(), - isComponent ? "component" : "builder", - componentOrBuilderName); - return new AutoValue_DefineComponentClassesMetadata(componentOrBuilderElement, isComponent); - } -} diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java index 60864c2c2..aa69e40e1 100644 --- a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java +++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentMetadatas.java @@ -163,25 +163,15 @@ final class DefineComponentMetadatas { ? Optional.empty() : Optional.of(get(parent, childPath)); - ClassName componentClassName = ClassName.get(component); - ProcessorErrors.checkState( parentComponent.isPresent() - || componentClassName.equals(ClassNames.SINGLETON_COMPONENT), + || ClassName.get(component).equals(ClassNames.SINGLETON_COMPONENT), component, "@DefineComponent %s is missing a parent declaration.\n" + "Please declare the parent, for example: @DefineComponent(parent =" + " SingletonComponent.class)", component); - ProcessorErrors.checkState( - componentClassName.equals(ClassNames.SINGLETON_COMPONENT) - || !componentClassName.simpleName().equals(ClassNames.SINGLETON_COMPONENT.simpleName()), - component, - "Cannot have a component with the same simple name as the reserved %s: %s", - ClassNames.SINGLETON_COMPONENT.simpleName(), - componentClassName); - return new AutoValue_DefineComponentMetadatas_DefineComponentMetadata( component, scopes, parentComponent); } diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java index f7e54a0a7..e0bfca930 100644 --- a/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java +++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponentProcessor.java @@ -22,6 +22,8 @@ import com.google.auto.service.AutoService; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.TypeSpec; import dagger.hilt.processor.internal.BaseProcessor; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.Processors; @@ -68,13 +70,18 @@ public final class DefineComponentProcessor extends BaseProcessor { } private void generateFile(String member, TypeElement typeElement) throws IOException { - Processors.generateAggregatingClass( - ClassNames.DEFINE_COMPONENT_CLASSES_PACKAGE, - AnnotationSpec.builder(ClassNames.DEFINE_COMPONENT_CLASSES) - .addMember(member, "$S", typeElement.getQualifiedName()) - .build(), - typeElement, - getClass(), - getProcessingEnv()); + TypeSpec.Builder builder = + TypeSpec.interfaceBuilder(Processors.getFullEnclosedName(typeElement)) + .addOriginatingElement(typeElement) + .addAnnotation( + AnnotationSpec.builder(ClassNames.DEFINE_COMPONENT_CLASSES) + .addMember(member, "$S", typeElement.getQualifiedName()) + .build()); + + Processors.addGeneratedAnnotation(builder, processingEnv, getClass()); + + JavaFile.builder(DefineComponents.AGGREGATING_PACKAGE, builder.build()) + .build() + .writeTo(processingEnv.getFiler()); } } diff --git a/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java b/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java index efa501191..0b330111a 100644 --- a/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java +++ b/java/dagger/hilt/processor/internal/definecomponent/DefineComponents.java @@ -16,22 +16,32 @@ package dagger.hilt.processor.internal.definecomponent; +import static com.google.auto.common.AnnotationMirrors.getAnnotationElementAndValue; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; +import com.google.auto.common.MoreElements; +import com.google.auto.value.AutoValue; import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ListMultimap; import com.squareup.javapoet.ClassName; +import dagger.hilt.processor.internal.AnnotationValues; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.ComponentDescriptor; +import dagger.hilt.processor.internal.ComponentTree; import dagger.hilt.processor.internal.ProcessorErrors; +import dagger.hilt.processor.internal.Processors; import dagger.hilt.processor.internal.definecomponent.DefineComponentBuilderMetadatas.DefineComponentBuilderMetadata; import dagger.hilt.processor.internal.definecomponent.DefineComponentMetadatas.DefineComponentMetadata; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Optional; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; @@ -40,6 +50,8 @@ import javax.lang.model.util.Elements; * DefineComponentBuilderMetadata}. */ public final class DefineComponents { + static final String AGGREGATING_PACKAGE = + DefineComponents.class.getPackage().getName() + ".codegen"; public static DefineComponents create() { return new DefineComponents(); @@ -77,28 +89,14 @@ public final class DefineComponents { return builder.build(); } - /** Returns the set of aggregated {@link ComponentDescriptor}s. */ - public ImmutableSet<ComponentDescriptor> getComponentDescriptors(Elements elements) { - ImmutableSet<DefineComponentClassesMetadata> aggregatedMetadatas = - DefineComponentClassesMetadata.from(elements); - - ImmutableSet<DefineComponentMetadata> components = - aggregatedMetadatas.stream() - .filter(DefineComponentClassesMetadata::isComponent) - .map(DefineComponentClassesMetadata::element) - .map(componentMetadatas::get) - .collect(toImmutableSet()); - - ImmutableSet<DefineComponentBuilderMetadata> builders = - aggregatedMetadatas.stream() - .filter(DefineComponentClassesMetadata::isComponentBuilder) - .map(DefineComponentClassesMetadata::element) - .map(componentBuilderMetadatas::get) - .collect(toImmutableSet()); - + /** Returns the {@link ComponentTree} from the aggregated {@link ComponentDescriptor}s. */ + public ComponentTree getComponentTree(Elements elements) { + AggregatedMetadata aggregatedMetadata = + AggregatedMetadata.from(elements, componentMetadatas, componentBuilderMetadatas); ListMultimap<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMultimap = ArrayListMultimap.create(); - builders.forEach(builder -> builderMultimap.put(builder.componentMetadata(), builder)); + aggregatedMetadata.builders() + .forEach(builder -> builderMultimap.put(builder.componentMetadata(), builder)); // Check that there are not multiple builders per component for (DefineComponentMetadata componentMetadata : builderMultimap.keySet()) { @@ -121,9 +119,10 @@ public final class DefineComponents { Map<DefineComponentMetadata, DefineComponentBuilderMetadata> builderMap = new LinkedHashMap<>(); builderMultimap.entries().forEach(e -> builderMap.put(e.getKey(), e.getValue())); - return components.stream() + + return ComponentTree.from(aggregatedMetadata.components().stream() .map(componentMetadata -> toComponentDescriptor(componentMetadata, builderMap)) - .collect(toImmutableSet()); + .collect(toImmutableSet())); } private static ComponentDescriptor toComponentDescriptor( @@ -147,4 +146,80 @@ public final class DefineComponents { return builder.build(); } + + @AutoValue + abstract static class AggregatedMetadata { + /** Returns the aggregated metadata for {@link DefineComponentClasses#component()}. */ + abstract ImmutableList<DefineComponentMetadata> components(); + + /** Returns the aggregated metadata for {@link DefineComponentClasses#builder()}. */ + abstract ImmutableList<DefineComponentBuilderMetadata> builders(); + + static AggregatedMetadata from( + Elements elements, + DefineComponentMetadatas componentMetadatas, + DefineComponentBuilderMetadatas componentBuilderMetadatas) { + PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE); + + if (packageElement == null) { + return new AutoValue_DefineComponents_AggregatedMetadata( + ImmutableList.of(), ImmutableList.of()); + } + + ImmutableList.Builder<DefineComponentMetadata> components = ImmutableList.builder(); + ImmutableList.Builder<DefineComponentBuilderMetadata> builders = ImmutableList.builder(); + for (Element element : packageElement.getEnclosedElements()) { + ProcessorErrors.checkState( + MoreElements.isType(element), + element, + "Only types may be in package %s. Did you add custom code in the package?", + packageElement); + + TypeElement typeElement = MoreElements.asType(element); + ProcessorErrors.checkState( + Processors.hasAnnotation(typeElement, ClassNames.DEFINE_COMPONENT_CLASSES), + typeElement, + "Class, %s, must be annotated with @%s. Found: %s.", + typeElement, + ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(), + typeElement.getAnnotationMirrors()); + + Optional<TypeElement> component = defineComponentClass(elements, typeElement, "component"); + Optional<TypeElement> builder = defineComponentClass(elements, typeElement, "builder"); + ProcessorErrors.checkState( + component.isPresent() || builder.isPresent(), + typeElement, + "@DefineComponentClasses missing both `component` and `builder` members."); + + component.map(componentMetadatas::get).ifPresent(components::add); + builder.map(componentBuilderMetadatas::get).ifPresent(builders::add); + } + + return new AutoValue_DefineComponents_AggregatedMetadata( + components.build(), builders.build()); + } + + private static Optional<TypeElement> defineComponentClass( + Elements elements, Element element, String annotationMember) { + AnnotationMirror mirror = + Processors.getAnnotationMirror(element, ClassNames.DEFINE_COMPONENT_CLASSES); + AnnotationValue value = getAnnotationElementAndValue(mirror, annotationMember).getValue(); + String className = AnnotationValues.getString(value); + + if (className.isEmpty()) { // The default value. + return Optional.empty(); + } + + TypeElement type = elements.getTypeElement(className); + ProcessorErrors.checkState( + type != null, + element, + "%s.%s(), has invalid value: `%s`.", + ClassNames.DEFINE_COMPONENT_CLASSES.simpleName(), + annotationMember, + className); + + return Optional.of(type); + } + } } diff --git a/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointGenerator.java b/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointGenerator.java deleted file mode 100644 index ae341189c..000000000 --- a/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointGenerator.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.earlyentrypoint; - -import com.squareup.javapoet.AnnotationSpec; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import java.io.IOException; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.TypeElement; - -/** - * Generates an {@link dagger.hilt.android.internal.earlyentrypoint.AggregatedEarlyEntryPoint} - * annotation. - */ -final class AggregatedEarlyEntryPointGenerator { - - private final ProcessingEnvironment env; - private final TypeElement earlyEntryPoint; - - AggregatedEarlyEntryPointGenerator(TypeElement earlyEntryPoint, ProcessingEnvironment env) { - this.earlyEntryPoint = earlyEntryPoint; - this.env = env; - } - - void generate() throws IOException { - Processors.generateAggregatingClass( - ClassNames.AGGREGATED_EARLY_ENTRY_POINT_PACKAGE, - AnnotationSpec.builder(ClassNames.AGGREGATED_EARLY_ENTRY_POINT) - .addMember("earlyEntryPoint", "$S", earlyEntryPoint.getQualifiedName()) - .build(), - earlyEntryPoint, - getClass(), - env); - } -} diff --git a/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java b/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java deleted file mode 100644 index ed347bd17..000000000 --- a/java/dagger/hilt/processor/internal/earlyentrypoint/AggregatedEarlyEntryPointMetadata.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.earlyentrypoint; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * A class that represents the values stored in an {@link - * dagger.hilt.android.internal.uninstallmodules.AggregatedUninstallModules} annotation. - */ -@AutoValue -public abstract class AggregatedEarlyEntryPointMetadata { - - /** Returns the element annotated with {@link dagger.hilt.android.EarlyEntryPoint}. */ - public abstract TypeElement earlyEntryPoint(); - - /** Returns all aggregated deps in the aggregating package. */ - public static ImmutableSet<AggregatedEarlyEntryPointMetadata> from(Elements elements) { - return AggregatedElements.from( - ClassNames.AGGREGATED_EARLY_ENTRY_POINT_PACKAGE, - ClassNames.AGGREGATED_EARLY_ENTRY_POINT, - elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static AggregatedEarlyEntryPointMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_EARLY_ENTRY_POINT); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - return new AutoValue_AggregatedEarlyEntryPointMetadata( - elements.getTypeElement(AnnotationValues.getString(values.get("earlyEntryPoint")))); - } -} diff --git a/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD b/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD deleted file mode 100644 index 3573e8603..000000000 --- a/java/dagger/hilt/processor/internal/earlyentrypoint/BUILD +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) 2020 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. - -# Description: -# A processor for @dagger.hilt.android.EarlyEntryPoint. - -package(default_visibility = ["//:src"]) - -java_plugin( - name = "processor", - generates_api = 1, - processor_class = "dagger.hilt.processor.internal.earlyentrypoint.EarlyEntryPointProcessor", - deps = [":processor_lib"], -) - -java_library( - name = "processor_lib", - srcs = [ - "AggregatedEarlyEntryPointGenerator.java", - "EarlyEntryPointProcessor.java", - ], - deps = [ - "//java/dagger/hilt/processor/internal:base_processor", - "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:processors", - "//java/dagger/internal/guava:collect", - "@google_bazel_common//third_party/java/auto:service", - "@google_bazel_common//third_party/java/incap", - "@google_bazel_common//third_party/java/javapoet", - "@maven//:com_google_auto_auto_common", - ], -) - -java_library( - name = "aggregated_early_entry_point_metadata", - srcs = [ - "AggregatedEarlyEntryPointMetadata.java", - ], - deps = [ - "//java/dagger/hilt/processor/internal:aggregated_elements", - "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:processors", - "//java/dagger/internal/codegen/extension", - "//java/dagger/internal/guava:collect", - "@google_bazel_common//third_party/java/auto:value", - ], -) - -filegroup( - name = "srcs_filegroup", - srcs = glob(["*"]), -) diff --git a/java/dagger/hilt/processor/internal/earlyentrypoint/EarlyEntryPointProcessor.java b/java/dagger/hilt/processor/internal/earlyentrypoint/EarlyEntryPointProcessor.java deleted file mode 100644 index 848fa319d..000000000 --- a/java/dagger/hilt/processor/internal/earlyentrypoint/EarlyEntryPointProcessor.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.earlyentrypoint; - -import static com.google.auto.common.MoreElements.asType; -import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING; - -import com.google.auto.service.AutoService; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.BaseProcessor; -import dagger.hilt.processor.internal.ClassNames; -import javax.annotation.processing.Processor; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; - -/** Validates {@link dagger.hilt.android.EarlyEntryPoint} usages. */ -@IncrementalAnnotationProcessor(ISOLATING) -@AutoService(Processor.class) -public final class EarlyEntryPointProcessor extends BaseProcessor { - - @Override - public ImmutableSet<String> getSupportedAnnotationTypes() { - return ImmutableSet.of(ClassNames.EARLY_ENTRY_POINT.toString()); - } - - @Override - public void processEach(TypeElement annotation, Element element) throws Exception { - new AggregatedEarlyEntryPointGenerator(asType(element), getProcessingEnv()).generate(); - } -} diff --git a/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java b/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java deleted file mode 100644 index 722b99b72..000000000 --- a/java/dagger/hilt/processor/internal/root/AggregatedRootGenerator.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.root; - -import com.squareup.javapoet.AnnotationSpec; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import java.io.IOException; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.TypeElement; - -/** Generates an {@link dagger.hilt.internal.aggregatedroot.AggregatedRoot}. */ -final class AggregatedRootGenerator { - private final TypeElement rootElement; - private final TypeElement rootAnnotation; - private final ProcessingEnvironment processingEnv; - - AggregatedRootGenerator( - TypeElement rootElement, TypeElement rootAnnotation, ProcessingEnvironment processingEnv) { - this.rootElement = rootElement; - this.rootAnnotation = rootAnnotation; - this.processingEnv = processingEnv; - } - - void generate() throws IOException { - Processors.generateAggregatingClass( - ClassNames.AGGREGATED_ROOT_PACKAGE, - AnnotationSpec.builder(ClassNames.AGGREGATED_ROOT) - .addMember("root", "$S", rootElement.getQualifiedName()) - .addMember("rootAnnotation", "$T.class", rootAnnotation) - .build(), - rootElement, - getClass(), - processingEnv); - } -} diff --git a/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java b/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java deleted file mode 100644 index 961689b7c..000000000 --- a/java/dagger/hilt/processor/internal/root/AggregatedRootMetadata.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.root; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * Represents the values stored in an {@link dagger.hilt.internal.aggregatedroot.AggregatedRoot}. - */ -@AutoValue -abstract class AggregatedRootMetadata { - - /** Returns the element that was annotated with the root annotation. */ - abstract TypeElement rootElement(); - - /** Returns the root annotation as an element. */ - abstract TypeElement rootAnnotation(); - - static ImmutableSet<AggregatedRootMetadata> from(Elements elements) { - return AggregatedElements.from( - ClassNames.AGGREGATED_ROOT_PACKAGE, ClassNames.AGGREGATED_ROOT, elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static AggregatedRootMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_ROOT); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - return new AutoValue_AggregatedRootMetadata( - elements.getTypeElement(AnnotationValues.getString(values.get("root"))), - AnnotationValues.getTypeElement(values.get("rootAnnotation"))); - } -} diff --git a/java/dagger/hilt/processor/internal/root/BUILD b/java/dagger/hilt/processor/internal/root/BUILD index 5ce762fa6..709eb7f5f 100644 --- a/java/dagger/hilt/processor/internal/root/BUILD +++ b/java/dagger/hilt/processor/internal/root/BUILD @@ -29,14 +29,11 @@ java_plugin( java_library( name = "processor_lib", srcs = [ - "AggregatedRootGenerator.java", - "ComponentGenerator.java", - "EarlySingletonComponentCreatorGenerator.java", - "ProcessedRootSentinelGenerator.java", "RootFileFormatter.java", "RootGenerator.java", "RootProcessor.java", "TestComponentDataGenerator.java", + "TestComponentDataSupplierGenerator.java", "TestInjectorGenerator.java", ], deps = [ @@ -44,7 +41,6 @@ java_library( ":root_type", "//java/dagger/hilt/processor/internal:base_processor", "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:compiler_options", "//java/dagger/hilt/processor/internal:component_descriptor", "//java/dagger/hilt/processor/internal:component_names", "//java/dagger/hilt/processor/internal:processor_errors", @@ -66,18 +62,13 @@ java_library( java_library( name = "root_metadata", srcs = [ - "AggregatedRootMetadata.java", - "ComponentTree.java", - "ProcessedRootSentinelMetadata.java", "Root.java", "RootMetadata.java", "TestRootMetadata.java", ], deps = [ ":root_type", - "//java/dagger/hilt/processor/internal:aggregated_elements", "//java/dagger/hilt/processor/internal:classnames", - "//java/dagger/hilt/processor/internal:compiler_options", "//java/dagger/hilt/processor/internal:component_descriptor", "//java/dagger/hilt/processor/internal:kotlin", "//java/dagger/hilt/processor/internal:processor_errors", @@ -88,7 +79,6 @@ java_library( "//java/dagger/internal/codegen/kotlin", "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", - "//java/dagger/internal/guava:graph", "@google_bazel_common//third_party/java/auto:value", "@google_bazel_common//third_party/java/javapoet", "@maven//:com_google_auto_auto_common", @@ -100,6 +90,7 @@ java_library( srcs = ["RootType.java"], deps = [ "//java/dagger/hilt/processor/internal:classnames", + "//java/dagger/hilt/processor/internal:processor_errors", "//java/dagger/hilt/processor/internal:processors", "@google_bazel_common//third_party/java/javapoet", ], diff --git a/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java b/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java deleted file mode 100644 index 1dbda3982..000000000 --- a/java/dagger/hilt/processor/internal/root/EarlySingletonComponentCreatorGenerator.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.root; - -import com.squareup.javapoet.ClassName; -import com.squareup.javapoet.JavaFile; -import com.squareup.javapoet.MethodSpec; -import com.squareup.javapoet.TypeSpec; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import java.io.IOException; -import javax.annotation.processing.ProcessingEnvironment; - -/** Generator for the {@code EarlySingletonComponentCreator}. */ -final class EarlySingletonComponentCreatorGenerator { - private static final ClassName EARLY_SINGLETON_COMPONENT_CREATOR = - ClassName.get("dagger.hilt.android.internal.testing", "EarlySingletonComponentCreator"); - private static final ClassName EARLY_SINGLETON_COMPONENT_CREATOR_IMPL = - ClassName.get( - "dagger.hilt.android.internal.testing", "EarlySingletonComponentCreatorImpl"); - private static final ClassName DEFAULT_COMPONENT_IMPL = - ClassName.get( - "dagger.hilt.android.internal.testing.root", "DaggerDefault_HiltComponents_SingletonC"); - - static void generate(ProcessingEnvironment env) throws IOException { - TypeSpec.Builder builder = - TypeSpec.classBuilder(EARLY_SINGLETON_COMPONENT_CREATOR_IMPL) - .superclass(EARLY_SINGLETON_COMPONENT_CREATOR) - .addMethod( - MethodSpec.methodBuilder("create") - .returns(ClassName.OBJECT) - .addStatement( - "return $T.builder()\n" - + ".applicationContextModule(new $T($T.getApplicationContext()))\n" - + ".build()", - DEFAULT_COMPONENT_IMPL, - ClassNames.APPLICATION_CONTEXT_MODULE, - ClassNames.APPLICATION_PROVIDER) - .build()); - - Processors.addGeneratedAnnotation(builder, env, ClassNames.ROOT_PROCESSOR.toString()); - - JavaFile.builder(EARLY_SINGLETON_COMPONENT_CREATOR_IMPL.packageName(), builder.build()) - .build() - .writeTo(env.getFiler()); - } - - private EarlySingletonComponentCreatorGenerator() {} -} diff --git a/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelGenerator.java b/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelGenerator.java deleted file mode 100644 index 87efa20a1..000000000 --- a/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelGenerator.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.root; - -import com.squareup.javapoet.AnnotationSpec; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import java.io.IOException; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.TypeElement; - -/** Generates an {@link dagger.hilt.internal.processedrootsentinel.ProcessedRootSentinel}. */ -final class ProcessedRootSentinelGenerator { - private final TypeElement processedRoot; - private final ProcessingEnvironment processingEnv; - - ProcessedRootSentinelGenerator(TypeElement processedRoot, ProcessingEnvironment processingEnv) { - this.processedRoot = processedRoot; - this.processingEnv = processingEnv; - } - - void generate() throws IOException { - Processors.generateAggregatingClass( - ClassNames.PROCESSED_ROOT_SENTINEL_PACKAGE, - AnnotationSpec.builder(ClassNames.PROCESSED_ROOT_SENTINEL) - .addMember("roots", "$S", processedRoot.getQualifiedName()) - .build(), - processedRoot, - getClass(), - processingEnv); - } -} diff --git a/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java b/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java deleted file mode 100644 index d60015495..000000000 --- a/java/dagger/hilt/processor/internal/root/ProcessedRootSentinelMetadata.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.root; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * Represents the values stored in an {@link - * dagger.hilt.internal.processedrootsentinel.ProcessedRootSentinel}. - */ -@AutoValue -abstract class ProcessedRootSentinelMetadata { - - /** Returns the processed root elements. */ - abstract ImmutableSet<TypeElement> rootElements(); - - static ImmutableSet<ProcessedRootSentinelMetadata> from(Elements elements) { - return AggregatedElements.from( - ClassNames.PROCESSED_ROOT_SENTINEL_PACKAGE, - ClassNames.PROCESSED_ROOT_SENTINEL, - elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static ProcessedRootSentinelMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.PROCESSED_ROOT_SENTINEL); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - return new AutoValue_ProcessedRootSentinelMetadata( - AnnotationValues.getStrings(values.get("roots")).stream() - .map(elements::getTypeElement) - .collect(toImmutableSet())); - } -} diff --git a/java/dagger/hilt/processor/internal/root/Root.java b/java/dagger/hilt/processor/internal/root/Root.java index 24440b0c9..981636d62 100644 --- a/java/dagger/hilt/processor/internal/root/Root.java +++ b/java/dagger/hilt/processor/internal/root/Root.java @@ -19,7 +19,6 @@ package dagger.hilt.processor.internal.root; import com.google.auto.common.MoreElements; import com.google.auto.value.AutoValue; import com.squareup.javapoet.ClassName; -import dagger.hilt.processor.internal.ClassNames; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; @@ -27,37 +26,18 @@ import javax.lang.model.element.TypeElement; /** Metadata for a root element that can trigger the {@link RootProcessor}. */ @AutoValue abstract class Root { - /** - * Creates the default root for this (test) build compilation. - * - * <p>A default root installs only the global {@code InstallIn} and {@code TestInstallIn} - * dependencies. Test-specific dependencies are not installed in the default root. - * - * <p>The default root is used for two purposes: - * - * <ul> - * <li>To inject {@code EarlyEntryPoint} annotated interfaces. - * <li>To inject tests that only depend on global dependencies - * </ul> - */ - static Root createDefaultRoot(ProcessingEnvironment env) { - TypeElement rootElement = - env.getElementUtils().getTypeElement(ClassNames.DEFAULT_ROOT.canonicalName()); - return new AutoValue_Root(rootElement, /*isTestRoot=*/ true); - } - /** Creates a {@plainlink Root root} for the given {@plainlink Element element}. */ static Root create(Element element, ProcessingEnvironment env) { TypeElement rootElement = MoreElements.asType(element); - return new AutoValue_Root(rootElement, RootType.of(rootElement).isTestRoot()); + return new AutoValue_Root(RootType.of(env, rootElement), rootElement); } + /** Returns the type of the root {@code element}. */ + abstract RootType type(); + /** Returns the root element that should be used with processing. */ abstract TypeElement element(); - /** Returns {@code true} if this is a test root. */ - abstract boolean isTestRoot(); - /** Returns the class name of the root element. */ ClassName classname() { return ClassName.get(element()); @@ -68,8 +48,7 @@ abstract class Root { return element().toString(); } - /** Returns {@code true} if this uses the default root. */ - boolean isDefaultRoot() { - return classname().equals(ClassNames.DEFAULT_ROOT); + boolean isTestRoot() { + return type().isTestRoot(); } } diff --git a/java/dagger/hilt/processor/internal/root/RootGenerator.java b/java/dagger/hilt/processor/internal/root/RootGenerator.java index f87fbd90b..c2c55e6c4 100644 --- a/java/dagger/hilt/processor/internal/root/RootGenerator.java +++ b/java/dagger/hilt/processor/internal/root/RootGenerator.java @@ -16,7 +16,6 @@ package dagger.hilt.processor.internal.root; -import static com.google.common.base.Preconditions.checkState; import static dagger.hilt.processor.internal.Processors.toClassNames; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static javax.lang.model.element.Modifier.ABSTRACT; @@ -36,11 +35,11 @@ import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.ComponentDescriptor; +import dagger.hilt.processor.internal.ComponentGenerator; import dagger.hilt.processor.internal.ComponentNames; +import dagger.hilt.processor.internal.ComponentTree; import dagger.hilt.processor.internal.Processors; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.Modifier; @@ -48,27 +47,20 @@ import javax.lang.model.element.Modifier; /** Generates components and any other classes needed for a root. */ final class RootGenerator { - static void generate( - RootMetadata metadata, ComponentNames componentNames, ProcessingEnvironment env) - throws IOException { + static void generate(RootMetadata metadata, ProcessingEnvironment env) throws IOException { new RootGenerator( - RootMetadata.copyWithNewTree(metadata, filterDescriptors(metadata.componentTree())), - componentNames, - env) - .generateComponents(); + RootMetadata.copyWithNewTree( + metadata, + filterDescriptors(metadata.componentTree())), + env).generateComponents(); } private final RootMetadata metadata; private final ProcessingEnvironment env; private final Root root; - private final Map<String, Integer> simpleComponentNamesToDedupeSuffix = new HashMap<>(); - private final Map<ComponentDescriptor, ClassName> componentNameMap = new HashMap<>(); - private final ComponentNames componentNames; - private RootGenerator( - RootMetadata metadata, ComponentNames componentNames, ProcessingEnvironment env) { + private RootGenerator(RootMetadata metadata, ProcessingEnvironment env) { this.metadata = metadata; - this.componentNames = componentNames; this.env = env; this.root = metadata.root(); } @@ -76,9 +68,8 @@ final class RootGenerator { private void generateComponents() throws IOException { // TODO(bcorso): Consider moving all of this logic into ComponentGenerator? - ClassName componentsWrapperClassName = getComponentsWrapperClassName(); TypeSpec.Builder componentsWrapper = - TypeSpec.classBuilder(componentsWrapperClassName) + TypeSpec.classBuilder(getComponentsWrapperClassName()) .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PRIVATE).build()); @@ -102,6 +93,7 @@ final class RootGenerator { new ComponentGenerator( env, getComponentClassName(componentDescriptor), + root.element(), Optional.empty(), modules, metadata.entryPoints(componentDescriptor.component()), @@ -109,14 +101,11 @@ final class RootGenerator { ImmutableList.of(), componentAnnotation(componentDescriptor), componentBuilder(componentDescriptor)) - .typeSpecBuilder() - .addModifiers(Modifier.STATIC) - .build()); + .generate().toBuilder().addModifiers(Modifier.STATIC).build()); } RootFileFormatter.write( - JavaFile.builder(componentsWrapperClassName.packageName(), componentsWrapper.build()) - .build(), + JavaFile.builder(root.classname().packageName(), componentsWrapper.build()).build(), env.getFiler()); } @@ -141,7 +130,7 @@ final class RootGenerator { } private ImmutableMap<ComponentDescriptor, ClassName> subcomponentBuilderModules( - TypeSpec.Builder componentsWrapper) { + TypeSpec.Builder componentsWrapper) throws IOException { ImmutableMap.Builder<ComponentDescriptor, ClassName> modules = ImmutableMap.builder(); for (ComponentDescriptor descriptor : metadata.componentTree().getComponentDescriptors()) { // Root component builders don't have subcomponent builder modules @@ -162,7 +151,7 @@ final class RootGenerator { // @Binds FooSubcomponentInterfaceBuilder bind(FooSubcomponent.Builder builder); // } private TypeSpec subcomponentBuilderModule( - ClassName componentName, ClassName builderName, ClassName moduleName) { + ClassName componentName, ClassName builderName, ClassName moduleName) throws IOException { TypeSpec.Builder subcomponentBuilderModule = TypeSpec.interfaceBuilder(moduleName) .addOriginatingElement(root.element()) @@ -221,38 +210,10 @@ final class RootGenerator { } private ClassName getComponentsWrapperClassName() { - return componentNames.generatedComponentsWrapper(root.classname()); + return ComponentNames.generatedComponentsWrapper(root.classname()); } private ClassName getComponentClassName(ComponentDescriptor componentDescriptor) { - if (componentNameMap.containsKey(componentDescriptor)) { - return componentNameMap.get(componentDescriptor); - } - - // Disallow any component names with the same name as our SingletonComponent because we treat - // that component specially and things may break. - checkState( - componentDescriptor.component().equals(ClassNames.SINGLETON_COMPONENT) - || !componentDescriptor.component().simpleName().equals( - ClassNames.SINGLETON_COMPONENT.simpleName()), - "Cannot have a component with the same simple name as the reserved %s: %s", - ClassNames.SINGLETON_COMPONENT.simpleName(), - componentDescriptor.component()); - - ClassName generatedComponent = - componentNames.generatedComponent(root.classname(), componentDescriptor.component()); - - Integer suffix = simpleComponentNamesToDedupeSuffix.get(generatedComponent.simpleName()); - if (suffix != null) { - // If an entry exists, use the suffix in the map and the replace it with the value incremented - generatedComponent = Processors.append(generatedComponent, String.valueOf(suffix)); - simpleComponentNamesToDedupeSuffix.put(generatedComponent.simpleName(), suffix + 1); - } else { - // Otherwise, just add an entry for any possible future duplicates - simpleComponentNamesToDedupeSuffix.put(generatedComponent.simpleName(), 2); - } - - componentNameMap.put(componentDescriptor, generatedComponent); - return generatedComponent; + return ComponentNames.generatedComponent(root.classname(), componentDescriptor.component()); } } diff --git a/java/dagger/hilt/processor/internal/root/RootMetadata.java b/java/dagger/hilt/processor/internal/root/RootMetadata.java index b39b590ef..4c43f1d61 100644 --- a/java/dagger/hilt/processor/internal/root/RootMetadata.java +++ b/java/dagger/hilt/processor/internal/root/RootMetadata.java @@ -16,22 +16,20 @@ package dagger.hilt.processor.internal.root; -import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Suppliers.memoize; -import static dagger.hilt.processor.internal.HiltCompilerOptions.isSharedTestComponentsEnabled; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static javax.lang.model.element.Modifier.ABSTRACT; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.STATIC; import com.google.common.base.Supplier; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeName; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.ComponentDescriptor; +import dagger.hilt.processor.internal.ComponentTree; import dagger.hilt.processor.internal.KotlinMetadataUtils; import dagger.hilt.processor.internal.Processors; import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies; @@ -55,42 +53,22 @@ public final class RootMetadata { ComponentTree componentTree, ComponentDependencies deps, ProcessingEnvironment env) { - return createInternal(root, ImmutableList.of(), componentTree, deps, env); - } - - static RootMetadata createForDefaultRoot( - Root root, - ImmutableList<RootMetadata> rootsUsingDefaultComponents, - ComponentTree componentTree, - ComponentDependencies deps, - ProcessingEnvironment env) { - checkState(root.isDefaultRoot()); - return createInternal(root, rootsUsingDefaultComponents, componentTree, deps, env); - } - - static RootMetadata copyWithNewTree(RootMetadata other, ComponentTree componentTree) { - return createInternal( - other.root, other.rootsUsingDefaultComponents, componentTree, other.deps, other.env); - } - - private static RootMetadata createInternal( - Root root, - ImmutableList<RootMetadata> rootsUsingDefaultComponents, - ComponentTree componentTree, - ComponentDependencies deps, - ProcessingEnvironment env) { - RootMetadata metadata = - new RootMetadata(root, componentTree, deps, rootsUsingDefaultComponents, env); + RootMetadata metadata = new RootMetadata(root, componentTree, deps, env); metadata.validate(); return metadata; } + static RootMetadata copyWithNewTree( + RootMetadata other, + ComponentTree componentTree) { + return create(other.root, componentTree, other.deps, other.env); + } + private final Root root; private final ProcessingEnvironment env; private final Elements elements; private final ComponentTree componentTree; private final ComponentDependencies deps; - private final ImmutableList<RootMetadata> rootsUsingDefaultComponents; private final Supplier<ImmutableSetMultimap<ClassName, ClassName>> scopesByComponent = memoize(this::getScopesByComponentUncached); private final Supplier<TestRootMetadata> testRootMetadata = @@ -100,14 +78,12 @@ public final class RootMetadata { Root root, ComponentTree componentTree, ComponentDependencies deps, - ImmutableList<RootMetadata> rootsUsingDefaultComponents, ProcessingEnvironment env) { this.root = root; this.env = env; this.elements = env.getElementUtils(); this.componentTree = componentTree; this.deps = deps; - this.rootsUsingDefaultComponents = rootsUsingDefaultComponents; } public Root root() { @@ -126,25 +102,9 @@ public final class RootMetadata { return deps.modules().get(componentName, root.classname(), root.isTestRoot()); } - /** - * Returns {@code true} if this is a test root that provides no test-specific dependencies or sets - * other options that would prevent it from sharing components with other test roots. - */ - // TODO(groakley): Allow more tests to share modules, e.g. tests that uninstall the same module. - // In that case, this might instead return which shared dep grouping should be used. - public boolean canShareTestComponents() { - return isSharedTestComponentsEnabled(env) - && root.isTestRoot() - && !deps.includesTestDeps(root.classname()); - } - public ImmutableSet<TypeName> entryPoints(ClassName componentName) { return ImmutableSet.<TypeName>builder() .addAll(getUserDefinedEntryPoints(componentName)) - .add( - root.isTestRoot() && componentName.equals(ClassNames.SINGLETON_COMPONENT) - ? ClassNames.TEST_SINGLETON_COMPONENT - : ClassNames.GENERATED_COMPONENT) .add(componentName) .build(); } @@ -168,7 +128,6 @@ public final class RootMetadata { } public TestRootMetadata testRootMetadata() { - checkState(!root.isDefaultRoot(), "The default root does not have TestRootMetadata!"); return testRootMetadata.get(); } @@ -190,7 +149,7 @@ public final class RootMetadata { for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) { ClassName componentName = componentDescriptor.component(); for (TypeElement extraModule : modulesThatDaggerCannotConstruct(componentName)) { - if (root.isTestRoot() && !componentName.equals(ClassNames.SINGLETON_COMPONENT)) { + if (root.type().isTestRoot() && !componentName.equals(ClassNames.SINGLETON_COMPONENT)) { env.getMessager() .printMessage( Diagnostic.Kind.ERROR, @@ -198,7 +157,7 @@ public final class RootMetadata { + "static provision methods or have a visible, no-arg constructor. Found: " + extraModule.getQualifiedName(), root.element()); - } else if (!root.isTestRoot()) { + } else if (!root.type().isTestRoot()) { env.getMessager() .printMessage( Diagnostic.Kind.ERROR, @@ -213,19 +172,10 @@ public final class RootMetadata { private ImmutableSet<TypeName> getUserDefinedEntryPoints(ClassName componentName) { ImmutableSet.Builder<TypeName> entryPointSet = ImmutableSet.builder(); - if (root.isDefaultRoot() && !rootsUsingDefaultComponents.isEmpty()) { - // Add entry points for shared component - rootsUsingDefaultComponents.stream() - .flatMap(metadata -> metadata.entryPoints(componentName).stream()) - .forEach(entryPointSet::add); - } else if (root.isDefaultRoot() && componentName.equals(ClassNames.SINGLETON_COMPONENT)) { - // We only do this for SingletonComponent because EarlyEntryPoints can only be installed - // in the SingletonComponent. - deps.earlyEntryPoints().forEach(entryPointSet::add); - } else { - deps.entryPoints().get(componentName, root.classname(), root.isTestRoot()).stream() - .map(ClassName::get) - .forEach(entryPointSet::add); + entryPointSet.add(ClassNames.GENERATED_COMPONENT); + for (TypeElement element : + deps.entryPoints().get(componentName, root.classname(), root.isTestRoot())) { + entryPointSet.add(ClassName.get(element)); } return entryPointSet.build(); } @@ -238,7 +188,7 @@ public final class RootMetadata { .flatMap(descriptor -> descriptor.scopes().stream()) .collect(toImmutableSet()); - AliasOfs aliasOfs = AliasOfs.create(env.getElementUtils(), defineComponentScopes); + AliasOfs aliasOfs = new AliasOfs(env, defineComponentScopes); for (ComponentDescriptor componentDescriptor : componentTree.getComponentDescriptors()) { for (ClassName scope : componentDescriptor.scopes()) { diff --git a/java/dagger/hilt/processor/internal/root/RootProcessor.java b/java/dagger/hilt/processor/internal/root/RootProcessor.java index 1ee4446d4..8f8c94874 100644 --- a/java/dagger/hilt/processor/internal/root/RootProcessor.java +++ b/java/dagger/hilt/processor/internal/root/RootProcessor.java @@ -17,11 +17,8 @@ package dagger.hilt.processor.internal.root; import static com.google.common.base.Preconditions.checkState; -import static dagger.hilt.processor.internal.HiltCompilerOptions.isCrossCompilationRootValidationDisabled; -import static dagger.hilt.processor.internal.HiltCompilerOptions.isSharedTestComponentsEnabled; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; -import static java.util.Comparator.comparing; import static javax.lang.model.element.Modifier.PUBLIC; import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING; @@ -31,17 +28,16 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.ClassName; import dagger.hilt.processor.internal.BaseProcessor; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.ComponentDescriptor; -import dagger.hilt.processor.internal.ComponentNames; +import dagger.hilt.processor.internal.ComponentTree; import dagger.hilt.processor.internal.ProcessorErrors; import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies; import dagger.hilt.processor.internal.definecomponent.DefineComponents; import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputs; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; -import java.util.Comparator; import java.util.HashSet; +import java.util.List; import java.util.Set; import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.Processor; @@ -54,10 +50,9 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessor; @IncrementalAnnotationProcessor(AGGREGATING) @AutoService(Processor.class) public final class RootProcessor extends BaseProcessor { - private static final Comparator<TypeElement> QUALIFIED_NAME_COMPARATOR = - comparing(TypeElement::getQualifiedName, (n1, n2) -> n1.toString().compareTo(n2.toString())); - + private final List<ClassName> rootNames = new ArrayList<>(); private final Set<ClassName> processed = new HashSet<>(); + private boolean isTestEnv; // TODO(bcorso): Consider using a Dagger component to create/scope these objects private final DefineComponents defineComponents = DefineComponents.create(); private GeneratesRootInputs generatesRootInputs; @@ -81,13 +76,25 @@ public final class RootProcessor extends BaseProcessor { @Override public void processEach(TypeElement annotation, Element element) throws Exception { TypeElement rootElement = MoreElements.asType(element); - RootType rootType = RootType.of(rootElement); - if (rootType.isTestRoot()) { + boolean isTestRoot = RootType.of(getProcessingEnv(), rootElement).isTestRoot(); + checkState( + rootNames.isEmpty() || isTestEnv == isTestRoot, + "Cannot mix test roots with non-test roots:" + + "\n\tNon-Test Roots: %s" + + "\n\tTest Roots: %s", + isTestRoot ? rootNames : rootElement, + isTestRoot ? rootElement : rootNames); + isTestEnv = isTestRoot; + + rootNames.add(ClassName.get(rootElement)); + if (isTestEnv) { new TestInjectorGenerator( - getProcessingEnv(), TestRootMetadata.of(getProcessingEnv(), rootElement)) - .generate(); + getProcessingEnv(), + TestRootMetadata.of(getProcessingEnv(), rootElement)).generate(); + } else { + ProcessorErrors.checkState( + rootNames.size() <= 1, element, "More than one root found: %s", rootNames); } - new AggregatedRootGenerator(rootElement, annotation, getProcessingEnv()).generate(); } @Override @@ -107,22 +114,14 @@ public final class RootProcessor extends BaseProcessor { return; } - ImmutableSet<Root> allRoots = - AggregatedRootMetadata.from(getElementUtils()).stream() - .map(metadata -> Root.create(metadata.rootElement(), getProcessingEnv())) - .collect(toImmutableSet()); - - ImmutableSet<Root> processedRoots = - ProcessedRootSentinelMetadata.from(getElementUtils()).stream() - .flatMap(metadata -> metadata.rootElements().stream()) + ImmutableList<Root> rootsToProcess = + rootNames.stream() + .filter(rootName -> !processed.contains(rootName)) + // We create a new root element each round to avoid the jdk8 bug where + // TypeElement.equals does not work for elements across processing rounds. + .map(rootName -> getElementUtils().getTypeElement(rootName.toString())) .map(rootElement -> Root.create(rootElement, getProcessingEnv())) - .collect(toImmutableSet()); - - ImmutableSet<Root> rootsToProcess = - allRoots.stream() - .filter(root -> !processedRoots.contains(root)) - .filter(root -> !processed.contains(rootName(root))) - .collect(toImmutableSet()); + .collect(toImmutableList()); if (rootsToProcess.isEmpty()) { // Skip further processing since there's no roots that need processing. @@ -133,163 +132,40 @@ public final class RootProcessor extends BaseProcessor { // all roots. We should consider if it's worth trying to continue processing for other // roots. At the moment, I think it's rare that if one root failed the others would not. try { - validateRoots(allRoots, rootsToProcess); - - boolean isTestEnv = rootsToProcess.stream().anyMatch(Root::isTestRoot); - ComponentNames componentNames = - isTestEnv && isSharedTestComponentsEnabled(getProcessingEnv()) - ? ComponentNames.withRenamingIntoPackage( - ClassNames.DEFAULT_ROOT.packageName(), - rootsToProcess.stream().map(Root::element).collect(toImmutableList())) - : ComponentNames.withoutRenaming(); - - ImmutableSet<ComponentDescriptor> componentDescriptors = - defineComponents.getComponentDescriptors(getElementUtils()); - ComponentTree tree = ComponentTree.from(componentDescriptors); - ComponentDependencies deps = - ComponentDependencies.from(componentDescriptors, getElementUtils()); + ComponentTree tree = defineComponents.getComponentTree(getElementUtils()); + ComponentDependencies deps = ComponentDependencies.from( + tree.getComponentDescriptors(), getElementUtils()); ImmutableList<RootMetadata> rootMetadatas = rootsToProcess.stream() .map(root -> RootMetadata.create(root, tree, deps, getProcessingEnv())) .collect(toImmutableList()); for (RootMetadata rootMetadata : rootMetadatas) { - if (!rootMetadata.canShareTestComponents()) { - generateComponents(rootMetadata, componentNames); - } + setProcessingState(rootMetadata.root()); + generateComponents(rootMetadata); } if (isTestEnv) { - ImmutableList<RootMetadata> rootsThatCanShareComponents = - rootMetadatas.stream() - .filter(RootMetadata::canShareTestComponents) - .collect(toImmutableList()); - generateTestComponentData(rootMetadatas, componentNames); - if (deps.hasEarlyEntryPoints() || !rootsThatCanShareComponents.isEmpty()) { - Root defaultRoot = Root.createDefaultRoot(getProcessingEnv()); - generateComponents( - RootMetadata.createForDefaultRoot( - defaultRoot, rootsThatCanShareComponents, tree, deps, getProcessingEnv()), - componentNames); - EarlySingletonComponentCreatorGenerator.generate(getProcessingEnv()); - } + generateTestComponentData(rootMetadatas); } } catch (Exception e) { for (Root root : rootsToProcess) { - processed.add(rootName(root)); + processed.add(root.classname()); } throw e; - } finally { - rootsToProcess.forEach(this::setProcessingState); - // Calculate the roots processed in this round. We do this in the finally-block rather than in - // the try-block because the catch-block can change the processing state. - ImmutableSet<Root> rootsProcessedInRound = - rootsToProcess.stream() - // Only add a sentinel for processed roots. Skip preprocessed roots since those will - // will be processed in the next round. - .filter(root -> processed.contains(rootName(root))) - .collect(toImmutableSet()); - for (Root root : rootsProcessedInRound) { - new ProcessedRootSentinelGenerator(rootElement(root), getProcessingEnv()).generate(); - } - } - } - - private void validateRoots(ImmutableSet<Root> allRoots, ImmutableSet<Root> rootsToProcess) { - - ImmutableSet<TypeElement> rootElementsToProcess = - rootsToProcess.stream() - .map(Root::element) - .sorted(QUALIFIED_NAME_COMPARATOR) - .collect(toImmutableSet()); - - ImmutableSet<TypeElement> appRootElementsToProcess = - rootsToProcess.stream() - .filter(root -> !root.isTestRoot()) - .map(Root::element) - .sorted(QUALIFIED_NAME_COMPARATOR) - .collect(toImmutableSet()); - - // Perform validation between roots in this compilation unit. - if (!appRootElementsToProcess.isEmpty()) { - ImmutableSet<TypeElement> testRootElementsToProcess = - rootsToProcess.stream() - .filter(Root::isTestRoot) - .map(Root::element) - .sorted(QUALIFIED_NAME_COMPARATOR) - .collect(toImmutableSet()); - - ProcessorErrors.checkState( - testRootElementsToProcess.isEmpty(), - "Cannot process test roots and app roots in the same compilation unit:" - + "\n\tApp root in this compilation unit: %s" - + "\n\tTest roots in this compilation unit: %s", - appRootElementsToProcess, - testRootElementsToProcess); - - ProcessorErrors.checkState( - appRootElementsToProcess.size() == 1, - "Cannot process multiple app roots in the same compilation unit: %s", - appRootElementsToProcess); - } - - // Perform validation across roots previous compilation units. - if (!isCrossCompilationRootValidationDisabled(rootElementsToProcess, getProcessingEnv())) { - ImmutableSet<TypeElement> processedTestRootElements = - allRoots.stream() - .filter(Root::isTestRoot) - .filter(root -> !rootsToProcess.contains(root)) - .map(Root::element) - .sorted(QUALIFIED_NAME_COMPARATOR) - .collect(toImmutableSet()); - - // TODO(b/185742783): Add an explanation or link to docs to explain why we're forbidding this. - ProcessorErrors.checkState( - processedTestRootElements.isEmpty(), - "Cannot process new roots when there are test roots from a previous compilation unit:" - + "\n\tTest roots from previous compilation unit: %s" - + "\n\tAll roots from this compilation unit: %s", - processedTestRootElements, - rootElementsToProcess); - - ImmutableSet<TypeElement> processedAppRootElements = - allRoots.stream() - .filter(root -> !root.isTestRoot()) - .filter(root -> !rootsToProcess.contains(root)) - .map(Root::element) - .sorted(QUALIFIED_NAME_COMPARATOR) - .collect(toImmutableSet()); - - ProcessorErrors.checkState( - processedAppRootElements.isEmpty() || appRootElementsToProcess.isEmpty(), - "Cannot process app roots in this compilation unit since there are app roots in a " - + "previous compilation unit:" - + "\n\tApp roots in previous compilation unit: %s" - + "\n\tApp roots in this compilation unit: %s", - processedAppRootElements, - appRootElementsToProcess); } } private void setProcessingState(Root root) { - processed.add(rootName(root)); + processed.add(root.classname()); } - private ClassName rootName(Root root) { - return ClassName.get(rootElement(root)); + private void generateComponents(RootMetadata rootMetadata) throws IOException { + RootGenerator.generate(rootMetadata, getProcessingEnv()); } - private TypeElement rootElement(Root root) { - return root.element(); - } - - private void generateComponents(RootMetadata rootMetadata, ComponentNames componentNames) + private void generateTestComponentData(ImmutableList<RootMetadata> rootMetadatas) throws IOException { - RootGenerator.generate(rootMetadata, componentNames, getProcessingEnv()); - } - - private void generateTestComponentData( - ImmutableList<RootMetadata> rootMetadatas, ComponentNames componentNames) throws IOException { for (RootMetadata rootMetadata : rootMetadatas) { // TODO(bcorso): Consider moving this check earlier into processEach. TypeElement testElement = rootMetadata.testRootMetadata().testElement(); @@ -298,7 +174,8 @@ public final class RootProcessor extends BaseProcessor { testElement, "Hilt tests must be public, but found: %s", testElement); - new TestComponentDataGenerator(getProcessingEnv(), rootMetadata, componentNames).generate(); + new TestComponentDataGenerator(getProcessingEnv(), rootMetadata).generate(); } + new TestComponentDataSupplierGenerator(getProcessingEnv(), rootMetadatas).generate(); } } diff --git a/java/dagger/hilt/processor/internal/root/RootType.java b/java/dagger/hilt/processor/internal/root/RootType.java index 807f71d73..3545231c7 100644 --- a/java/dagger/hilt/processor/internal/root/RootType.java +++ b/java/dagger/hilt/processor/internal/root/RootType.java @@ -19,6 +19,7 @@ package dagger.hilt.processor.internal.root; import com.squareup.javapoet.ClassName; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.Processors; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.TypeElement; /** The valid root types for Hilt applications. */ @@ -46,7 +47,7 @@ import javax.lang.model.element.TypeElement; return annotation; } - public static RootType of(TypeElement element) { + public static RootType of(ProcessingEnvironment env, TypeElement element) { if (Processors.hasAnnotation(element, ClassNames.HILT_ANDROID_APP)) { return ROOT; } else if (Processors.hasAnnotation(element, ClassNames.HILT_ANDROID_TEST)) { diff --git a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java index 101c124d9..284f8cdee 100644 --- a/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java +++ b/java/dagger/hilt/processor/internal/root/TestComponentDataGenerator.java @@ -20,7 +20,6 @@ import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import static java.util.stream.Collectors.joining; import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; -import static javax.lang.model.element.Modifier.PROTECTED; import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import static javax.lang.model.util.ElementFilter.constructorsIn; @@ -46,28 +45,24 @@ public final class TestComponentDataGenerator { private final ProcessingEnvironment processingEnv; private final RootMetadata rootMetadata; private final ClassName name; - private final ComponentNames componentNames; public TestComponentDataGenerator( ProcessingEnvironment processingEnv, - RootMetadata rootMetadata, - ComponentNames componentNames) { + RootMetadata rootMetadata) { this.processingEnv = processingEnv; this.rootMetadata = rootMetadata; - this.componentNames = componentNames; this.name = Processors.append( Processors.getEnclosedClassName(rootMetadata.testRootMetadata().testName()), - "_TestComponentDataSupplier"); + "_ComponentDataHolder"); } /** * * * <pre><code>{@code - * public final class FooTest_TestComponentDataSupplier extends TestComponentDataSupplier { - * @Override - * protected TestComponentData get() { + * public final class FooTest_ComponentDataHolder { + * public static TestComponentData get() { * return new TestComponentData( * false, // waitForBindValue * testInstance -> injectInternal(($1T) testInstance), @@ -88,15 +83,15 @@ public final class TestComponentDataGenerator { public void generate() throws IOException { TypeSpec.Builder generator = TypeSpec.classBuilder(name) - .superclass(ClassNames.TEST_COMPONENT_DATA_SUPPLIER) .addModifiers(PUBLIC, FINAL) + .addMethod(MethodSpec.constructorBuilder().addModifiers(PRIVATE).build()) .addMethod(getMethod()) .addMethod(getTestInjectInternalMethod()); Processors.addGeneratedAnnotation( generator, processingEnv, ClassNames.ROOT_PROCESSOR.toString()); - JavaFile.builder(name.packageName(), generator.build()) + JavaFile.builder(rootMetadata.testRootMetadata().testName().packageName(), generator.build()) .build() .writeTo(processingEnv.getFiler()); } @@ -104,11 +99,8 @@ public final class TestComponentDataGenerator { private MethodSpec getMethod() { TypeElement testElement = rootMetadata.testRootMetadata().testElement(); ClassName component = - componentNames.generatedComponent( - rootMetadata.canShareTestComponents() - ? ClassNames.DEFAULT_ROOT - : ClassName.get(testElement), - ClassNames.SINGLETON_COMPONENT); + ComponentNames.generatedComponent( + ClassName.get(testElement), ClassNames.SINGLETON_COMPONENT); ImmutableSet<TypeElement> daggerRequiredModules = rootMetadata.modulesThatDaggerCannotConstruct(ClassNames.SINGLETON_COMPONENT); ImmutableSet<TypeElement> hiltRequiredModules = @@ -117,7 +109,7 @@ public final class TestComponentDataGenerator { .collect(toImmutableSet()); return MethodSpec.methodBuilder("get") - .addModifiers(PROTECTED) + .addModifiers(PUBLIC, STATIC) .returns(ClassNames.TEST_COMPONENT_DATA) .addStatement( "return new $T($L, $L, $L, $L, $L)", @@ -210,14 +202,14 @@ public final class TestComponentDataGenerator { AnnotationSpec.builder(SuppressWarnings.class) .addMember("value", "$S", "unchecked") .build()) - .addStatement(callInjectTest(testElement)) + .addStatement("$L.injectTest(testInstance)", getInjector(testElement)) .build(); } - private CodeBlock callInjectTest(TypeElement testElement) { + private static CodeBlock getInjector(TypeElement testElement) { return CodeBlock.of( - "(($T) (($T) $T.getApplicationContext()).generatedComponent()).injectTest(testInstance)", - rootMetadata.testRootMetadata().testInjectorName(), + "(($T) (($T) $T.getApplicationContext()).generatedComponent())", + ClassNames.TEST_INJECTOR, ClassNames.GENERATED_COMPONENT_MANAGER, ClassNames.APPLICATION_PROVIDER); } diff --git a/java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java b/java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java new file mode 100644 index 000000000..e5a83b63c --- /dev/null +++ b/java/dagger/hilt/processor/internal/root/TestComponentDataSupplierGenerator.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 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.hilt.processor.internal.root; + +import static javax.lang.model.element.Modifier.FINAL; +import static javax.lang.model.element.Modifier.PRIVATE; +import static javax.lang.model.element.Modifier.PROTECTED; +import static javax.lang.model.element.Modifier.PUBLIC; + +import com.google.common.collect.ImmutableList; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.FieldSpec; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.WildcardTypeName; +import dagger.hilt.processor.internal.ClassNames; +import dagger.hilt.processor.internal.Processors; +import java.io.IOException; +import javax.annotation.processing.ProcessingEnvironment; + +/** Generates an implementation of {@link dagger.hilt.android.internal.TestComponentDataSupplier} */ +public final class TestComponentDataSupplierGenerator { + private static final ClassName TEST_COMPONENT_DATA_SUPPLIER_IMPL = + ClassName.get("dagger.hilt.android.internal.testing", "TestComponentDataSupplierImpl"); + private static final ParameterizedTypeName CLASS_TYPE = + ParameterizedTypeName.get(ClassNames.CLASS, WildcardTypeName.subtypeOf(TypeName.OBJECT)); + private static final ParameterizedTypeName TEST_COMPONENT_DATA_MAP_TYPE = + ParameterizedTypeName.get(ClassNames.MAP, CLASS_TYPE, ClassNames.TEST_COMPONENT_DATA); + + private final ProcessingEnvironment processingEnv; + private final ImmutableList<RootMetadata> rootMetadatas; + + public TestComponentDataSupplierGenerator( + ProcessingEnvironment processingEnv, + ImmutableList<RootMetadata> rootMetadatas) { + this.processingEnv = processingEnv; + this.rootMetadatas = rootMetadatas; + } + + /** + * <pre><code>{@code + * public final class TestComponentDataSupplierImpl extends TestComponentDataSupplier { + * private final Map<Class<?>, TestComponentData> testComponentDataMap = new HashMap<>(); + * + * protected TestComponentDataSupplierImpl() { + * testComponentDataMap.put(FooTest.class, new FooTest_ComponentData()); + * testComponentDataMap.put(BarTest.class, new BarTest_ComponentData()); + * ... + * } + * + * @Override + * protected Map<Class<?>, TestComponentData> get() { + * return testComponentDataMap; + * } + * } + * }</code></pre> + */ + public void generate() throws IOException { + TypeSpec.Builder generator = + TypeSpec.classBuilder(TEST_COMPONENT_DATA_SUPPLIER_IMPL) + .addModifiers(PUBLIC, FINAL) + .superclass(ClassNames.TEST_COMPONENT_DATA_SUPPLIER) + .addField( + FieldSpec.builder( + TEST_COMPONENT_DATA_MAP_TYPE, "testComponentDataMap", PRIVATE, FINAL) + .initializer("new $T<>($L)", ClassNames.HASH_MAP, rootMetadatas.size()) + .build()) + .addMethod(constructor()) + .addMethod(getMethod()); + + Processors.addGeneratedAnnotation( + generator, processingEnv, ClassNames.ROOT_PROCESSOR.toString()); + + JavaFile.builder(TEST_COMPONENT_DATA_SUPPLIER_IMPL.packageName(), generator.build()) + .build() + .writeTo(processingEnv.getFiler()); + } + + + private MethodSpec constructor() { + MethodSpec.Builder constructor = MethodSpec.constructorBuilder(); + for (RootMetadata rootMetadata : rootMetadatas) { + ClassName testName = rootMetadata.testRootMetadata().testName(); + ClassName testComponentDataHolderName = + Processors.append(Processors.getEnclosedClassName(testName), "_ComponentDataHolder"); + constructor.addStatement( + "testComponentDataMap.put($T.class, $T.get())", + testName, + testComponentDataHolderName); + } + return constructor.build(); + } + + private MethodSpec getMethod() { + return MethodSpec.methodBuilder("get") + .addAnnotation(Override.class) + .addModifiers(PROTECTED) + .returns(TEST_COMPONENT_DATA_MAP_TYPE) + .addStatement("return testComponentDataMap") + .build(); + } +} diff --git a/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java b/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java index b7200e7fb..fc97feda6 100644 --- a/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java +++ b/java/dagger/hilt/processor/internal/root/TestInjectorGenerator.java @@ -20,6 +20,7 @@ import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeSpec; import dagger.hilt.processor.internal.ClassNames; import dagger.hilt.processor.internal.Processors; @@ -40,9 +41,7 @@ public final class TestInjectorGenerator { // @GeneratedEntryPoint // @InstallIn(SingletonComponent.class) - // public interface FooTest_GeneratedInjector { - // void injectTest(FooTest fooTest); - // } + // public interface FooTest_GeneratedInjector extends TestInjector<FooTest> {} public void generate() throws IOException { TypeSpec.Builder builder = TypeSpec.interfaceBuilder(metadata.testInjectorName()) @@ -54,8 +53,11 @@ public final class TestInjectorGenerator { .addMember("value", "$T.class", installInComponent(metadata.testElement())) .build()) .addModifiers(Modifier.PUBLIC) + .addSuperinterface( + ParameterizedTypeName.get(ClassNames.TEST_INJECTOR, metadata.testName())) .addMethod( MethodSpec.methodBuilder("injectTest") + .addAnnotation(Override.class) .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) .addParameter( metadata.testName(), diff --git a/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesGenerator.java b/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesGenerator.java deleted file mode 100644 index 654a690c4..000000000 --- a/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesGenerator.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.uninstallmodules; - -import com.google.common.collect.ImmutableList; -import com.squareup.javapoet.AnnotationSpec; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import java.io.IOException; -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.TypeElement; - -/** - * Generates an {@link dagger.hilt.android.internal.uninstallmodules.AggregatedUninstallModules} - * annotation. - */ -final class AggregatedUninstallModulesGenerator { - - private final ProcessingEnvironment env; - private final TypeElement testElement; - private final ImmutableList<TypeElement> uninstallModuleElements; - - AggregatedUninstallModulesGenerator( - TypeElement testElement, - ImmutableList<TypeElement> uninstallModuleElements, - ProcessingEnvironment env) { - this.testElement = testElement; - this.uninstallModuleElements = uninstallModuleElements; - this.env = env; - } - - void generate() throws IOException { - Processors.generateAggregatingClass( - ClassNames.AGGREGATED_UNINSTALL_MODULES_PACKAGE, - aggregatedUninstallModulesAnnotation(), - testElement, - getClass(), - env); - } - - private AnnotationSpec aggregatedUninstallModulesAnnotation() { - AnnotationSpec.Builder builder = - AnnotationSpec.builder(ClassNames.AGGREGATED_UNINSTALL_MODULES); - builder.addMember("test", "$S", testElement.getQualifiedName()); - uninstallModuleElements.stream() - .map(TypeElement::getQualifiedName) - .forEach(uninstallModule -> builder.addMember("uninstallModules", "$S", uninstallModule)); - return builder.build(); - } -} diff --git a/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java b/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java deleted file mode 100644 index eee78490d..000000000 --- a/java/dagger/hilt/processor/internal/uninstallmodules/AggregatedUninstallModulesMetadata.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2021 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.hilt.processor.internal.uninstallmodules; - -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; - -import com.google.auto.value.AutoValue; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import dagger.hilt.processor.internal.AggregatedElements; -import dagger.hilt.processor.internal.AnnotationValues; -import dagger.hilt.processor.internal.ClassNames; -import dagger.hilt.processor.internal.Processors; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.AnnotationValue; -import javax.lang.model.element.TypeElement; -import javax.lang.model.util.Elements; - -/** - * A class that represents the values stored in an - * {@link dagger.hilt.android.internal.uninstallmodules.AggregatedUninstallModules} annotation. - */ -@AutoValue -public abstract class AggregatedUninstallModulesMetadata { - - /** Returns the test annotated with {@link dagger.hilt.android.testing.UninstallModules}. */ - public abstract TypeElement testElement(); - - /** - * Returns the list of uninstall modules in {@link dagger.hilt.android.testing.UninstallModules}. - */ - public abstract ImmutableList<TypeElement> uninstallModuleElements(); - - /** Returns all aggregated deps in the aggregating package mapped by the top-level element. */ - public static ImmutableSet<AggregatedUninstallModulesMetadata> from(Elements elements) { - return AggregatedElements.from( - ClassNames.AGGREGATED_UNINSTALL_MODULES_PACKAGE, - ClassNames.AGGREGATED_UNINSTALL_MODULES, - elements) - .stream() - .map(aggregatedElement -> create(aggregatedElement, elements)) - .collect(toImmutableSet()); - } - - private static AggregatedUninstallModulesMetadata create(TypeElement element, Elements elements) { - AnnotationMirror annotationMirror = - Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_UNINSTALL_MODULES); - - ImmutableMap<String, AnnotationValue> values = - Processors.getAnnotationValues(elements, annotationMirror); - - return new AutoValue_AggregatedUninstallModulesMetadata( - elements.getTypeElement(AnnotationValues.getString(values.get("test"))), - AnnotationValues.getAnnotationValues(values.get("uninstallModules")).stream() - .map(AnnotationValues::getString) - .map(elements::getTypeElement) - .collect(toImmutableList())); - } -} diff --git a/java/dagger/hilt/proguard-rules.pro b/java/dagger/hilt/proguard-rules.pro deleted file mode 100644 index 3b953dfa1..000000000 --- a/java/dagger/hilt/proguard-rules.pro +++ /dev/null @@ -1,3 +0,0 @@ -# Keep for the reflective cast done in EntryPoints. -# See b/183070411#comment4 for more info. --keep,allowobfuscation,allowshrinking @dagger.hilt.EntryPoint class *
\ No newline at end of file diff --git a/java/dagger/internal/DaggerGenerated.java b/java/dagger/internal/DaggerGenerated.java deleted file mode 100644 index 6a872e0f3..000000000 --- a/java/dagger/internal/DaggerGenerated.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2021 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; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.CLASS; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** Annotates the top-level class of each Dagger generated source file. */ -@Documented -@Retention(CLASS) -@Target(TYPE) -public @interface DaggerGenerated {} diff --git a/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java b/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java index dbd2b3365..abc0436a0 100644 --- a/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java +++ b/java/dagger/internal/codegen/AssistedFactoryProcessingStep.java @@ -34,7 +34,6 @@ import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import com.google.auto.common.MoreElements; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; @@ -138,7 +137,7 @@ final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<Typ } ImmutableSet<ExecutableElement> abstractFactoryMethods = - AssistedInjectionAnnotations.assistedFactoryMethods(factory, elements); + AssistedInjectionAnnotations.assistedFactoryMethods(factory, elements, types); if (abstractFactoryMethods.isEmpty()) { report.addError( @@ -227,6 +226,11 @@ final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<Typ } @Override + public ClassName nameGeneratedType(ProvisionBinding binding) { + return generatedClassNameForBinding(binding); + } + + @Override public Element originatingElement(ProvisionBinding binding) { return binding.bindingElement().get(); } @@ -264,10 +268,10 @@ final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<Typ // } // } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(ProvisionBinding binding) { + public Optional<TypeSpec.Builder> write(ProvisionBinding binding) { TypeElement factory = asType(binding.bindingElement().get()); - ClassName name = generatedClassNameForBinding(binding); + ClassName name = nameGeneratedType(binding); TypeSpec.Builder builder = TypeSpec.classBuilder(name) .addModifiers(PUBLIC, FINAL) @@ -329,7 +333,7 @@ final class AssistedFactoryProcessingStep extends TypeCheckingProcessingStep<Typ name, delegateFactoryParam) .build()); - return ImmutableList.of(builder); + return Optional.of(builder); } /** Returns the generated factory {@link TypeName type} for an @AssistedInject constructor. */ diff --git a/java/dagger/internal/codegen/AssistedInjectProcessingStep.java b/java/dagger/internal/codegen/AssistedInjectProcessingStep.java index 167c4aebe..4f2f5b7b7 100644 --- a/java/dagger/internal/codegen/AssistedInjectProcessingStep.java +++ b/java/dagger/internal/codegen/AssistedInjectProcessingStep.java @@ -76,10 +76,7 @@ final class AssistedInjectProcessingStep extends TypeCheckingProcessingStep<Exec for (AssistedParameter assistedParameter : assistedParameters) { if (!uniqueAssistedParameters.add(assistedParameter)) { report.addError( - String.format("@AssistedInject constructor has duplicate @Assisted type: %s. " - + "Consider setting an identifier on the parameter by using " - + "@Assisted(\"identifier\") in both the factory and @AssistedInject constructor", - assistedParameter), + "@AssistedInject constructor has duplicate @Assisted type: " + assistedParameter, assistedParameter.variableElement()); } } diff --git a/java/dagger/internal/codegen/AssistedProcessingStep.java b/java/dagger/internal/codegen/AssistedProcessingStep.java index 12a0d34b4..3173987e1 100644 --- a/java/dagger/internal/codegen/AssistedProcessingStep.java +++ b/java/dagger/internal/codegen/AssistedProcessingStep.java @@ -27,6 +27,7 @@ import dagger.internal.codegen.binding.AssistedInjectionAnnotations; import dagger.internal.codegen.binding.InjectionAnnotations; import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import dagger.internal.codegen.langmodel.DaggerElements; +import dagger.internal.codegen.langmodel.DaggerTypes; import dagger.internal.codegen.validation.TypeCheckingProcessingStep; import dagger.internal.codegen.validation.ValidationReport; import java.lang.annotation.Annotation; @@ -46,6 +47,7 @@ final class AssistedProcessingStep extends TypeCheckingProcessingStep<VariableEl private final KotlinMetadataUtil kotlinMetadataUtil; private final InjectionAnnotations injectionAnnotations; private final DaggerElements elements; + private final DaggerTypes types; private final Messager messager; @Inject @@ -53,11 +55,13 @@ final class AssistedProcessingStep extends TypeCheckingProcessingStep<VariableEl KotlinMetadataUtil kotlinMetadataUtil, InjectionAnnotations injectionAnnotations, DaggerElements elements, + DaggerTypes types, Messager messager) { super(MoreElements::asVariable); this.kotlinMetadataUtil = kotlinMetadataUtil; this.injectionAnnotations = injectionAnnotations; this.elements = elements; + this.types = types; this.messager = messager; } @@ -109,7 +113,7 @@ final class AssistedProcessingStep extends TypeCheckingProcessingStep<VariableEl TypeElement enclosingElement = closestEnclosingTypeElement(element); return AssistedInjectionAnnotations.isAssistedFactoryType(enclosingElement) // This assumes we've already validated AssistedFactory and that a valid method exists. - && AssistedInjectionAnnotations.assistedFactoryMethod(enclosingElement, elements) + && AssistedInjectionAnnotations.assistedFactoryMethod(enclosingElement, elements, types) .equals(element); } return false; diff --git a/java/dagger/internal/codegen/BUILD b/java/dagger/internal/codegen/BUILD index e2f74e9e7..be970be12 100644 --- a/java/dagger/internal/codegen/BUILD +++ b/java/dagger/internal/codegen/BUILD @@ -127,8 +127,6 @@ java_plugin( "genclass=${package}.Dagger${outerclasses}${classname}", "annotation=dagger.producers.ProductionComponent;" + "genclass=${package}.Dagger${outerclasses}${classname}", - "annotation=dagger.MapKey;" + - "genclass=${package}.${outerclasses}${classname}Creator", ], deps = [":processor"], ) diff --git a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java index 120714b33..8a535fe13 100644 --- a/java/dagger/internal/codegen/ProcessingEnvironmentModule.java +++ b/java/dagger/internal/codegen/ProcessingEnvironmentModule.java @@ -22,18 +22,15 @@ import dagger.Module; import dagger.Provides; import dagger.Reusable; import dagger.internal.codegen.SpiModule.ProcessorClassLoader; -import dagger.internal.codegen.base.ClearableCache; import dagger.internal.codegen.compileroption.CompilerOptions; import dagger.internal.codegen.compileroption.ProcessingEnvironmentCompilerOptions; import dagger.internal.codegen.compileroption.ProcessingOptions; import dagger.internal.codegen.langmodel.DaggerElements; -import dagger.multibindings.IntoSet; import dagger.spi.BindingGraphPlugin; import java.util.Map; import javax.annotation.processing.Filer; import javax.annotation.processing.Messager; import javax.annotation.processing.ProcessingEnvironment; -import javax.inject.Singleton; import javax.lang.model.SourceVersion; import javax.lang.model.util.Types; @@ -76,15 +73,10 @@ interface ProcessingEnvironmentModule { } @Provides - @Singleton static DaggerElements daggerElements(ProcessingEnvironment processingEnvironment) { return new DaggerElements(processingEnvironment); } - @Binds - @IntoSet - ClearableCache daggerElementAsClearableCache(DaggerElements elements); - @Provides @ProcessorClassLoader static ClassLoader processorClassloader(ProcessingEnvironment processingEnvironment) { diff --git a/java/dagger/internal/codegen/base/ComponentAnnotation.java b/java/dagger/internal/codegen/base/ComponentAnnotation.java index b70ef54bb..c9b3580c1 100644 --- a/java/dagger/internal/codegen/base/ComponentAnnotation.java +++ b/java/dagger/internal/codegen/base/ComponentAnnotation.java @@ -57,19 +57,6 @@ public abstract class ComponentAnnotation { private static final ImmutableSet<Class<? extends Annotation>> SUBCOMPONENT_ANNOTATIONS = ImmutableSet.of(Subcomponent.class, ProductionSubcomponent.class); - // TODO(erichang): Move ComponentCreatorAnnotation into /base and use that here? - /** The component/subcomponent creator annotation types. */ - private static final ImmutableSet<Class<? extends Annotation>> CREATOR_ANNOTATIONS = - ImmutableSet.of( - Component.Builder.class, - Component.Factory.class, - ProductionComponent.Builder.class, - ProductionComponent.Factory.class, - Subcomponent.Builder.class, - Subcomponent.Factory.class, - ProductionSubcomponent.Builder.class, - ProductionSubcomponent.Factory.class); - /** All component annotation types. */ private static final ImmutableSet<Class<? extends Annotation>> ALL_COMPONENT_ANNOTATIONS = ImmutableSet.<Class<? extends Annotation>>builder() @@ -77,13 +64,6 @@ public abstract class ComponentAnnotation { .addAll(SUBCOMPONENT_ANNOTATIONS) .build(); - /** All component and creator annotation types. */ - private static final ImmutableSet<Class<? extends Annotation>> - ALL_COMPONENT_AND_CREATOR_ANNOTATIONS = ImmutableSet.<Class<? extends Annotation>>builder() - .addAll(ALL_COMPONENT_ANNOTATIONS) - .addAll(CREATOR_ANNOTATIONS) - .build(); - /** The annotation itself. */ public abstract AnnotationMirror annotation(); @@ -226,11 +206,6 @@ public abstract class ComponentAnnotation { return ALL_COMPONENT_ANNOTATIONS; } - /** All component and creator annotation types. */ - public static ImmutableSet<Class<? extends Annotation>> allComponentAndCreatorAnnotations() { - return ALL_COMPONENT_AND_CREATOR_ANNOTATIONS; - } - /** * An actual component annotation. * diff --git a/java/dagger/internal/codegen/base/Keys.java b/java/dagger/internal/codegen/base/Keys.java index 4d139266d..a25f9963f 100644 --- a/java/dagger/internal/codegen/base/Keys.java +++ b/java/dagger/internal/codegen/base/Keys.java @@ -16,10 +16,6 @@ package dagger.internal.codegen.base; -import static com.google.auto.common.MoreTypes.asTypeElement; -import static dagger.internal.codegen.base.ComponentAnnotation.allComponentAndCreatorAnnotations; -import static dagger.internal.codegen.langmodel.DaggerElements.isAnyAnnotationPresent; - import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import dagger.internal.codegen.langmodel.DaggerTypes; @@ -92,14 +88,4 @@ public final class Keys { }, null); } - - /** - * Returns {@code true} if the given key is for a component/subcomponent or a creator of a - * component/subcomponent. - */ - public static boolean isComponentOrCreator(Key key) { - return !key.qualifier().isPresent() - && key.type().getKind() == TypeKind.DECLARED - && isAnyAnnotationPresent(asTypeElement(key.type()), allComponentAndCreatorAnnotations()); - } } diff --git a/java/dagger/internal/codegen/base/SourceFileGenerator.java b/java/dagger/internal/codegen/base/SourceFileGenerator.java index ada67d30d..02348a4f4 100644 --- a/java/dagger/internal/codegen/base/SourceFileGenerator.java +++ b/java/dagger/internal/codegen/base/SourceFileGenerator.java @@ -16,20 +16,17 @@ package dagger.internal.codegen.base; - import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.RAWTYPES; import static dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression.UNCHECKED; import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; -import dagger.internal.DaggerGenerated; import dagger.internal.codegen.javapoet.AnnotationSpecs; import dagger.internal.codegen.javapoet.AnnotationSpecs.Suppression; import dagger.internal.codegen.langmodel.DaggerElements; @@ -77,21 +74,22 @@ public abstract class SourceFileGenerator<T> { /** Generates a source file to be compiled for {@code T}. */ public void generate(T input) throws SourceFileGenerationException { - for (TypeSpec.Builder type : topLevelTypes(input)) { - try { - buildJavaFile(input, type).writeTo(filer); - } catch (Exception e) { - // if the code above threw a SFGE, use that - Throwables.propagateIfPossible(e, SourceFileGenerationException.class); - // otherwise, throw a new one - throw new SourceFileGenerationException(Optional.empty(), e, originatingElement(input)); - } + Optional<TypeSpec.Builder> type = write(input); + if (!type.isPresent()) { + return; + } + try { + buildJavaFile(input, type.get()).writeTo(filer); + } catch (Exception e) { + // if the code above threw a SFGE, use that + Throwables.propagateIfPossible(e, SourceFileGenerationException.class); + // otherwise, throw a new one + throw new SourceFileGenerationException(Optional.empty(), e, originatingElement(input)); } } private JavaFile buildJavaFile(T input, TypeSpec.Builder typeSpecBuilder) { typeSpecBuilder.addOriginatingElement(originatingElement(input)); - typeSpecBuilder.addAnnotation(DaggerGenerated.class); Optional<AnnotationSpec> generatedAnnotation = generatedAnnotation(elements, sourceVersion) .map( @@ -111,9 +109,7 @@ public abstract class SourceFileGenerator<T> { .build())); JavaFile.Builder javaFileBuilder = - JavaFile.builder( - elements.getPackageOf(originatingElement(input)).getQualifiedName().toString(), - typeSpecBuilder.build()) + JavaFile.builder(nameGeneratedType(input).packageName(), typeSpecBuilder.build()) .skipJavaLangImports(true); if (!generatedAnnotation.isPresent()) { javaFileBuilder.addFileComment("Generated by Dagger ($L).", GENERATED_COMMENTS); @@ -121,16 +117,19 @@ public abstract class SourceFileGenerator<T> { return javaFileBuilder.build(); } + /** Implementations should return the {@link ClassName} for the top-level type to be generated. */ + public abstract ClassName nameGeneratedType(T input); + /** Returns the originating element of the generating type. */ public abstract Element originatingElement(T input); /** - * Returns {@link TypeSpec.Builder types} be generated for {@code T}, or an empty list if no types - * should be generated. - * - * <p>Every type will be generated in its own file. + * Returns a {@link TypeSpec.Builder type} to be generated for {@code T}, or {@link + * Optional#empty()} if no file should be generated. */ - public abstract ImmutableList<TypeSpec.Builder> topLevelTypes(T input); + // TODO(ronshapiro): write() makes more sense in JavaWriter where all writers are mutable. + // consider renaming to something like typeBuilder() which conveys the mutability of the result + public abstract Optional<TypeSpec.Builder> write(T input); /** Returns {@link Suppression}s that are applied to files generated by this generator. */ // TODO(b/134590785): When suppressions are removed locally, remove this and inline the usages diff --git a/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java index f9929aef6..8d6ee5df6 100644 --- a/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java +++ b/java/dagger/internal/codegen/binding/AssistedInjectionAnnotations.java @@ -59,14 +59,14 @@ import javax.lang.model.type.TypeMirror; public final class AssistedInjectionAnnotations { /** Returns the factory method for the given factory {@link TypeElement}. */ public static ExecutableElement assistedFactoryMethod( - TypeElement factory, DaggerElements elements) { - return getOnlyElement(assistedFactoryMethods(factory, elements)); + TypeElement factory, DaggerElements elements, DaggerTypes types) { + return getOnlyElement(assistedFactoryMethods(factory, elements, types)); } /** Returns the list of abstract factory methods for the given factory {@link TypeElement}. */ public static ImmutableSet<ExecutableElement> assistedFactoryMethods( - TypeElement factory, DaggerElements elements) { - return elements.getLocalAndInheritedMethods(factory).stream() + TypeElement factory, DaggerElements elements, DaggerTypes types) { + return MoreElements.getLocalAndInheritedMethods(factory, types, elements).stream() .filter(method -> method.getModifiers().contains(ABSTRACT)) .filter(method -> !method.isDefault()) .collect(toImmutableSet()); @@ -170,7 +170,7 @@ public final class AssistedInjectionAnnotations { TypeMirror factory, DaggerElements elements, DaggerTypes types) { DeclaredType factoryType = asDeclared(factory); TypeElement factoryElement = asTypeElement(factoryType); - ExecutableElement factoryMethod = assistedFactoryMethod(factoryElement, elements); + ExecutableElement factoryMethod = assistedFactoryMethod(factoryElement, elements, types); ExecutableType factoryMethodType = asExecutable(types.asMemberOf(factoryType, factoryMethod)); DeclaredType assistedInjectType = asDeclared(factoryMethodType.getReturnType()); return new AutoValue_AssistedInjectionAnnotations_AssistedFactoryMetadata( diff --git a/java/dagger/internal/codegen/binding/BindingFactory.java b/java/dagger/internal/codegen/binding/BindingFactory.java index eb7c1322a..6f2fc805e 100644 --- a/java/dagger/internal/codegen/binding/BindingFactory.java +++ b/java/dagger/internal/codegen/binding/BindingFactory.java @@ -191,7 +191,7 @@ public final class BindingFactory { } ExecutableElement factoryMethod = - AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements); + AssistedInjectionAnnotations.assistedFactoryMethod(factory, elements, types); ExecutableType factoryMethodType = MoreTypes.asExecutable(types.asMemberOf(factoryType, factoryMethod)); return ProvisionBinding.builder() diff --git a/java/dagger/internal/codegen/binding/BindingGraph.java b/java/dagger/internal/codegen/binding/BindingGraph.java index 533d113f1..4936a0526 100644 --- a/java/dagger/internal/codegen/binding/BindingGraph.java +++ b/java/dagger/internal/codegen/binding/BindingGraph.java @@ -16,7 +16,6 @@ package dagger.internal.codegen.binding; -import static com.google.common.collect.Iterables.transform; import static dagger.internal.codegen.extension.DaggerCollectors.toOptional; import static dagger.internal.codegen.extension.DaggerStreams.presentValues; import static dagger.internal.codegen.extension.DaggerStreams.stream; @@ -27,12 +26,11 @@ import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet; import com.google.auto.value.AutoValue; import com.google.auto.value.extension.memoized.Memoized; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Multimaps; import com.google.common.collect.Sets; +import com.google.common.graph.Graphs; import com.google.common.graph.ImmutableNetwork; import com.google.common.graph.Traverser; import dagger.model.BindingGraph.ChildFactoryMethodEdge; @@ -41,9 +39,7 @@ import dagger.model.BindingGraph.Edge; import dagger.model.BindingGraph.Node; import dagger.model.ComponentPath; import dagger.model.Key; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import javax.lang.model.element.ExecutableElement; @@ -108,15 +104,6 @@ public abstract class BindingGraph { public ImmutableSetMultimap<Class<? extends Node>, ? extends Node> nodesByClass() { return super.nodesByClass(); } - - /** - * Returns an index of each {@link BindingNode} by its {@link ComponentPath}. Accessing this for - * a component and its parent components is faster than doing a graph traversal. - */ - @Memoized - ImmutableListMultimap<ComponentPath, BindingNode> bindingsByComponent() { - return Multimaps.index(transform(bindings(), BindingNode.class::cast), Node::componentPath); - } } static BindingGraph create( @@ -128,12 +115,12 @@ public abstract class BindingGraph { Optional<BindingGraph> parent, ComponentNode componentNode, TopLevelBindingGraph topLevelBindingGraph) { - List<BindingNode> reachableBindingNodes = new ArrayList<>(); - for (ComponentPath path = componentNode.componentPath(); - !path.components().isEmpty(); - path = ComponentPath.create(path.components().subList(0, path.components().size() - 1))) { - reachableBindingNodes.addAll(topLevelBindingGraph.bindingsByComponent().get(path)); - } + ImmutableSet<BindingNode> reachableBindingNodes = + Graphs.reachableNodes(topLevelBindingGraph.network().asGraph(), componentNode).stream() + .filter(node -> isSubpath(componentNode.componentPath(), node.componentPath())) + .filter(node -> node instanceof BindingNode) + .map(node -> (BindingNode) node) + .collect(toImmutableSet()); // Construct the maps of the ContributionBindings and MembersInjectionBindings. Map<Key, BindingNode> contributionBindings = new HashMap<>(); @@ -344,4 +331,17 @@ public abstract class BindingGraph { .addAll(membersInjectionBindings.values()) .build(); } + + // TODO(bcorso): Move this to ComponentPath + private static boolean isSubpath(ComponentPath path, ComponentPath subpath) { + if (path.components().size() < subpath.components().size()) { + return false; + } + for (int i = 0; i < subpath.components().size(); i++) { + if (!path.components().get(i).equals(subpath.components().get(i))) { + return false; + } + } + return true; + } } diff --git a/java/dagger/internal/codegen/binding/BindingGraphFactory.java b/java/dagger/internal/codegen/binding/BindingGraphFactory.java index a94f6b01e..2c15e2625 100644 --- a/java/dagger/internal/codegen/binding/BindingGraphFactory.java +++ b/java/dagger/internal/codegen/binding/BindingGraphFactory.java @@ -47,7 +47,6 @@ import dagger.MembersInjector; import dagger.Reusable; import dagger.internal.codegen.base.ClearableCache; import dagger.internal.codegen.base.ContributionType; -import dagger.internal.codegen.base.Keys; import dagger.internal.codegen.base.MapType; import dagger.internal.codegen.base.OptionalType; import dagger.internal.codegen.compileroption.CompilerOptions; @@ -797,7 +796,7 @@ public final class BindingGraphFactory implements ClearableCache { * 2. If there are any explicit bindings in this component, they may conflict with those in * the ancestor component, so resolve them here so that conflicts can be caught. */ - if (getPreviouslyResolvedBindings(key).isPresent() && !Keys.isComponentOrCreator(key)) { + if (getPreviouslyResolvedBindings(key).isPresent()) { /* Resolve in the parent in case there are multibinding contributions or conflicts in some * component between this one and the previously-resolved one. */ parentResolver.get().resolve(key); diff --git a/java/dagger/internal/codegen/binding/BindsTypeChecker.java b/java/dagger/internal/codegen/binding/BindsTypeChecker.java index d850fd373..f3e0a1b81 100644 --- a/java/dagger/internal/codegen/binding/BindsTypeChecker.java +++ b/java/dagger/internal/codegen/binding/BindsTypeChecker.java @@ -18,6 +18,7 @@ package dagger.internal.codegen.binding; import static com.google.common.collect.Iterables.getOnlyElement; +import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; import com.google.common.collect.ImmutableList; import dagger.internal.codegen.base.ContributionType; @@ -82,8 +83,8 @@ public final class BindsTypeChecker { // type.asElement().getEnclosedElements() is not used because some non-standard JDKs (e.g. // J2CL) don't redefine Set.add() (whose only purpose of being redefined in the standard JDK // is documentation, and J2CL's implementation doesn't declare docs for JDK types). - // getLocalAndInheritedMethods ensures that the method will always be present. - elements.getLocalAndInheritedMethods(MoreTypes.asTypeElement(type))) { + // MoreElements.getLocalAndInheritedMethods ensures that the method will always be present. + MoreElements.getLocalAndInheritedMethods(MoreTypes.asTypeElement(type), types, elements)) { if (method.getSimpleName().contentEquals(methodName)) { methodsForName.add(method); } diff --git a/java/dagger/internal/codegen/binding/ComponentDescriptor.java b/java/dagger/internal/codegen/binding/ComponentDescriptor.java index f6ea62c77..a7e4cc4f3 100644 --- a/java/dagger/internal/codegen/binding/ComponentDescriptor.java +++ b/java/dagger/internal/codegen/binding/ComponentDescriptor.java @@ -45,8 +45,6 @@ import dagger.model.DependencyRequest; import dagger.model.Scope; import dagger.producers.CancellationPolicy; import dagger.producers.ProductionComponent; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Stream; @@ -242,17 +240,19 @@ public abstract class ComponentDescriptor { /** Returns the first component method associated with this binding request, if one exists. */ public Optional<ComponentMethodDescriptor> firstMatchingComponentMethod(BindingRequest request) { - return Optional.ofNullable(firstMatchingComponentMethods().get(request)); + return componentMethods().stream() + .filter(method -> doesComponentMethodMatch(method, request)) + .findFirst(); } - @Memoized - ImmutableMap<BindingRequest, ComponentMethodDescriptor> - firstMatchingComponentMethods() { - Map<BindingRequest, ComponentMethodDescriptor> methods = new HashMap<>(); - for (ComponentMethodDescriptor method : entryPointMethods()) { - methods.putIfAbsent(BindingRequest.bindingRequest(method.dependencyRequest().get()), method); - } - return ImmutableMap.copyOf(methods); + /** Returns true if the component method matches the binding request. */ + private static boolean doesComponentMethodMatch( + ComponentMethodDescriptor componentMethod, BindingRequest request) { + return componentMethod + .dependencyRequest() + .map(BindingRequest::bindingRequest) + .filter(request::equals) + .isPresent(); } /** The entry point methods on the component type. Each has a {@link DependencyRequest}. */ diff --git a/java/dagger/internal/codegen/binding/ComponentRequirement.java b/java/dagger/internal/codegen/binding/ComponentRequirement.java index 9d54f4dc3..fa24b56f3 100644 --- a/java/dagger/internal/codegen/binding/ComponentRequirement.java +++ b/java/dagger/internal/codegen/binding/ComponentRequirement.java @@ -16,6 +16,7 @@ package dagger.internal.codegen.binding; +import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import static dagger.internal.codegen.binding.SourceFiles.simpleVariableName; @@ -37,6 +38,7 @@ import dagger.BindsOptionalOf; import dagger.Provides; import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import dagger.internal.codegen.langmodel.DaggerElements; +import dagger.internal.codegen.langmodel.DaggerTypes; import dagger.model.BindingKind; import dagger.model.Key; import dagger.multibindings.Multibinds; @@ -113,12 +115,14 @@ public abstract class ComponentRequirement { * of the default behavior in {@link #nullPolicy}. * * <p>Some implementations' null policy can be determined upon construction (e.g., for binding - * instances), but others' require Elements which must wait until {@link #nullPolicy} is called. + * instances), but others' require Elements and Types, which must wait until {@link #nullPolicy} + * is called. */ abstract Optional<NullPolicy> overrideNullPolicy(); /** The requirement's null policy. */ - public NullPolicy nullPolicy(DaggerElements elements, KotlinMetadataUtil metadataUtil) { + public NullPolicy nullPolicy( + DaggerElements elements, DaggerTypes types, KotlinMetadataUtil metadataUtil) { if (overrideNullPolicy().isPresent()) { return overrideNullPolicy().get(); } @@ -126,7 +130,9 @@ public abstract class ComponentRequirement { case MODULE: return componentCanMakeNewInstances(typeElement(), metadataUtil) ? NullPolicy.NEW - : requiresAPassedInstance(elements, metadataUtil) ? NullPolicy.THROW : NullPolicy.ALLOW; + : requiresAPassedInstance(elements, types, metadataUtil) + ? NullPolicy.THROW + : NullPolicy.ALLOW; case DEPENDENCY: case BOUND_INSTANCE: return NullPolicy.THROW; @@ -138,12 +144,13 @@ public abstract class ComponentRequirement { * Returns true if the passed {@link ComponentRequirement} requires a passed instance in order to * be used within a component. */ - public boolean requiresAPassedInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil) { + public boolean requiresAPassedInstance( + DaggerElements elements, DaggerTypes types, KotlinMetadataUtil metadataUtil) { if (!kind().isModule()) { // Bound instances and dependencies always require the user to provide an instance. return true; } - return requiresModuleInstance(elements, metadataUtil) + return requiresModuleInstance(elements, types, metadataUtil) && !componentCanMakeNewInstances(typeElement(), metadataUtil); } @@ -157,7 +164,8 @@ public abstract class ComponentRequirement { * <p>Alternatively, if the module is a Kotlin Object then the binding methods are considered * {@code static}, requiring no module instance. */ - private boolean requiresModuleInstance(DaggerElements elements, KotlinMetadataUtil metadataUtil) { + private boolean requiresModuleInstance( + DaggerElements elements, DaggerTypes types, KotlinMetadataUtil metadataUtil) { boolean isKotlinObject = metadataUtil.isObjectClass(typeElement()) || metadataUtil.isCompanionObjectClass(typeElement()); @@ -165,7 +173,8 @@ public abstract class ComponentRequirement { return false; } - ImmutableSet<ExecutableElement> methods = elements.getLocalAndInheritedMethods(typeElement()); + ImmutableSet<ExecutableElement> methods = + getLocalAndInheritedMethods(typeElement(), types, elements); return methods.stream() .filter(this::isBindingMethod) .map(ExecutableElement::getModifiers) diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java b/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java index 66270c697..e1b35daf9 100644 --- a/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java +++ b/java/dagger/internal/codegen/componentgenerator/ComponentCreatorImplementationFactory.java @@ -237,7 +237,7 @@ final class ComponentCreatorImplementationFactory { method.addStatement( "this.$N = $L", fields.get(requirement), - requirement.nullPolicy(elements, metadataUtil).equals(NullPolicy.ALLOW) + requirement.nullPolicy(elements, types, metadataUtil).equals(NullPolicy.ALLOW) ? CodeBlock.of("$N", parameter) : CodeBlock.of("$T.checkNotNull($N)", Preconditions.class, parameter)); return maybeReturnThis(method); @@ -310,7 +310,7 @@ final class ComponentCreatorImplementationFactory { private void addNullHandlingForField( ComponentRequirement requirement, FieldSpec field, MethodSpec.Builder factoryMethod) { - switch (requirement.nullPolicy(elements, metadataUtil)) { + switch (requirement.nullPolicy(elements, types, metadataUtil)) { case NEW: checkState(requirement.kind().isModule()); factoryMethod @@ -334,7 +334,7 @@ final class ComponentCreatorImplementationFactory { private void addNullHandlingForParameter( ComponentRequirement requirement, String parameter, MethodSpec.Builder factoryMethod) { - if (!requirement.nullPolicy(elements, metadataUtil).equals(NullPolicy.ALLOW)) { + if (!requirement.nullPolicy(elements, types, metadataUtil).equals(NullPolicy.ALLOW)) { // Factory method parameters are always required unless they are a nullable // binds-instance (i.e. ALLOW) factoryMethod.addStatement("$T.checkNotNull($L)", Preconditions.class, parameter); diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java index 098da81e6..e04ee14a5 100644 --- a/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java +++ b/java/dagger/internal/codegen/componentgenerator/ComponentGenerator.java @@ -19,7 +19,6 @@ package dagger.internal.codegen.componentgenerator; import static com.google.common.base.Verify.verify; import static dagger.internal.codegen.binding.SourceFiles.classFileName; -import com.google.common.collect.ImmutableList; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeSpec; import dagger.Component; @@ -27,6 +26,7 @@ import dagger.internal.codegen.base.SourceFileGenerator; import dagger.internal.codegen.binding.BindingGraph; import dagger.internal.codegen.langmodel.DaggerElements; import dagger.internal.codegen.writing.ComponentImplementation; +import java.util.Optional; import javax.annotation.processing.Filer; import javax.inject.Inject; import javax.lang.model.SourceVersion; @@ -47,6 +47,11 @@ final class ComponentGenerator extends SourceFileGenerator<BindingGraph> { this.componentImplementationFactory = componentImplementationFactory; } + @Override + public ClassName nameGeneratedType(BindingGraph input) { + return componentName(input.componentTypeElement()); + } + static ClassName componentName(TypeElement componentDefinitionType) { ClassName componentName = ClassName.get(componentDefinitionType); return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName)); @@ -58,11 +63,10 @@ final class ComponentGenerator extends SourceFileGenerator<BindingGraph> { } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(BindingGraph bindingGraph) { + public Optional<TypeSpec.Builder> write(BindingGraph bindingGraph) { ComponentImplementation componentImplementation = componentImplementationFactory.createComponentImplementation(bindingGraph); - verify( - componentImplementation.name().equals(componentName(bindingGraph.componentTypeElement()))); - return ImmutableList.of(componentImplementation.generate()); + verify(componentImplementation.name().equals(nameGeneratedType(bindingGraph))); + return Optional.of(componentImplementation.generate()); } } diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java b/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java index 179c411e4..84179d602 100644 --- a/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java +++ b/java/dagger/internal/codegen/componentgenerator/ComponentGeneratorModule.java @@ -18,9 +18,11 @@ package dagger.internal.codegen.componentgenerator; import dagger.Binds; import dagger.Module; +import dagger.internal.codegen.base.ClearableCache; import dagger.internal.codegen.base.SourceFileGenerator; import dagger.internal.codegen.binding.BindingGraph; import dagger.internal.codegen.binding.ComponentDescriptor; +import dagger.multibindings.IntoSet; /** Provides bindings needed to generated the component. */ @Module(subcomponents = TopLevelImplementationComponent.class) @@ -39,4 +41,8 @@ public interface ComponentGeneratorModule { @Binds abstract SourceFileGenerator<ComponentDescriptor> componentHjarGenerator( ComponentHjarGenerator hjarGenerator); + + @Binds + @IntoSet + ClearableCache componentImplementationFactory(ComponentImplementationFactory cache); } diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java index c8b8c97c3..b386a4f23 100644 --- a/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java +++ b/java/dagger/internal/codegen/componentgenerator/ComponentHjarGenerator.java @@ -33,7 +33,6 @@ import static javax.lang.model.element.Modifier.STATIC; import com.google.auto.common.MoreTypes; import com.google.common.base.Ascii; -import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.MethodSpec; @@ -49,6 +48,7 @@ import dagger.internal.codegen.kotlin.KotlinMetadataUtil; import dagger.internal.codegen.langmodel.DaggerElements; import dagger.internal.codegen.langmodel.DaggerTypes; import dagger.producers.internal.CancellationListener; +import java.util.Optional; import java.util.Set; import java.util.stream.Stream; import javax.annotation.processing.Filer; @@ -91,13 +91,18 @@ final class ComponentHjarGenerator extends SourceFileGenerator<ComponentDescript } @Override + public ClassName nameGeneratedType(ComponentDescriptor input) { + return componentName(input.typeElement()); + } + + @Override public Element originatingElement(ComponentDescriptor input) { return input.typeElement(); } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(ComponentDescriptor componentDescriptor) { - ClassName generatedTypeName = componentName(componentDescriptor.typeElement()); + public Optional<TypeSpec.Builder> write(ComponentDescriptor componentDescriptor) { + ClassName generatedTypeName = nameGeneratedType(componentDescriptor); TypeSpec.Builder generatedComponent = TypeSpec.classBuilder(generatedTypeName) .addModifiers(FINAL) @@ -143,7 +148,8 @@ final class ComponentHjarGenerator extends SourceFileGenerator<ComponentDescript && !hasBindsInstanceMethods(componentDescriptor) && componentRequirements(componentDescriptor) .noneMatch( - requirement -> requirement.requiresAPassedInstance(elements, metadataUtil))) { + requirement -> + requirement.requiresAPassedInstance(elements, types, metadataUtil))) { generatedComponent.addMethod(createMethod(componentDescriptor)); } @@ -168,7 +174,7 @@ final class ComponentHjarGenerator extends SourceFileGenerator<ComponentDescript .addMethod(onProducerFutureCancelledMethod()); } - return ImmutableList.of(generatedComponent); + return Optional.of(generatedComponent); } private MethodSpec emptyComponentMethod(TypeElement typeElement, ExecutableElement baseMethod) { diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java index 2be7d3861..04cb80f94 100644 --- a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java +++ b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationBuilder.java @@ -16,6 +16,7 @@ package dagger.internal.codegen.componentgenerator; +import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.auto.common.MoreTypes.asDeclared; import static com.google.common.base.Preconditions.checkState; import static com.squareup.javapoet.MethodSpec.constructorBuilder; @@ -145,8 +146,7 @@ public final class ComponentImplementationBuilder { .map(ComponentCreatorImplementation::spec) .ifPresent(this::addCreatorClass); - elements - .getLocalAndInheritedMethods(graph.componentTypeElement()) + getLocalAndInheritedMethods(graph.componentTypeElement(), types, elements) .forEach(method -> componentImplementation.claimMethodName(method.getSimpleName())); addFactoryMethods(); @@ -495,7 +495,7 @@ public final class ComponentImplementationBuilder { private boolean canInstantiateAllRequirements() { return !Iterables.any( graph.componentRequirements(), - dependency -> dependency.requiresAPassedInstance(elements, metadataUtil)); + dependency -> dependency.requiresAPassedInstance(elements, types, metadataUtil)); } private void createSubcomponentFactoryMethod(ExecutableElement factoryMethod) { diff --git a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java index 0d29b8693..fdfcc9dce 100644 --- a/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java +++ b/java/dagger/internal/codegen/componentgenerator/ComponentImplementationFactory.java @@ -16,20 +16,26 @@ package dagger.internal.codegen.componentgenerator; +import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; import static dagger.internal.codegen.componentgenerator.ComponentGenerator.componentName; +import dagger.internal.codegen.base.ClearableCache; import dagger.internal.codegen.binding.BindingGraph; import dagger.internal.codegen.binding.KeyFactory; import dagger.internal.codegen.compileroption.CompilerOptions; import dagger.internal.codegen.writing.ComponentImplementation; import dagger.internal.codegen.writing.SubcomponentNames; +import java.util.HashMap; +import java.util.Map; import java.util.Optional; import javax.inject.Inject; import javax.inject.Singleton; +import javax.lang.model.element.TypeElement; /** Factory for {@link ComponentImplementation}s. */ @Singleton -final class ComponentImplementationFactory { +final class ComponentImplementationFactory implements ClearableCache { + private final Map<TypeElement, ComponentImplementation> topLevelComponentCache = new HashMap<>(); private final KeyFactory keyFactory; private final CompilerOptions compilerOptions; private final TopLevelImplementationComponent.Builder topLevelImplementationComponentBuilder; @@ -48,6 +54,13 @@ final class ComponentImplementationFactory { * Returns a top-level (non-nested) component implementation for a binding graph. */ ComponentImplementation createComponentImplementation(BindingGraph bindingGraph) { + return reentrantComputeIfAbsent( + topLevelComponentCache, + bindingGraph.componentTypeElement(), + component -> createComponentImplementationUncached(bindingGraph)); + } + + private ComponentImplementation createComponentImplementationUncached(BindingGraph bindingGraph) { ComponentImplementation componentImplementation = ComponentImplementation.topLevelComponentImplementation( bindingGraph, @@ -69,4 +82,9 @@ final class ComponentImplementationFactory { .componentImplementationBuilder() .build(); } + + @Override + public void clearCache() { + topLevelComponentCache.clear(); + } } diff --git a/java/dagger/internal/codegen/kotlin/KotlinMetadata.java b/java/dagger/internal/codegen/kotlin/KotlinMetadata.java index 5fb49f005..296da4465 100644 --- a/java/dagger/internal/codegen/kotlin/KotlinMetadata.java +++ b/java/dagger/internal/codegen/kotlin/KotlinMetadata.java @@ -362,7 +362,6 @@ abstract class KotlinMetadata { Builder addConstructor(FunctionMetadata constructor) { constructorsBuilder().add(constructor); - functionsBySignatureBuilder().put(constructor.signature(), constructor); return this; } diff --git a/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java b/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java index 980ff3438..76d28f0ce 100644 --- a/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java +++ b/java/dagger/internal/codegen/kotlin/KotlinMetadataUtil.java @@ -100,11 +100,6 @@ public final class KotlinMetadataUtil { && metadataFactory.create(typeElement).classMetadata().flags(IS_COMPANION_OBJECT); } - /** Returns {@code true} if this type element is a Kotlin object or companion object. */ - public boolean isObjectOrCompanionObjectClass(TypeElement typeElement) { - return isObjectClass(typeElement) || isCompanionObjectClass(typeElement); - } - /* Returns {@code true} if this type element has a Kotlin Companion Object. */ public boolean hasEnclosedCompanionObject(TypeElement typeElement) { return hasMetadata(typeElement) @@ -136,15 +131,6 @@ public final class KotlinMetadataUtil { } /** - * Returns {@code true} if the given type element was declared {@code internal} in its Kotlin - * source. - */ - public boolean isVisibilityInternal(TypeElement type) { - return hasMetadata(type) - && metadataFactory.create(type).classMetadata().flags(Flag.IS_INTERNAL); - } - - /** * Returns {@code true} if the given executable element was declared {@code internal} in its * Kotlin source. */ diff --git a/java/dagger/internal/codegen/langmodel/BUILD b/java/dagger/internal/codegen/langmodel/BUILD index 25f1e6210..670f4aa99 100644 --- a/java/dagger/internal/codegen/langmodel/BUILD +++ b/java/dagger/internal/codegen/langmodel/BUILD @@ -26,7 +26,6 @@ java_library( tags = ["maven:merged"], deps = [ "//java/dagger:core", - "//java/dagger/internal/codegen/base:shared", "//java/dagger/internal/guava:base", "//java/dagger/internal/guava:collect", "//java/dagger/internal/guava:concurrent", diff --git a/java/dagger/internal/codegen/langmodel/DaggerElements.java b/java/dagger/internal/codegen/langmodel/DaggerElements.java index 51c2a605a..12cec31d5 100644 --- a/java/dagger/internal/codegen/langmodel/DaggerElements.java +++ b/java/dagger/internal/codegen/langmodel/DaggerElements.java @@ -17,6 +17,7 @@ package dagger.internal.codegen.langmodel; import static com.google.auto.common.MoreElements.asExecutable; +import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.auto.common.MoreElements.hasModifiers; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.collect.Lists.asList; @@ -33,12 +34,10 @@ import com.google.common.collect.Iterables; import com.google.common.graph.Traverser; import com.squareup.javapoet.ClassName; import dagger.Reusable; -import dagger.internal.codegen.base.ClearableCache; import java.io.Writer; import java.lang.annotation.Annotation; import java.util.Collection; import java.util.Comparator; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -76,9 +75,8 @@ import javax.lang.model.util.Types; /** Extension of {@link Elements} that adds Dagger-specific methods. */ @Reusable -public final class DaggerElements implements Elements, ClearableCache { - private final Map<TypeElement, ImmutableSet<ExecutableElement>> getLocalAndInheritedMethodsCache = - new HashMap<>(); +public final class DaggerElements implements Elements { + private final Elements elements; private final Types types; @@ -102,13 +100,8 @@ public final class DaggerElements implements Elements, ClearableCache { private static final Traverser<Element> GET_ENCLOSED_ELEMENTS = Traverser.forTree(Element::getEnclosedElements); - public ImmutableSet<ExecutableElement> getLocalAndInheritedMethods(TypeElement type) { - return getLocalAndInheritedMethodsCache.computeIfAbsent( - type, k -> MoreElements.getLocalAndInheritedMethods(type, types, elements)); - } - public ImmutableSet<ExecutableElement> getUnimplementedMethods(TypeElement type) { - return FluentIterable.from(getLocalAndInheritedMethods(type)) + return FluentIterable.from(getLocalAndInheritedMethods(type, types, elements)) .filter(hasModifiers(ABSTRACT)) .toSet(); } @@ -125,7 +118,7 @@ public final class DaggerElements implements Elements, ClearableCache { /** Returns the type element for a class name. */ public TypeElement getTypeElement(ClassName className) { - return getTypeElement(className.canonicalName()); + return getTypeElement(className.withoutAnnotations().toString()); } /** Returns the argument or the closest enclosing element that is a {@link TypeElement}. */ @@ -499,9 +492,4 @@ public final class DaggerElements implements Elements, ClearableCache { public boolean isFunctionalInterface(TypeElement type) { return elements.isFunctionalInterface(type); } - - @Override - public void clearCache() { - getLocalAndInheritedMethodsCache.clear(); - } } diff --git a/java/dagger/internal/codegen/validation/BindingElementValidator.java b/java/dagger/internal/codegen/validation/BindingElementValidator.java index b8f9912b4..4557745ee 100644 --- a/java/dagger/internal/codegen/validation/BindingElementValidator.java +++ b/java/dagger/internal/codegen/validation/BindingElementValidator.java @@ -17,6 +17,7 @@ package dagger.internal.codegen.validation; import static com.google.auto.common.MoreTypes.asTypeElement; +import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verifyNotNull; import static dagger.internal.codegen.base.Scopes.scopesOf; import static dagger.internal.codegen.base.Util.reentrantComputeIfAbsent; @@ -141,12 +142,10 @@ public abstract class BindingElementValidator<E extends Element> { protected abstract class ElementValidator { protected final E element; protected final ValidationReport.Builder<E> report; - private final ImmutableCollection<? extends AnnotationMirror> qualifiers; protected ElementValidator(E element) { this.element = element; this.report = ValidationReport.about(element); - qualifiers = injectionAnnotations.getQualifiers(element); } /** Checks the element for validity. */ @@ -186,15 +185,10 @@ public abstract class BindingElementValidator<E extends Element> { protected void checkType() { switch (ContributionType.fromBindingElement(element)) { case UNIQUE: - // Validate that a unique binding is not attempting to bind a framework type. This - // validation is only appropriate for unique bindings because multibindings may collect - // framework types. E.g. Set<Provider<Foo>> is perfectly reasonable. + /* Validate that a unique binding is not attempting to bind a framework type. This + * validation is only appropriate for unique bindings because multibindings may collect + * framework types. E.g. Set<Provider<Foo>> is perfectly reasonable. */ checkFrameworkType(); - - // Validate that a unique binding is not attempting to bind an unqualified assisted type. - // This validation is only appropriate for unique bindings because multibindings may - // collect assisted types. - checkAssistedType(); // fall through case SET: @@ -214,26 +208,22 @@ public abstract class BindingElementValidator<E extends Element> { TypeKind kind = keyType.getKind(); if (kind.equals(VOID)) { report.addError(bindingElements("must %s a value (not void)", bindingElementTypeVerb())); - } else if (!(kind.isPrimitive() - || kind.equals(DECLARED) - || kind.equals(ARRAY) - || kind.equals(TYPEVAR))) { + } else if (kind == DECLARED) { + checkNotAssistedInject(keyType); + } else if (!(kind.isPrimitive() || kind.equals(ARRAY) || kind.equals(TYPEVAR))) { report.addError(badTypeMessage()); } } - /** Adds errors for unqualified assisted types. */ - private void checkAssistedType() { - if (qualifiers.isEmpty() - && bindingElementType().isPresent() - && bindingElementType().get().getKind() == DECLARED) { - TypeElement keyElement = asTypeElement(bindingElementType().get()); - if (isAssistedInjectionType(keyElement)) { - report.addError("Dagger does not support providing @AssistedInject types.", keyElement); - } - if (isAssistedFactoryType(keyElement)) { - report.addError("Dagger does not support providing @AssistedFactory types.", keyElement); - } + /** Adds errors for a method return type. */ + private void checkNotAssistedInject(TypeMirror keyType) { + checkState(keyType.getKind() == TypeKind.DECLARED); + TypeElement keyElement = asTypeElement(keyType); + if (isAssistedInjectionType(keyElement)) { + report.addError("Dagger does not support providing @AssistedInject types.", keyElement); + } + if (isAssistedFactoryType(keyElement)) { + report.addError("Dagger does not support providing @AssistedFactory types.", keyElement); } } @@ -264,6 +254,8 @@ public abstract class BindingElementValidator<E extends Element> { * Adds an error if the element has more than one {@linkplain Qualifier qualifier} annotation. */ private void checkQualifiers() { + ImmutableCollection<? extends AnnotationMirror> qualifiers = + injectionAnnotations.getQualifiers(element); if (qualifiers.size() > 1) { for (AnnotationMirror qualifier : qualifiers) { report.addError( diff --git a/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java index 28b1c2bc8..aa20232b9 100644 --- a/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java +++ b/java/dagger/internal/codegen/validation/ComponentDescriptorValidator.java @@ -318,7 +318,7 @@ public final class ComponentDescriptorValidator { Set<ComponentRequirement> mustBePassed = Sets.filter( componentModuleAndDependencyRequirements, - input -> input.nullPolicy(elements, metadataUtil).equals(NullPolicy.THROW)); + input -> input.nullPolicy(elements, types, metadataUtil).equals(NullPolicy.THROW)); // Component requirements that the creator must be able to set, but can't Set<ComponentRequirement> missingRequirements = Sets.difference(mustBePassed, creatorModuleAndDependencyRequirements); diff --git a/java/dagger/internal/codegen/validation/ComponentValidator.java b/java/dagger/internal/codegen/validation/ComponentValidator.java index 72a44f251..b3322e5b2 100644 --- a/java/dagger/internal/codegen/validation/ComponentValidator.java +++ b/java/dagger/internal/codegen/validation/ComponentValidator.java @@ -17,6 +17,7 @@ package dagger.internal.codegen.validation; import static com.google.auto.common.MoreElements.asType; +import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods; import static com.google.auto.common.MoreElements.isAnnotationPresent; import static com.google.auto.common.MoreTypes.asDeclared; import static com.google.auto.common.MoreTypes.asExecutable; @@ -242,7 +243,8 @@ public final class ComponentValidator implements ClearableCache { } private void validateComponentMethods() { - elements.getUnimplementedMethods(component).stream() + getLocalAndInheritedMethods(component, types, elements).stream() + .filter(method -> method.getModifiers().contains(ABSTRACT)) .map(ComponentMethodValidator::new) .forEachOrdered(ComponentMethodValidator::validateMethod); } diff --git a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java index f28049753..68c960478 100644 --- a/java/dagger/internal/codegen/validation/DependencyRequestValidator.java +++ b/java/dagger/internal/codegen/validation/DependencyRequestValidator.java @@ -77,21 +77,11 @@ final class DependencyRequestValidator { // Don't validate assisted parameters. These are not dependency requests. return; } - if (missingQualifierMetadata(requestElement)) { - report.addError( - "Unable to read annotations on an injected Kotlin property. The Dagger compiler must" - + " also be applied to any project containing @Inject properties.", - requestElement); - - // Skip any further validation if we don't have valid metadata for a type that needs it. - return; - } - - new Validator(report, requestElement, requestType).validate(); + checkQualifiers(report, requestElement); + checkType(report, requestElement, requestType); } - /** Returns {@code true} if a kotlin inject field is missing metadata about its qualifiers. */ - private boolean missingQualifierMetadata(Element requestElement) { + private void checkQualifiers(ValidationReport.Builder<?> report, Element requestElement) { if (requestElement.getKind() == ElementKind.FIELD // static injected fields are not supported, no need to get qualifier from kotlin metadata && !requestElement.getModifiers().contains(STATIC) @@ -101,80 +91,64 @@ final class DependencyRequestValidator { Optional.ofNullable( elements.getTypeElement( membersInjectorNameForType(asType(requestElement.getEnclosingElement())))); - return !membersInjector.isPresent(); - } - return false; - } - - private final class Validator { - private final ValidationReport.Builder<?> report; - private final Element requestElement; - private final TypeMirror requestType; - private final TypeMirror keyType; - private final RequestKind requestKind; - private final ImmutableCollection<? extends AnnotationMirror> qualifiers; - - - Validator(ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) { - this.report = report; - this.requestElement = requestElement; - this.requestType = requestType; - this.keyType = extractKeyType(requestType); - this.requestKind = RequestKinds.getRequestKind(requestType); - this.qualifiers = injectionAnnotations.getQualifiers(requestElement); - } - - void validate() { - checkQualifiers(); - checkType(); + if (!membersInjector.isPresent()) { + report.addError( + "Unable to read annotations on an injected Kotlin property. The Dagger compiler must" + + " also be applied to any project containing @Inject properties.", + requestElement); + return; // finish checking qualifiers since current information is unreliable. + } } - private void checkQualifiers() { - if (qualifiers.size() > 1) { - for (AnnotationMirror qualifier : qualifiers) { - report.addError( - "A single dependency request may not use more than one @Qualifier", - requestElement, - qualifier); - } + ImmutableCollection<? extends AnnotationMirror> qualifiers = + injectionAnnotations.getQualifiers(requestElement); + if (qualifiers.size() > 1) { + for (AnnotationMirror qualifier : qualifiers) { + report.addError( + "A single dependency request may not use more than one @Qualifier", + requestElement, + qualifier); } } + } - private void checkType() { - if (qualifiers.isEmpty() && keyType.getKind() == TypeKind.DECLARED) { - TypeElement typeElement = asTypeElement(keyType); - if (isAssistedInjectionType(typeElement)) { - report.addError( - "Dagger does not support injecting @AssistedInject type, " - + requestType - + ". Did you mean to inject its assisted factory type instead?", - requestElement); - } - if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) { - report.addError( - "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " - + "or Produced<T> when T is an @AssistedFactory-annotated type such as " - + keyType, - requestElement); - } + private void checkType( + ValidationReport.Builder<?> report, Element requestElement, TypeMirror requestType) { + TypeMirror keyType = extractKeyType(requestType); + RequestKind requestKind = RequestKinds.getRequestKind(requestType); + if (keyType.getKind() == TypeKind.DECLARED) { + TypeElement typeElement = asTypeElement(keyType); + if (isAssistedInjectionType(typeElement)) { + report.addError( + "Dagger does not support injecting @AssistedInject type, " + + requestType + + ". Did you mean to inject its assisted factory type instead?", + requestElement); } - if (keyType.getKind().equals(WILDCARD)) { - // TODO(ronshapiro): Explore creating this message using RequestKinds. + if (requestKind != RequestKind.INSTANCE && isAssistedFactoryType(typeElement)) { report.addError( "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " - + "or Produced<T> when T is a wildcard type such as " + + "or Produced<T> when T is an @AssistedFactory-annotated type such as " + keyType, requestElement); } - if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) { - DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType); - if (membersInjectorType.getTypeArguments().isEmpty()) { - report.addError("Cannot inject a raw MembersInjector", requestElement); - } else { - report.addSubreport( - membersInjectionValidator.validateMembersInjectionRequest( - requestElement, membersInjectorType.getTypeArguments().get(0))); - } + } + if (keyType.getKind().equals(WILDCARD)) { + // TODO(ronshapiro): Explore creating this message using RequestKinds. + report.addError( + "Dagger does not support injecting Provider<T>, Lazy<T>, Producer<T>, " + + "or Produced<T> when T is a wildcard type such as " + + keyType, + requestElement); + } + if (MoreTypes.isType(keyType) && MoreTypes.isTypeOf(MembersInjector.class, keyType)) { + DeclaredType membersInjectorType = MoreTypes.asDeclared(keyType); + if (membersInjectorType.getTypeArguments().isEmpty()) { + report.addError("Cannot inject a raw MembersInjector", requestElement); + } else { + report.addSubreport( + membersInjectionValidator.validateMembersInjectionRequest( + requestElement, membersInjectorType.getTypeArguments().get(0))); } } } diff --git a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java index 9787f4f37..06f68f68d 100644 --- a/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java +++ b/java/dagger/internal/codegen/validation/InjectBindingRegistryImpl.java @@ -24,7 +24,6 @@ import static dagger.internal.codegen.base.Keys.isValidMembersInjectionKey; import static dagger.internal.codegen.binding.AssistedInjectionAnnotations.assistedInjectedConstructors; import static dagger.internal.codegen.binding.InjectionAnnotations.injectedConstructors; import static dagger.internal.codegen.binding.SourceFiles.generatedClassNameForBinding; -import static dagger.internal.codegen.langmodel.DaggerTypes.unwrapType; import com.google.auto.common.MoreElements; import com.google.auto.common.MoreTypes; @@ -113,11 +112,7 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { /** Caches the binding and generates it if it needs generation. */ void tryRegisterBinding(B binding, boolean warnIfNotAlreadyGenerated) { tryToCacheBinding(binding); - - @SuppressWarnings("unchecked") - B maybeUnresolved = - binding.unresolved().isPresent() ? (B) binding.unresolved().get() : binding; - tryToGenerateBinding(maybeUnresolved, warnIfNotAlreadyGenerated); + tryToGenerateBinding(binding, warnIfNotAlreadyGenerated); } /** @@ -204,6 +199,9 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { */ private void registerBinding(ProvisionBinding binding, boolean warnIfNotAlreadyGenerated) { provisionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated); + if (binding.unresolved().isPresent()) { + provisionBindings.tryToGenerateBinding(binding.unresolved().get(), warnIfNotAlreadyGenerated); + } } /** @@ -231,6 +229,10 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { } membersInjectionBindings.tryRegisterBinding(binding, warnIfNotAlreadyGenerated); + if (binding.unresolved().isPresent()) { + membersInjectionBindings.tryToGenerateBinding( + binding.unresolved().get(), warnIfNotAlreadyGenerated); + } } @Override @@ -253,16 +255,15 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { ValidationReport<TypeElement> report = injectValidator.validateConstructor(constructorElement); report.printMessagesTo(messager); - if (!report.isClean()) { - return Optional.empty(); - } - - ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType); - registerBinding(binding, warnIfNotAlreadyGenerated); - if (!binding.injectionSites().isEmpty()) { - tryRegisterMembersInjectedType(typeElement, resolvedType, warnIfNotAlreadyGenerated); + if (report.isClean()) { + ProvisionBinding binding = bindingFactory.injectionBinding(constructorElement, resolvedType); + registerBinding(binding, warnIfNotAlreadyGenerated); + if (!binding.injectionSites().isEmpty()) { + tryRegisterMembersInjectedType(typeElement, resolvedType, warnIfNotAlreadyGenerated); + } + return Optional.of(binding); } - return Optional.of(binding); + return Optional.empty(); } @Override @@ -285,18 +286,17 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { ValidationReport<TypeElement> report = injectValidator.validateMembersInjectionType(typeElement); report.printMessagesTo(messager); - if (!report.isClean()) { - return Optional.empty(); - } - - MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType); - registerBinding(binding, warnIfNotAlreadyGenerated); - for (Optional<DeclaredType> supertype = types.nonObjectSuperclass(type); - supertype.isPresent(); - supertype = types.nonObjectSuperclass(supertype.get())) { - getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get())); + if (report.isClean()) { + MembersInjectionBinding binding = bindingFactory.membersInjectionBinding(type, resolvedType); + registerBinding(binding, warnIfNotAlreadyGenerated); + for (Optional<DeclaredType> supertype = types.nonObjectSuperclass(type); + supertype.isPresent(); + supertype = types.nonObjectSuperclass(supertype.get())) { + getOrFindMembersInjectionBinding(keyFactory.forMembersInjectedType(supertype.get())); + } + return Optional.of(binding); } - return Optional.of(binding); + return Optional.empty(); } @CanIgnoreReturnValue @@ -352,7 +352,7 @@ final class InjectBindingRegistryImpl implements InjectBindingRegistry { if (!isValidMembersInjectionKey(key)) { return Optional.empty(); } - Key membersInjectionKey = keyFactory.forMembersInjectedType(unwrapType(key.type())); + Key membersInjectionKey = keyFactory.forMembersInjectedType(types.unwrapType(key.type())); return getOrFindMembersInjectionBinding(membersInjectionKey) .map(binding -> bindingFactory.membersInjectorBinding(key, binding)); } diff --git a/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java b/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java index 1c2eb1c9a..2c72e5f0d 100644 --- a/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java +++ b/java/dagger/internal/codegen/validation/MonitoringModuleGenerator.java @@ -26,7 +26,6 @@ import static javax.lang.model.element.Modifier.ABSTRACT; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.STATIC; -import com.google.common.collect.ImmutableList; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; @@ -39,6 +38,7 @@ import dagger.multibindings.Multibinds; import dagger.producers.ProductionScope; import dagger.producers.monitoring.ProductionComponentMonitor; import dagger.producers.monitoring.internal.Monitors; +import java.util.Optional; import javax.annotation.processing.Filer; import javax.inject.Inject; import javax.lang.model.SourceVersion; @@ -54,14 +54,19 @@ final class MonitoringModuleGenerator extends SourceFileGenerator<TypeElement> { } @Override + public ClassName nameGeneratedType(TypeElement componentElement) { + return SourceFiles.generatedMonitoringModuleName(componentElement); + } + + @Override public Element originatingElement(TypeElement componentElement) { return componentElement; } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(TypeElement componentElement) { - return ImmutableList.of( - classBuilder(SourceFiles.generatedMonitoringModuleName(componentElement)) + public Optional<TypeSpec.Builder> write(TypeElement componentElement) { + return Optional.of( + classBuilder(nameGeneratedType(componentElement)) .addAnnotation(Module.class) .addModifiers(ABSTRACT) .addMethod(privateConstructor()) diff --git a/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java b/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java index fa3a16cac..96e6340af 100644 --- a/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java +++ b/java/dagger/internal/codegen/writing/AnnotationCreatorGenerator.java @@ -39,6 +39,7 @@ import com.squareup.javapoet.TypeSpec; import dagger.internal.codegen.base.SourceFileGenerator; import dagger.internal.codegen.langmodel.DaggerElements; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; import javax.annotation.processing.Filer; import javax.inject.Inject; @@ -87,13 +88,18 @@ public class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> } @Override + public ClassName nameGeneratedType(TypeElement annotationType) { + return getAnnotationCreatorClassName(annotationType); + } + + @Override public Element originatingElement(TypeElement annotationType) { return annotationType; } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(TypeElement annotationType) { - ClassName generatedTypeName = getAnnotationCreatorClassName(annotationType); + public Optional<TypeSpec.Builder> write(TypeElement annotationType) { + ClassName generatedTypeName = nameGeneratedType(annotationType); TypeSpec.Builder annotationCreatorBuilder = classBuilder(generatedTypeName) .addModifiers(PUBLIC, FINAL) @@ -103,7 +109,7 @@ public class AnnotationCreatorGenerator extends SourceFileGenerator<TypeElement> annotationCreatorBuilder.addMethod(buildCreateMethod(generatedTypeName, annotationElement)); } - return ImmutableList.of(annotationCreatorBuilder); + return Optional.of(annotationCreatorBuilder); } private MethodSpec buildCreateMethod(ClassName generatedTypeName, TypeElement annotationElement) { diff --git a/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java b/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java index d90ab71f1..d0c481c7a 100644 --- a/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java +++ b/java/dagger/internal/codegen/writing/AssistedFactoryBindingExpression.java @@ -83,7 +83,7 @@ final class AssistedFactoryBindingExpression extends SimpleInvocationBindingExpr private TypeSpec anonymousfactoryImpl(Expression assistedInjectionExpression) { TypeElement factory = asType(binding.bindingElement().get()); DeclaredType factoryType = asDeclared(binding.key().type()); - ExecutableElement factoryMethod = assistedFactoryMethod(factory, elements); + ExecutableElement factoryMethod = assistedFactoryMethod(factory, elements, types); // We can't use MethodSpec.overriding directly because we need to control the parameter names. MethodSpec factoryOverride = MethodSpec.overriding(factoryMethod, factoryType, types).build(); diff --git a/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java b/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java index dc15c998b..5f1f0bdd8 100644 --- a/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java +++ b/java/dagger/internal/codegen/writing/ComponentBindingExpressions.java @@ -212,14 +212,19 @@ public final class ComponentBindingExpressions { public MethodSpec getComponentMethod(ComponentMethodDescriptor componentMethod) { checkArgument(componentMethod.dependencyRequest().isPresent()); BindingRequest request = bindingRequest(componentMethod.dependencyRequest().get()); - return MethodSpec.overriding( + MethodSpec.Builder method = + MethodSpec.overriding( componentMethod.methodElement(), MoreTypes.asDeclared(graph.componentTypeElement().asType()), - types) - .addCode( - getBindingExpression(request) - .getComponentMethodImplementation(componentMethod, componentImplementation)) - .build(); + types); + // Even though this is not used if the method is abstract, we need to invoke the binding + // expression in order for the side of effect of the method being added to the + // ComponentImplementation + CodeBlock methodBody = + getBindingExpression(request) + .getComponentMethodImplementation(componentMethod, componentImplementation); + + return method.addCode(methodBody).build(); } /** Returns the {@link BindingExpression} for the given {@link BindingRequest}. */ @@ -455,7 +460,8 @@ public final class ComponentBindingExpressions { private BindingExpression providerBindingExpression(ContributionBinding binding) { if (binding.kind().equals(DELEGATE) && !needsCaching(binding)) { return new DelegateBindingExpression(binding, RequestKind.PROVIDER, this, types, elements); - } else if (isFastInit() + } else if (compilerOptions.fastInit( + topLevelComponentImplementation.componentDescriptor().typeElement()) && frameworkInstanceCreationExpression(binding).useInnerSwitchingProvider() && !(instanceBindingExpression(binding) instanceof DerivedFromFrameworkInstanceBindingExpression)) { @@ -487,27 +493,24 @@ public final class ComponentBindingExpressions { /** * Returns a binding expression for {@link RequestKind#INSTANCE} requests. + * + * <p>If there is a direct expression (not calling {@link Provider#get()}) we can use for an + * instance of this binding, return it, wrapped in a method if the binding {@linkplain + * #needsCaching(ContributionBinding) needs to be cached} or the expression has dependencies. + * + * <p>In fastInit mode, we can use direct expressions unless the binding needs to be cached. */ private BindingExpression instanceBindingExpression(ContributionBinding binding) { Optional<BindingExpression> maybeDirectInstanceExpression = unscopedDirectInstanceExpression(binding); - if (maybeDirectInstanceExpression.isPresent()) { - // If this is the case where we don't need to use Provider#get() because there's no caching - // and it isn't an assisted factory, or because we're in fastInit mode (since fastInit avoids - // using Providers), we can try to use the direct expression, possibly wrapped in a method - // if necessary (e.g. it has dependencies). - if ((!needsCaching(binding) && binding.kind() != BindingKind.ASSISTED_FACTORY) - || isFastInit()) { - BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get(); - // While this can't require caching in default mode, if we're in fastInit mode and we need - // caching we also need to wrap it in a method. - return directInstanceExpression.requiresMethodEncapsulation() || needsCaching(binding) - ? wrapInMethod( - binding, - bindingRequest(binding.key(), RequestKind.INSTANCE), - directInstanceExpression) - : directInstanceExpression; - } + if (canUseDirectInstanceExpression(binding) && maybeDirectInstanceExpression.isPresent()) { + BindingExpression directInstanceExpression = maybeDirectInstanceExpression.get(); + return directInstanceExpression.requiresMethodEncapsulation() || needsCaching(binding) + ? wrapInMethod( + binding, + bindingRequest(binding.key(), RequestKind.INSTANCE), + directInstanceExpression) + : directInstanceExpression; } return new DerivedFromFrameworkInstanceBindingExpression( binding.key(), FrameworkType.PROVIDER, RequestKind.INSTANCE, this, types); @@ -607,12 +610,27 @@ public final class ComponentBindingExpressions { * MapFactory} or {@code SetFactory}. */ private boolean useStaticFactoryCreation(ContributionBinding binding) { - return !isFastInit() + return !compilerOptions.fastInit( + topLevelComponentImplementation.componentDescriptor().typeElement()) || binding.kind().equals(MULTIBOUND_MAP) || binding.kind().equals(MULTIBOUND_SET); } /** + * Returns {@code true} if we can use a direct (not {@code Provider.get()}) expression for this + * binding. If the binding doesn't {@linkplain #needsCaching(ContributionBinding) need to be + * cached} and the binding is not an {@link BindingKind.ASSISTED_FACTORY}, we can. + * + * <p>In fastInit mode, we can use a direct expression even if the binding {@linkplain + * #needsCaching(ContributionBinding) needs to be cached}. + */ + private boolean canUseDirectInstanceExpression(ContributionBinding binding) { + return (!needsCaching(binding) && binding.kind() != BindingKind.ASSISTED_FACTORY) + || compilerOptions.fastInit( + topLevelComponentImplementation.componentDescriptor().typeElement()); + } + + /** * Returns a binding expression that uses a given one as the body of a method that users call. If * a component provision method matches it, it will be the method implemented. If it does not * match a component provision method and the binding is modifiable, then a new public modifiable @@ -672,7 +690,8 @@ public final class ComponentBindingExpressions { private MethodImplementationStrategy methodImplementationStrategy( ContributionBinding binding, BindingRequest request) { - if (isFastInit()) { + if (compilerOptions.fastInit( + topLevelComponentImplementation.componentDescriptor().typeElement())) { if (request.isRequestKind(RequestKind.PROVIDER)) { return MethodImplementationStrategy.SINGLE_CHECK; } else if (request.isRequestKind(RequestKind.INSTANCE) && needsCaching(binding)) { @@ -699,9 +718,4 @@ public final class ComponentBindingExpressions { } return true; } - - private boolean isFastInit() { - return compilerOptions.fastInit( - topLevelComponentImplementation.componentDescriptor().typeElement()); - } } diff --git a/java/dagger/internal/codegen/writing/ComponentImplementation.java b/java/dagger/internal/codegen/writing/ComponentImplementation.java index a09620e87..ef6912460 100644 --- a/java/dagger/internal/codegen/writing/ComponentImplementation.java +++ b/java/dagger/internal/codegen/writing/ComponentImplementation.java @@ -276,10 +276,7 @@ public final class ComponentImplementation { "%s is not a child component of %s", childDescriptor.typeElement(), componentDescriptor().typeElement()); - // TODO(erichang): Hacky fix to shorten the suffix if we're too deeply - // nested to save on file name length. 2 chosen arbitrarily. - String suffix = name.simpleNames().size() > 2 ? "I" : "Impl"; - return name.nestedClass(subcomponentNames.get(childDescriptor) + suffix); + return name.nestedClass(subcomponentNames.get(childDescriptor) + "Impl"); } /** diff --git a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java index af5d45e66..5a40c0206 100644 --- a/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java +++ b/java/dagger/internal/codegen/writing/DependencyMethodProviderCreationExpression.java @@ -96,7 +96,7 @@ final class DependencyMethodProviderCreationExpression COMPONENT_PROVISION_FACTORY, classBuilder(factoryClassName()) .addSuperinterface(providerOf(keyType)) - .addModifiers(PRIVATE, STATIC, FINAL) + .addModifiers(PRIVATE, STATIC) .addField(dependencyClassName, dependency().variableName(), PRIVATE, FINAL) .addMethod( constructorBuilder() diff --git a/java/dagger/internal/codegen/writing/FactoryGenerator.java b/java/dagger/internal/codegen/writing/FactoryGenerator.java index 03ad27cbb..70edd1a95 100644 --- a/java/dagger/internal/codegen/writing/FactoryGenerator.java +++ b/java/dagger/internal/codegen/writing/FactoryGenerator.java @@ -97,27 +97,32 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding } @Override + public ClassName nameGeneratedType(ProvisionBinding binding) { + return generatedClassNameForBinding(binding); + } + + @Override public Element originatingElement(ProvisionBinding binding) { // we only create factories for bindings that have a binding element return binding.bindingElement().get(); } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(ProvisionBinding binding) { + public Optional<TypeSpec.Builder> write(ProvisionBinding binding) { // We don't want to write out resolved bindings -- we want to write out the generic version. checkArgument(!binding.unresolved().isPresent()); checkArgument(binding.bindingElement().isPresent()); if (binding.factoryCreationStrategy().equals(DELEGATE)) { - return ImmutableList.of(); + return Optional.empty(); } - return ImmutableList.of(factoryBuilder(binding)); + return Optional.of(factoryBuilder(binding)); } private TypeSpec.Builder factoryBuilder(ProvisionBinding binding) { TypeSpec.Builder factoryBuilder = - classBuilder(generatedClassNameForBinding(binding)) + classBuilder(nameGeneratedType(binding)) .addModifiers(PUBLIC, FINAL) .addTypeVariables(bindingTypeElementTypeVariableNames(binding)); @@ -189,9 +194,8 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding switch (binding.factoryCreationStrategy()) { case SINGLETON_INSTANCE: FieldSpec.Builder instanceFieldBuilder = - FieldSpec.builder( - generatedClassNameForBinding(binding), "INSTANCE", PRIVATE, STATIC, FINAL) - .initializer("new $T()", generatedClassNameForBinding(binding)); + FieldSpec.builder(nameGeneratedType(binding), "INSTANCE", PRIVATE, STATIC, FINAL) + .initializer("new $T()", nameGeneratedType(binding)); if (!bindingTypeElementTypeVariableNames(binding).isEmpty()) { // If the factory has type parameters, ignore them in the field declaration & initializer @@ -199,8 +203,7 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding createMethodBuilder.addAnnotation(suppressWarnings(UNCHECKED)); } - ClassName instanceHolderName = - generatedClassNameForBinding(binding).nestedClass("InstanceHolder"); + ClassName instanceHolderName = nameGeneratedType(binding).nestedClass("InstanceHolder"); createMethodBuilder.addStatement("return $T.INSTANCE", instanceHolderName); factoryBuilder.addType( TypeSpec.classBuilder(instanceHolderName) @@ -245,7 +248,7 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding request -> frameworkTypeUsageStatement( CodeBlock.of("$N", frameworkFields.get(request)), request.kind()), - generatedClassNameForBinding(binding), + nameGeneratedType(binding), moduleParameter(binding).map(module -> CodeBlock.of("$N", module)), compilerOptions, metadataUtil); @@ -262,7 +265,7 @@ public final class FactoryGenerator extends SourceFileGenerator<ProvisionBinding .addCode( InjectionSiteMethod.invokeAll( binding.injectionSites(), - generatedClassNameForBinding(binding), + nameGeneratedType(binding), instance, binding.key().type(), frameworkFieldUsages(binding.dependencies(), frameworkFields)::get, diff --git a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java index d4dbde794..5a4b6f132 100644 --- a/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java +++ b/java/dagger/internal/codegen/writing/HjarSourceFileGenerator.java @@ -19,15 +19,14 @@ package dagger.internal.codegen.writing; import static com.squareup.javapoet.MethodSpec.constructorBuilder; import static com.squareup.javapoet.MethodSpec.methodBuilder; import static com.squareup.javapoet.TypeSpec.classBuilder; -import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList; import static javax.lang.model.element.Modifier.PRIVATE; -import com.google.common.collect.ImmutableList; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.FieldSpec; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import dagger.internal.codegen.base.SourceFileGenerator; +import java.util.Optional; import javax.lang.model.element.Element; import javax.lang.model.element.Modifier; @@ -48,15 +47,18 @@ public final class HjarSourceFileGenerator<T> extends SourceFileGenerator<T> { } @Override + public ClassName nameGeneratedType(T input) { + return delegate.nameGeneratedType(input); + } + + @Override public Element originatingElement(T input) { return delegate.originatingElement(input); } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(T input) { - return delegate.topLevelTypes(input).stream() - .map(completeType -> skeletonType(completeType.build())) - .collect(toImmutableList()); + public Optional<TypeSpec.Builder> write(T input) { + return delegate.write(input).map(completeType -> skeletonType(completeType.build())); } private TypeSpec.Builder skeletonType(TypeSpec completeType) { diff --git a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java index 889f8985c..d52735963 100644 --- a/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java +++ b/java/dagger/internal/codegen/writing/InaccessibleMapKeyProxyGenerator.java @@ -22,13 +22,14 @@ import static javax.lang.model.element.Modifier.FINAL; import static javax.lang.model.element.Modifier.PRIVATE; import static javax.lang.model.element.Modifier.PUBLIC; -import com.google.common.collect.ImmutableList; +import com.squareup.javapoet.ClassName; import com.squareup.javapoet.TypeSpec; import dagger.internal.codegen.base.SourceFileGenerator; import dagger.internal.codegen.binding.ContributionBinding; import dagger.internal.codegen.binding.MapKeys; import dagger.internal.codegen.langmodel.DaggerElements; import dagger.internal.codegen.langmodel.DaggerTypes; +import java.util.Optional; import javax.annotation.processing.Filer; import javax.inject.Inject; import javax.lang.model.SourceVersion; @@ -52,21 +53,24 @@ public final class InaccessibleMapKeyProxyGenerator } @Override + public ClassName nameGeneratedType(ContributionBinding binding) { + return MapKeys.mapKeyProxyClassName(binding); + } + + @Override public Element originatingElement(ContributionBinding binding) { // a map key is only ever present on bindings that have a binding element return binding.bindingElement().get(); } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(ContributionBinding binding) { + public Optional<TypeSpec.Builder> write(ContributionBinding binding) { return MapKeys.mapKeyFactoryMethod(binding, types, elements) .map( method -> - classBuilder(MapKeys.mapKeyProxyClassName(binding)) + classBuilder(nameGeneratedType(binding)) .addModifiers(PUBLIC, FINAL) .addMethod(constructorBuilder().addModifiers(PRIVATE).build()) - .addMethod(method)) - .map(ImmutableList::of) - .orElse(ImmutableList.of()); + .addMethod(method)); } } diff --git a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java index 8630ce6ca..df618b070 100644 --- a/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java +++ b/java/dagger/internal/codegen/writing/MembersInjectorGenerator.java @@ -61,6 +61,7 @@ import dagger.internal.codegen.langmodel.DaggerTypes; import dagger.internal.codegen.writing.InjectionMethods.InjectionSiteMethod; import dagger.model.DependencyRequest; import java.util.Map.Entry; +import java.util.Optional; import javax.annotation.processing.Filer; import javax.inject.Inject; import javax.lang.model.SourceVersion; @@ -86,15 +87,20 @@ public final class MembersInjectorGenerator extends SourceFileGenerator<MembersI } @Override + public ClassName nameGeneratedType(MembersInjectionBinding binding) { + return membersInjectorNameForType(binding.membersInjectedType()); + } + + @Override public Element originatingElement(MembersInjectionBinding binding) { return binding.membersInjectedType(); } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(MembersInjectionBinding binding) { + public Optional<TypeSpec.Builder> write(MembersInjectionBinding binding) { // Empty members injection bindings are special and don't need source files. if (binding.injectionSites().isEmpty()) { - return ImmutableList.of(); + return Optional.empty(); } // Members injectors for classes with no local injection sites and no @Inject @@ -102,7 +108,7 @@ public final class MembersInjectorGenerator extends SourceFileGenerator<MembersI if (!binding.hasLocalInjectionSites() && injectedConstructors(binding.membersInjectedType()).isEmpty() && assistedInjectedConstructors(binding.membersInjectedType()).isEmpty()) { - return ImmutableList.of(); + return Optional.empty(); } @@ -112,7 +118,7 @@ public final class MembersInjectorGenerator extends SourceFileGenerator<MembersI "tried to generate a MembersInjector for a binding of a resolved generic type: %s", binding); - ClassName generatedTypeName = membersInjectorNameForType(binding.membersInjectedType()); + ClassName generatedTypeName = nameGeneratedType(binding); ImmutableList<TypeVariableName> typeParameters = bindingTypeElementTypeVariableNames(binding); TypeSpec.Builder injectorTypeBuilder = classBuilder(generatedTypeName) @@ -216,6 +222,6 @@ public final class MembersInjectorGenerator extends SourceFileGenerator<MembersI gwtIncompatibleAnnotation(binding).ifPresent(injectorTypeBuilder::addAnnotation); - return ImmutableList.of(injectorTypeBuilder); + return Optional.of(injectorTypeBuilder); } } diff --git a/java/dagger/internal/codegen/writing/ModuleProxies.java b/java/dagger/internal/codegen/writing/ModuleProxies.java index 6d174f824..fcd9b56e5 100644 --- a/java/dagger/internal/codegen/writing/ModuleProxies.java +++ b/java/dagger/internal/codegen/writing/ModuleProxies.java @@ -27,7 +27,6 @@ import static javax.lang.model.element.Modifier.PUBLIC; import static javax.lang.model.element.Modifier.STATIC; import static javax.lang.model.util.ElementFilter.constructorsIn; -import com.google.common.collect.ImmutableList; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.CodeBlock; import com.squareup.javapoet.TypeSpec; @@ -79,20 +78,25 @@ public final class ModuleProxies { } @Override + public ClassName nameGeneratedType(TypeElement moduleElement) { + return moduleProxies.constructorProxyTypeName(moduleElement); + } + + @Override public Element originatingElement(TypeElement moduleElement) { return moduleElement; } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(TypeElement moduleElement) { + public Optional<TypeSpec.Builder> write(TypeElement moduleElement) { ModuleKind.checkIsModule(moduleElement, metadataUtil); return moduleProxies.nonPublicNullaryConstructor(moduleElement).isPresent() - ? ImmutableList.of(buildProxy(moduleElement)) - : ImmutableList.of(); + ? Optional.of(buildProxy(moduleElement)) + : Optional.empty(); } private TypeSpec.Builder buildProxy(TypeElement moduleElement) { - return classBuilder(moduleProxies.constructorProxyTypeName(moduleElement)) + return classBuilder(nameGeneratedType(moduleElement)) .addModifiers(PUBLIC, FINAL) .addMethod(constructorBuilder().addModifiers(PRIVATE).build()) .addMethod( diff --git a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java index a013be940..5e5292359 100644 --- a/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java +++ b/java/dagger/internal/codegen/writing/ProducerFactoryGenerator.java @@ -103,6 +103,10 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti this.keyFactory = keyFactory; } + @Override + public ClassName nameGeneratedType(ProductionBinding binding) { + return generatedClassNameForBinding(binding); + } @Override public Element originatingElement(ProductionBinding binding) { @@ -111,7 +115,7 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti } @Override - public ImmutableList<TypeSpec.Builder> topLevelTypes(ProductionBinding binding) { + public Optional<TypeSpec.Builder> write(ProductionBinding binding) { // We don't want to write out resolved bindings -- we want to write out the generic version. checkArgument(!binding.unresolved().isPresent()); checkArgument(binding.bindingElement().isPresent()); @@ -119,7 +123,7 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti TypeName providedTypeName = TypeName.get(binding.contributedType()); TypeName futureTypeName = listenableFutureOf(providedTypeName); - ClassName generatedTypeName = generatedClassNameForBinding(binding); + ClassName generatedTypeName = nameGeneratedType(binding); TypeSpec.Builder factoryBuilder = classBuilder(generatedTypeName) .addModifiers(PUBLIC, FINAL) @@ -229,7 +233,7 @@ public final class ProducerFactoryGenerator extends SourceFileGenerator<Producti gwtIncompatibleAnnotation(binding).ifPresent(factoryBuilder::addAnnotation); // TODO(gak): write a sensible toString - return ImmutableList.of(factoryBuilder); + return Optional.of(factoryBuilder); } private MethodSpec staticFactoryMethod(ProductionBinding binding, MethodSpec constructor) { |