aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-03-08 02:01:35 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-03-08 02:01:35 +0000
commit529fadac881c901379b71de78522c45d78b1b292 (patch)
treee7dfe5daeab98b225bb9f472e43ea319259f4e73
parent65a3186e65e87308d442222838c4bc5f1a0deb77 (diff)
parent02fc1d59868bdded8cd1605983d579b2bf75e3e0 (diff)
downloadauto-android13-s2-release.tar.gz
Change-Id: Ia497db4441b97eacdee6c8ff77ab0d0ac6bdd96e
-rw-r--r--.github/dependabot.yml22
-rw-r--r--.github/workflows/ci.yml97
-rw-r--r--.travis.yml28
-rw-r--r--README.md2
-rwxr-xr-xandroid-annotation-stubs/gen_annotations.sh1
-rw-r--r--android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java40
-rw-r--r--common/Android.bp1
-rw-r--r--common/README.md80
-rw-r--r--common/pom.xml16
-rw-r--r--common/src/main/java/com/google/auto/common/AnnotationMirrors.java97
-rw-r--r--common/src/main/java/com/google/auto/common/AnnotationValues.java199
-rw-r--r--common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java37
-rw-r--r--common/src/main/java/com/google/auto/common/GeneratedAnnotationSpecs.java8
-rw-r--r--common/src/main/java/com/google/auto/common/MoreElements.java78
-rw-r--r--common/src/main/java/com/google/auto/common/MoreStreams.java75
-rw-r--r--common/src/main/java/com/google/auto/common/MoreTypes.java70
-rw-r--r--common/src/main/java/com/google/auto/common/Overrides.java25
-rw-r--r--common/src/main/java/com/google/auto/common/SimpleAnnotationMirror.java10
-rw-r--r--common/src/main/java/com/google/auto/common/SuperficialValidation.java57
-rw-r--r--common/src/main/java/com/google/auto/common/Visibility.java8
-rw-r--r--common/src/main/java/com/google/auto/common/package-info.java17
-rw-r--r--common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java212
-rw-r--r--common/src/test/java/com/google/auto/common/GeneratedAnnotationsTest.java22
-rw-r--r--common/src/test/java/com/google/auto/common/MoreElementsTest.java151
-rw-r--r--common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java50
-rw-r--r--common/src/test/java/com/google/auto/common/MoreTypesTest.java370
-rw-r--r--common/src/test/java/com/google/auto/common/OverridesTest.java186
-rw-r--r--common/src/test/java/com/google/auto/common/SimpleAnnotationMirrorTest.java1
-rw-r--r--common/src/test/java/com/google/auto/common/SimpleTypeAnnotationValueTest.java26
-rw-r--r--common/src/test/java/com/google/auto/common/SuperficialValidationTest.java342
-rw-r--r--common/src/test/java/com/google/auto/common/VisibilityTest.java80
-rw-r--r--factory/pom.xml84
-rw-r--r--factory/src/it/functional/pom.xml10
-rw-r--r--factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java26
-rw-r--r--factory/src/it/functional/src/main/java/com/google/auto/factory/DependencyImpl.java3
-rw-r--r--factory/src/it/functional/src/main/java/com/google/auto/factory/GenericFoo.java5
-rw-r--r--factory/src/it/functional/src/main/java/com/google/auto/factory/GuiceModule.java3
-rwxr-xr-x[-rw-r--r--]factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java4
-rwxr-xr-x[-rw-r--r--]factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java0
-rw-r--r--factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java19
-rw-r--r--factory/src/main/java/com/google/auto/factory/AutoFactory.java4
-rw-r--r--factory/src/main/java/com/google/auto/factory/Provided.java2
-rw-r--r--factory/src/main/java/com/google/auto/factory/package-info.java17
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/AnnotationValues.java28
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java88
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java107
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/Elements2.java10
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java88
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java136
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java36
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java103
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/ImplementationMethodDescriptor.java16
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/Key.java25
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/Mirrors.java53
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/Parameter.java23
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/ProvidedChecker.java66
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/ProviderField.java4
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/TypeVariables.java19
-rw-r--r--factory/src/main/java/com/google/auto/factory/processor/package-info.java3
-rw-r--r--factory/src/test/java/com/google/auto/factory/processor/AutoFactoryDeclarationTest.java3
-rw-r--r--factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java751
-rw-r--r--factory/src/test/resources/bad/FactoryExtendingAbstractClassWithConstructorParams.java (renamed from factory/src/test/resources/good/FactoryExtendingAbstractClassWithConstructorParams.java)4
-rw-r--r--factory/src/test/resources/bad/InvalidCustomName.java2
-rw-r--r--factory/src/test/resources/expected/CheckerFrameworkNullableFactory.java21
-rw-r--r--factory/src/test/resources/expected/ClassUsingQualifierWithArgsFactory.java11
-rw-r--r--factory/src/test/resources/expected/ConstructorAnnotatedFactory.java9
-rw-r--r--factory/src/test/resources/expected/ConstructorAnnotatedNonFinalFactory.java9
-rw-r--r--factory/src/test/resources/expected/ConstructorAnnotatedThrowsFactory.java59
-rw-r--r--factory/src/test/resources/expected/CustomNamedFactory.java9
-rw-r--r--factory/src/test/resources/expected/CustomNullableFactory.java6
-rw-r--r--factory/src/test/resources/expected/FactoryExtendingAbstractClassFactory.java12
-rw-r--r--factory/src/test/resources/expected/FactoryExtendingAbstractClassThrowsFactory.java39
-rw-r--r--factory/src/test/resources/expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java9
-rw-r--r--factory/src/test/resources/expected/FactoryImplementingGenericInterfaceExtensionFactory.java9
-rw-r--r--factory/src/test/resources/expected/MixedDepsImplementingInterfacesFactory.java21
-rw-r--r--factory/src/test/resources/expected/MultipleFactoriesConflictingParameterNamesFactory.java2
-rw-r--r--factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassAFactory.java6
-rw-r--r--factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassBFactory.java6
-rw-r--r--factory/src/test/resources/expected/MultipleProvidedParamsSameKeyFactory.java6
-rw-r--r--factory/src/test/resources/expected/NestedClassCustomNamedFactory.java9
-rw-r--r--factory/src/test/resources/expected/NestedClasses_SimpleNestedClassFactory.java9
-rw-r--r--factory/src/test/resources/expected/OnlyPrimitivesFactory.java9
-rw-r--r--factory/src/test/resources/expected/ProviderArgumentToCreateMethodFactory.java7
-rw-r--r--factory/src/test/resources/expected/PublicClassFactory.java9
-rw-r--r--factory/src/test/resources/expected/SimpleClassFactory.java9
-rw-r--r--factory/src/test/resources/expected/SimpleClassImplementingMarkerFactory.java9
-rw-r--r--factory/src/test/resources/expected/SimpleClassImplementingSimpleInterfaceFactory.java12
-rw-r--r--factory/src/test/resources/expected/SimpleClassMixedDepsFactory.java10
-rw-r--r--factory/src/test/resources/expected/SimpleClassNonFinalFactory.java9
-rw-r--r--factory/src/test/resources/expected/SimpleClassNullableParametersFactory.java6
-rw-r--r--factory/src/test/resources/expected/SimpleClassPassedDepsFactory.java9
-rw-r--r--factory/src/test/resources/expected/SimpleClassProvidedDepsFactory.java6
-rw-r--r--factory/src/test/resources/expected/SimpleClassProvidedProviderDepsFactory.java6
-rw-r--r--factory/src/test/resources/expected/SimpleClassThrowsFactory.java33
-rw-r--r--factory/src/test/resources/expected/SimpleClassVarargsFactory.java5
-rw-r--r--factory/src/test/resources/good/CheckerFrameworkNullable.java5
-rw-r--r--factory/src/test/resources/good/ConstructorAnnotated.java16
-rw-r--r--factory/src/test/resources/good/ConstructorAnnotatedNonFinal.java16
-rw-r--r--factory/src/test/resources/good/ConstructorAnnotatedThrows.java36
-rw-r--r--factory/src/test/resources/good/FactoryExtendingAbstractClass.java2
-rw-r--r--factory/src/test/resources/good/FactoryExtendingAbstractClassThrows.java29
-rw-r--r--factory/src/test/resources/good/FactoryExtendingAbstractClassWithMultipleConstructors.java5
-rw-r--r--factory/src/test/resources/good/FactoryImplementingCreateMethod.java2
-rw-r--r--factory/src/test/resources/good/MixedDepsImplementingInterfaces.java6
-rw-r--r--factory/src/test/resources/good/MultipleFactoriesImplementingInterface.java6
-rw-r--r--factory/src/test/resources/good/SimpleClassImplementingMarker.java9
-rw-r--r--factory/src/test/resources/good/SimpleClassProvidedDeps.java6
-rw-r--r--factory/src/test/resources/good/SimpleClassThrows.java24
-rw-r--r--factory/src/test/resources/support/QualifierWithArgs.java1
-rw-r--r--service/pom.xml14
-rw-r--r--service/processor/pom.xml2
-rw-r--r--service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java128
-rw-r--r--service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java4
-rw-r--r--service/processor/src/main/java/com/google/auto/service/processor/package-info.java2
-rw-r--r--service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java138
-rw-r--r--service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti1
-rw-r--r--service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti1
-rw-r--r--service/processor/src/test/resources/test/AnotherService.java2
-rw-r--r--service/processor/src/test/resources/test/AnotherServiceProvider.java2
-rw-r--r--service/processor/src/test/resources/test/Enclosing.java4
-rw-r--r--service/processor/src/test/resources/test/EnclosingGeneric.java31
-rw-r--r--service/processor/src/test/resources/test/GenericService.java22
-rw-r--r--service/processor/src/test/resources/test/GenericServiceProvider.java25
-rw-r--r--service/processor/src/test/resources/test/GenericServiceProviderSuppressWarnings.java26
-rw-r--r--service/processor/src/test/resources/test/SomeService.java2
-rw-r--r--service/processor/src/test/resources/test/SomeServiceProvider1.java2
-rw-r--r--service/processor/src/test/resources/test/SomeServiceProvider2.java2
-rwxr-xr-xutil/generate-latest-docs.sh44
-rwxr-xr-xutil/publish-snapshot-on-commit.sh14
-rw-r--r--util/settings.xml9
-rw-r--r--value/Android.bp17
-rw-r--r--value/annotations/pom.xml1
-rw-r--r--value/pom.xml18
-rw-r--r--value/processor/pom.xml88
-rw-r--r--value/src/it/functional/pom.xml98
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/AutoAnnotationTest.java93
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java99
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java580
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/AutoOneOfTest.java10
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java60
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/AutoValueTest.java224
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/CompileWithEclipseTest.java44
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/GradleTest.java187
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt22
-rw-r--r--value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java3
-rw-r--r--value/src/it/gwtserializer/pom.xml16
-rw-r--r--value/src/main/java/com/google/auto/value/AutoBuilder.java64
-rw-r--r--value/src/main/java/com/google/auto/value/AutoValue.java2
-rw-r--r--value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java16
-rw-r--r--value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java77
-rw-r--r--value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizedValidator.java8
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/g3doc/index.md (renamed from value/src/main/java/com/google/auto/value/extension/serializable/userguide/index.md)2
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/g3doc/serializer-extension.md (renamed from value/src/main/java/com/google/auto/value/extension/serializable/userguide/serializer-extension.md)1
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtension.java5
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/serializer/SerializerFactoryLoader.java12
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableListSerializerExtension.java13
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableMapSerializerExtension.java21
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/SerializerFactoryImpl.java8
-rw-r--r--value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerFactory.java13
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/ToPrettyString.java68
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/processor/Annotations.java44
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ClassNames.java25
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ExtensionClassTypeSpecBuilder.java297
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringExtension.java562
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringMethods.java66
-rw-r--r--value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringValidator.java142
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoAnnotationProcessor.java125
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoAnnotationTemplateVars.java12
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java417
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoBuilderTemplateVars.java27
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java39
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoOneOfTemplateVars.java2
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueBuilderProcessor.java2
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java155
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java61
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueTemplateVars.java111
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java (renamed from value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java)272
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueishTemplateVars.java (renamed from value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfTemplateVars.java)11
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java444
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java247
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java148
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderSpec.java133
-rw-r--r--value/src/main/java/com/google/auto/value/processor/ClassNames.java2
-rw-r--r--value/src/main/java/com/google/auto/value/processor/ErrorReporter.java5
-rw-r--r--value/src/main/java/com/google/auto/value/processor/GwtCompatibility.java4
-rw-r--r--value/src/main/java/com/google/auto/value/processor/GwtSerialization.java20
-rw-r--r--value/src/main/java/com/google/auto/value/processor/Nullables.java192
-rw-r--r--value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java166
-rw-r--r--value/src/main/java/com/google/auto/value/processor/Reformatter.java52
-rw-r--r--value/src/main/java/com/google/auto/value/processor/SimpleMethod.java16
-rw-r--r--value/src/main/java/com/google/auto/value/processor/SimpleServiceLoader.java81
-rw-r--r--value/src/main/java/com/google/auto/value/processor/TemplateVars.java7
-rw-r--r--value/src/main/java/com/google/auto/value/processor/TypeEncoder.java49
-rw-r--r--value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java6
-rw-r--r--value/src/main/java/com/google/auto/value/processor/TypeVariables.java72
-rw-r--r--value/src/main/java/com/google/auto/value/processor/autoannotation.vm5
-rw-r--r--value/src/main/java/com/google/auto/value/processor/autobuilder.vm41
-rw-r--r--value/src/main/java/com/google/auto/value/processor/autooneof.vm10
-rw-r--r--value/src/main/java/com/google/auto/value/processor/autovalue.vm241
-rw-r--r--value/src/main/java/com/google/auto/value/processor/builder.vm285
-rw-r--r--value/src/main/java/com/google/auto/value/processor/equalshashcode.vm2
-rw-r--r--value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java5
-rw-r--r--value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java56
-rw-r--r--value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java34
-rw-r--r--value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java9
-rw-r--r--value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java961
-rw-r--r--value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java240
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java28
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java47
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java875
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java22
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java187
-rw-r--r--value/src/test/java/com/google/auto/value/processor/ExtensionTest.java320
-rw-r--r--value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java16
-rw-r--r--value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java26
-rw-r--r--value/src/test/java/com/google/auto/value/processor/NullablesTest.java179
-rw-r--r--value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java63
-rw-r--r--value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java18
-rw-r--r--value/src/test/java/com/google/auto/value/processor/ReformatterTest.java69
-rw-r--r--value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java31
-rw-r--r--value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java3
-rw-r--r--value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java3
-rw-r--r--value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java20
-rw-r--r--value/userguide/autobuilder.md491
-rw-r--r--value/userguide/builders-howto.md47
-rw-r--r--value/userguide/builders.md19
-rw-r--r--value/userguide/extensions.md2
-rw-r--r--value/userguide/howto.md54
-rw-r--r--value/userguide/index.md21
-rw-r--r--value/userguide/practices.md1
-rw-r--r--value/userguide/why.md1
231 files changed, 12135 insertions, 3360 deletions
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..18cc4cef
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,22 @@
+version: 2
+updates:
+ - package-ecosystem: "maven"
+ directory: "/common"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "maven"
+ directory: "/factory"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "maven"
+ directory: "/service"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "maven"
+ directory: "/value"
+ schedule:
+ interval: "daily"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..ec922435
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,97 @@
+name: CI
+
+on:
+ push:
+ branches:
+ - master
+ pull_request:
+ branches:
+ - master
+
+jobs:
+ test:
+ name: "JDK ${{ matrix.java }}"
+ strategy:
+ matrix:
+ java: [ 8, 11 ]
+ runs-on: ubuntu-latest
+ steps:
+ # Cancel any previous runs for the same branch that are still running.
+ - name: 'Cancel previous runs'
+ uses: styfle/cancel-workflow-action@0.9.0
+ with:
+ access_token: ${{ github.token }}
+ - name: 'Check out repository'
+ uses: actions/checkout@v2.3.4
+ - name: 'Cache local Maven repository'
+ uses: actions/cache@v2.1.6
+ with:
+ path: ~/.m2/repository
+ key: maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ maven-
+ - name: 'Set up JDK ${{ matrix.java }}'
+ uses: actions/setup-java@v2
+ with:
+ java-version: ${{ matrix.java }}
+ distribution: 'zulu'
+ - name: 'Install'
+ shell: bash
+ run: mvn -B dependency:go-offline test clean -U --quiet --fail-never -DskipTests=true -f build-pom.xml
+ - name: 'Test'
+ shell: bash
+ run: mvn -B verify -U --fail-at-end -Dsource.skip=true -Dmaven.javadoc.skip=true -f build-pom.xml
+
+ publish_snapshot:
+ name: 'Publish snapshot'
+ needs: test
+ if: github.event_name == 'push' && github.repository == 'google/auto'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2.3.4
+ - name: 'Cache local Maven repository'
+ uses: actions/cache@v2.1.6
+ with:
+ path: ~/.m2/repository
+ key: maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ maven-
+ - name: 'Set up JDK 11'
+ uses: actions/setup-java@v2
+ with:
+ java-version: 11
+ distribution: 'zulu'
+ server-id: sonatype-nexus-snapshots
+ server-username: CI_DEPLOY_USERNAME
+ server-password: CI_DEPLOY_PASSWORD
+ - name: 'Publish'
+ env:
+ CI_DEPLOY_USERNAME: ${{ secrets.CI_DEPLOY_USERNAME }}
+ CI_DEPLOY_PASSWORD: ${{ secrets.CI_DEPLOY_PASSWORD }}
+ run: ./util/publish-snapshot-on-commit.sh
+
+ generate_docs:
+ name: 'Generate latest docs'
+ needs: test
+ if: github.event_name == 'push' && github.repository == 'google/auto'
+ runs-on: ubuntu-latest
+ steps:
+ - name: 'Check out repository'
+ uses: actions/checkout@v2.3.4
+ - name: 'Cache local Maven repository'
+ uses: actions/cache@v2.1.6
+ with:
+ path: ~/.m2/repository
+ key: maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ maven-
+ - name: 'Set up JDK 11'
+ uses: actions/setup-java@v2
+ with:
+ java-version: 11
+ distribution: 'zulu'
+ - name: 'Generate latest docs'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: ./util/generate-latest-docs.sh
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index b0be6faa..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-sudo: false
-
-language: java
-
-install:
- - mvn -B -U -f build-pom.xml dependency:go-offline test clean --quiet --fail-never -DskipTests=true
-
-script:
- - mvn -B -U -f build-pom.xml verify --fail-at-end -Dsource.skip=true -Dmaven.javadoc.skip=true
-
-jdk:
- - openjdk9
- - openjdk8
-
-env:
- global:
- - secure: "bWNSSMURwYC0oZWIMZRd7dy5+JdoyZ060d427TAqFRJmOkYtlR+dBbBggjeJmM0PEkQDzeBrWwln/Vq3lnRPv8czA7hSg/R33r3GzTyi1GZhjCYN2mPW8qp4qgqlloh78aaOODUNSJsOtQqPDJPmhLLfD6UCY0eq9zHhweIjYdw="
- - secure: "s5V9d8MKl7ZHqCxuYLljLSD4sp9KLtYkk9hVxEPqCLAi4zA70WkX9h+GZI1gAOpcavomfrWcgSDT2ZReiuNpwx7OtczdS4zB+s6mo4F598iRs4bhSLiPT+Hzvx6BSwf1ZKZTYEhrUPGmKOp2T29AxMV7D0Q+P7n574ubvpUuZmA="
- - secure: "T24JAd60zthkeLBmenvZn6+qI43uvfuLwVb70Ljhbc19XDYEZV4Zm/kaafsisP5+F6kV4GjFaT+NCq2sJlwvPSMMRvU1JJgmNVh8TmtswkC/PHKonkMkOsj2KmFP0RRSPdvQv2NrSguZUq8mg+2pvnPO0qoPg4VeIODPGtAxNb8="
-
-after_success:
- - util/generate-latest-docs.sh
- - util/publish-snapshot-on-commit.sh
-
-branches:
- only:
- - master
- - /^release.*$/
diff --git a/README.md b/README.md
index 366f9404..bbefd275 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Auto
-[![Build Status](https://travis-ci.org/google/auto.svg?branch=master)](https://travis-ci.org/google/auto)
+[![Build Status](https://github.com/google/auto/actions/workflows/ci.yml/badge.svg)](https://github.com/google/auto/actions/workflows/ci.yml)
A collection of source code generators for [Java][java].
diff --git a/android-annotation-stubs/gen_annotations.sh b/android-annotation-stubs/gen_annotations.sh
index 90c9fcfb..4766af97 100755
--- a/android-annotation-stubs/gen_annotations.sh
+++ b/android-annotation-stubs/gen_annotations.sh
@@ -6,6 +6,7 @@ declare -A IMPORT
ANNOTATIONS=(
net.ltgt.gradle.incap.IncrementalAnnotationProcessor
+ org.checkerframework.checker.nullness.qual.Nullable
)
PARAMETER["net.ltgt.gradle.incap.IncrementalAnnotationProcessor"]="IncrementalAnnotationProcessorType"
diff --git a/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java b/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java
new file mode 100644
index 00000000..276d64c1
--- /dev/null
+++ b/android-annotation-stubs/src/org/checkerframework/checker/nullness/qual/Nullable.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * 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 org.checkerframework.checker.nullness.qual;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/* This is an annotation stub to avoid dependencies on annotations that aren't
+ * in the Android platform source tree. */
+
+@Target({
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.FIELD,
+ ElementType.LOCAL_VARIABLE,
+ ElementType.METHOD,
+ ElementType.PACKAGE,
+ ElementType.PARAMETER,
+ ElementType.TYPE,
+ ElementType.TYPE_PARAMETER,
+ ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.SOURCE)
+public @interface Nullable {}
diff --git a/common/Android.bp b/common/Android.bp
index 25f70998..19e2acdc 100644
--- a/common/Android.bp
+++ b/common/Android.bp
@@ -11,6 +11,7 @@ java_library_host {
name: "auto_common",
srcs: ["src/main/java/**/*.java"],
libs: [
+ "auto_android_annotation_stubs",
"guava",
"javapoet",
],
diff --git a/common/README.md b/common/README.md
index 990aa31b..9f8eb79e 100644
--- a/common/README.md
+++ b/common/README.md
@@ -1,28 +1,30 @@
-Auto Common Utilities
-========
+# Auto Common Utilities
## Overview
-The Auto project has a set of common utilities to help ease use of the annotation processing
-environment.
+The Auto project has a set of common utilities to help ease use of the
+annotation processing environment.
## Utility classes of note
- * MoreTypes - utilities and Equivalence wrappers for TypeMirror and related subtypes
- * MoreElements - utilities for Element and related subtypes
- * SuperficialValidation - very simple scanner to ensure an Element is valid and free from
- distortion from upstream compilation errors
- * Visibility - utilities for working with Elements' visibility levels (public, protected, etc.)
- * BasicAnnotationProcessor/ProcessingStep - simple types that
- - implement a validating annotation processor
- - defer invalid elements until later
- - break processor actions into multiple steps (which may each handle different annotations)
+* MoreTypes - utilities and Equivalence wrappers for TypeMirror and related
+ subtypes
+* MoreElements - utilities for Element and related subtypes
+* SuperficialValidation - very simple scanner to ensure an Element is valid
+ and free from distortion from upstream compilation errors
+* Visibility - utilities for working with Elements' visibility levels (public,
+ protected, etc.)
+* BasicAnnotationProcessor/ProcessingStep - simple types that
+ - implement a validating annotation processor
+ - defer invalid elements until later
+ - break processor actions into multiple steps (which may each handle
+ different annotations)
## Usage/Setup
-Auto common utilities have a standard [Maven](http://maven.apache.org) setup which can also be
-used from Gradle, Ivy, Ant, or other systems which consume binary artifacts from the central Maven
-binary artifact repositories.
+Auto common utilities have a standard [Maven](http://maven.apache.org) setup
+which can also be used from Gradle, Ivy, Ant, or other systems which consume
+binary artifacts from the central Maven binary artifact repositories.
```xml
<dependency>
@@ -31,49 +33,3 @@ binary artifact repositories.
<version>1.0-SNAPSHOT</version> <!-- or use a known release version -->
</dependency>
```
-
-## Processor Resilience
-
-Auto Common Utilities is used by a variety of annotation processors in Google and new versions
-may have breaking changes. Users of auto-common are urged to use
-[shade](https://maven.apache.org/plugins/maven-shade-plugin/) or
-[jarjar](https://code.google.com/p/jarjar/) (or something similar) in packaging their processors
-so that conflicting versions of this library do not adversely interact with each other.
-
-For example, in a Maven build you can repackage `com.google.auto.common` into
-`your.processor.shaded.auto.common` like this:
-
-```xml
-<project>
- <!-- your other config -->
- <build>
- <plugins>
- <plugin>
- <artifactId>maven-shade-plugin</artifactId>
- <executions>
- <execution>
- <phase>package</phase>
- <goals>
- <goal>shade</goal>
- </goals>
- <configuration>
- <artifactSet>
- <excludes>
- <!-- exclude dependencies you don't want to bundle in your processor -->
- </excludes>
- </artifactSet>
- <relocations>
- <relocation>
- <pattern>com.google.auto.common</pattern>
- <shadedPattern>your.processor.shaded.auto.common</shadedPattern>
- </relocation>
- </relocations>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
-</project>
-```
-
diff --git a/common/pom.xml b/common/pom.xml
index e4a23e02..2754b81d 100644
--- a/common/pom.xml
+++ b/common/pom.xml
@@ -36,8 +36,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
- <guava.version>29.0-jre</guava.version>
- <truth.version>1.0.1</truth.version>
+ <guava.version>30.1.1-jre</guava.version>
+ <truth.version>1.1.3</truth.version>
</properties>
<scm>
@@ -89,13 +89,13 @@
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
- <version>0.18</version>
+ <version>0.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.12</version>
+ <version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -107,7 +107,7 @@
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
- <version>3.22.0</version>
+ <version>3.25.0</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -116,7 +116,7 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -128,14 +128,14 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
</plugin>
</plugins>
</build>
diff --git a/common/src/main/java/com/google/auto/common/AnnotationMirrors.java b/common/src/main/java/com/google/auto/common/AnnotationMirrors.java
index 62e5834e..9ce5cd9b 100644
--- a/common/src/main/java/com/google/auto/common/AnnotationMirrors.java
+++ b/common/src/main/java/com/google/auto/common/AnnotationMirrors.java
@@ -16,21 +16,21 @@
package com.google.auto.common;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Collections.unmodifiableMap;
import com.google.common.base.Equivalence;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.lang.annotation.Annotation;
import java.util.Arrays;
-import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
@@ -45,20 +45,32 @@ public final class AnnotationMirrors {
new Equivalence<AnnotationMirror>() {
@Override
protected boolean doEquivalent(AnnotationMirror left, AnnotationMirror right) {
- return MoreTypes.equivalence().equivalent(left.getAnnotationType(),
- right.getAnnotationType()) && AnnotationValues.equivalence().pairwise().equivalent(
- getAnnotationValuesWithDefaults(left).values(),
- getAnnotationValuesWithDefaults(right).values());
+ return MoreTypes.equivalence()
+ .equivalent(left.getAnnotationType(), right.getAnnotationType())
+ && AnnotationValues.equivalence()
+ .pairwise()
+ .equivalent(
+ getAnnotationValuesWithDefaults(left).values(),
+ getAnnotationValuesWithDefaults(right).values());
}
+
@Override
protected int doHash(AnnotationMirror annotation) {
DeclaredType type = annotation.getAnnotationType();
Iterable<AnnotationValue> annotationValues =
getAnnotationValuesWithDefaults(annotation).values();
- return Arrays.hashCode(new int[] {MoreTypes.equivalence().hash(type),
- AnnotationValues.equivalence().pairwise().hash(annotationValues)});
+ return Arrays.hashCode(
+ new int[] {
+ MoreTypes.equivalence().hash(type),
+ AnnotationValues.equivalence().pairwise().hash(annotationValues)
+ });
+ }
+
+ @Override
+ public String toString() {
+ return "AnnotationMirrors.equivalence()";
}
- };
+ };
/**
* Returns an {@link Equivalence} for {@link AnnotationMirror} as some implementations
@@ -83,8 +95,10 @@ public final class AnnotationMirrors {
public static ImmutableMap<ExecutableElement, AnnotationValue> getAnnotationValuesWithDefaults(
AnnotationMirror annotation) {
ImmutableMap.Builder<ExecutableElement, AnnotationValue> values = ImmutableMap.builder();
- Map<? extends ExecutableElement, ? extends AnnotationValue> declaredValues =
- annotation.getElementValues();
+ // Use unmodifiableMap to eliminate wildcards, which cause issues for our nullness checker.
+ @SuppressWarnings("GetElementValues")
+ Map<ExecutableElement, AnnotationValue> declaredValues =
+ unmodifiableMap(annotation.getElementValues());
for (ExecutableElement method :
ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
// Must iterate and put in this order, to ensure consistency in generated code.
@@ -95,8 +109,10 @@ public final class AnnotationMirrors {
} else {
throw new IllegalStateException(
"Unset annotation value without default should never happen: "
- + MoreElements.asType(method.getEnclosingElement()).getQualifiedName()
- + '.' + method.getSimpleName() + "()");
+ + MoreElements.asType(method.getEnclosingElement()).getQualifiedName()
+ + '.'
+ + method.getSimpleName()
+ + "()");
}
}
return values.build();
@@ -131,25 +147,48 @@ public final class AnnotationMirrors {
return entry;
}
}
- throw new IllegalArgumentException(String.format("@%s does not define an element %s()",
- MoreElements.asType(annotationMirror.getAnnotationType().asElement()).getQualifiedName(),
- elementName));
+ throw new IllegalArgumentException(
+ String.format(
+ "@%s does not define an element %s()",
+ MoreElements.asType(annotationMirror.getAnnotationType().asElement())
+ .getQualifiedName(),
+ elementName));
+ }
+
+ /**
+ * Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
+ * Element} which are themselves annotated with {@code annotationClass}.
+ */
+ public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
+ Element element, Class<? extends Annotation> annotationClass) {
+ String name = annotationClass.getCanonicalName();
+ if (name == null) {
+ return ImmutableSet.of();
+ }
+ return getAnnotatedAnnotations(element, name);
+ }
+
+ /**
+ * Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
+ * Element} which are themselves annotated with {@code annotation}.
+ */
+ public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
+ Element element, TypeElement annotation) {
+ return element.getAnnotationMirrors().stream()
+ .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotation))
+ .collect(toImmutableSet());
}
/**
- * Returns all {@linkplain AnnotationMirror annotations} that are present on the given
- * {@link Element} which are themselves annotated with {@code annotationType}.
+ * Returns all {@linkplain AnnotationMirror annotations} that are present on the given {@link
+ * Element} which are themselves annotated with an annotation whose type's canonical name is
+ * {@code annotationName}.
*/
- public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(Element element,
- final Class<? extends Annotation> annotationType) {
- List<? extends AnnotationMirror> annotations = element.getAnnotationMirrors();
- return FluentIterable.from(annotations)
- .filter(new Predicate<AnnotationMirror>() {
- @Override public boolean apply(AnnotationMirror input) {
- return isAnnotationPresent(input.getAnnotationType().asElement(), annotationType);
- }
- })
- .toSet();
+ public static ImmutableSet<? extends AnnotationMirror> getAnnotatedAnnotations(
+ Element element, String annotationName) {
+ return element.getAnnotationMirrors().stream()
+ .filter(input -> isAnnotationPresent(input.getAnnotationType().asElement(), annotationName))
+ .collect(toImmutableSet());
}
private AnnotationMirrors() {}
diff --git a/common/src/main/java/com/google/auto/common/AnnotationValues.java b/common/src/main/java/com/google/auto/common/AnnotationValues.java
index e2339018..0712e56a 100644
--- a/common/src/main/java/com/google/auto/common/AnnotationValues.java
+++ b/common/src/main/java/com/google/auto/common/AnnotationValues.java
@@ -15,8 +15,8 @@
*/
package com.google.auto.common;
+import static com.google.auto.common.MoreStreams.toImmutableList;
import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.common.base.Equivalence;
import com.google.common.collect.ImmutableList;
@@ -37,91 +37,120 @@ import javax.lang.model.util.SimpleAnnotationValueVisitor8;
public final class AnnotationValues {
private static final Equivalence<AnnotationValue> ANNOTATION_VALUE_EQUIVALENCE =
new Equivalence<AnnotationValue>() {
- @Override protected boolean doEquivalent(AnnotationValue left, AnnotationValue right) {
- return left.accept(new SimpleAnnotationValueVisitor8<Boolean, AnnotationValue>() {
- // LHS is not an annotation or array of annotation values, so just test equality.
- @Override protected Boolean defaultAction(Object left, AnnotationValue right) {
- return left.equals(right.accept(
- new SimpleAnnotationValueVisitor8<Object, Void>() {
- @Override protected Object defaultAction(Object object, Void unused) {
- return object;
- }
- }, null));
- }
-
- // LHS is an annotation mirror so test equivalence for RHS annotation mirrors
- // and false for other types.
- @Override public Boolean visitAnnotation(AnnotationMirror left, AnnotationValue right) {
- return right.accept(
- new SimpleAnnotationValueVisitor8<Boolean, AnnotationMirror>() {
- @Override protected Boolean defaultAction(Object right, AnnotationMirror left) {
- return false; // Not an annotation mirror, so can't be equal to such.
- }
- @Override
- public Boolean visitAnnotation(AnnotationMirror right, AnnotationMirror left) {
- return AnnotationMirrors.equivalence().equivalent(left, right);
- }
- }, left);
- }
-
- // LHS is a list of annotation values have to collect-test equivalences, or false
- // for any other types.
- @Override
- public Boolean visitArray(List<? extends AnnotationValue> left, AnnotationValue right) {
- return right.accept(
- new SimpleAnnotationValueVisitor8<Boolean, List<? extends AnnotationValue>>() {
- @Override protected Boolean defaultAction(
- Object ignored, List<? extends AnnotationValue> alsoIgnored) {
- return false; // Not an array, so can't be equal to such.
- }
-
- @SuppressWarnings("unchecked") // safe covariant cast
- @Override public Boolean visitArray(
- List<? extends AnnotationValue> right ,
- List<? extends AnnotationValue> left) {
- return AnnotationValues.equivalence().pairwise().equivalent(
- (List<AnnotationValue>) left, (List<AnnotationValue>) right);
- }
- }, left);
- }
-
- @Override
- public Boolean visitType(TypeMirror left, AnnotationValue right) {
- return right.accept(
- new SimpleAnnotationValueVisitor8<Boolean, TypeMirror>() {
- @Override protected Boolean defaultAction(
- Object ignored, TypeMirror alsoIgnored) {
- return false; // Not an annotation mirror, so can't be equal to such.
- }
-
- @Override public Boolean visitType(TypeMirror right, TypeMirror left) {
- return MoreTypes.equivalence().equivalent(left, right);
- }
- }, left);
- }
- }, right);
+ @Override
+ protected boolean doEquivalent(AnnotationValue left, AnnotationValue right) {
+ return left.accept(
+ new SimpleAnnotationValueVisitor8<Boolean, AnnotationValue>() {
+ // LHS is not an annotation or array of annotation values, so just test equality.
+ @Override
+ protected Boolean defaultAction(Object left, AnnotationValue right) {
+ return left.equals(
+ right.accept(
+ new SimpleAnnotationValueVisitor8<Object, Void>() {
+ @Override
+ protected Object defaultAction(Object object, Void unused) {
+ return object;
+ }
+ },
+ null));
+ }
+
+ // LHS is an annotation mirror so test equivalence for RHS annotation mirrors
+ // and false for other types.
+ @Override
+ public Boolean visitAnnotation(AnnotationMirror left, AnnotationValue right) {
+ return right.accept(
+ new SimpleAnnotationValueVisitor8<Boolean, AnnotationMirror>() {
+ @Override
+ protected Boolean defaultAction(Object right, AnnotationMirror left) {
+ return false; // Not an annotation mirror, so can't be equal to such.
+ }
+
+ @Override
+ public Boolean visitAnnotation(
+ AnnotationMirror right, AnnotationMirror left) {
+ return AnnotationMirrors.equivalence().equivalent(left, right);
+ }
+ },
+ left);
+ }
+
+ // LHS is a list of annotation values have to collect-test equivalences, or false
+ // for any other types.
+ @Override
+ public Boolean visitArray(
+ List<? extends AnnotationValue> left, AnnotationValue right) {
+ return right.accept(
+ new SimpleAnnotationValueVisitor8<
+ Boolean, List<? extends AnnotationValue>>() {
+ @Override
+ protected Boolean defaultAction(
+ Object ignored, List<? extends AnnotationValue> alsoIgnored) {
+ return false; // Not an array, so can't be equal to such.
+ }
+
+ @SuppressWarnings("unchecked") // safe covariant cast
+ @Override
+ public Boolean visitArray(
+ List<? extends AnnotationValue> right,
+ List<? extends AnnotationValue> left) {
+ return AnnotationValues.equivalence()
+ .pairwise()
+ .equivalent(
+ (List<AnnotationValue>) left, (List<AnnotationValue>) right);
+ }
+ },
+ left);
+ }
+
+ @Override
+ public Boolean visitType(TypeMirror left, AnnotationValue right) {
+ return right.accept(
+ new SimpleAnnotationValueVisitor8<Boolean, TypeMirror>() {
+ @Override
+ protected Boolean defaultAction(Object ignored, TypeMirror alsoIgnored) {
+ return false; // Not an annotation mirror, so can't be equal to such.
+ }
+
+ @Override
+ public Boolean visitType(TypeMirror right, TypeMirror left) {
+ return MoreTypes.equivalence().equivalent(left, right);
+ }
+ },
+ left);
+ }
+ },
+ right);
}
- @Override protected int doHash(AnnotationValue value) {
- return value.accept(new SimpleAnnotationValueVisitor8<Integer, Void>() {
- @Override public Integer visitAnnotation(AnnotationMirror value, Void ignore) {
- return AnnotationMirrors.equivalence().hash(value);
- }
-
- @SuppressWarnings("unchecked") // safe covariant cast
- @Override public Integer visitArray(
- List<? extends AnnotationValue> values, Void ignore) {
- return AnnotationValues.equivalence().pairwise().hash((List<AnnotationValue>) values);
- }
-
- @Override public Integer visitType(TypeMirror value, Void ignore) {
- return MoreTypes.equivalence().hash(value);
- }
-
- @Override protected Integer defaultAction(Object value, Void ignored) {
- return value.hashCode();
- }
- }, null);
+ @Override
+ protected int doHash(AnnotationValue value) {
+ return value.accept(
+ new SimpleAnnotationValueVisitor8<Integer, Void>() {
+ @Override
+ public Integer visitAnnotation(AnnotationMirror value, Void ignore) {
+ return AnnotationMirrors.equivalence().hash(value);
+ }
+
+ @SuppressWarnings("unchecked") // safe covariant cast
+ @Override
+ public Integer visitArray(List<? extends AnnotationValue> values, Void ignore) {
+ return AnnotationValues.equivalence()
+ .pairwise()
+ .hash((List<AnnotationValue>) values);
+ }
+
+ @Override
+ public Integer visitType(TypeMirror value, Void ignore) {
+ return MoreTypes.equivalence().hash(value);
+ }
+
+ @Override
+ protected Integer defaultAction(Object value, Void ignored) {
+ return value.hashCode();
+ }
+ },
+ null);
}
};
@@ -209,7 +238,6 @@ public final class AnnotationValues {
return value;
}
}
- ;
/**
* Returns the value as a VariableElement.
@@ -486,4 +514,3 @@ public final class AnnotationValues {
private AnnotationValues() {}
}
-
diff --git a/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java b/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java
index 375a4cb8..d951aaf4 100644
--- a/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java
+++ b/common/src/main/java/com/google/auto/common/BasicAnnotationProcessor.java
@@ -17,14 +17,14 @@ package com.google.auto.common;
import static com.google.auto.common.MoreElements.asExecutable;
import static com.google.auto.common.MoreElements.asPackage;
+import static com.google.auto.common.MoreStreams.toImmutableMap;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
import static com.google.auto.common.SuperficialValidation.validateElement;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.transform;
import static com.google.common.collect.Multimaps.filterKeys;
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.Collectors.toMap;
+import static java.util.Objects.requireNonNull;
import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.tools.Diagnostic.Kind.ERROR;
@@ -56,6 +56,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.type.ErrorType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleElementVisitor8;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* An abstract {@link Processor} implementation that defers processing of {@link Element}s to later
@@ -162,14 +163,14 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
checkState(steps != null);
return steps.stream()
.flatMap(step -> getSupportedAnnotationTypeElements(step).stream())
- .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
+ .collect(toImmutableSet());
}
private ImmutableSet<TypeElement> getSupportedAnnotationTypeElements(Step step) {
return step.annotations().stream()
.map(elements::getTypeElement)
.filter(Objects::nonNull)
- .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
+ .collect(toImmutableSet());
}
/**
@@ -181,7 +182,7 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
checkState(steps != null);
return steps.stream()
.flatMap(step -> step.annotations().stream())
- .collect(collectingAndThen(toList(), ImmutableSet::copyOf));
+ .collect(toImmutableSet());
}
@Override
@@ -287,10 +288,7 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
// Look at the elements we've found and the new elements from this round and validate them.
for (TypeElement annotationType : getSupportedAnnotationTypeElements()) {
- Set<? extends Element> roundElements =
- (annotationType == null)
- ? ImmutableSet.of()
- : roundEnv.getElementsAnnotatedWith(annotationType);
+ Set<? extends Element> roundElements = roundEnv.getElementsAnnotatedWith(annotationType);
ImmutableSet<Element> prevRoundElements = deferredElementsByAnnotation.get(annotationType);
for (Element element : Sets.union(roundElements, prevRoundElements)) {
ElementName elementName = ElementName.forAnnotatedElement(element);
@@ -376,7 +374,7 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
* IllegalArgumentException} if the provided {@link Element} is a {@link PackageElement} or is
* otherwise not enclosed by a type.
*/
- // TODO(cgruber) move to MoreElements and make public.
+ // TODO(user) move to MoreElements and make public.
private static TypeElement getEnclosingType(Element element) {
return element.accept(
new SimpleElementVisitor8<TypeElement, Void>() {
@@ -478,10 +476,9 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
this.annotationsByName =
processingStep.annotations().stream()
.collect(
- collectingAndThen(
- toMap(
- Class::getCanonicalName, (Class<? extends Annotation> aClass) -> aClass),
- ImmutableMap::copyOf));
+ toImmutableMap(
+ c -> requireNonNull(c.getCanonicalName()),
+ (Class<? extends Annotation> aClass) -> aClass));
}
@Override
@@ -502,8 +499,12 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
elements
.asMap()
.forEach(
- (annotation, annotatedElements) ->
- builder.putAll(annotationsByName.get(annotation), annotatedElements));
+ (annotationName, annotatedElements) -> {
+ Class<? extends Annotation> annotation = annotationsByName.get(annotationName);
+ if (annotation != null) { // should not be null
+ builder.putAll(annotation, annotatedElements);
+ }
+ });
return builder.build();
}
}
@@ -561,7 +562,7 @@ public abstract class BasicAnnotationProcessor extends AbstractProcessor {
}
@Override
- public boolean equals(Object object) {
+ public boolean equals(@Nullable Object object) {
if (!(object instanceof ElementName)) {
return false;
}
diff --git a/common/src/main/java/com/google/auto/common/GeneratedAnnotationSpecs.java b/common/src/main/java/com/google/auto/common/GeneratedAnnotationSpecs.java
index bb35e22f..09d9029e 100644
--- a/common/src/main/java/com/google/auto/common/GeneratedAnnotationSpecs.java
+++ b/common/src/main/java/com/google/auto/common/GeneratedAnnotationSpecs.java
@@ -27,7 +27,7 @@ public final class GeneratedAnnotationSpecs {
private GeneratedAnnotationSpecs() {}
/**
- * Returns {@code @Generated("processorClass"} if either {@code
+ * Returns {@code @Generated("processorClass")} if either {@code
* javax.annotation.processing.Generated} or {@code javax.annotation.Generated} is {@linkplain
* GeneratedAnnotations#generatedAnnotation(Elements) available at compile time}.
*
@@ -41,7 +41,7 @@ public final class GeneratedAnnotationSpecs {
}
/**
- * Returns {@code @Generated(value = "processorClass", comments = "comments"} if either {@code
+ * Returns {@code @Generated(value = "processorClass", comments = "comments")} if either {@code
* javax.annotation.processing.Generated} or {@code javax.annotation.Generated} is {@linkplain
* GeneratedAnnotations#generatedAnnotation(Elements) available at compile time}.
*
@@ -55,7 +55,7 @@ public final class GeneratedAnnotationSpecs {
}
/**
- * Returns {@code @Generated("processorClass"} for the target {@code SourceVersion}.
+ * Returns {@code @Generated("processorClass")} for the target {@code SourceVersion}.
*
* <p>Returns {@code javax.annotation.processing.Generated} for JDK 9 and newer, {@code
* javax.annotation.Generated} for earlier releases, and Optional#empty()} if the annotation is
@@ -68,7 +68,7 @@ public final class GeneratedAnnotationSpecs {
}
/**
- * Returns {@code @Generated(value = "processorClass", comments = "comments"} for the target
+ * Returns {@code @Generated(value = "processorClass", comments = "comments")} for the target
* {@code SourceVersion}.
*
* <p>Returns {@code javax.annotation.processing.Generated} for JDK 9 and newer, {@code
diff --git a/common/src/main/java/com/google/auto/common/MoreElements.java b/common/src/main/java/com/google/auto/common/MoreElements.java
index 5e8e3541..dfbbaeef 100644
--- a/common/src/main/java/com/google/auto/common/MoreElements.java
+++ b/common/src/main/java/com/google/auto/common/MoreElements.java
@@ -16,6 +16,7 @@
*/
package com.google.auto.common;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.lang.model.element.Modifier.STATIC;
@@ -212,29 +213,80 @@ public final class MoreElements {
}
/**
- * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose
- * {@linkplain AnnotationMirror#getAnnotationType() annotation type} has the same canonical name
- * as that of {@code annotationClass}. This method is a safer alternative to calling
- * {@link Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
+ * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+ * AnnotationMirror#getAnnotationType() annotation type} has the same canonical name as that of
+ * {@code annotationClass}. This method is a safer alternative to calling {@link
+ * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
* annotation proxies.
*/
- public static boolean isAnnotationPresent(Element element,
- Class<? extends Annotation> annotationClass) {
+ public static boolean isAnnotationPresent(
+ Element element, Class<? extends Annotation> annotationClass) {
return getAnnotationMirror(element, annotationClass).isPresent();
}
/**
+ * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+ * AnnotationMirror#getAnnotationType() annotation type} has the same fully qualified name as that
+ * of {@code annotation}. This method is a safer alternative to calling {@link
+ * Element#getAnnotation} and checking for {@code null} as it avoids any interaction with
+ * annotation proxies.
+ */
+ public static boolean isAnnotationPresent(Element element, TypeElement annotation) {
+ return getAnnotationMirror(element, annotation).isPresent();
+ }
+
+ /**
+ * Returns {@code true} iff the given element has an {@link AnnotationMirror} whose {@linkplain
+ * AnnotationMirror#getAnnotationType() annotation type} has {@code annotationName} as its
+ * canonical name. This method is a safer alternative to calling {@link Element#getAnnotation} and
+ * checking for {@code null} as it avoids any interaction with annotation proxies.
+ */
+ public static boolean isAnnotationPresent(Element element, String annotationName) {
+ return getAnnotationMirror(element, annotationName).isPresent();
+ }
+
+ /**
* Returns an {@link AnnotationMirror} for the annotation of type {@code annotationClass} on
* {@code element}, or {@link Optional#absent()} if no such annotation exists. This method is a
* safer alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
* annotation proxies.
*/
- public static Optional<AnnotationMirror> getAnnotationMirror(Element element,
- Class<? extends Annotation> annotationClass) {
- String annotationClassName = annotationClass.getCanonicalName();
+ public static Optional<AnnotationMirror> getAnnotationMirror(
+ Element element, Class<? extends Annotation> annotationClass) {
+ String name = annotationClass.getCanonicalName();
+ if (name == null) {
+ return Optional.absent();
+ }
+ return getAnnotationMirror(element, name);
+ }
+
+ /**
+ * Returns an {@link AnnotationMirror} for the annotation of type {@code annotation} on {@code
+ * element}, or {@link Optional#absent()} if no such annotation exists. This method is a safer
+ * alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
+ * annotation proxies.
+ */
+ public static Optional<AnnotationMirror> getAnnotationMirror(
+ Element element, TypeElement annotation) {
+ for (AnnotationMirror elementAnnotation : element.getAnnotationMirrors()) {
+ if (elementAnnotation.getAnnotationType().asElement().equals(annotation)) {
+ return Optional.of(elementAnnotation);
+ }
+ }
+ return Optional.absent();
+ }
+
+ /**
+ * Returns an {@link AnnotationMirror} for the annotation whose type's canonical name is on {@code
+ * element}, or {@link Optional#absent()} if no such annotation exists. This method is a safer
+ * alternative to calling {@link Element#getAnnotation} as it avoids any interaction with
+ * annotation proxies.
+ */
+ public static Optional<AnnotationMirror> getAnnotationMirror(
+ Element element, String annotationName) {
for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
TypeElement annotationTypeElement = asType(annotationMirror.getAnnotationType().asElement());
- if (annotationTypeElement.getQualifiedName().contentEquals(annotationClassName)) {
+ if (annotationTypeElement.getQualifiedName().contentEquals(annotationName)) {
return Optional.of(annotationMirror);
}
}
@@ -435,9 +487,9 @@ public final class MoreElements {
}
}
}
- Set<ExecutableElement> methods = new LinkedHashSet<ExecutableElement>(methodMap.values());
- methods.removeAll(overridden);
- return ImmutableSet.copyOf(methods);
+ return methodMap.values().stream()
+ .filter(m -> !overridden.contains(m))
+ .collect(toImmutableSet());
}
// Add to `methods` the static and instance methods from `type`. This means all methods from
diff --git a/common/src/main/java/com/google/auto/common/MoreStreams.java b/common/src/main/java/com/google/auto/common/MoreStreams.java
new file mode 100644
index 00000000..934514ab
--- /dev/null
+++ b/common/src/main/java/com/google/auto/common/MoreStreams.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.common;
+
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.toList;
+
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+/**
+ * A utility class that provides Android compatible alternatives to Guava's streaming APIs.
+ *
+ * <p>This is useful when the Android flavor of Guava somehow finds its way onto the processor
+ * classpath.
+ */
+public final class MoreStreams {
+
+ /** Returns a collector for an {@link ImmutableList}. */
+ public static <T> Collector<T, ?, ImmutableList<T>> toImmutableList() {
+ return collectingAndThen(toList(), ImmutableList::copyOf);
+ }
+
+ /** Returns a collector for an {@link ImmutableSet}. */
+ public static <T> Collector<T, ?, ImmutableSet<T>> toImmutableSet() {
+ return collectingAndThen(toList(), ImmutableSet::copyOf);
+ }
+
+ /** Returns a collector for an {@link ImmutableMap}. */
+ public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap(
+ Function<? super T, K> keyMapper, Function<? super T, V> valueMapper) {
+ return Collectors.mapping(
+ value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
+ Collector.of(
+ ImmutableMap::builder,
+ (ImmutableMap.Builder<K, V> builder, Map.Entry<K, V> entry) -> builder.put(entry),
+ (left, right) -> left.putAll(right.build()),
+ ImmutableMap.Builder::build));
+ }
+
+ /** Returns a collector for an {@link ImmutableBiMap}. */
+ public static <T, K, V> Collector<T, ?, ImmutableBiMap<K, V>> toImmutableBiMap(
+ Function<? super T, K> keyMapper, Function<? super T, V> valueMapper) {
+ return Collectors.mapping(
+ value -> Maps.immutableEntry(keyMapper.apply(value), valueMapper.apply(value)),
+ Collector.of(
+ ImmutableBiMap::builder,
+ (ImmutableBiMap.Builder<K, V> builder, Map.Entry<K, V> entry) -> builder.put(entry),
+ (left, right) -> left.putAll(right.build()),
+ ImmutableBiMap.Builder::build));
+ }
+
+ private MoreStreams() {}
+}
diff --git a/common/src/main/java/com/google/auto/common/MoreTypes.java b/common/src/main/java/com/google/auto/common/MoreTypes.java
index e09680bc..1a490626 100644
--- a/common/src/main/java/com/google/auto/common/MoreTypes.java
+++ b/common/src/main/java/com/google/auto/common/MoreTypes.java
@@ -25,7 +25,6 @@ import static javax.lang.model.type.TypeKind.TYPEVAR;
import static javax.lang.model.type.TypeKind.WILDCARD;
import com.google.common.base.Equivalence;
-import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -55,6 +54,7 @@ import javax.lang.model.type.WildcardType;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Utilities related to {@link TypeMirror} instances.
@@ -75,6 +75,11 @@ public final class MoreTypes {
protected int doHash(TypeMirror t) {
return MoreTypes.hash(t, ImmutableSet.<Element>of());
}
+
+ @Override
+ public String toString() {
+ return "MoreTypes.equivalence()";
+ }
}
/**
@@ -135,13 +140,11 @@ public final class MoreTypes {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(@Nullable Object o) {
if (o instanceof ComparedElements) {
ComparedElements that = (ComparedElements) o;
int nArguments = aArguments.size();
- if (!this.a.equals(that.a)
- || !this.b.equals(that.b)
- || nArguments != bArguments.size()) {
+ if (!this.a.equals(that.a) || !this.b.equals(that.b) || nArguments != bArguments.size()) {
// The arguments must be the same size, but we check anyway.
return false;
}
@@ -294,7 +297,14 @@ public final class MoreTypes {
}
@SuppressWarnings("TypeEquals")
- private static boolean equal(TypeMirror a, TypeMirror b, Set<ComparedElements> visiting) {
+ private static boolean equal(
+ @Nullable TypeMirror a, @Nullable TypeMirror b, Set<ComparedElements> visiting) {
+ if (a == b) {
+ return true;
+ }
+ if (a == null || b == null) {
+ return false;
+ }
// TypeMirror.equals is not guaranteed to return true for types that are equal, but we can
// assume that if it does return true then the types are equal. This check also avoids getting
// stuck in infinite recursion when Eclipse decrees that the upper bound of the second K in
@@ -302,13 +312,15 @@ public final class MoreTypes {
// The javac implementation of ExecutableType, at least in some versions, does not take thrown
// exceptions into account in its equals implementation, so avoid this optimization for
// ExecutableType.
- if (Objects.equal(a, b) && !(a instanceof ExecutableType)) {
+ @SuppressWarnings("TypesEquals")
+ boolean equal = a.equals(b);
+ if (equal && !(a instanceof ExecutableType)) {
return true;
}
EqualVisitorParam p = new EqualVisitorParam();
p.type = b;
p.visiting = visiting;
- return (a == b) || (a != null && b != null && a.accept(EqualVisitor.INSTANCE, p));
+ return a.accept(EqualVisitor.INSTANCE, p);
}
/**
@@ -318,7 +330,7 @@ public final class MoreTypes {
* <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=508222">this bug</a> whereby
* the Eclipse compiler returns a value for static classes that is not NoType.
*/
- private static TypeMirror enclosingType(DeclaredType t) {
+ private static @Nullable TypeMirror enclosingType(DeclaredType t) {
TypeMirror enclosing = t.getEnclosingType();
if (enclosing.getKind().equals(TypeKind.NONE)
|| t.asElement().getModifiers().contains(Modifier.STATIC)) {
@@ -337,16 +349,14 @@ public final class MoreTypes {
Iterator<? extends TypeMirror> aIterator = a.iterator();
Iterator<? extends TypeMirror> bIterator = b.iterator();
while (aIterator.hasNext()) {
- if (!bIterator.hasNext()) {
- return false;
- }
+ // We checked that the lists have the same size, so we know that bIterator.hasNext() too.
TypeMirror nextMirrorA = aIterator.next();
TypeMirror nextMirrorB = bIterator.next();
if (!equal(nextMirrorA, nextMirrorB, visiting)) {
return false;
}
}
- return !aIterator.hasNext();
+ return true;
}
private static final int HASH_SEED = 17;
@@ -433,7 +443,7 @@ public final class MoreTypes {
public Integer visitUnknown(TypeMirror t, Set<Element> visiting) {
throw new UnsupportedOperationException();
}
- };
+ }
private static int hashList(List<? extends TypeMirror> mirrors, Set<Element> visiting) {
int result = HASH_SEED;
@@ -460,17 +470,17 @@ public final class MoreTypes {
}
private static final class ReferencedTypes
- extends SimpleTypeVisitor8<Void, ImmutableSet.Builder<TypeElement>> {
+ extends SimpleTypeVisitor8<@Nullable Void, ImmutableSet.Builder<TypeElement>> {
private static final ReferencedTypes INSTANCE = new ReferencedTypes();
@Override
- public Void visitArray(ArrayType t, ImmutableSet.Builder<TypeElement> p) {
+ public @Nullable Void visitArray(ArrayType t, ImmutableSet.Builder<TypeElement> p) {
t.getComponentType().accept(this, p);
return null;
}
@Override
- public Void visitDeclared(DeclaredType t, ImmutableSet.Builder<TypeElement> p) {
+ public @Nullable Void visitDeclared(DeclaredType t, ImmutableSet.Builder<TypeElement> p) {
p.add(MoreElements.asType(t.asElement()));
for (TypeMirror typeArgument : t.getTypeArguments()) {
typeArgument.accept(this, p);
@@ -479,14 +489,14 @@ public final class MoreTypes {
}
@Override
- public Void visitTypeVariable(TypeVariable t, ImmutableSet.Builder<TypeElement> p) {
+ public @Nullable Void visitTypeVariable(TypeVariable t, ImmutableSet.Builder<TypeElement> p) {
t.getLowerBound().accept(this, p);
t.getUpperBound().accept(this, p);
return null;
}
@Override
- public Void visitWildcard(WildcardType t, ImmutableSet.Builder<TypeElement> p) {
+ public @Nullable Void visitWildcard(WildcardType t, ImmutableSet.Builder<TypeElement> p) {
TypeMirror extendsBound = t.getExtendsBound();
if (extendsBound != null) {
extendsBound.accept(this, p);
@@ -534,7 +544,8 @@ public final class MoreTypes {
public Element visitTypeVariable(TypeVariable t, Void p) {
return t.asElement();
}
- };
+ }
+ ;
// TODO(gak): consider removing these two methods as they're pretty trivial now
public static TypeElement asTypeElement(TypeMirror mirror) {
@@ -833,6 +844,11 @@ public final class MoreTypes {
}
@Override
+ public Boolean visitError(ErrorType errorType, Void p) {
+ return false;
+ }
+
+ @Override
public Boolean visitPrimitive(PrimitiveType type, Void p) {
switch (type.getKind()) {
case BOOLEAN:
@@ -873,11 +889,11 @@ public final class MoreTypes {
* {@link Optional#absent()} if {@code type} is an interface or {@link Object} or its superclass
* is {@link Object}.
*/
- // TODO(user): Remove unused parameter Elements?
- public static Optional<DeclaredType> nonObjectSuperclass(Types types, Elements elements,
- DeclaredType type) {
+ // TODO(bcorso): Remove unused parameter Elements?
+ public static Optional<DeclaredType> nonObjectSuperclass(
+ Types types, Elements elements, DeclaredType type) {
checkNotNull(types);
- checkNotNull(elements); // This is no longer used, but here to avoid changing the API.
+ checkNotNull(elements); // This is no longer used, but here to avoid changing the API.
checkNotNull(type);
TypeMirror superclassType = asTypeElement(type).getSuperclass();
@@ -885,7 +901,7 @@ public final class MoreTypes {
return Optional.absent();
}
- DeclaredType superclass = asDeclared(superclassType);
+ DeclaredType superclass = asDeclared(superclassType);
if (isObjectType(superclass)) {
return Optional.absent();
}
@@ -912,8 +928,8 @@ public final class MoreTypes {
* {@code container} of type {@code Set<String>}, and a variable corresponding to the {@code E e}
* parameter in the {@code Set.add(E e)} method, this will return a TypeMirror for {@code String}.
*/
- public static TypeMirror asMemberOf(Types types, DeclaredType container,
- VariableElement variable) {
+ public static TypeMirror asMemberOf(
+ Types types, DeclaredType container, VariableElement variable) {
if (variable.getKind().equals(ElementKind.PARAMETER)) {
ExecutableElement methodOrConstructor =
MoreElements.asExecutable(variable.getEnclosingElement());
diff --git a/common/src/main/java/com/google/auto/common/Overrides.java b/common/src/main/java/com/google/auto/common/Overrides.java
index 19a45862..775c304a 100644
--- a/common/src/main/java/com/google/auto/common/Overrides.java
+++ b/common/src/main/java/com/google/auto/common/Overrides.java
@@ -15,6 +15,8 @@
*/
package com.google.auto.common;
+import static java.util.stream.Collectors.toList;
+
import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import com.google.common.collect.ImmutableList;
@@ -38,6 +40,7 @@ import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Determines if one method overrides another. This class defines two ways of doing that:
@@ -114,12 +117,11 @@ abstract class Overrides {
// can't be overridden.
return false;
}
- TypeElement overriddenType;
if (!(overridden.getEnclosingElement() instanceof TypeElement)) {
return false;
// We don't know how this could happen but we avoid blowing up if it does.
}
- overriddenType = MoreElements.asType(overridden.getEnclosingElement());
+ TypeElement overriddenType = MoreElements.asType(overridden.getEnclosingElement());
// We erase the types before checking subtypes, because the TypeMirror we get for List<E> is
// not a subtype of the one we get for Collection<E> since the two E instances are not the
// same. For the purposes of overriding, type parameters in the containing type should not
@@ -141,7 +143,8 @@ abstract class Overrides {
// the enclosing elements rather than the methods themselves for the reason described
// at the start of the method.
ExecutableElement inherited = methodFromSuperclasses(in, overridden);
- return !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
+ return inherited != null
+ && !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
} else if (overriddenType.getKind().isInterface()) {
// ...overrides from C another method mI declared in interface I. We've already checked
// the conditions (assuming that the only alternative to mI being abstract or default is
@@ -157,7 +160,8 @@ abstract class Overrides {
// to methodFromSuperclasses above.
if (overrider.getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableElement inherited = methodFromSuperinterfaces(in, overridden);
- return !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
+ return inherited != null
+ && !overridden.getEnclosingElement().equals(inherited.getEnclosingElement());
} else {
return true;
}
@@ -215,6 +219,7 @@ abstract class Overrides {
* implements List<E>}. The parameter types are erased since the purpose of this method is to
* determine whether two methods are candidates for one to override the other.
*/
+ @Nullable
ImmutableList<TypeMirror> erasedParameterTypes(ExecutableElement method, TypeElement in) {
if (method.getParameters().isEmpty()) {
return ImmutableList.of();
@@ -241,6 +246,7 @@ abstract class Overrides {
*/
private final Map<TypeParameterElement, TypeMirror> typeBindings = Maps.newLinkedHashMap();
+ @Nullable
ImmutableList<TypeMirror> erasedParameterTypes(ExecutableElement method, TypeElement in) {
if (method.getEnclosingElement().equals(in)) {
ImmutableList.Builder<TypeMirror> params = ImmutableList.builder();
@@ -261,6 +267,10 @@ abstract class Overrides {
TypeElement element = MoreElements.asType(declared.asElement());
List<? extends TypeMirror> actuals = declared.getTypeArguments();
List<? extends TypeParameterElement> formals = element.getTypeParameters();
+ if (actuals.isEmpty()) {
+ // Either the formal type arguments are also empty or `declared` is raw.
+ actuals = formals.stream().map(t -> t.getBounds().get(0)).collect(toList());
+ }
Verify.verify(actuals.size() == formals.size());
for (int i = 0; i < actuals.size(); i++) {
typeBindings.put(formals.get(i), actuals.get(i));
@@ -315,7 +325,7 @@ abstract class Overrides {
* or the nearest override in a superclass of the given type, or null if the method is not
* found in the given type or any of its superclasses.
*/
- ExecutableElement methodFromSuperclasses(TypeElement in, ExecutableElement method) {
+ @Nullable ExecutableElement methodFromSuperclasses(TypeElement in, ExecutableElement method) {
for (TypeElement t = in; t != null; t = superclass(t)) {
ExecutableElement tMethod = methodInType(t, method);
if (tMethod != null) {
@@ -330,6 +340,7 @@ abstract class Overrides {
* itself, or the nearest override in a superinterface of the given type, or null if the method
* is not found in the given type or any of its transitive superinterfaces.
*/
+ @Nullable
ExecutableElement methodFromSuperinterfaces(TypeElement in, ExecutableElement method) {
TypeElement methodContainer = MoreElements.asType(method.getEnclosingElement());
Preconditions.checkArgument(methodContainer.getKind().isInterface());
@@ -366,7 +377,7 @@ abstract class Overrides {
* Returns the method from within the given type that has the same erased signature as the given
* method, or null if there is no such method.
*/
- private ExecutableElement methodInType(TypeElement type, ExecutableElement method) {
+ private @Nullable ExecutableElement methodInType(TypeElement type, ExecutableElement method) {
int nParams = method.getParameters().size();
List<TypeMirror> params = erasedParameterTypes(method, type);
if (params == null) {
@@ -388,7 +399,7 @@ abstract class Overrides {
return null;
}
- private TypeElement superclass(TypeElement type) {
+ private @Nullable TypeElement superclass(TypeElement type) {
TypeMirror sup = type.getSuperclass();
if (sup.getKind() == TypeKind.DECLARED) {
return MoreElements.asType(typeUtils.asElement(sup));
diff --git a/common/src/main/java/com/google/auto/common/SimpleAnnotationMirror.java b/common/src/main/java/com/google/auto/common/SimpleAnnotationMirror.java
index 7d508e32..7952eb37 100644
--- a/common/src/main/java/com/google/auto/common/SimpleAnnotationMirror.java
+++ b/common/src/main/java/com/google/auto/common/SimpleAnnotationMirror.java
@@ -16,8 +16,8 @@
package com.google.auto.common;
+import static com.google.auto.common.MoreStreams.toImmutableMap;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static javax.lang.model.util.ElementFilter.methodsIn;
import com.google.common.base.Joiner;
@@ -32,6 +32,7 @@ import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* A simple implementation of the {@link AnnotationMirror} interface.
@@ -65,7 +66,7 @@ public final class SimpleAnnotationMirror implements AnnotationMirror {
missingMembers.add(memberName);
}
}
-
+
checkArgument(
unusedValues.isEmpty(),
"namedValues has entries for members that are not in %s: %s",
@@ -77,8 +78,7 @@ public final class SimpleAnnotationMirror implements AnnotationMirror {
this.annotationType = annotationType;
this.namedValues = ImmutableMap.copyOf(namedValues);
this.elementValues =
- methodsIn(annotationType.getEnclosedElements())
- .stream()
+ methodsIn(annotationType.getEnclosedElements()).stream()
.collect(toImmutableMap(e -> e, e -> values.get(e.getSimpleName().toString())));
}
@@ -123,7 +123,7 @@ public final class SimpleAnnotationMirror implements AnnotationMirror {
}
@Override
- public boolean equals(Object other) {
+ public boolean equals(@Nullable Object other) {
return other instanceof AnnotationMirror
&& AnnotationMirrors.equivalence().equivalent(this, (AnnotationMirror) other);
}
diff --git a/common/src/main/java/com/google/auto/common/SuperficialValidation.java b/common/src/main/java/com/google/auto/common/SuperficialValidation.java
index 5ef4dbf2..614e2626 100644
--- a/common/src/main/java/com/google/auto/common/SuperficialValidation.java
+++ b/common/src/main/java/com/google/auto/common/SuperficialValidation.java
@@ -57,23 +57,27 @@ public final class SuperficialValidation {
private static final ElementVisitor<Boolean, Void> ELEMENT_VALIDATING_VISITOR =
new AbstractElementVisitor8<Boolean, Void>() {
- @Override public Boolean visitPackage(PackageElement e, Void p) {
+ @Override
+ public Boolean visitPackage(PackageElement e, Void p) {
// don't validate enclosed elements because it will return types in the package
return validateAnnotations(e.getAnnotationMirrors());
}
- @Override public Boolean visitType(TypeElement e, Void p) {
+ @Override
+ public Boolean visitType(TypeElement e, Void p) {
return isValidBaseElement(e)
&& validateElements(e.getTypeParameters())
&& validateTypes(e.getInterfaces())
&& validateType(e.getSuperclass());
}
- @Override public Boolean visitVariable(VariableElement e, Void p) {
+ @Override
+ public Boolean visitVariable(VariableElement e, Void p) {
return isValidBaseElement(e);
}
- @Override public Boolean visitExecutable(ExecutableElement e, Void p) {
+ @Override
+ public Boolean visitExecutable(ExecutableElement e, Void p) {
AnnotationValue defaultValue = e.getDefaultValue();
return isValidBaseElement(e)
&& (defaultValue == null || validateAnnotationValue(defaultValue, e.getReturnType()))
@@ -83,12 +87,13 @@ public final class SuperficialValidation {
&& validateElements(e.getParameters());
}
- @Override public Boolean visitTypeParameter(TypeParameterElement e, Void p) {
- return isValidBaseElement(e)
- && validateTypes(e.getBounds());
+ @Override
+ public Boolean visitTypeParameter(TypeParameterElement e, Void p) {
+ return isValidBaseElement(e) && validateTypes(e.getBounds());
}
- @Override public Boolean visitUnknown(Element e, Void p) {
+ @Override
+ public Boolean visitUnknown(Element e, Void p) {
// just assume that unknown elements are OK
return true;
}
@@ -206,16 +211,19 @@ public final class SuperficialValidation {
private static final AnnotationValueVisitor<Boolean, TypeMirror> VALUE_VALIDATING_VISITOR =
new SimpleAnnotationValueVisitor8<Boolean, TypeMirror>() {
- @Override protected Boolean defaultAction(Object o, TypeMirror expectedType) {
+ @Override
+ protected Boolean defaultAction(Object o, TypeMirror expectedType) {
return MoreTypes.isTypeOf(o.getClass(), expectedType);
}
- @Override public Boolean visitUnknown(AnnotationValue av, TypeMirror expectedType) {
+ @Override
+ public Boolean visitUnknown(AnnotationValue av, TypeMirror expectedType) {
// just take the default action for the unknown
return defaultAction(av, expectedType);
}
- @Override public Boolean visitAnnotation(AnnotationMirror a, TypeMirror expectedType) {
+ @Override
+ public Boolean visitAnnotation(AnnotationMirror a, TypeMirror expectedType) {
return MoreTypes.equivalence().equivalent(a.getAnnotationType(), expectedType)
&& validateAnnotation(a);
}
@@ -235,7 +243,8 @@ public final class SuperficialValidation {
&& validateElement(enumConstant);
}
- @Override public Boolean visitType(TypeMirror type, TypeMirror ignored) {
+ @Override
+ public Boolean visitType(TypeMirror type, TypeMirror ignored) {
// We could check assignability here, but would require a Types instance. Since this
// isn't really the sort of thing that shows up in a bad AST from upstream compilation
// we ignore the expected type and just validate the type. It might be wrong, but
@@ -243,35 +252,43 @@ public final class SuperficialValidation {
return validateType(type);
}
- @Override public Boolean visitBoolean(boolean b, TypeMirror expectedType) {
+ @Override
+ public Boolean visitBoolean(boolean b, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Boolean.TYPE, expectedType);
}
- @Override public Boolean visitByte(byte b, TypeMirror expectedType) {
+ @Override
+ public Boolean visitByte(byte b, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Byte.TYPE, expectedType);
}
- @Override public Boolean visitChar(char c, TypeMirror expectedType) {
+ @Override
+ public Boolean visitChar(char c, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Character.TYPE, expectedType);
}
- @Override public Boolean visitDouble(double d, TypeMirror expectedType) {
+ @Override
+ public Boolean visitDouble(double d, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Double.TYPE, expectedType);
}
- @Override public Boolean visitFloat(float f, TypeMirror expectedType) {
+ @Override
+ public Boolean visitFloat(float f, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Float.TYPE, expectedType);
}
- @Override public Boolean visitInt(int i, TypeMirror expectedType) {
+ @Override
+ public Boolean visitInt(int i, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Integer.TYPE, expectedType);
}
- @Override public Boolean visitLong(long l, TypeMirror expectedType) {
+ @Override
+ public Boolean visitLong(long l, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Long.TYPE, expectedType);
}
- @Override public Boolean visitShort(short s, TypeMirror expectedType) {
+ @Override
+ public Boolean visitShort(short s, TypeMirror expectedType) {
return MoreTypes.isTypeOf(Short.TYPE, expectedType);
}
};
diff --git a/common/src/main/java/com/google/auto/common/Visibility.java b/common/src/main/java/com/google/auto/common/Visibility.java
index f82fdd59..36f4ad6d 100644
--- a/common/src/main/java/com/google/auto/common/Visibility.java
+++ b/common/src/main/java/com/google/auto/common/Visibility.java
@@ -16,14 +16,15 @@
package com.google.auto.common;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.Comparators.min;
import static javax.lang.model.element.ElementKind.PACKAGE;
import com.google.common.base.Enums;
-import com.google.common.collect.Ordering;
import java.util.Set;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Represents the visibility of a given {@link Element}: {@code public}, {@code protected},
@@ -41,7 +42,7 @@ public enum Visibility {
// TODO(ronshapiro): remove this and reference ElementKind.MODULE directly once we start building
// with -source 9
- private static final ElementKind MODULE =
+ private static final @Nullable ElementKind MODULE =
Enums.getIfPresent(ElementKind.class, "MODULE").orNull();
/**
@@ -76,8 +77,7 @@ public enum Visibility {
Visibility effectiveVisibility = PUBLIC;
Element currentElement = element;
while (currentElement != null) {
- effectiveVisibility =
- Ordering.natural().min(effectiveVisibility, ofElement(currentElement));
+ effectiveVisibility = min(effectiveVisibility, ofElement(currentElement));
currentElement = currentElement.getEnclosingElement();
}
return effectiveVisibility;
diff --git a/common/src/main/java/com/google/auto/common/package-info.java b/common/src/main/java/com/google/auto/common/package-info.java
new file mode 100644
index 00000000..22b0c45a
--- /dev/null
+++ b/common/src/main/java/com/google/auto/common/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.common;
+
diff --git a/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java b/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java
index dfc043ab..b1dfe3b3 100644
--- a/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java
+++ b/common/src/test/java/com/google/auto/common/AnnotationMirrorsTest.java
@@ -22,12 +22,17 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static org.junit.Assert.fail;
+import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EquivalenceTester;
+import com.google.common.truth.Correspondence;
import com.google.testing.compile.CompilationRule;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor6;
@@ -46,110 +51,134 @@ public class AnnotationMirrorsTest {
private Elements elements;
- @Before public void setUp() {
+ @Before
+ public void setUp() {
this.elements = compilationRule.getElements();
}
@interface SimpleAnnotation {}
- @SimpleAnnotation class SimplyAnnotated {}
- @SimpleAnnotation class AlsoSimplyAnnotated {}
+ @SimpleAnnotation
+ static class SimplyAnnotated {}
+
+ @SimpleAnnotation
+ static class AlsoSimplyAnnotated {}
enum SimpleEnum {
- BLAH, FOO
+ BLAH,
+ FOO
}
@interface Outer {
SimpleEnum value();
}
- @Outer(BLAH) static class TestClassBlah {}
- @Outer(BLAH) static class TestClassBlah2 {}
- @Outer(FOO) static class TestClassFoo {}
+ @Outer(BLAH)
+ static class TestClassBlah {}
+
+ @Outer(BLAH)
+ static class TestClassBlah2 {}
+
+ @Outer(FOO)
+ static class TestClassFoo {}
@interface DefaultingOuter {
SimpleEnum value() default SimpleEnum.BLAH;
}
- @DefaultingOuter class TestWithDefaultingOuterDefault {}
- @DefaultingOuter(BLAH) class TestWithDefaultingOuterBlah {}
- @DefaultingOuter(FOO) class TestWithDefaultingOuterFoo {}
+ @DefaultingOuter
+ static class TestWithDefaultingOuterDefault {}
+
+ @DefaultingOuter(BLAH)
+ static class TestWithDefaultingOuterBlah {}
+
+ @DefaultingOuter(FOO)
+ static class TestWithDefaultingOuterFoo {}
@interface AnnotatedOuter {
DefaultingOuter value();
}
- @AnnotatedOuter(@DefaultingOuter) class TestDefaultNestedAnnotated {}
- @AnnotatedOuter(@DefaultingOuter(BLAH)) class TestBlahNestedAnnotated {}
- @AnnotatedOuter(@DefaultingOuter(FOO)) class TestFooNestedAnnotated {}
+ @AnnotatedOuter(@DefaultingOuter)
+ static class TestDefaultNestedAnnotated {}
+
+ @AnnotatedOuter(@DefaultingOuter(BLAH))
+ static class TestBlahNestedAnnotated {}
+
+ @AnnotatedOuter(@DefaultingOuter(FOO))
+ static class TestFooNestedAnnotated {}
@interface OuterWithValueArray {
DefaultingOuter[] value() default {};
}
- @OuterWithValueArray class TestValueArrayWithDefault {}
- @OuterWithValueArray({}) class TestValueArrayWithEmpty {}
+ @OuterWithValueArray
+ static class TestValueArrayWithDefault {}
+
+ @OuterWithValueArray({})
+ static class TestValueArrayWithEmpty {}
- @OuterWithValueArray({@DefaultingOuter}) class TestValueArrayWithOneDefault {}
- @OuterWithValueArray(@DefaultingOuter(BLAH)) class TestValueArrayWithOneBlah {}
- @OuterWithValueArray(@DefaultingOuter(FOO)) class TestValueArrayWithOneFoo {}
+ @OuterWithValueArray({@DefaultingOuter})
+ static class TestValueArrayWithOneDefault {}
+
+ @OuterWithValueArray(@DefaultingOuter(BLAH))
+ static class TestValueArrayWithOneBlah {}
+
+ @OuterWithValueArray(@DefaultingOuter(FOO))
+ static class TestValueArrayWithOneFoo {}
@OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter})
class TestValueArrayWithFooAndDefaultBlah {}
+
@OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter(BLAH)})
class TestValueArrayWithFooBlah {}
+
@OuterWithValueArray({@DefaultingOuter(FOO), @DefaultingOuter(BLAH)})
class TestValueArrayWithFooBlah2 {} // Different instances than on TestValueArrayWithFooBlah.
+
@OuterWithValueArray({@DefaultingOuter(BLAH), @DefaultingOuter(FOO)})
class TestValueArrayWithBlahFoo {}
- @Test public void testEquivalences() {
+ @Test
+ public void testEquivalences() {
EquivalenceTester<AnnotationMirror> tester =
EquivalenceTester.of(AnnotationMirrors.equivalence());
tester.addEquivalenceGroup(
- annotationOn(SimplyAnnotated.class),
- annotationOn(AlsoSimplyAnnotated.class));
+ annotationOn(SimplyAnnotated.class), annotationOn(AlsoSimplyAnnotated.class));
tester.addEquivalenceGroup(
- annotationOn(TestClassBlah.class),
- annotationOn(TestClassBlah2.class));
+ annotationOn(TestClassBlah.class), annotationOn(TestClassBlah2.class));
- tester.addEquivalenceGroup(
- annotationOn(TestClassFoo.class));
+ tester.addEquivalenceGroup(annotationOn(TestClassFoo.class));
tester.addEquivalenceGroup(
annotationOn(TestWithDefaultingOuterDefault.class),
annotationOn(TestWithDefaultingOuterBlah.class));
- tester.addEquivalenceGroup(
- annotationOn(TestWithDefaultingOuterFoo.class));
+ tester.addEquivalenceGroup(annotationOn(TestWithDefaultingOuterFoo.class));
tester.addEquivalenceGroup(
annotationOn(TestDefaultNestedAnnotated.class),
annotationOn(TestBlahNestedAnnotated.class));
- tester.addEquivalenceGroup(
- annotationOn(TestFooNestedAnnotated.class));
+ tester.addEquivalenceGroup(annotationOn(TestFooNestedAnnotated.class));
tester.addEquivalenceGroup(
- annotationOn(TestValueArrayWithDefault.class),
- annotationOn(TestValueArrayWithEmpty.class));
+ annotationOn(TestValueArrayWithDefault.class), annotationOn(TestValueArrayWithEmpty.class));
tester.addEquivalenceGroup(
annotationOn(TestValueArrayWithOneDefault.class),
annotationOn(TestValueArrayWithOneBlah.class));
- tester.addEquivalenceGroup(
- annotationOn(TestValueArrayWithOneFoo.class));
+ tester.addEquivalenceGroup(annotationOn(TestValueArrayWithOneFoo.class));
tester.addEquivalenceGroup(
annotationOn(TestValueArrayWithFooAndDefaultBlah.class),
annotationOn(TestValueArrayWithFooBlah.class),
annotationOn(TestValueArrayWithFooBlah2.class));
- tester.addEquivalenceGroup(
- annotationOn(TestValueArrayWithBlahFoo.class));
+ tester.addEquivalenceGroup(annotationOn(TestValueArrayWithBlahFoo.class));
tester.test();
}
@@ -158,44 +187,61 @@ public class AnnotationMirrorsTest {
String value() default "default";
}
- @Stringy class StringyUnset {}
- @Stringy("foo") class StringySet {}
+ @Stringy
+ static class StringyUnset {}
+
+ @Stringy("foo")
+ static class StringySet {}
- @Test public void testGetDefaultValuesUnset() {
+ @Test
+ public void testGetDefaultValuesUnset() {
assertThat(annotationOn(StringyUnset.class).getElementValues()).isEmpty();
- Iterable<AnnotationValue> values = AnnotationMirrors.getAnnotationValuesWithDefaults(
- annotationOn(StringyUnset.class)).values();
- String value = getOnlyElement(values).accept(new SimpleAnnotationValueVisitor6<String, Void>() {
- @Override public String visitString(String value, Void ignored) {
- return value;
- }
- }, null);
+ Iterable<AnnotationValue> values =
+ AnnotationMirrors.getAnnotationValuesWithDefaults(annotationOn(StringyUnset.class))
+ .values();
+ String value =
+ getOnlyElement(values)
+ .accept(
+ new SimpleAnnotationValueVisitor6<String, Void>() {
+ @Override
+ public String visitString(String value, Void ignored) {
+ return value;
+ }
+ },
+ null);
assertThat(value).isEqualTo("default");
}
- @Test public void testGetDefaultValuesSet() {
- Iterable<AnnotationValue> values = AnnotationMirrors.getAnnotationValuesWithDefaults(
- annotationOn(StringySet.class)).values();
- String value = getOnlyElement(values).accept(new SimpleAnnotationValueVisitor6<String, Void>() {
- @Override public String visitString(String value, Void ignored) {
- return value;
- }
- }, null);
+ @Test
+ public void testGetDefaultValuesSet() {
+ Iterable<AnnotationValue> values =
+ AnnotationMirrors.getAnnotationValuesWithDefaults(annotationOn(StringySet.class)).values();
+ String value =
+ getOnlyElement(values)
+ .accept(
+ new SimpleAnnotationValueVisitor6<String, Void>() {
+ @Override
+ public String visitString(String value, Void ignored) {
+ return value;
+ }
+ },
+ null);
assertThat(value).isEqualTo("foo");
}
- @Test public void testGetValueEntry() {
+ @Test
+ public void testGetValueEntry() {
Map.Entry<ExecutableElement, AnnotationValue> elementValue =
- AnnotationMirrors.getAnnotationElementAndValue(
- annotationOn(TestClassBlah.class), "value");
+ AnnotationMirrors.getAnnotationElementAndValue(annotationOn(TestClassBlah.class), "value");
assertThat(elementValue.getKey().getSimpleName().toString()).isEqualTo("value");
assertThat(elementValue.getValue().getValue()).isInstanceOf(VariableElement.class);
- AnnotationValue value = AnnotationMirrors.getAnnotationValue(
- annotationOn(TestClassBlah.class), "value");
+ AnnotationValue value =
+ AnnotationMirrors.getAnnotationValue(annotationOn(TestClassBlah.class), "value");
assertThat(value.getValue()).isInstanceOf(VariableElement.class);
}
- @Test public void testGetValueEntryFailure() {
+ @Test
+ public void testGetValueEntryFailure() {
try {
AnnotationMirrors.getAnnotationValue(annotationOn(TestClassBlah.class), "a");
} catch (IllegalArgumentException e) {
@@ -212,4 +258,52 @@ public class AnnotationMirrorsTest {
return getOnlyElement(elements.getTypeElement(clazz.getCanonicalName()).getAnnotationMirrors());
}
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface AnnotatingAnnotation {}
+
+ @AnnotatingAnnotation
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface AnnotatedAnnotation1 {}
+
+ @AnnotatingAnnotation
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface AnnotatedAnnotation2 {}
+
+ @Retention(RetentionPolicy.RUNTIME)
+ private @interface NotAnnotatedAnnotation {}
+
+ @AnnotatedAnnotation1
+ @NotAnnotatedAnnotation
+ @AnnotatedAnnotation2
+ private static final class AnnotatedClass {}
+
+ @Test
+ public void getAnnotatedAnnotations() {
+ TypeElement element = elements.getTypeElement(AnnotatedClass.class.getCanonicalName());
+
+ // Test Class API
+ getAnnotatedAnnotationsAsserts(
+ AnnotationMirrors.getAnnotatedAnnotations(element, AnnotatingAnnotation.class));
+
+ // Test String API
+ String annotatingAnnotationName = AnnotatingAnnotation.class.getCanonicalName();
+ getAnnotatedAnnotationsAsserts(
+ AnnotationMirrors.getAnnotatedAnnotations(element, annotatingAnnotationName));
+
+ // Test TypeElement API
+ TypeElement annotatingAnnotationElement = elements.getTypeElement(annotatingAnnotationName);
+ getAnnotatedAnnotationsAsserts(
+ AnnotationMirrors.getAnnotatedAnnotations(element, annotatingAnnotationElement));
+ }
+
+ private void getAnnotatedAnnotationsAsserts(
+ ImmutableSet<? extends AnnotationMirror> annotatedAnnotations) {
+ assertThat(annotatedAnnotations)
+ .comparingElementsUsing(
+ Correspondence.transforming(
+ (AnnotationMirror a) -> MoreTypes.asTypeElement(a.getAnnotationType()), "has type"))
+ .containsExactly(
+ elements.getTypeElement(AnnotatedAnnotation1.class.getCanonicalName()),
+ elements.getTypeElement(AnnotatedAnnotation2.class.getCanonicalName()));
+ }
}
diff --git a/common/src/test/java/com/google/auto/common/GeneratedAnnotationsTest.java b/common/src/test/java/com/google/auto/common/GeneratedAnnotationsTest.java
index 1c816c10..f9426527 100644
--- a/common/src/test/java/com/google/auto/common/GeneratedAnnotationsTest.java
+++ b/common/src/test/java/com/google/auto/common/GeneratedAnnotationsTest.java
@@ -18,6 +18,7 @@ package com.google.auto.common;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import static org.junit.Assume.assumeTrue;
import com.google.common.collect.ImmutableList;
@@ -31,6 +32,7 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.file.Files;
+import java.util.Objects;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
@@ -44,6 +46,7 @@ import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
@@ -99,12 +102,12 @@ public class GeneratedAnnotationsTest {
* Run {@link TestProcessor} in a compilation with the given {@code options}, and prevent the
* compilation from accessing classes with the qualified names in {@code maskFromClasspath}.
*/
- private String runProcessor(ImmutableList<String> options, String packageToMask)
+ private String runProcessor(ImmutableList<String> options, @Nullable String packageToMask)
throws IOException {
File tempDir = temporaryFolder.newFolder();
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardFileManager =
- compiler.getStandardFileManager(/* diagnostics= */ null, /* locale= */ null, UTF_8);
+ compiler.getStandardFileManager(/* diagnosticListener= */ null, /* locale= */ null, UTF_8);
standardFileManager.setLocation(StandardLocation.CLASS_OUTPUT, ImmutableList.of(tempDir));
StandardJavaFileManager proxyFileManager =
Reflection.newProxy(
@@ -142,18 +145,20 @@ public class GeneratedAnnotationsTest {
*/
private static class FileManagerInvocationHandler implements InvocationHandler {
private final StandardJavaFileManager fileManager;
- private final String packageToMask;
+ private final @Nullable String packageToMask;
- FileManagerInvocationHandler(StandardJavaFileManager fileManager, String packageToMask) {
+ FileManagerInvocationHandler(
+ StandardJavaFileManager fileManager, @Nullable String packageToMask) {
this.fileManager = fileManager;
this.packageToMask = packageToMask;
}
@Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ public Object invoke(Object proxy, Method method, @Nullable Object @Nullable [] args)
+ throws Throwable {
if (method.getName().equals("list")) {
- String packageName = (String) args[1];
- if (packageName.equals(packageToMask)) {
+ String packageName = (String) requireNonNull(args)[1];
+ if (Objects.equals(packageName, packageToMask)) {
return ImmutableList.of();
}
}
@@ -187,8 +192,7 @@ public class GeneratedAnnotationsTest {
// An alternative would be to delete this test method. JDK8 always has
// javax.annotation.Generated so it isn't really meaningful to test it without.
ImmutableList<String> options = ImmutableList.of("-source", "8", "-target", "8");
- String generated =
- runProcessor(options, "javax.annotation");
+ String generated = runProcessor(options, "javax.annotation");
assertThat(generated).doesNotContain(JAVAX_ANNOTATION_GENERATED);
assertThat(generated).doesNotContain(JAVAX_ANNOTATION_PROCESSING_GENERATED);
}
diff --git a/common/src/test/java/com/google/auto/common/MoreElementsTest.java b/common/src/test/java/com/google/auto/common/MoreElementsTest.java
index 95043cf3..b98b79b9 100644
--- a/common/src/test/java/com/google/auto/common/MoreElementsTest.java
+++ b/common/src/test/java/com/google/auto/common/MoreElementsTest.java
@@ -18,6 +18,7 @@ package com.google.auto.common;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static java.util.Objects.requireNonNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -58,13 +59,14 @@ public class MoreElementsTest {
@Rule public CompilationRule compilation = new CompilationRule();
@Rule public Expect expect = Expect.create();
+ private Elements elements;
private PackageElement javaLangPackageElement;
private TypeElement objectElement;
private TypeElement stringElement;
@Before
public void initializeTestElements() {
- Elements elements = compilation.getElements();
+ this.elements = compilation.getElements();
this.javaLangPackageElement = elements.getPackageElement("java.lang");
this.objectElement = elements.getTypeElement(Object.class.getCanonicalName());
this.stringElement = elements.getTypeElement(String.class.getCanonicalName());
@@ -80,8 +82,7 @@ public class MoreElementsTest {
@Test
public void asPackage() {
- assertThat(MoreElements.asPackage(javaLangPackageElement))
- .isEqualTo(javaLangPackageElement);
+ assertThat(MoreElements.asPackage(javaLangPackageElement)).isEqualTo(javaLangPackageElement);
}
@Test
@@ -89,19 +90,20 @@ public class MoreElementsTest {
try {
MoreElements.asPackage(stringElement);
fail();
- } catch (IllegalArgumentException expected) {}
+ } catch (IllegalArgumentException expected) {
+ }
}
- @Test public void asTypeElement() {
- Element typeElement =
- compilation.getElements().getTypeElement(String.class.getCanonicalName());
+ @Test
+ public void asTypeElement() {
+ Element typeElement = elements.getTypeElement(String.class.getCanonicalName());
assertTrue(MoreElements.isType(typeElement));
assertThat(MoreElements.asType(typeElement)).isEqualTo(typeElement);
}
- @Test public void asTypeElement_notATypeElement() {
- TypeElement typeElement =
- compilation.getElements().getTypeElement(String.class.getCanonicalName());
+ @Test
+ public void asTypeElement_notATypeElement() {
+ TypeElement typeElement = elements.getTypeElement(String.class.getCanonicalName());
for (ExecutableElement e : ElementFilter.methodsIn(typeElement.getEnclosedElements())) {
assertFalse(MoreElements.isType(e));
try {
@@ -143,7 +145,8 @@ public class MoreElementsTest {
try {
MoreElements.asType(javaLangPackageElement);
fail();
- } catch (IllegalArgumentException expected) {}
+ } catch (IllegalArgumentException expected) {
+ }
}
@Test
@@ -158,7 +161,8 @@ public class MoreElementsTest {
try {
MoreElements.asVariable(javaLangPackageElement);
fail();
- } catch (IllegalArgumentException expected) {}
+ } catch (IllegalArgumentException expected) {
+ }
}
@Test
@@ -166,8 +170,8 @@ public class MoreElementsTest {
for (Element methodElement : ElementFilter.methodsIn(stringElement.getEnclosedElements())) {
assertThat(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement);
}
- for (Element methodElement
- : ElementFilter.constructorsIn(stringElement.getEnclosedElements())) {
+ for (Element methodElement :
+ ElementFilter.constructorsIn(stringElement.getEnclosedElements())) {
assertThat(MoreElements.asExecutable(methodElement)).isEqualTo(methodElement);
}
}
@@ -177,7 +181,8 @@ public class MoreElementsTest {
try {
MoreElements.asExecutable(javaLangPackageElement);
fail();
- } catch (IllegalArgumentException expected) {}
+ } catch (IllegalArgumentException expected) {
+ }
}
@Retention(RetentionPolicy.RUNTIME)
@@ -190,39 +195,90 @@ public class MoreElementsTest {
@Test
public void isAnnotationPresent() {
TypeElement annotatedAnnotationElement =
- compilation.getElements().getTypeElement(AnnotatedAnnotation.class.getCanonicalName());
- assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, Documented.class))
- .isTrue();
- assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, InnerAnnotation.class))
- .isTrue();
- assertThat(MoreElements.isAnnotationPresent(annotatedAnnotationElement, SuppressWarnings.class))
- .isFalse();
+ elements.getTypeElement(AnnotatedAnnotation.class.getCanonicalName());
+
+ // Test Class API
+ isAnnotationPresentAsserts(
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, Documented.class),
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, InnerAnnotation.class),
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, SuppressWarnings.class));
+
+ // Test String API
+ String documentedName = Documented.class.getCanonicalName();
+ String innerAnnotationName = InnerAnnotation.class.getCanonicalName();
+ String suppressWarningsName = SuppressWarnings.class.getCanonicalName();
+ isAnnotationPresentAsserts(
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, documentedName),
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, innerAnnotationName),
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, suppressWarningsName));
+
+ // Test TypeElement API
+ TypeElement documentedElement = elements.getTypeElement(documentedName);
+ TypeElement innerAnnotationElement = elements.getTypeElement(innerAnnotationName);
+ TypeElement suppressWarningsElement = elements.getTypeElement(suppressWarningsName);
+ isAnnotationPresentAsserts(
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, documentedElement),
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, innerAnnotationElement),
+ MoreElements.isAnnotationPresent(annotatedAnnotationElement, suppressWarningsElement));
+ }
+
+ private void isAnnotationPresentAsserts(
+ boolean isDocumentedPresent,
+ boolean isInnerAnnotationPresent,
+ boolean isSuppressWarningsPresent) {
+ assertThat(isDocumentedPresent).isTrue();
+ assertThat(isInnerAnnotationPresent).isTrue();
+ assertThat(isSuppressWarningsPresent).isFalse();
}
@Test
public void getAnnotationMirror() {
TypeElement element =
- compilation.getElements().getTypeElement(AnnotatedAnnotation.class.getCanonicalName());
-
- Optional<AnnotationMirror> documented =
- MoreElements.getAnnotationMirror(element, Documented.class);
- Optional<AnnotationMirror> innerAnnotation =
- MoreElements.getAnnotationMirror(element, InnerAnnotation.class);
- Optional<AnnotationMirror> suppressWarnings =
- MoreElements.getAnnotationMirror(element, SuppressWarnings.class);
-
+ elements.getTypeElement(AnnotatedAnnotation.class.getCanonicalName());
+
+ // Test Class API
+ getAnnotationMirrorAsserts(
+ MoreElements.getAnnotationMirror(element, Documented.class),
+ MoreElements.getAnnotationMirror(element, InnerAnnotation.class),
+ MoreElements.getAnnotationMirror(element, SuppressWarnings.class));
+
+ // Test String API
+ String documentedName = Documented.class.getCanonicalName();
+ String innerAnnotationName = InnerAnnotation.class.getCanonicalName();
+ String suppressWarningsName = SuppressWarnings.class.getCanonicalName();
+ getAnnotationMirrorAsserts(
+ MoreElements.getAnnotationMirror(element, documentedName),
+ MoreElements.getAnnotationMirror(element, innerAnnotationName),
+ MoreElements.getAnnotationMirror(element, suppressWarningsName));
+
+ // Test TypeElement API
+ TypeElement documentedElement = elements.getTypeElement(documentedName);
+ TypeElement innerAnnotationElement = elements.getTypeElement(innerAnnotationName);
+ TypeElement suppressWarningsElement = elements.getTypeElement(suppressWarningsName);
+ getAnnotationMirrorAsserts(
+ MoreElements.getAnnotationMirror(element, documentedElement),
+ MoreElements.getAnnotationMirror(element, innerAnnotationElement),
+ MoreElements.getAnnotationMirror(element, suppressWarningsElement));
+ }
+
+ private void getAnnotationMirrorAsserts(
+ Optional<AnnotationMirror> documented,
+ Optional<AnnotationMirror> innerAnnotation,
+ Optional<AnnotationMirror> suppressWarnings) {
expect.that(documented).isPresent();
expect.that(innerAnnotation).isPresent();
expect.that(suppressWarnings).isAbsent();
Element annotationElement = documented.get().getAnnotationType().asElement();
expect.that(MoreElements.isType(annotationElement)).isTrue();
- expect.that(MoreElements.asType(annotationElement).getQualifiedName().toString())
+ expect
+ .that(MoreElements.asType(annotationElement).getQualifiedName().toString())
.isEqualTo(Documented.class.getCanonicalName());
annotationElement = innerAnnotation.get().getAnnotationType().asElement();
expect.that(MoreElements.isType(annotationElement)).isTrue();
- expect.that(MoreElements.asType(annotationElement).getQualifiedName().toString())
+ expect
+ .that(MoreElements.asType(annotationElement).getQualifiedName().toString())
.isEqualTo(InnerAnnotation.class.getCanonicalName());
}
@@ -231,6 +287,7 @@ public class MoreElementsTest {
abstract String foo();
+ @SuppressWarnings("unused")
private void privateMethod() {}
}
@@ -259,7 +316,6 @@ public class MoreElementsTest {
@Test
public void getLocalAndInheritedMethods_Old() {
- Elements elements = compilation.getElements();
Types types = compilation.getTypes();
TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT);
TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG);
@@ -270,19 +326,20 @@ public class MoreElementsTest {
Set<ExecutableElement> objectMethods = visibleMethodsFromObject();
assertThat(childTypeMethods).containsAtLeastElementsIn(objectMethods);
Set<ExecutableElement> nonObjectMethods = Sets.difference(childTypeMethods, objectMethods);
- assertThat(nonObjectMethods).containsExactly(
+ assertThat(nonObjectMethods)
+ .containsExactly(
getMethod(ParentInterface.class, "bar", longMirror),
getMethod(ParentClass.class, "foo"),
getMethod(Child.class, "bar"),
getMethod(Child.class, "baz"),
getMethod(Child.class, "buh", intMirror),
getMethod(Child.class, "buh", intMirror, intMirror))
- .inOrder();;
+ .inOrder();
+ ;
}
@Test
public void getLocalAndInheritedMethods() {
- Elements elements = compilation.getElements();
Types types = compilation.getTypes();
TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT);
TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG);
@@ -293,7 +350,8 @@ public class MoreElementsTest {
Set<ExecutableElement> objectMethods = visibleMethodsFromObject();
assertThat(childTypeMethods).containsAtLeastElementsIn(objectMethods);
Set<ExecutableElement> nonObjectMethods = Sets.difference(childTypeMethods, objectMethods);
- assertThat(nonObjectMethods).containsExactly(
+ assertThat(nonObjectMethods)
+ .containsExactly(
getMethod(ParentInterface.class, "bar", longMirror),
getMethod(ParentClass.class, "foo"),
getMethod(Child.class, "bar"),
@@ -305,7 +363,6 @@ public class MoreElementsTest {
@Test
public void getAllMethods() {
- Elements elements = compilation.getElements();
Types types = compilation.getTypes();
TypeMirror intMirror = types.getPrimitiveType(TypeKind.INT);
TypeMirror longMirror = types.getPrimitiveType(TypeKind.LONG);
@@ -316,7 +373,8 @@ public class MoreElementsTest {
Set<ExecutableElement> objectMethods = allMethodsFromObject();
assertThat(childTypeMethods).containsAtLeastElementsIn(objectMethods);
Set<ExecutableElement> nonObjectMethods = Sets.difference(childTypeMethods, objectMethods);
- assertThat(nonObjectMethods).containsExactly(
+ assertThat(nonObjectMethods)
+ .containsExactly(
getMethod(ParentInterface.class, "staticMethod"),
getMethod(ParentInterface.class, "bar", longMirror),
getMethod(ParentClass.class, "staticMethod"),
@@ -355,10 +413,9 @@ public class MoreElementsTest {
// Example from https://github.com/williamlian/daggerbug
@Test
public void getLocalAndInheritedMethods_DaggerBug() {
- Elements elementUtils = compilation.getElements();
- TypeElement main = elementUtils.getTypeElement(Main.ParentComponent.class.getCanonicalName());
- Set<ExecutableElement> methods = MoreElements.getLocalAndInheritedMethods(
- main, compilation.getTypes(), elementUtils);
+ TypeElement main = elements.getTypeElement(Main.ParentComponent.class.getCanonicalName());
+ Set<ExecutableElement> methods =
+ MoreElements.getLocalAndInheritedMethods(main, compilation.getTypes(), elements);
assertThat(methods).hasSize(1);
ExecutableElement method = methods.iterator().next();
assertThat(method.getSimpleName().toString()).isEqualTo("injectable");
@@ -404,7 +461,7 @@ public class MoreElementsTest {
}
private ExecutableElement getMethod(Class<?> c, String methodName, TypeMirror... parameterTypes) {
- TypeElement type = compilation.getElements().getTypeElement(c.getCanonicalName());
+ TypeElement type = elements.getTypeElement(c.getCanonicalName());
Types types = compilation.getTypes();
ExecutableElement found = null;
for (ExecutableElement method : ElementFilter.methodsIn(type.getEnclosedElements())) {
@@ -423,7 +480,7 @@ public class MoreElementsTest {
}
}
assertWithMessage(methodName + Arrays.toString(parameterTypes)).that(found).isNotNull();
- return found;
+ return requireNonNull(found);
}
private abstract static class AbstractAbstractList extends AbstractList<String> {}
@@ -458,8 +515,6 @@ public class MoreElementsTest {
// are implemented in AbstractList.
@Test
public void getLocalAndInheritedMethods_AbstractList() {
- Elements elements = compilation.getElements();
-
TypeElement abstractType =
elements.getTypeElement(AbstractAbstractList.class.getCanonicalName());
Set<ExecutableElement> abstractTypeMethods =
diff --git a/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java b/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java
index 05a0a119..7cd7865d 100644
--- a/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java
+++ b/common/src/test/java/com/google/auto/common/MoreTypesIsTypeOfTest.java
@@ -43,13 +43,15 @@ public class MoreTypesIsTypeOfTest {
private Elements elements;
- @Before public void setUp() {
+ @Before
+ public void setUp() {
this.elements = compilationRule.getElements();
}
private interface TestType {}
- @Test public void isTypeOf_DeclaredType() {
+ @Test
+ public void isTypeOf_declaredType() {
assertTrue(MoreTypes.isType(typeElementFor(TestType.class).asType()));
assertWithMessage("mirror represents the TestType")
.that(MoreTypes.isTypeOf(TestType.class, typeElementFor(TestType.class).asType()))
@@ -63,7 +65,8 @@ public class MoreTypesIsTypeOfTest {
String[] array();
}
- @Test public void isTypeOf_ArrayType() {
+ @Test
+ public void isTypeOf_arrayType() {
assertTrue(MoreTypes.isType(typeElementFor(ArrayType.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(ArrayType.class));
assertWithMessage("array mirror represents an array Class object")
@@ -75,7 +78,8 @@ public class MoreTypesIsTypeOfTest {
boolean method();
}
- @Test public void isTypeOf_PrimitiveBoolean() {
+ @Test
+ public void isTypeOf_primitiveBoolean() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveBoolean.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveBoolean.class));
assertWithMessage("mirror of a boolean").that(MoreTypes.isTypeOf(Boolean.TYPE, type)).isTrue();
@@ -85,7 +89,8 @@ public class MoreTypesIsTypeOfTest {
byte method();
}
- @Test public void isTypeOf_PrimitiveByte() {
+ @Test
+ public void isTypeOf_primitiveByte() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveByte.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveByte.class));
assertWithMessage("mirror of a byte").that(MoreTypes.isTypeOf(Byte.TYPE, type)).isTrue();
@@ -95,7 +100,8 @@ public class MoreTypesIsTypeOfTest {
char method();
}
- @Test public void isTypeOf_PrimitiveChar() {
+ @Test
+ public void isTypeOf_primitiveChar() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveChar.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveChar.class));
assertWithMessage("mirror of a char").that(MoreTypes.isTypeOf(Character.TYPE, type)).isTrue();
@@ -105,7 +111,8 @@ public class MoreTypesIsTypeOfTest {
double method();
}
- @Test public void isTypeOf_PrimitiveDouble() {
+ @Test
+ public void isTypeOf_primitiveDouble() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveDouble.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveDouble.class));
assertWithMessage("mirror of a double").that(MoreTypes.isTypeOf(Double.TYPE, type)).isTrue();
@@ -115,7 +122,8 @@ public class MoreTypesIsTypeOfTest {
float method();
}
- @Test public void isTypeOf_PrimitiveFloat() {
+ @Test
+ public void isTypeOf_primitiveFloat() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveFloat.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveFloat.class));
assertWithMessage("mirror of a float").that(MoreTypes.isTypeOf(Float.TYPE, type)).isTrue();
@@ -125,7 +133,8 @@ public class MoreTypesIsTypeOfTest {
int method();
}
- @Test public void isTypeOf_PrimitiveInt() {
+ @Test
+ public void isTypeOf_primitiveInt() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveInt.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveInt.class));
assertWithMessage("mirror of a int").that(MoreTypes.isTypeOf(Integer.TYPE, type)).isTrue();
@@ -135,7 +144,8 @@ public class MoreTypesIsTypeOfTest {
long method();
}
- @Test public void isTypeOf_PrimitiveLong() {
+ @Test
+ public void isTypeOf_primitiveLong() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveLong.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveLong.class));
assertWithMessage("mirror of a long").that(MoreTypes.isTypeOf(Long.TYPE, type)).isTrue();
@@ -145,7 +155,8 @@ public class MoreTypesIsTypeOfTest {
short method();
}
- @Test public void isTypeOf_PrimitiveShort() {
+ @Test
+ public void isTypeOf_primitiveShort() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveShort.class).asType()));
TypeMirror type = extractReturnTypeFromHolder(typeElementFor(PrimitiveShort.class));
assertWithMessage("mirror of a short").that(MoreTypes.isTypeOf(Short.TYPE, type)).isTrue();
@@ -155,7 +166,8 @@ public class MoreTypesIsTypeOfTest {
void method();
}
- @Test public void isTypeOf_void() {
+ @Test
+ public void isTypeOf_primitiveVoid() {
assertTrue(MoreTypes.isType(typeElementFor(PrimitiveVoid.class).asType()));
TypeMirror primitive = extractReturnTypeFromHolder(typeElementFor(PrimitiveVoid.class));
assertWithMessage("mirror of a void").that(MoreTypes.isTypeOf(Void.TYPE, primitive)).isTrue();
@@ -165,21 +177,25 @@ public class MoreTypesIsTypeOfTest {
Void method();
}
- @Test public void isTypeOf_Void() {
+ @Test
+ public void isTypeOf_declaredVoid() {
assertTrue(MoreTypes.isType(typeElementFor(DeclaredVoid.class).asType()));
TypeMirror declared = extractReturnTypeFromHolder(typeElementFor(DeclaredVoid.class));
assertWithMessage("mirror of a void").that(MoreTypes.isTypeOf(Void.class, declared)).isTrue();
}
- @Test public void isTypeOf_fail() {
- assertFalse(MoreTypes.isType(
- getOnlyElement(typeElementFor(DeclaredVoid.class).getEnclosedElements()).asType()));
+ @Test
+ public void isTypeOf_fail() {
+ assertFalse(
+ MoreTypes.isType(
+ getOnlyElement(typeElementFor(DeclaredVoid.class).getEnclosedElements()).asType()));
TypeMirror method =
getOnlyElement(typeElementFor(DeclaredVoid.class).getEnclosedElements()).asType();
try {
MoreTypes.isTypeOf(String.class, method);
fail();
- } catch (IllegalArgumentException expected) {}
+ } catch (IllegalArgumentException expected) {
+ }
}
// Utility methods for this test.
diff --git a/common/src/test/java/com/google/auto/common/MoreTypesTest.java b/common/src/test/java/com/google/auto/common/MoreTypesTest.java
index 3cd360db..b8e84e08 100644
--- a/common/src/test/java/com/google/auto/common/MoreTypesTest.java
+++ b/common/src/test/java/com/google/auto/common/MoreTypesTest.java
@@ -16,11 +16,12 @@
package com.google.auto.common;
import static com.google.common.truth.Truth.assertThat;
+import static java.util.Objects.requireNonNull;
import static javax.lang.model.type.TypeKind.NONE;
import static javax.lang.model.type.TypeKind.VOID;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
-import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
@@ -49,6 +50,7 @@ import javax.lang.model.type.WildcardType;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -90,48 +92,51 @@ public class MoreTypesTest {
DeclaredType containerOfString = types.getDeclaredType(container, stringType);
TypeMirror containedInObject = types.asMemberOf(containerOfObject, contained);
TypeMirror containedInString = types.asMemberOf(containerOfString, contained);
- EquivalenceTester<TypeMirror> tester = EquivalenceTester.<TypeMirror>of(MoreTypes.equivalence())
- .addEquivalenceGroup(types.getNullType())
- .addEquivalenceGroup(types.getNoType(NONE))
- .addEquivalenceGroup(types.getNoType(VOID))
- .addEquivalenceGroup(objectType)
- .addEquivalenceGroup(stringType)
- .addEquivalenceGroup(containedInObject)
- .addEquivalenceGroup(containedInString)
- .addEquivalenceGroup(funkyBounds.asType())
- .addEquivalenceGroup(funkyBounds2.asType())
- .addEquivalenceGroup(funkierBounds.asType())
- .addEquivalenceGroup(funkyBoundsVar, funkyBounds2Var)
- .addEquivalenceGroup(funkierBoundsVar)
- // Enum<E extends Enum<E>>
- .addEquivalenceGroup(enumElement.asType())
- // Map<K, V>
- .addEquivalenceGroup(mapType)
- .addEquivalenceGroup(mapOfObjectToObjectType)
- // Map<?, ?>
- .addEquivalenceGroup(types.getDeclaredType(mapElement, wildcard, wildcard))
- // Map
- .addEquivalenceGroup(types.erasure(mapType), types.erasure(mapOfObjectToObjectType))
- .addEquivalenceGroup(types.getDeclaredType(mapElement, objectType, stringType))
- .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, objectType))
- .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, stringType))
- .addEquivalenceGroup(setOfSetOfObject)
- .addEquivalenceGroup(setOfSetOfString)
- .addEquivalenceGroup(setOfSetOfSetOfObject)
- .addEquivalenceGroup(setOfSetOfSetOfString)
- .addEquivalenceGroup(wildcard)
- // ? extends Object
- .addEquivalenceGroup(types.getWildcardType(objectType, null))
- // ? extends String
- .addEquivalenceGroup(types.getWildcardType(stringType, null))
- // ? super String
- .addEquivalenceGroup(types.getWildcardType(null, stringType))
- // Map<String, Map<String, Set<Object>>>
- .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType,
- types.getDeclaredType(mapElement, stringType,
- types.getDeclaredType(setElement, objectType))))
- .addEquivalenceGroup(FAKE_ERROR_TYPE)
- ;
+ EquivalenceTester<TypeMirror> tester =
+ EquivalenceTester.<TypeMirror>of(MoreTypes.equivalence())
+ .addEquivalenceGroup(types.getNullType())
+ .addEquivalenceGroup(types.getNoType(NONE))
+ .addEquivalenceGroup(types.getNoType(VOID))
+ .addEquivalenceGroup(objectType)
+ .addEquivalenceGroup(stringType)
+ .addEquivalenceGroup(containedInObject)
+ .addEquivalenceGroup(containedInString)
+ .addEquivalenceGroup(funkyBounds.asType())
+ .addEquivalenceGroup(funkyBounds2.asType())
+ .addEquivalenceGroup(funkierBounds.asType())
+ .addEquivalenceGroup(funkyBoundsVar, funkyBounds2Var)
+ .addEquivalenceGroup(funkierBoundsVar)
+ // Enum<E extends Enum<E>>
+ .addEquivalenceGroup(enumElement.asType())
+ // Map<K, V>
+ .addEquivalenceGroup(mapType)
+ .addEquivalenceGroup(mapOfObjectToObjectType)
+ // Map<?, ?>
+ .addEquivalenceGroup(types.getDeclaredType(mapElement, wildcard, wildcard))
+ // Map
+ .addEquivalenceGroup(types.erasure(mapType), types.erasure(mapOfObjectToObjectType))
+ .addEquivalenceGroup(types.getDeclaredType(mapElement, objectType, stringType))
+ .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, objectType))
+ .addEquivalenceGroup(types.getDeclaredType(mapElement, stringType, stringType))
+ .addEquivalenceGroup(setOfSetOfObject)
+ .addEquivalenceGroup(setOfSetOfString)
+ .addEquivalenceGroup(setOfSetOfSetOfObject)
+ .addEquivalenceGroup(setOfSetOfSetOfString)
+ .addEquivalenceGroup(wildcard)
+ // ? extends Object
+ .addEquivalenceGroup(types.getWildcardType(objectType, null))
+ // ? extends String
+ .addEquivalenceGroup(types.getWildcardType(stringType, null))
+ // ? super String
+ .addEquivalenceGroup(types.getWildcardType(null, stringType))
+ // Map<String, Map<String, Set<Object>>>
+ .addEquivalenceGroup(
+ types.getDeclaredType(
+ mapElement,
+ stringType,
+ types.getDeclaredType(
+ mapElement, stringType, types.getDeclaredType(setElement, objectType))))
+ .addEquivalenceGroup(FAKE_ERROR_TYPE);
for (TypeKind kind : TypeKind.values()) {
if (kind.isPrimitive()) {
@@ -144,20 +149,18 @@ public class MoreTypesTest {
}
}
- ImmutableSet<Class<?>> testClasses = ImmutableSet.of(
- ExecutableElementsGroupA.class,
- ExecutableElementsGroupB.class,
- ExecutableElementsGroupC.class,
- ExecutableElementsGroupD.class,
- ExecutableElementsGroupE.class);
+ ImmutableSet<Class<?>> testClasses =
+ ImmutableSet.of(
+ ExecutableElementsGroupA.class,
+ ExecutableElementsGroupB.class,
+ ExecutableElementsGroupC.class,
+ ExecutableElementsGroupD.class,
+ ExecutableElementsGroupE.class);
for (Class<?> testClass : testClasses) {
- ImmutableList<TypeMirror> equivalenceGroup = FluentIterable.from(
- elements.getTypeElement(testClass.getCanonicalName()).getEnclosedElements())
- .transform(new Function<Element, TypeMirror>() {
- @Override public TypeMirror apply(Element input) {
- return input.asType();
- }
- })
+ ImmutableList<TypeMirror> equivalenceGroup =
+ FluentIterable.from(
+ elements.getTypeElement(testClass.getCanonicalName()).getEnclosedElements())
+ .transform(Element::asType)
.toList();
tester.addEquivalenceGroup(equivalenceGroup);
}
@@ -168,35 +171,45 @@ public class MoreTypesTest {
@SuppressWarnings("unused")
private static final class ExecutableElementsGroupA {
ExecutableElementsGroupA() {}
+
void a() {}
+
public static void b() {}
}
@SuppressWarnings("unused")
private static final class ExecutableElementsGroupB {
ExecutableElementsGroupB(String s) {}
+
void a(String s) {}
+
public static void b(String s) {}
}
@SuppressWarnings("unused")
private static final class ExecutableElementsGroupC {
ExecutableElementsGroupC() throws Exception {}
+
void a() throws Exception {}
+
public static void b() throws Exception {}
}
@SuppressWarnings("unused")
private static final class ExecutableElementsGroupD {
ExecutableElementsGroupD() throws RuntimeException {}
+
void a() throws RuntimeException {}
+
public static void b() throws RuntimeException {}
}
@SuppressWarnings("unused")
private static final class ExecutableElementsGroupE {
<T> ExecutableElementsGroupE() {}
+
<T> void a() {}
+
public static <T> void b() {}
}
@@ -214,53 +227,44 @@ public class MoreTypesTest {
@SuppressWarnings("unused")
private static final class FunkierBounds<T extends Number & Comparable<T> & Cloneable> {}
- @Test public void testReferencedTypes() {
+ @Test
+ public void testReferencedTypes() {
Elements elements = compilationRule.getElements();
- TypeElement testDataElement = elements
- .getTypeElement(ReferencedTypesTestData.class.getCanonicalName());
+ TypeElement testDataElement =
+ elements.getTypeElement(ReferencedTypesTestData.class.getCanonicalName());
ImmutableMap<String, VariableElement> fieldIndex =
FluentIterable.from(ElementFilter.fieldsIn(testDataElement.getEnclosedElements()))
- .uniqueIndex(new Function<VariableElement, String>() {
- @Override public String apply(VariableElement input) {
- return input.getSimpleName().toString();
- }
- });
-
- TypeElement objectElement =
- elements.getTypeElement(Object.class.getCanonicalName());
- TypeElement stringElement =
- elements.getTypeElement(String.class.getCanonicalName());
- TypeElement integerElement =
- elements.getTypeElement(Integer.class.getCanonicalName());
- TypeElement setElement =
- elements.getTypeElement(Set.class.getCanonicalName());
- TypeElement mapElement =
- elements.getTypeElement(Map.class.getCanonicalName());
+ .uniqueIndex(input -> input.getSimpleName().toString());
+
+ TypeElement objectElement = elements.getTypeElement(Object.class.getCanonicalName());
+ TypeElement stringElement = elements.getTypeElement(String.class.getCanonicalName());
+ TypeElement integerElement = elements.getTypeElement(Integer.class.getCanonicalName());
+ TypeElement setElement = elements.getTypeElement(Set.class.getCanonicalName());
+ TypeElement mapElement = elements.getTypeElement(Map.class.getCanonicalName());
TypeElement charSequenceElement =
elements.getTypeElement(CharSequence.class.getCanonicalName());
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f1").asType()))
- .containsExactly(objectElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f2").asType()))
- .containsExactly(setElement, stringElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f3").asType()))
+ assertThat(referencedTypes(fieldIndex, "f1")).containsExactly(objectElement);
+ assertThat(referencedTypes(fieldIndex, "f2")).containsExactly(setElement, stringElement);
+ assertThat(referencedTypes(fieldIndex, "f3"))
.containsExactly(mapElement, stringElement, objectElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f4").asType()))
- .containsExactly(integerElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f5").asType()))
- .containsExactly(setElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f6").asType()))
- .containsExactly(setElement, charSequenceElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f7").asType()))
+ assertThat(referencedTypes(fieldIndex, "f4")).containsExactly(integerElement);
+ assertThat(referencedTypes(fieldIndex, "f5")).containsExactly(setElement);
+ assertThat(referencedTypes(fieldIndex, "f6")).containsExactly(setElement, charSequenceElement);
+ assertThat(referencedTypes(fieldIndex, "f7"))
.containsExactly(mapElement, stringElement, setElement, charSequenceElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f8").asType()))
- .containsExactly(stringElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f9").asType()))
- .containsExactly(stringElement);
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f10").asType())).isEmpty();
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f11").asType())).isEmpty();
- assertThat(MoreTypes.referencedTypes(fieldIndex.get("f12").asType()))
- .containsExactly(setElement, stringElement);
+ assertThat(referencedTypes(fieldIndex, "f8")).containsExactly(stringElement);
+ assertThat(referencedTypes(fieldIndex, "f9")).containsExactly(stringElement);
+ assertThat(referencedTypes(fieldIndex, "f10")).isEmpty();
+ assertThat(referencedTypes(fieldIndex, "f11")).isEmpty();
+ assertThat(referencedTypes(fieldIndex, "f12")).containsExactly(setElement, stringElement);
+ }
+
+ private static ImmutableSet<TypeElement> referencedTypes(
+ ImmutableMap<String, VariableElement> fieldIndex, String fieldName) {
+ VariableElement field = fieldIndex.get(fieldName);
+ requireNonNull(field, fieldName);
+ return MoreTypes.referencedTypes(field.asType());
}
@SuppressWarnings("unused") // types used in compiler tests
@@ -280,20 +284,23 @@ public class MoreTypesTest {
}
private static class Parent<T> {}
+
private static class ChildA extends Parent<Number> {}
+
private static class ChildB extends Parent<String> {}
+
private static class GenericChild<T> extends Parent<T> {}
+
private interface InterfaceType {}
@Test
public void asElement_throws() {
- TypeMirror javaDotLang =
- compilationRule.getElements().getPackageElement("java.lang").asType();
+ TypeMirror javaDotLang = compilationRule.getElements().getPackageElement("java.lang").asType();
try {
MoreTypes.asElement(javaDotLang);
fail();
- } catch (IllegalArgumentException expected) {}
-
+ } catch (IllegalArgumentException expected) {
+ }
}
@Test
@@ -301,8 +308,9 @@ public class MoreTypesTest {
Elements elements = compilationRule.getElements();
TypeElement stringElement = elements.getTypeElement("java.lang.String");
assertThat(MoreTypes.asElement(stringElement.asType())).isEqualTo(stringElement);
- TypeParameterElement setParameterElement = Iterables.getOnlyElement(
- compilationRule.getElements().getTypeElement("java.util.Set").getTypeParameters());
+ TypeParameterElement setParameterElement =
+ Iterables.getOnlyElement(
+ compilationRule.getElements().getTypeElement("java.util.Set").getTypeParameters());
assertThat(MoreTypes.asElement(setParameterElement.asType())).isEqualTo(setParameterElement);
// we don't test error types because those are very hard to get predictably
}
@@ -320,8 +328,7 @@ public class MoreTypesTest {
TypeElement genericChild = elements.getTypeElement(GenericChild.class.getCanonicalName());
TypeMirror genericChildOfNumber = types.getDeclaredType(genericChild, numberType);
TypeMirror genericChildOfInteger = types.getDeclaredType(genericChild, integerType);
- TypeMirror objectType =
- elements.getTypeElement(Object.class.getCanonicalName()).asType();
+ TypeMirror objectType = elements.getTypeElement(Object.class.getCanonicalName()).asType();
TypeMirror interfaceType =
elements.getTypeElement(InterfaceType.class.getCanonicalName()).asType();
@@ -343,18 +350,20 @@ public class MoreTypesTest {
Optional<DeclaredType> parentOfGenericChildOfInteger =
MoreTypes.nonObjectSuperclass(types, elements, (DeclaredType) genericChildOfInteger);
- EquivalenceTester<TypeMirror> tester = EquivalenceTester.<TypeMirror>of(MoreTypes.equivalence())
- .addEquivalenceGroup(parentOfChildA.get(),
- types.getDeclaredType(parent, numberType),
- parentOfGenericChildOfNumber.get())
- .addEquivalenceGroup(parentOfChildB.get(), types.getDeclaredType(parent, stringType))
- .addEquivalenceGroup(parentOfGenericChild.get(), parent.asType())
- .addEquivalenceGroup(parentOfGenericChildOfInteger.get(),
- types.getDeclaredType(parent, integerType));
+ EquivalenceTester<TypeMirror> tester =
+ EquivalenceTester.<TypeMirror>of(MoreTypes.equivalence())
+ .addEquivalenceGroup(
+ parentOfChildA.get(),
+ types.getDeclaredType(parent, numberType),
+ parentOfGenericChildOfNumber.get())
+ .addEquivalenceGroup(parentOfChildB.get(), types.getDeclaredType(parent, stringType))
+ .addEquivalenceGroup(parentOfGenericChild.get(), parent.asType())
+ .addEquivalenceGroup(
+ parentOfGenericChildOfInteger.get(), types.getDeclaredType(parent, integerType));
tester.test();
}
-
+
@Test
public void testAsMemberOf_variableElement() {
Types types = compilationRule.getTypes();
@@ -364,11 +373,13 @@ public class MoreTypesTest {
TypeMirror integerType = elements.getTypeElement(Integer.class.getCanonicalName()).asType();
TypeElement paramsElement = elements.getTypeElement(Params.class.getCanonicalName());
- VariableElement tParam = Iterables.getOnlyElement(Iterables.getOnlyElement(
- ElementFilter.methodsIn(paramsElement.getEnclosedElements())).getParameters());
+ VariableElement tParam =
+ Iterables.getOnlyElement(
+ Iterables.getOnlyElement(ElementFilter.methodsIn(paramsElement.getEnclosedElements()))
+ .getParameters());
VariableElement tField =
- Iterables.getOnlyElement(ElementFilter.fieldsIn(paramsElement.getEnclosedElements()));
-
+ Iterables.getOnlyElement(ElementFilter.fieldsIn(paramsElement.getEnclosedElements()));
+
DeclaredType numberParams =
(DeclaredType) elements.getTypeElement(NumberParams.class.getCanonicalName()).asType();
DeclaredType stringParams =
@@ -376,7 +387,7 @@ public class MoreTypesTest {
TypeElement genericParams = elements.getTypeElement(GenericParams.class.getCanonicalName());
DeclaredType genericParamsOfNumber = types.getDeclaredType(genericParams, numberType);
DeclaredType genericParamsOfInteger = types.getDeclaredType(genericParams, integerType);
-
+
TypeMirror fieldOfNumberParams = MoreTypes.asMemberOf(types, numberParams, tField);
TypeMirror paramOfNumberParams = MoreTypes.asMemberOf(types, numberParams, tParam);
TypeMirror fieldOfStringParams = MoreTypes.asMemberOf(types, stringParams, tField);
@@ -388,62 +399,76 @@ public class MoreTypesTest {
TypeMirror paramOfGenericOfInteger =
MoreTypes.asMemberOf(types, genericParamsOfInteger, tParam);
- EquivalenceTester<TypeMirror> tester = EquivalenceTester.<TypeMirror>of(MoreTypes.equivalence())
- .addEquivalenceGroup(fieldOfNumberParams, paramOfNumberParams, fieldOfGenericOfNumber,
- paramOfGenericOfNumber, numberType)
- .addEquivalenceGroup(fieldOfStringParams, paramOfStringParams, stringType)
- .addEquivalenceGroup(fieldOfGenericOfInteger, paramOfGenericOfInteger, integerType);
+ EquivalenceTester<TypeMirror> tester =
+ EquivalenceTester.<TypeMirror>of(MoreTypes.equivalence())
+ .addEquivalenceGroup(
+ fieldOfNumberParams,
+ paramOfNumberParams,
+ fieldOfGenericOfNumber,
+ paramOfGenericOfNumber,
+ numberType)
+ .addEquivalenceGroup(fieldOfStringParams, paramOfStringParams, stringType)
+ .addEquivalenceGroup(fieldOfGenericOfInteger, paramOfGenericOfInteger, integerType);
tester.test();
}
-
- private static class Params<T> {
- @SuppressWarnings("unused") T t;
- @SuppressWarnings("unused") void add(T t) {}
- }
- private static class NumberParams extends Params<Number> {}
- private static class StringParams extends Params<String> {}
- private static class GenericParams<T> extends Params<T> {}
-
- private static final ErrorType FAKE_ERROR_TYPE = new ErrorType() {
- @Override
- public TypeKind getKind() {
- return TypeKind.ERROR;
- }
- @Override
- public <R, P> R accept(TypeVisitor<R, P> v, P p) {
- return v.visitError(this, p);
- }
-
- @Override
- public List<? extends TypeMirror> getTypeArguments() {
- return ImmutableList.of();
- }
-
- @Override
- public TypeMirror getEnclosingType() {
- return null;
- }
+ private static class Params<T> {
+ @SuppressWarnings("unused")
+ T t;
- @Override
- public Element asElement() {
- return null;
- }
+ @SuppressWarnings("unused")
+ void add(T t) {}
+ }
- // JDK8 Compatibility:
+ private static class NumberParams extends Params<Number> {}
- public <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
- return null;
- }
+ private static class StringParams extends Params<String> {}
- public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
- return null;
- }
+ private static class GenericParams<T> extends Params<T> {}
- public List<? extends AnnotationMirror> getAnnotationMirrors() {
- return null;
- }
- };
+ private static final ErrorType FAKE_ERROR_TYPE =
+ new ErrorType() {
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.ERROR;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitError(this, p);
+ }
+
+ @Override
+ public ImmutableList<? extends TypeMirror> getTypeArguments() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public @Nullable TypeMirror getEnclosingType() {
+ return null;
+ }
+
+ @Override
+ public @Nullable Element asElement() {
+ return null;
+ }
+
+ @Override
+ public <A extends Annotation> A @Nullable [] getAnnotationsByType(Class<A> annotationType) {
+ return null;
+ }
+
+ @Override
+ public <A extends Annotation> @Nullable A getAnnotation(Class<A> annotationType) {
+ return null;
+ }
+
+ @Override
+ @SuppressWarnings("MutableMethodReturnType")
+ public List<? extends AnnotationMirror> getAnnotationMirrors() {
+ return ImmutableList.of();
+ }
+ };
@Test
public void testIsConversionFromObjectUnchecked_yes() {
@@ -471,6 +496,21 @@ public class MoreTypesTest {
}
}
+ @Test
+ public void testIsTypeOf() {
+ Types types = compilationRule.getTypes();
+ PrimitiveType intType = types.getPrimitiveType(TypeKind.INT);
+ TypeMirror integerType = types.boxedClass(intType).asType();
+ WildcardType wildcardType = types.getWildcardType(null, null);
+ expect.that(MoreTypes.isTypeOf(int.class, intType)).isTrue();
+ expect.that(MoreTypes.isTypeOf(Integer.class, integerType)).isTrue();
+ expect.that(MoreTypes.isTypeOf(Integer.class, intType)).isFalse();
+ expect.that(MoreTypes.isTypeOf(int.class, integerType)).isFalse();
+ expect.that(MoreTypes.isTypeOf(Integer.class, FAKE_ERROR_TYPE)).isFalse();
+ assertThrows(
+ IllegalArgumentException.class, () -> MoreTypes.isTypeOf(Integer.class, wildcardType));
+ }
+
// The type of every field here is such that casting to it provokes an "unchecked" warning.
@SuppressWarnings("unused")
private static class Unchecked<T> {
diff --git a/common/src/test/java/com/google/auto/common/OverridesTest.java b/common/src/test/java/com/google/auto/common/OverridesTest.java
index afb79760..c5ccc5f6 100644
--- a/common/src/test/java/com/google/auto/common/OverridesTest.java
+++ b/common/src/test/java/com/google/auto/common/OverridesTest.java
@@ -17,6 +17,7 @@ package com.google.auto.common;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import static javax.lang.model.util.ElementFilter.methodsIn;
import com.google.common.base.Converter;
@@ -57,6 +58,7 @@ import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.StandardLocation;
import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -103,6 +105,7 @@ public class OverridesTest {
abstract void initUtils(OverridesTest test);
}
+
private final CompilerType compilerType;
private Types typeUtils;
@@ -126,12 +129,15 @@ public class OverridesTest {
static class TypesForInheritance {
interface One {
void m();
+
void m(String x);
+
void n();
}
interface Two {
void m();
+
void m(int x);
}
@@ -142,28 +148,50 @@ public class OverridesTest {
static class ChildOfParent extends Parent {}
static class ChildOfOne implements One {
- @Override public void m() {}
- @Override public void m(String x) {}
- @Override public void n() {}
+ @Override
+ public void m() {}
+
+ @Override
+ public void m(String x) {}
+
+ @Override
+ public void n() {}
}
static class ChildOfOneAndTwo implements One, Two {
- @Override public void m() {}
- @Override public void m(String x) {}
- @Override public void m(int x) {}
- @Override public void n() {}
+ @Override
+ public void m() {}
+
+ @Override
+ public void m(String x) {}
+
+ @Override
+ public void m(int x) {}
+
+ @Override
+ public void n() {}
}
static class ChildOfParentAndOne extends Parent implements One {
- @Override public void m() {}
- @Override public void m(String x) {}
- @Override public void n() {}
+ @Override
+ public void m() {}
+
+ @Override
+ public void m(String x) {}
+
+ @Override
+ public void n() {}
}
static class ChildOfParentAndOneAndTwo extends Parent implements One, Two {
- @Override public void m(String x) {}
- @Override public void m(int x) {}
- @Override public void n() {}
+ @Override
+ public void m(String x) {}
+
+ @Override
+ public void m(int x) {}
+
+ @Override
+ public void n() {}
}
abstract static class AbstractChildOfOne implements One {}
@@ -194,14 +222,20 @@ public class OverridesTest {
abstract static class BindingDeclaration implements HasKey {
abstract Optional<Element> bindingElement();
+
abstract Optional<TypeElement> contributingModule();
}
- abstract static class MultibindingDeclaration
- extends BindingDeclaration implements HasBindingType, HasContributionType {
- @Override public abstract Key key();
- @Override public abstract ContributionType contributionType();
- @Override public abstract BindingType bindingType();
+ abstract static class MultibindingDeclaration extends BindingDeclaration
+ implements HasBindingType, HasContributionType {
+ @Override
+ public abstract Key key();
+
+ @Override
+ public abstract ContributionType contributionType();
+
+ @Override
+ public abstract BindingType bindingType();
}
}
@@ -221,16 +255,26 @@ public class OverridesTest {
}
static class TypesForGenerics {
- interface XCollection<E> {
+ interface GCollection<E> {
boolean add(E x);
}
- interface XList<E> extends XCollection<E> {
- @Override public boolean add(E x);
+ interface GList<E> extends GCollection<E> {
+ @Override
+ boolean add(E x);
}
- static class StringList implements XList<String> {
- @Override public boolean add(String x) {
+ static class StringList implements GList<String> {
+ @Override
+ public boolean add(String x) {
+ return false;
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ static class RawList implements GList {
+ @Override
+ public boolean add(Object x) {
return false;
}
}
@@ -243,7 +287,8 @@ public class OverridesTest {
}
static class RawChildOfRaw extends RawParent {
- @Override void frob(List x) {}
+ @Override
+ void frob(List x) {}
}
static class NonRawParent {
@@ -251,7 +296,8 @@ public class OverridesTest {
}
static class RawChildOfNonRaw extends NonRawParent {
- @Override void frob(List x) {}
+ @Override
+ void frob(List x) {}
}
}
@@ -291,8 +337,9 @@ public class OverridesTest {
// since the two Es are not the same.
@Test
public void overridesDiamond() {
- checkOverridesInSet(ImmutableSet.<Class<?>>of(
- Collection.class, List.class, AbstractCollection.class, AbstractList.class));
+ checkOverridesInSet(
+ ImmutableSet.<Class<?>>of(
+ Collection.class, List.class, AbstractCollection.class, AbstractList.class));
}
private void checkOverridesInContainedClasses(Class<?> container) {
@@ -324,10 +371,13 @@ public class OverridesTest {
expect
.withMessage(
"%s.%s overrides %s.%s in %s: javac says %s, we say %s",
- overrider.getEnclosingElement(), overrider,
- overridden.getEnclosingElement(), overridden,
+ overrider.getEnclosingElement(),
+ overrider,
+ overridden.getEnclosingElement(),
+ overridden,
in,
- javacSays, weSay)
+ javacSays,
+ weSay)
.fail();
}
}
@@ -355,7 +405,7 @@ public class OverridesTest {
}
}
assertThat(found).isNotNull();
- return found;
+ return requireNonNull(found);
}
// These skeletal parallels to the real collection classes ensure that the test is independent
@@ -375,8 +425,8 @@ public class OverridesTest {
}
}
- private abstract static class XAbstractList<E>
- extends XAbstractCollection<E> implements XList<E> {
+ private abstract static class XAbstractList<E> extends XAbstractCollection<E>
+ implements XList<E> {
@Override
public boolean add(E e) {
return true;
@@ -440,7 +490,7 @@ public class OverridesTest {
extends Converter<String, Range<T>> {
@Override
protected String doBackward(Range<T> b) {
- return null;
+ return "";
}
}
@@ -470,9 +520,8 @@ public class OverridesTest {
explicitOverrides.methodFromSuperclasses(xAbstractStringList, add);
assertThat(addInAbstractStringList).isNull();
- ExecutableElement addInStringList =
- explicitOverrides.methodFromSuperclasses(xStringList, add);
- assertThat(addInStringList.getEnclosingElement()).isEqualTo(xAbstractList);
+ ExecutableElement addInStringList = explicitOverrides.methodFromSuperclasses(xStringList, add);
+ assertThat(requireNonNull(addInStringList).getEnclosingElement()).isEqualTo(xAbstractList);
}
@Test
@@ -487,20 +536,21 @@ public class OverridesTest {
ExecutableElement addInAbstractStringList =
explicitOverrides.methodFromSuperinterfaces(xAbstractStringList, add);
- assertThat(addInAbstractStringList.getEnclosingElement()).isEqualTo(xCollection);
+ assertThat(requireNonNull(addInAbstractStringList).getEnclosingElement())
+ .isEqualTo(xCollection);
ExecutableElement addInNumberList =
explicitOverrides.methodFromSuperinterfaces(xNumberList, add);
- assertThat(addInNumberList.getEnclosingElement()).isEqualTo(xAbstractList);
+ assertThat(requireNonNull(addInNumberList).getEnclosingElement()).isEqualTo(xAbstractList);
- ExecutableElement addInList =
- explicitOverrides.methodFromSuperinterfaces(xList, add);
- assertThat(addInList.getEnclosingElement()).isEqualTo(xCollection);
+ ExecutableElement addInList = explicitOverrides.methodFromSuperinterfaces(xList, add);
+ assertThat(requireNonNull(addInList).getEnclosingElement()).isEqualTo(xCollection);
}
- private void assertTypeListsEqual(List<TypeMirror> actual, List<TypeMirror> expected) {
- assertThat(actual.size()).isEqualTo(expected.size());
- for (int i = 0; i < actual.size(); i++) {
+ private void assertTypeListsEqual(@Nullable List<TypeMirror> actual, List<TypeMirror> expected) {
+ requireNonNull(actual);
+ assertThat(actual).hasSize(expected.size());
+ for (int i = 0; i < actual.size(); i++) {
assertThat(typeUtils.isSameType(actual.get(i), expected.get(i))).isTrue();
}
}
@@ -552,10 +602,11 @@ public class OverridesTest {
// it hard for ecj to find the boot class path. Elsewhere it is unnecessary but harmless.
File rtJar = new File(StandardSystemProperty.JAVA_HOME.value() + "/lib/rt.jar");
if (rtJar.exists()) {
- List<File> bootClassPath = ImmutableList.<File>builder()
- .add(rtJar)
- .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH))
- .build();
+ List<File> bootClassPath =
+ ImmutableList.<File>builder()
+ .add(rtJar)
+ .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH))
+ .build();
fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath);
}
Iterable<? extends JavaFileObject> sources = fileManager.getJavaFileObjects(dummySourceFile);
@@ -583,8 +634,7 @@ public class OverridesTest {
}
@Override
- public boolean process(
- Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
ecjCompilation.elements = processingEnv.getElementUtils();
ecjCompilation.types = processingEnv.getTypeUtils();
@@ -643,24 +693,24 @@ public class OverridesTest {
private static final TypeVisitor<String, Void> ERASED_STRING_TYPE_VISITOR =
new SimpleTypeVisitor6<String, Void>() {
- @Override
- protected String defaultAction(TypeMirror e, Void p) {
- return e.toString();
- }
+ @Override
+ protected String defaultAction(TypeMirror e, Void p) {
+ return e.toString();
+ }
- @Override
- public String visitArray(ArrayType t, Void p) {
- return visit(t.getComponentType()) + "[]";
- }
+ @Override
+ public String visitArray(ArrayType t, Void p) {
+ return visit(t.getComponentType()) + "[]";
+ }
- @Override
- public String visitDeclared(DeclaredType t, Void p) {
- return MoreElements.asType(t.asElement()).getQualifiedName().toString();
- }
+ @Override
+ public String visitDeclared(DeclaredType t, Void p) {
+ return MoreElements.asType(t.asElement()).getQualifiedName().toString();
+ }
- @Override
- public String visitTypeVariable(TypeVariable t, Void p) {
- return visit(t.getUpperBound());
- }
- };
+ @Override
+ public String visitTypeVariable(TypeVariable t, Void p) {
+ return visit(t.getUpperBound());
+ }
+ };
}
diff --git a/common/src/test/java/com/google/auto/common/SimpleAnnotationMirrorTest.java b/common/src/test/java/com/google/auto/common/SimpleAnnotationMirrorTest.java
index d73e1b6c..0bad83db 100644
--- a/common/src/test/java/com/google/auto/common/SimpleAnnotationMirrorTest.java
+++ b/common/src/test/java/com/google/auto/common/SimpleAnnotationMirrorTest.java
@@ -46,6 +46,7 @@ public class SimpleAnnotationMirrorTest {
@interface MultipleValues {
int value1();
+
int value2();
}
diff --git a/common/src/test/java/com/google/auto/common/SimpleTypeAnnotationValueTest.java b/common/src/test/java/com/google/auto/common/SimpleTypeAnnotationValueTest.java
index 4fc61b51..ea85365b 100644
--- a/common/src/test/java/com/google/auto/common/SimpleTypeAnnotationValueTest.java
+++ b/common/src/test/java/com/google/auto/common/SimpleTypeAnnotationValueTest.java
@@ -28,6 +28,7 @@ import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleAnnotationValueVisitor8;
import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -70,18 +71,21 @@ public class SimpleTypeAnnotationValueTest {
@Test
public void visitorMethod() {
- SimpleTypeAnnotationValue.of(objectType).accept(new SimpleAnnotationValueVisitor8<Void, Void>(){
- @Override
- public Void visitType(TypeMirror typeMirror, Void aVoid) {
- // do nothing, expected case
- return null;
- }
+ SimpleTypeAnnotationValue.of(objectType)
+ .accept(
+ new SimpleAnnotationValueVisitor8<@Nullable Void, @Nullable Void>() {
+ @Override
+ public @Nullable Void visitType(TypeMirror typeMirror, @Nullable Void aVoid) {
+ // do nothing, expected case
+ return null;
+ }
- @Override
- protected Void defaultAction(Object o, Void aVoid) {
- throw new AssertionError();
- }
- }, null);
+ @Override
+ protected @Nullable Void defaultAction(Object o, @Nullable Void aVoid) {
+ throw new AssertionError();
+ }
+ },
+ null);
}
@Test
diff --git a/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java b/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java
index 15e54fff..c9bcf778 100644
--- a/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java
+++ b/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java
@@ -35,231 +35,263 @@ import org.junit.runners.JUnit4;
public class SuperficialValidationTest {
@Test
public void missingReturnType() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "abstract class TestClass {",
- " abstract MissingType blah();",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "abstract class TestClass {",
+ " abstract MissingType blah();",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void missingGenericReturnType() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "abstract class TestClass {",
- " abstract MissingType<?> blah();",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "abstract class TestClass {",
+ " abstract MissingType<?> blah();",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void missingReturnTypeTypeParameter() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "import java.util.Map;",
- "import java.util.Set;",
- "",
- "abstract class TestClass {",
- " abstract Map<Set<?>, MissingType<?>> blah();",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import java.util.Map;",
+ "import java.util.Set;",
+ "",
+ "abstract class TestClass {",
+ " abstract Map<Set<?>, MissingType<?>> blah();",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void missingTypeParameter() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "class TestClass<T extends MissingType> {}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass", //
+ "package test;",
+ "",
+ "class TestClass<T extends MissingType> {}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void missingParameterType() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "abstract class TestClass {",
- " abstract void foo(MissingType x);",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "abstract class TestClass {",
+ " abstract void foo(MissingType x);",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void missingAnnotation() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "@MissingAnnotation",
- "class TestClass {}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass", //
+ "package test;",
+ "",
+ "@MissingAnnotation",
+ "class TestClass {}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void handlesRecursiveTypeParams() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "class TestClass<T extends Comparable<T>> {}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass", //
+ "package test;",
+ "",
+ "class TestClass<T extends Comparable<T>> {}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue();
+ }
+ })
.compilesWithoutError();
}
@Test
public void handlesRecursiveType() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "abstract class TestClass {",
- " abstract TestClass foo(TestClass x);",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "abstract class TestClass {",
+ " abstract TestClass foo(TestClass x);",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue();
+ }
+ })
.compilesWithoutError();
}
@Test
public void missingWildcardBound() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "import java.util.Set;",
- "",
- "class TestClass {",
- " Set<? extends MissingType> extendsTest() {",
- " return null;",
- " }",
- "",
- " Set<? super MissingType> superTest() {",
- " return null;",
- " }",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "import java.util.Set;",
+ "",
+ "class TestClass {",
+ " Set<? extends MissingType> extendsTest() {",
+ " return null;",
+ " }",
+ "",
+ " Set<? super MissingType> superTest() {",
+ " return null;",
+ " }",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void missingIntersection() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
- "test.TestClass",
- "package test;",
- "",
- "class TestClass<T extends Number & Missing> {}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.TestClass",
+ "package test;",
+ "",
+ "class TestClass<T extends Number & Missing> {}");
assertAbout(javaSource())
.that(javaFileObject)
- .processedWith(new AssertingProcessor() {
- @Override void runAssertions() {
- TypeElement testClassElement =
- processingEnv.getElementUtils().getTypeElement("test.TestClass");
- assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
- }
- })
+ .processedWith(
+ new AssertingProcessor() {
+ @Override
+ void runAssertions() {
+ TypeElement testClassElement =
+ processingEnv.getElementUtils().getTypeElement("test.TestClass");
+ assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
+ }
+ })
.failsToCompile();
}
@Test
public void invalidAnnotationValue() {
- JavaFileObject javaFileObject = JavaFileObjects.forSourceLines("test.Outer",
- "package test;",
- "",
- "final class Outer {",
- " @interface TestAnnotation {",
- " Class[] classes();",
- " }",
- "",
- " @TestAnnotation(classes = Foo)",
- " static class TestClass {}",
- "}");
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "test.Outer",
+ "package test;",
+ "",
+ "final class Outer {",
+ " @interface TestAnnotation {",
+ " Class[] classes();",
+ " }",
+ "",
+ " @TestAnnotation(classes = Foo)",
+ " static class TestClass {}",
+ "}");
assertAbout(javaSource())
.that(javaFileObject)
.processedWith(
diff --git a/common/src/test/java/com/google/auto/common/VisibilityTest.java b/common/src/test/java/com/google/auto/common/VisibilityTest.java
index 6a80b7af..fc5e630b 100644
--- a/common/src/test/java/com/google/auto/common/VisibilityTest.java
+++ b/common/src/test/java/com/google/auto/common/VisibilityTest.java
@@ -39,9 +39,10 @@ public class VisibilityTest {
public void packageVisibility() {
assertThat(Visibility.ofElement(compilation.getElements().getPackageElement("java.lang")))
.isEqualTo(PUBLIC);
- assertThat(Visibility.ofElement(
- compilation.getElements().getPackageElement("com.google.auto.common")))
- .isEqualTo(PUBLIC);
+ assertThat(
+ Visibility.ofElement(
+ compilation.getElements().getPackageElement("com.google.auto.common")))
+ .isEqualTo(PUBLIC);
}
@Test
@@ -61,32 +62,44 @@ public class VisibilityTest {
@SuppressWarnings("unused")
public static class PublicClass {
public static class NestedPublicClass {}
+
protected static class NestedProtectedClass {}
+
static class NestedDefaultClass {}
+
private static class NestedPrivateClass {}
}
@SuppressWarnings("unused")
protected static class ProtectedClass {
public static class NestedPublicClass {}
+
protected static class NestedProtectedClass {}
+
static class NestedDefaultClass {}
+
private static class NestedPrivateClass {}
}
@SuppressWarnings("unused")
static class DefaultClass {
public static class NestedPublicClass {}
+
protected static class NestedProtectedClass {}
+
static class NestedDefaultClass {}
+
private static class NestedPrivateClass {}
}
@SuppressWarnings("unused")
private static class PrivateClass {
public static class NestedPublicClass {}
+
protected static class NestedProtectedClass {}
+
static class NestedDefaultClass {}
+
private static class NestedPrivateClass {}
}
@@ -94,21 +107,25 @@ public class VisibilityTest {
public void classVisibility() {
assertThat(Visibility.ofElement(compilation.getElements().getTypeElement("java.util.Map")))
.isEqualTo(PUBLIC);
- assertThat(Visibility.ofElement(
- compilation.getElements().getTypeElement("java.util.Map.Entry")))
- .isEqualTo(PUBLIC);
- assertThat(Visibility.ofElement(
- compilation.getElements().getTypeElement(PublicClass.class.getCanonicalName())))
- .isEqualTo(PUBLIC);
- assertThat(Visibility.ofElement(
- compilation.getElements().getTypeElement(ProtectedClass.class.getCanonicalName())))
- .isEqualTo(PROTECTED);
- assertThat(Visibility.ofElement(
- compilation.getElements().getTypeElement(DefaultClass.class.getCanonicalName())))
- .isEqualTo(DEFAULT);
- assertThat(Visibility.ofElement(
- compilation.getElements().getTypeElement(PrivateClass.class.getCanonicalName())))
- .isEqualTo(PRIVATE);
+ assertThat(
+ Visibility.ofElement(compilation.getElements().getTypeElement("java.util.Map.Entry")))
+ .isEqualTo(PUBLIC);
+ assertThat(
+ Visibility.ofElement(
+ compilation.getElements().getTypeElement(PublicClass.class.getCanonicalName())))
+ .isEqualTo(PUBLIC);
+ assertThat(
+ Visibility.ofElement(
+ compilation.getElements().getTypeElement(ProtectedClass.class.getCanonicalName())))
+ .isEqualTo(PROTECTED);
+ assertThat(
+ Visibility.ofElement(
+ compilation.getElements().getTypeElement(DefaultClass.class.getCanonicalName())))
+ .isEqualTo(DEFAULT);
+ assertThat(
+ Visibility.ofElement(
+ compilation.getElements().getTypeElement(PrivateClass.class.getCanonicalName())))
+ .isEqualTo(PRIVATE);
}
@Test
@@ -118,14 +135,11 @@ public class VisibilityTest {
assertThat(effectiveVisiblityOfClass(DefaultClass.class)).isEqualTo(DEFAULT);
assertThat(effectiveVisiblityOfClass(PrivateClass.class)).isEqualTo(PRIVATE);
- assertThat(effectiveVisiblityOfClass(PublicClass.NestedPublicClass.class))
- .isEqualTo(PUBLIC);
+ assertThat(effectiveVisiblityOfClass(PublicClass.NestedPublicClass.class)).isEqualTo(PUBLIC);
assertThat(effectiveVisiblityOfClass(PublicClass.NestedProtectedClass.class))
.isEqualTo(PROTECTED);
- assertThat(effectiveVisiblityOfClass(PublicClass.NestedDefaultClass.class))
- .isEqualTo(DEFAULT);
- assertThat(effectiveVisiblityOfClass(PublicClass.NestedPrivateClass.class))
- .isEqualTo(PRIVATE);
+ assertThat(effectiveVisiblityOfClass(PublicClass.NestedDefaultClass.class)).isEqualTo(DEFAULT);
+ assertThat(effectiveVisiblityOfClass(PublicClass.NestedPrivateClass.class)).isEqualTo(PRIVATE);
assertThat(effectiveVisiblityOfClass(ProtectedClass.NestedPublicClass.class))
.isEqualTo(PROTECTED);
@@ -136,23 +150,17 @@ public class VisibilityTest {
assertThat(effectiveVisiblityOfClass(ProtectedClass.NestedPrivateClass.class))
.isEqualTo(PRIVATE);
- assertThat(effectiveVisiblityOfClass(DefaultClass.NestedPublicClass.class))
- .isEqualTo(DEFAULT);
+ assertThat(effectiveVisiblityOfClass(DefaultClass.NestedPublicClass.class)).isEqualTo(DEFAULT);
assertThat(effectiveVisiblityOfClass(DefaultClass.NestedProtectedClass.class))
.isEqualTo(DEFAULT);
- assertThat(effectiveVisiblityOfClass(DefaultClass.NestedDefaultClass.class))
- .isEqualTo(DEFAULT);
- assertThat(effectiveVisiblityOfClass(DefaultClass.NestedPrivateClass.class))
- .isEqualTo(PRIVATE);
+ assertThat(effectiveVisiblityOfClass(DefaultClass.NestedDefaultClass.class)).isEqualTo(DEFAULT);
+ assertThat(effectiveVisiblityOfClass(DefaultClass.NestedPrivateClass.class)).isEqualTo(PRIVATE);
- assertThat(effectiveVisiblityOfClass(PrivateClass.NestedPublicClass.class))
- .isEqualTo(PRIVATE);
+ assertThat(effectiveVisiblityOfClass(PrivateClass.NestedPublicClass.class)).isEqualTo(PRIVATE);
assertThat(effectiveVisiblityOfClass(PrivateClass.NestedProtectedClass.class))
.isEqualTo(PRIVATE);
- assertThat(effectiveVisiblityOfClass(PrivateClass.NestedDefaultClass.class))
- .isEqualTo(PRIVATE);
- assertThat(effectiveVisiblityOfClass(PrivateClass.NestedPrivateClass.class))
- .isEqualTo(PRIVATE);
+ assertThat(effectiveVisiblityOfClass(PrivateClass.NestedDefaultClass.class)).isEqualTo(PRIVATE);
+ assertThat(effectiveVisiblityOfClass(PrivateClass.NestedPrivateClass.class)).isEqualTo(PRIVATE);
}
private Visibility effectiveVisiblityOfClass(Class<?> clazz) {
diff --git a/factory/pom.xml b/factory/pom.xml
index 48bcfeff..629223a9 100644
--- a/factory/pom.xml
+++ b/factory/pom.xml
@@ -36,9 +36,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ <auto-service.version>1.0</auto-service.version>
+ <auto-value.version>1.8.1</auto-value.version>
<java.version>1.8</java.version>
- <guava.version>28.2-jre</guava.version>
- <truth.version>1.0.1</truth.version>
+ <guava.version>30.1.1-jre</guava.version>
+ <truth.version>1.1.3</truth.version>
</properties>
<scm>
@@ -69,43 +71,25 @@
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
- <version>0.10</version>
+ <version>1.1</version>
</dependency>
<dependency>
<groupId>com.google.auto.value</groupId>
<artifactId>auto-value-annotations</artifactId>
- <version>1.7</version>
- </dependency>
- <dependency>
- <groupId>com.google.auto.value</groupId>
- <artifactId>auto-value</artifactId>
- <version>1.7</version>
- <scope>provided</scope>
+ <version>${auto-value.version}</version>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
- <artifactId>auto-service</artifactId>
- <version>1.0-rc6</version>
- <scope>provided</scope>
+ <artifactId>auto-service-annotations</artifactId>
+ <version>${auto-service.version}</version>
</dependency>
<dependency>
<groupId>net.ltgt.gradle.incap</groupId>
<artifactId>incap</artifactId>
- <version>0.2</version>
+ <version>0.3</version>
<scope>provided</scope>
</dependency>
<dependency>
- <groupId>net.ltgt.gradle.incap</groupId>
- <artifactId>incap-processor</artifactId>
- <version>0.2</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.google.googlejavaformat</groupId>
- <artifactId>google-java-format</artifactId>
- <version>1.7</version>
- </dependency>
- <dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
@@ -113,7 +97,7 @@
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
- <version>1.12.1</version>
+ <version>1.13.0</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
@@ -124,13 +108,13 @@
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
- <version>0.18</version>
+ <version>0.19</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.13</version>
+ <version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -157,29 +141,54 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service</artifactId>
+ <version>${auto-service.version}</version>
+ </path>
+ <path>
+ <groupId>com.google.auto.value</groupId>
+ <artifactId>auto-value</artifactId>
+ <version>${auto-value.version}</version>
+ </path>
+ <path>
+ <groupId>net.ltgt.gradle.incap</groupId>
+ <artifactId>incap-processor</artifactId>
+ <version>0.3</version>
+ </path>
+ </annotationProcessorPaths>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
</plugin>
<plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.22.2</version>
+ <configuration>
+ <argLine>${test.jvm.flags}</argLine>
+ </configuration>
+ </plugin>
+ <plugin>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-invoker-plugin</artifactId>
- <version>3.0.1</version>
+ <version>3.2.2</version>
<configuration>
<addTestClassPath>true</addTestClassPath>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
@@ -205,4 +214,15 @@
</plugin>
</plugins>
</build>
+ <profiles>
+ <profile>
+ <id>open-modules</id>
+ <activation>
+ <jdk>[9,)</jdk>
+ </activation>
+ <properties>
+ <test.jvm.flags>--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</test.jvm.flags>
+ </properties>
+ </profile>
+ </profiles>
</project>
diff --git a/factory/src/it/functional/pom.xml b/factory/src/it/functional/pom.xml
index 56e8f6f2..a7a1853e 100644
--- a/factory/src/it/functional/pom.xml
+++ b/factory/src/it/functional/pom.xml
@@ -45,7 +45,7 @@
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
- <version>27.0.1-jre</version>
+ <version>29.0-jre</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
@@ -66,7 +66,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.12</version>
+ <version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -82,12 +82,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
@@ -99,7 +99,7 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.5</version>
</dependency>
</dependencies>
</plugin>
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java
index 4c4a38af..f32e9f8d 100644
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/DaggerModule.java
@@ -16,44 +16,44 @@
package com.google.auto.factory;
import com.google.auto.factory.otherpackage.OtherPackage;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@Module
-final class DaggerModule {
- @Provides Dependency provideDependency(DependencyImpl impl) {
- return impl;
- }
+abstract class DaggerModule {
+ private DaggerModule() {} // no instances
- @Provides
+ @Binds
+ abstract Dependency provideDependency(DependencyImpl impl);
+
+ @Binds
@Qualifier
- Dependency provideQualifiedDependency(QualifiedDependencyImpl impl) {
- return impl;
- }
+ abstract Dependency provideQualifiedDependency(QualifiedDependencyImpl impl);
@Provides
- int providePrimitive() {
+ static int providePrimitive() {
return 1;
}
@Provides
@Qualifier
- int provideQualifiedPrimitive() {
+ static int provideQualifiedPrimitive() {
return 2;
}
@Provides
- Number provideNumber() {
+ static Number provideNumber() {
return 3;
}
@Provides
- ReferencePackage provideReferencePackage(ReferencePackageFactory factory) {
+ static ReferencePackage provideReferencePackage(ReferencePackageFactory factory) {
return factory.create(17);
}
@Provides
- OtherPackage provideOtherPackage() {
+ static OtherPackage provideOtherPackage() {
return new OtherPackage(null, 23);
}
}
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/DependencyImpl.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/DependencyImpl.java
index 4c019ea6..d94a3cf6 100644
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/DependencyImpl.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/DependencyImpl.java
@@ -18,5 +18,6 @@ package com.google.auto.factory;
import javax.inject.Inject;
public class DependencyImpl implements Dependency {
- @Inject DependencyImpl() {}
+ @Inject
+ DependencyImpl() {}
}
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/GenericFoo.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/GenericFoo.java
index f7c13b41..31954a19 100644
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/GenericFoo.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/GenericFoo.java
@@ -28,10 +28,7 @@ public class GenericFoo<A, B extends List<? extends A>, C, E extends Enum<E>> {
private final E depE;
<D extends IntAccessor & StringAccessor> GenericFoo(
- @Provided Provider<A> depA,
- B depB,
- D depD,
- E depE) {
+ @Provided Provider<A> depA, B depB, D depD, E depE) {
this.depA = depA.get();
this.depB = depB;
this.depDIntAccessor = depD;
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/GuiceModule.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/GuiceModule.java
index 45d4d26e..77346655 100644
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/GuiceModule.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/GuiceModule.java
@@ -18,7 +18,8 @@ package com.google.auto.factory;
import com.google.inject.AbstractModule;
public class GuiceModule extends AbstractModule {
- @Override protected void configure() {
+ @Override
+ protected void configure() {
bind(Dependency.class).to(DependencyImpl.class);
bind(Dependency.class).annotatedWith(Qualifier.class).to(QualifiedDependencyImpl.class);
bind(Integer.class).toInstance(1);
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java
index 22aff651..4f67c3b3 100644..100755
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/ReferencePackage.java
@@ -25,9 +25,7 @@ public class ReferencePackage {
private final int random;
@Inject
- public ReferencePackage(
- @Provided OtherPackageFactory otherPackageFactory,
- int random) {
+ ReferencePackage(@Provided OtherPackageFactory otherPackageFactory, int random) {
this.otherPackageFactory = otherPackageFactory;
this.random = random;
}
diff --git a/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java b/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java
index b9925a18..b9925a18 100644..100755
--- a/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java
+++ b/factory/src/it/functional/src/main/java/com/google/auto/factory/otherpackage/OtherPackage.java
diff --git a/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java b/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java
index e1412117..3caa5a54 100644
--- a/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java
+++ b/factory/src/it/functional/src/test/java/com/google/auto/factory/DependencyInjectionIntegrationTest.java
@@ -19,7 +19,8 @@ public class DependencyInjectionIntegrationTest {
private static final IntAndStringAccessor INT_AND_STRING_ACCESSOR = new IntAndStringAccessor() {};
- @Test public void daggerInjectedFactory() {
+ @Test
+ public void daggerInjectedFactory() {
FooFactory fooFactory = DaggerFactoryComponent.create().factory();
Foo one = fooFactory.create("A");
Foo two = fooFactory.create("B");
@@ -46,8 +47,9 @@ public class DependencyInjectionIntegrationTest {
genericFooFactory.create(ImmutableList.of(3L), INT_AND_STRING_ACCESSOR, DepE.VALUE_1);
ArrayList<Double> intAndStringAccessorArrayList = new ArrayList<>();
intAndStringAccessorArrayList.add(4.0);
- GenericFoo<Number, ArrayList<Double>, Long, DepE> four = genericFooFactory.create(
- intAndStringAccessorArrayList, INT_AND_STRING_ACCESSOR, DepE.VALUE_2);
+ GenericFoo<Number, ArrayList<Double>, Long, DepE> four =
+ genericFooFactory.create(
+ intAndStringAccessorArrayList, INT_AND_STRING_ACCESSOR, DepE.VALUE_2);
assertThat(three.getDepA()).isEqualTo(3);
ImmutableList<Long> unusedLongList = three.getDepB();
assertThat(three.getDepB()).containsExactly(3L);
@@ -75,7 +77,8 @@ public class DependencyInjectionIntegrationTest {
assertThat(otherPackage.random()).isEqualTo(5);
}
- @Test public void guiceInjectedFactory() {
+ @Test
+ public void guiceInjectedFactory() {
FooFactory fooFactory = Guice.createInjector(new GuiceModule()).getInstance(FooFactory.class);
Foo one = fooFactory.create("A");
Foo two = fooFactory.create("B");
@@ -103,8 +106,9 @@ public class DependencyInjectionIntegrationTest {
genericFooFactory.create(ImmutableList.of(3L), INT_AND_STRING_ACCESSOR, DepE.VALUE_1);
ArrayList<Double> intAndStringAccessorArrayList = new ArrayList<>();
intAndStringAccessorArrayList.add(4.0);
- GenericFoo<Number, ArrayList<Double>, Long, DepE> four = genericFooFactory.create(
- intAndStringAccessorArrayList, INT_AND_STRING_ACCESSOR, DepE.VALUE_2);
+ GenericFoo<Number, ArrayList<Double>, Long, DepE> four =
+ genericFooFactory.create(
+ intAndStringAccessorArrayList, INT_AND_STRING_ACCESSOR, DepE.VALUE_2);
assertThat(three.getDepA()).isEqualTo(3);
ImmutableList<Long> unusedLongList = three.getDepB();
assertThat(three.getDepB()).containsExactly(3L);
@@ -124,8 +128,7 @@ public class DependencyInjectionIntegrationTest {
@Test
public void guiceInjectedPackageSpanningFactory() {
ReferencePackageFactory referencePackageFactory =
- Guice.createInjector(new GuiceModule())
- .getInstance(ReferencePackageFactory.class);
+ Guice.createInjector(new GuiceModule()).getInstance(ReferencePackageFactory.class);
ReferencePackage referencePackage = referencePackageFactory.create(5);
OtherPackage otherPackage = referencePackage.otherPackage();
assertThat(otherPackage.referencePackageFactory()).isNotSameInstanceAs(referencePackageFactory);
diff --git a/factory/src/main/java/com/google/auto/factory/AutoFactory.java b/factory/src/main/java/com/google/auto/factory/AutoFactory.java
index 2ef84cc3..3b5d90ea 100644
--- a/factory/src/main/java/com/google/auto/factory/AutoFactory.java
+++ b/factory/src/main/java/com/google/auto/factory/AutoFactory.java
@@ -32,7 +32,7 @@ import java.lang.annotation.Target;
*
* @author Gregory Kick
*/
-@Target({ TYPE, CONSTRUCTOR })
+@Target({TYPE, CONSTRUCTOR})
public @interface AutoFactory {
/**
* The <i>simple</i> name of the generated factory; the factory is always generated in the same
@@ -50,7 +50,7 @@ public @interface AutoFactory {
/**
* A list of interfaces that the generated factory is required to implement.
*/
- Class<?>[] implementing() default { };
+ Class<?>[] implementing() default {};
/**
* The type that the generated factory is require to extend.
diff --git a/factory/src/main/java/com/google/auto/factory/Provided.java b/factory/src/main/java/com/google/auto/factory/Provided.java
index e81e4aa6..226a16f4 100644
--- a/factory/src/main/java/com/google/auto/factory/Provided.java
+++ b/factory/src/main/java/com/google/auto/factory/Provided.java
@@ -26,4 +26,4 @@ import java.lang.annotation.Target;
* @author Gregory Kick
*/
@Target(PARAMETER)
-public @interface Provided { }
+public @interface Provided {}
diff --git a/factory/src/main/java/com/google/auto/factory/package-info.java b/factory/src/main/java/com/google/auto/factory/package-info.java
new file mode 100644
index 00000000..ea1ddd88
--- /dev/null
+++ b/factory/src/main/java/com/google/auto/factory/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.factory;
+
diff --git a/factory/src/main/java/com/google/auto/factory/processor/AnnotationValues.java b/factory/src/main/java/com/google/auto/factory/processor/AnnotationValues.java
index b767c47f..53d38a40 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/AnnotationValues.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/AnnotationValues.java
@@ -32,24 +32,29 @@ final class AnnotationValues {
static boolean asBoolean(AnnotationValue value) {
return value.accept(
new SimpleAnnotationValueVisitor6<Boolean, Void>() {
- @Override protected Boolean defaultAction(Object o, Void p) {
+ @Override
+ protected Boolean defaultAction(Object o, Void p) {
throw new IllegalArgumentException();
}
- @Override public Boolean visitBoolean(boolean b, Void p) {
+ @Override
+ public Boolean visitBoolean(boolean b, Void p) {
return b;
}
- }, null);
+ },
+ null);
}
static TypeElement asType(AnnotationValue value) {
return value.accept(
new SimpleAnnotationValueVisitor6<TypeElement, Void>() {
- @Override protected TypeElement defaultAction(Object o, Void p) {
+ @Override
+ protected TypeElement defaultAction(Object o, Void p) {
throw new IllegalArgumentException();
}
- @Override public TypeElement visitType(TypeMirror t, Void p) {
+ @Override
+ public TypeElement visitType(TypeMirror t, Void p) {
return t.accept(
new SimpleTypeVisitor6<TypeElement, Void>() {
@Override
@@ -59,12 +64,14 @@ final class AnnotationValues {
@Override
public TypeElement visitDeclared(DeclaredType t, Void p) {
- return Iterables.getOnlyElement(ElementFilter.typesIn(
- ImmutableList.of(t.asElement())));
+ return Iterables.getOnlyElement(
+ ElementFilter.typesIn(ImmutableList.of(t.asElement())));
}
- }, null);
+ },
+ null);
}
- }, null);
+ },
+ null);
}
static ImmutableList<? extends AnnotationValue> asList(AnnotationValue value) {
@@ -80,6 +87,7 @@ final class AnnotationValues {
List<? extends AnnotationValue> vals, Void p) {
return ImmutableList.copyOf(vals);
}
- }, null);
+ },
+ null);
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java
index e2da6eae..889d8e41 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryDeclaration.java
@@ -21,13 +21,13 @@ import static com.google.common.base.Preconditions.checkArgument;
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 java.util.Objects.requireNonNull;
import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.lang.model.util.ElementFilter.typesIn;
import static javax.tools.Diagnostic.Kind.ERROR;
import com.google.auto.factory.AutoFactory;
import com.google.auto.value.AutoValue;
-import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
@@ -36,6 +36,7 @@ import com.google.common.collect.ImmutableSet;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import javax.annotation.processing.Messager;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
@@ -54,12 +55,19 @@ import javax.lang.model.util.Elements;
@AutoValue
abstract class AutoFactoryDeclaration {
abstract TypeElement targetType();
+
abstract Element target();
+
abstract Optional<String> className();
+
abstract TypeElement extendingType();
+
abstract ImmutableSet<TypeElement> implementingTypes();
+
abstract boolean allowSubclasses();
+
abstract AnnotationMirror mirror();
+
abstract ImmutableMap<String, AnnotationValue> valuesMap();
PackageAndClass getFactoryName() {
@@ -97,52 +105,70 @@ abstract class AutoFactoryDeclaration {
Optional<AutoFactoryDeclaration> createIfValid(Element element) {
checkNotNull(element);
AnnotationMirror mirror = Mirrors.getAnnotationMirror(element, AutoFactory.class).get();
- checkArgument(Mirrors.getQualifiedName(mirror.getAnnotationType()).
- contentEquals(AutoFactory.class.getName()));
+ checkArgument(
+ Mirrors.getQualifiedName(mirror.getAnnotationType())
+ .contentEquals(AutoFactory.class.getName()));
Map<String, AnnotationValue> values =
Mirrors.simplifyAnnotationValueMap(elements.getElementValuesWithDefaults(mirror));
checkState(values.size() == 4);
- // className value is a string, so we can just call toString
- AnnotationValue classNameValue = values.get("className");
+ // className value is a string, so we can just call toString. We know values.get("className")
+ // is non-null because @AutoFactory has an annotation element of that name.
+ AnnotationValue classNameValue = requireNonNull(values.get("className"));
String className = classNameValue.getValue().toString();
if (!className.isEmpty() && !isValidIdentifier(className)) {
- messager.printMessage(ERROR,
+ messager.printMessage(
+ ERROR,
String.format("\"%s\" is not a valid Java identifier", className),
- element, mirror, classNameValue);
- return Optional.absent();
+ element,
+ mirror,
+ classNameValue);
+ return Optional.empty();
}
AnnotationValue extendingValue = checkNotNull(values.get("extending"));
TypeElement extendingType = AnnotationValues.asType(extendingValue);
if (extendingType == null) {
- messager.printMessage(ERROR, "Unable to find the type: "
- + extendingValue.getValue().toString(),
- element, mirror, extendingValue);
- return Optional.absent();
+ messager.printMessage(
+ ERROR,
+ "Unable to find the type: " + extendingValue.getValue(),
+ element,
+ mirror,
+ extendingValue);
+ return Optional.empty();
} else if (!isValidSupertypeForClass(extendingType)) {
- messager.printMessage(ERROR,
- String.format("%s is not a valid supertype for a factory. "
- + "Supertypes must be non-final classes.",
- extendingType.getQualifiedName()),
- element, mirror, extendingValue);
- return Optional.absent();
+ messager.printMessage(
+ ERROR,
+ String.format(
+ "%s is not a valid supertype for a factory. "
+ + "Supertypes must be non-final classes.",
+ extendingType.getQualifiedName()),
+ element,
+ mirror,
+ extendingValue);
+ return Optional.empty();
}
ImmutableList<ExecutableElement> noParameterConstructors =
FluentIterable.from(ElementFilter.constructorsIn(extendingType.getEnclosedElements()))
- .filter(new Predicate<ExecutableElement>() {
- @Override public boolean apply(ExecutableElement constructor) {
- return constructor.getParameters().isEmpty();
- }
- })
+ .filter(
+ new Predicate<ExecutableElement>() {
+ @Override
+ public boolean apply(ExecutableElement constructor) {
+ return constructor.getParameters().isEmpty();
+ }
+ })
.toList();
- if (noParameterConstructors.size() == 0) {
- messager.printMessage(ERROR,
- String.format("%s is not a valid supertype for a factory. "
- + "Factory supertypes must have a no-arg constructor.",
- extendingType.getQualifiedName()),
- element, mirror, extendingValue);
- return Optional.absent();
+ if (noParameterConstructors.isEmpty()) {
+ messager.printMessage(
+ ERROR,
+ String.format(
+ "%s is not a valid supertype for a factory. "
+ + "Factory supertypes must have a no-arg constructor.",
+ extendingType.getQualifiedName()),
+ element,
+ mirror,
+ extendingValue);
+ return Optional.empty();
} else if (noParameterConstructors.size() > 1) {
throw new IllegalStateException("Multiple constructors with no parameters??");
}
@@ -161,7 +187,7 @@ abstract class AutoFactoryDeclaration {
new AutoValue_AutoFactoryDeclaration(
getAnnotatedType(element),
element,
- className.isEmpty() ? Optional.<String>absent() : Optional.of(className),
+ className.isEmpty() ? Optional.empty() : Optional.of(className),
extendingType,
implementingTypes,
allowSubclasses,
diff --git a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
index 5cc1d94d..82349f21 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/AutoFactoryProcessor.java
@@ -19,7 +19,6 @@ import com.google.auto.common.MoreTypes;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
import com.google.auto.service.AutoService;
-import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
@@ -30,7 +29,9 @@ import com.google.common.collect.Iterables;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Messager;
@@ -83,8 +84,9 @@ public final class AutoFactoryProcessor extends AbstractProcessor {
try {
doProcess(roundEnv);
} catch (Throwable e) {
- messager.printMessage(Kind.ERROR, "Failed to process @AutoFactory annotations:\n"
- + Throwables.getStackTraceAsString(e));
+ messager.printMessage(
+ Kind.ERROR,
+ "Failed to process @AutoFactory annotations:\n" + Throwables.getStackTraceAsString(e));
}
return false;
}
@@ -127,49 +129,58 @@ public final class AutoFactoryProcessor extends AbstractProcessor {
simpleNamesToNames(indexedMethods.keySet());
FactoryWriter factoryWriter = new FactoryWriter(processingEnv, factoriesBeingCreated);
- indexedMethods.asMap().forEach(
- (factoryName, methodDescriptors) -> {
- // The sets of classes that are mentioned in the `extending` and `implementing` elements,
- // respectively, of the @AutoFactory annotations for this factory.
- ImmutableSet.Builder<TypeMirror> extending = newTypeSetBuilder();
- ImmutableSortedSet.Builder<TypeMirror> implementing = newTypeSetBuilder();
- boolean publicType = false;
- Boolean allowSubclasses = null;
- boolean skipCreation = false;
- for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) {
- extending.add(methodDescriptor.declaration().extendingType().asType());
- for (TypeElement implementingType :
- methodDescriptor.declaration().implementingTypes()) {
- implementing.add(implementingType.asType());
- }
- publicType |= methodDescriptor.publicMethod();
- if (allowSubclasses == null) {
- allowSubclasses = methodDescriptor.declaration().allowSubclasses();
- } else if (!allowSubclasses.equals(methodDescriptor.declaration().allowSubclasses())) {
- skipCreation = true;
- messager.printMessage(Kind.ERROR,
- "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.",
- methodDescriptor.declaration().target(),
- methodDescriptor.declaration().mirror(),
- methodDescriptor.declaration().valuesMap().get("allowSubclasses"));
- }
- }
- if (!skipCreation) {
- try {
- factoryWriter.writeFactory(
- FactoryDescriptor.create(
- factoryName,
- Iterables.getOnlyElement(extending.build()),
- implementing.build(),
- publicType,
- ImmutableSet.copyOf(methodDescriptors),
- implementationMethodDescriptors.get(factoryName),
- allowSubclasses));
- } catch (IOException e) {
- messager.printMessage(Kind.ERROR, "failed: " + e);
- }
- }
- });
+ indexedMethods
+ .asMap()
+ .forEach(
+ (factoryName, methodDescriptors) -> {
+ if (methodDescriptors.isEmpty()) {
+ // This shouldn't happen, but check anyway to avoid an exception for
+ // methodDescriptors.iterator().next() below.
+ return;
+ }
+ // The sets of classes that are mentioned in the `extending` and `implementing`
+ // elements, respectively, of the @AutoFactory annotations for this factory.
+ ImmutableSet.Builder<TypeMirror> extending = newTypeSetBuilder();
+ ImmutableSortedSet.Builder<TypeMirror> implementing = newTypeSetBuilder();
+ boolean publicType = false;
+ Set<Boolean> allowSubclassesSet = new HashSet<>();
+ boolean skipCreation = false;
+ for (FactoryMethodDescriptor methodDescriptor : methodDescriptors) {
+ extending.add(methodDescriptor.declaration().extendingType().asType());
+ for (TypeElement implementingType :
+ methodDescriptor.declaration().implementingTypes()) {
+ implementing.add(implementingType.asType());
+ }
+ publicType |= methodDescriptor.publicMethod();
+ allowSubclassesSet.add(methodDescriptor.declaration().allowSubclasses());
+ if (allowSubclassesSet.size() > 1) {
+ skipCreation = true;
+ messager.printMessage(
+ Kind.ERROR,
+ "Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.",
+ methodDescriptor.declaration().target(),
+ methodDescriptor.declaration().mirror(),
+ methodDescriptor.declaration().valuesMap().get("allowSubclasses"));
+ }
+ }
+ // The set can't be empty because we eliminated methodDescriptors.isEmpty() above.
+ boolean allowSubclasses = allowSubclassesSet.iterator().next();
+ if (!skipCreation) {
+ try {
+ factoryWriter.writeFactory(
+ FactoryDescriptor.create(
+ factoryName,
+ Iterables.getOnlyElement(extending.build()),
+ implementing.build(),
+ publicType,
+ ImmutableSet.copyOf(methodDescriptors),
+ implementationMethodDescriptors.get(factoryName),
+ allowSubclasses));
+ } catch (IOException e) {
+ messager.printMessage(Kind.ERROR, "failed: " + e);
+ }
+ }
+ });
}
private ImmutableSet<ImplementationMethodDescriptor> implementationMethods(
@@ -180,8 +191,7 @@ public final class AutoFactoryProcessor extends AbstractProcessor {
ElementFilter.methodsIn(elements.getAllMembers(supertype))) {
if (implementationMethod.getModifiers().contains(Modifier.ABSTRACT)) {
ExecutableType methodType =
- Elements2.getExecutableElementAsMemberOf(
- types, implementationMethod, supertype);
+ Elements2.getExecutableElementAsMemberOf(types, implementationMethod, supertype);
ImmutableSet<Parameter> passedParameters =
Parameter.forParameterList(
implementationMethod.getParameters(), methodType.getParameterTypes(), types);
@@ -192,6 +202,7 @@ public final class AutoFactoryProcessor extends AbstractProcessor {
.publicMethod()
.passedParameters(passedParameters)
.isVarArgs(implementationMethod.isVarArgs())
+ .exceptions(implementationMethod.getThrownTypes())
.build());
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Elements2.java b/factory/src/main/java/com/google/auto/factory/processor/Elements2.java
index 30230f7f..3663f370 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Elements2.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Elements2.java
@@ -22,10 +22,10 @@ import static javax.lang.model.element.ElementKind.PACKAGE;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.STATIC;
+import com.google.auto.common.MoreTypes;
import com.google.common.collect.ImmutableSet;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
@@ -33,7 +33,7 @@ import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
final class Elements2 {
- private Elements2() { }
+ private Elements2() {}
static ImmutableSet<ExecutableElement> getConstructors(TypeElement type) {
checkNotNull(type);
@@ -72,11 +72,11 @@ final class Elements2 {
throw new IllegalStateException(
"Expected subTypeElement.asType() to return a class/interface type.");
}
- TypeMirror subExecutableTypeMirror = types.asMemberOf(
- (DeclaredType) subTypeMirror, executableElement);
+ TypeMirror subExecutableTypeMirror =
+ types.asMemberOf(MoreTypes.asDeclared(subTypeMirror), executableElement);
if (!subExecutableTypeMirror.getKind().equals(TypeKind.EXECUTABLE)) {
throw new IllegalStateException("Expected subExecutableTypeMirror to be an executable type.");
}
- return (ExecutableType) subExecutableTypeMirror;
+ return MoreTypes.asExecutable(subExecutableTypeMirror);
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java
index 5ed2307a..019295f6 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptor.java
@@ -17,16 +17,15 @@ package com.google.auto.factory.processor;
import com.google.auto.value.AutoValue;
import com.google.common.base.CharMatcher;
-import com.google.common.base.Optional;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
-import java.util.Collection;
+import com.google.common.collect.Streams;
import java.util.HashSet;
-import java.util.Map.Entry;
+import java.util.Optional;
import java.util.Set;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeMirror;
@@ -47,20 +46,28 @@ abstract class FactoryDescriptor {
};
abstract PackageAndClass name();
+
abstract TypeMirror extendingType();
+
abstract ImmutableSet<TypeMirror> implementingTypes();
+
abstract boolean publicType();
+
abstract ImmutableSet<FactoryMethodDescriptor> methodDescriptors();
+
abstract ImmutableSet<ImplementationMethodDescriptor> implementationMethodDescriptors();
+
abstract boolean allowSubclasses();
+
abstract ImmutableMap<Key, ProviderField> providers();
final AutoFactoryDeclaration declaration() {
- return Iterables.getFirst(methodDescriptors(), null).declaration();
+ // There is always at least one method descriptor.
+ return methodDescriptors().iterator().next().declaration();
}
private static class UniqueNameSet {
- private final Set<String> uniqueNames = new HashSet<String>();
+ private final Set<String> uniqueNames = new HashSet<>();
/**
* Generates a unique name using {@code base}. If {@code base} has not yet been added, it will
@@ -92,33 +99,37 @@ abstract class FactoryDescriptor {
}
ImmutableMap.Builder<Key, ProviderField> providersBuilder = ImmutableMap.builder();
UniqueNameSet uniqueNames = new UniqueNameSet();
- for (Entry<Key, Collection<Parameter>> entry :
- parametersForProviders.build().asMap().entrySet()) {
- Key key = entry.getKey();
- switch (entry.getValue().size()) {
- case 0:
- throw new AssertionError();
- case 1:
- Parameter parameter = Iterables.getOnlyElement(entry.getValue());
- providersBuilder.put(
- key,
- ProviderField.create(
- uniqueNames.getUniqueName(parameter.name() + "Provider"),
- key,
- parameter.nullable()));
- break;
- default:
- String providerName =
- uniqueNames.getUniqueName(
- invalidIdentifierCharacters.replaceFrom(key.toString(), '_') + "Provider");
- Optional<AnnotationMirror> nullable = Optional.absent();
- for (Parameter param : entry.getValue()) {
- nullable = nullable.or(param.nullable());
- }
- providersBuilder.put(key, ProviderField.create(providerName, key, nullable));
- break;
- }
- }
+ parametersForProviders
+ .build()
+ .asMap()
+ .forEach(
+ (key, parameters) -> {
+ switch (parameters.size()) {
+ case 0:
+ throw new AssertionError();
+ case 1:
+ Parameter parameter = Iterables.getOnlyElement(parameters);
+ providersBuilder.put(
+ key,
+ ProviderField.create(
+ uniqueNames.getUniqueName(parameter.name() + "Provider"),
+ key,
+ parameter.nullable()));
+ break;
+ default:
+ String providerName =
+ uniqueNames.getUniqueName(
+ invalidIdentifierCharacters.replaceFrom(key.toString(), '_')
+ + "Provider");
+ Optional<AnnotationMirror> nullable =
+ parameters.stream()
+ .map(Parameter::nullable)
+ .flatMap(Streams::stream)
+ .findFirst();
+ providersBuilder.put(key, ProviderField.create(providerName, key, nullable));
+ break;
+ }
+ });
ImmutableBiMap<FactoryMethodDescriptor, ImplementationMethodDescriptor>
duplicateMethodDescriptors =
@@ -129,8 +140,8 @@ abstract class FactoryDescriptor {
getDeduplicatedMethodDescriptors(methodDescriptors, duplicateMethodDescriptors);
ImmutableSet<ImplementationMethodDescriptor> deduplicatedImplementationMethodDescriptors =
- ImmutableSet.copyOf(
- Sets.difference(implementationMethodDescriptors, duplicateMethodDescriptors.values()));
+ Sets.difference(implementationMethodDescriptors, duplicateMethodDescriptors.values())
+ .immutableCopy();
return new AutoValue_FactoryDescriptor(
name,
@@ -191,12 +202,12 @@ abstract class FactoryDescriptor {
duplicateMethodDescriptors.get(methodDescriptor);
FactoryMethodDescriptor newMethodDescriptor =
- (duplicateMethodDescriptor != null)
- ? methodDescriptor
- .toBuilder()
+ (duplicateMethodDescriptor != null)
+ ? methodDescriptor.toBuilder()
.overridingMethod(true)
.publicMethod(duplicateMethodDescriptor.publicMethod())
.returnType(duplicateMethodDescriptor.returnType())
+ .exceptions(duplicateMethodDescriptor.exceptions())
.build()
: methodDescriptor;
deduplicatedMethodDescriptors.add(newMethodDescriptor);
@@ -213,8 +224,7 @@ abstract class FactoryDescriptor {
* in the same order.
*/
private static boolean areDuplicateMethodDescriptors(
- FactoryMethodDescriptor factory,
- ImplementationMethodDescriptor implementation) {
+ FactoryMethodDescriptor factory, ImplementationMethodDescriptor implementation) {
if (!factory.name().equals(implementation.name())) {
return false;
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java
index d2331f42..70a21ea2 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryDescriptorGenerator.java
@@ -18,6 +18,8 @@ package com.google.auto.factory.processor;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.partitioningBy;
import static javax.lang.model.element.Modifier.ABSTRACT;
import static javax.lang.model.element.Modifier.PUBLIC;
import static javax.tools.Diagnostic.Kind.ERROR;
@@ -26,13 +28,11 @@ import com.google.auto.common.MoreElements;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
import com.google.common.base.Function;
-import com.google.common.base.Functions;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimaps;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import javax.annotation.processing.Messager;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Element;
@@ -54,9 +54,7 @@ final class FactoryDescriptorGenerator {
private final AutoFactoryDeclaration.Factory declarationFactory;
FactoryDescriptorGenerator(
- Messager messager,
- Types types,
- AutoFactoryDeclaration.Factory declarationFactory) {
+ Messager messager, Types types, AutoFactoryDeclaration.Factory declarationFactory) {
this.messager = messager;
this.types = types;
this.declarationFactory = declarationFactory;
@@ -68,70 +66,75 @@ final class FactoryDescriptorGenerator {
if (!declaration.isPresent()) {
return ImmutableSet.of();
}
- return element.accept(new ElementKindVisitor6<ImmutableSet<FactoryMethodDescriptor>, Void>() {
- @Override
- protected ImmutableSet<FactoryMethodDescriptor> defaultAction(Element e, Void p) {
- throw new AssertionError("@AutoFactory applied to an impossible element");
- }
+ return element.accept(
+ new ElementKindVisitor6<ImmutableSet<FactoryMethodDescriptor>, Void>() {
+ @Override
+ protected ImmutableSet<FactoryMethodDescriptor> defaultAction(Element e, Void p) {
+ throw new AssertionError("@AutoFactory applied to an impossible element");
+ }
- @Override
- public ImmutableSet<FactoryMethodDescriptor> visitTypeAsClass(TypeElement type, Void p) {
- if (type.getModifiers().contains(ABSTRACT)) {
- // applied to an abstract factory
- messager.printMessage(ERROR,
- "Auto-factory doesn't support being applied to abstract classes.", type, mirror);
- return ImmutableSet.of();
- } else {
- // applied to the type to be created
- ImmutableSet<ExecutableElement> constructors = Elements2.getConstructors(type);
- if (constructors.isEmpty()) {
- return generateDescriptorForDefaultConstructor(declaration.get(), type);
- } else {
- return FluentIterable.from(constructors)
- .transform(new Function<ExecutableElement, FactoryMethodDescriptor>() {
- @Override public FactoryMethodDescriptor apply(ExecutableElement constructor) {
- return generateDescriptorForConstructor(declaration.get(), constructor);
- }
- })
- .toSet();
+ @Override
+ public ImmutableSet<FactoryMethodDescriptor> visitTypeAsClass(TypeElement type, Void p) {
+ if (type.getModifiers().contains(ABSTRACT)) {
+ // applied to an abstract factory
+ messager.printMessage(
+ ERROR,
+ "Auto-factory doesn't support being applied to abstract classes.",
+ type,
+ mirror);
+ return ImmutableSet.of();
+ } else {
+ // applied to the type to be created
+ ImmutableSet<ExecutableElement> constructors = Elements2.getConstructors(type);
+ if (constructors.isEmpty()) {
+ return generateDescriptorForDefaultConstructor(declaration.get(), type);
+ } else {
+ return FluentIterable.from(constructors)
+ .transform(
+ new Function<ExecutableElement, FactoryMethodDescriptor>() {
+ @Override
+ public FactoryMethodDescriptor apply(ExecutableElement constructor) {
+ return generateDescriptorForConstructor(declaration.get(), constructor);
+ }
+ })
+ .toSet();
+ }
+ }
}
- }
- }
- @Override
- public ImmutableSet<FactoryMethodDescriptor> visitTypeAsInterface(TypeElement type, Void p) {
- // applied to the factory interface
- messager.printMessage(ERROR,
- "Auto-factory doesn't support being applied to interfaces.", type, mirror);
- return ImmutableSet.of();
- }
+ @Override
+ public ImmutableSet<FactoryMethodDescriptor> visitTypeAsInterface(
+ TypeElement type, Void p) {
+ // applied to the factory interface
+ messager.printMessage(
+ ERROR, "Auto-factory doesn't support being applied to interfaces.", type, mirror);
+ return ImmutableSet.of();
+ }
- @Override
- public ImmutableSet<FactoryMethodDescriptor> visitExecutableAsConstructor(ExecutableElement e,
- Void p) {
- // applied to a constructor of a type to be created
- return ImmutableSet.of(generateDescriptorForConstructor(declaration.get(), e));
- }
- }, null);
+ @Override
+ public ImmutableSet<FactoryMethodDescriptor> visitExecutableAsConstructor(
+ ExecutableElement e, Void p) {
+ // applied to a constructor of a type to be created
+ return ImmutableSet.of(generateDescriptorForConstructor(declaration.get(), e));
+ }
+ },
+ null);
}
- FactoryMethodDescriptor generateDescriptorForConstructor(final AutoFactoryDeclaration declaration,
- ExecutableElement constructor) {
+ FactoryMethodDescriptor generateDescriptorForConstructor(
+ final AutoFactoryDeclaration declaration, ExecutableElement constructor) {
checkNotNull(constructor);
checkArgument(constructor.getKind() == ElementKind.CONSTRUCTOR);
TypeElement classElement = MoreElements.asType(constructor.getEnclosingElement());
- ImmutableListMultimap<Boolean, ? extends VariableElement> parameterMap =
- Multimaps.index(constructor.getParameters(), Functions.forPredicate(
- new Predicate<VariableElement>() {
- @Override
- public boolean apply(VariableElement parameter) {
- return isAnnotationPresent(parameter, Provided.class);
- }
- }));
+ Map<Boolean, List<VariableElement>> parameterMap =
+ constructor.getParameters().stream()
+ .collect(partitioningBy(parameter -> isAnnotationPresent(parameter, Provided.class)));
+ // The map returned by partitioningBy always has entries for both key values but our
+ // null-checker isn't yet smart enough to know that.
ImmutableSet<Parameter> providedParameters =
- Parameter.forParameterList(parameterMap.get(true), types);
+ Parameter.forParameterList(requireNonNull(parameterMap.get(true)), types);
ImmutableSet<Parameter> passedParameters =
- Parameter.forParameterList(parameterMap.get(false), types);
+ Parameter.forParameterList(requireNonNull(parameterMap.get(false)), types);
return FactoryMethodDescriptor.builder(declaration)
.name("create")
.returnType(classElement.asType())
@@ -140,6 +143,8 @@ final class FactoryDescriptorGenerator {
.passedParameters(passedParameters)
.creationParameters(Parameter.forParameterList(constructor.getParameters(), types))
.isVarArgs(constructor.isVarArgs())
+ .exceptions(constructor.getThrownTypes())
+ .overridingMethod(false)
.build();
}
@@ -150,9 +155,12 @@ final class FactoryDescriptorGenerator {
.name("create")
.returnType(type.asType())
.publicMethod(type.getModifiers().contains(PUBLIC))
- .passedParameters(ImmutableSet.<Parameter>of())
- .creationParameters(ImmutableSet.<Parameter>of())
- .providedParameters(ImmutableSet.<Parameter>of())
+ .providedParameters(ImmutableSet.of())
+ .passedParameters(ImmutableSet.of())
+ .creationParameters(ImmutableSet.of())
+ .isVarArgs(false)
+ .exceptions(ImmutableSet.of())
+ .overridingMethod(false)
.build());
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java
index 43e5097d..45259573 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryMethodDescriptor.java
@@ -31,45 +31,65 @@ import javax.lang.model.type.TypeMirror;
@AutoValue
abstract class FactoryMethodDescriptor {
abstract AutoFactoryDeclaration declaration();
+
abstract String name();
+
abstract TypeMirror returnType();
+
abstract boolean publicMethod();
+
abstract boolean overridingMethod();
+
abstract ImmutableSet<Parameter> passedParameters();
+
abstract ImmutableSet<Parameter> providedParameters();
+
abstract ImmutableSet<Parameter> creationParameters();
- abstract Builder toBuilder();
+
abstract boolean isVarArgs();
+ abstract ImmutableSet<TypeMirror> exceptions();
+
+ abstract Builder toBuilder();
+
final PackageAndClass factoryName() {
return declaration().getFactoryName();
}
static Builder builder(AutoFactoryDeclaration declaration) {
- return new AutoValue_FactoryMethodDescriptor.Builder()
- .declaration(checkNotNull(declaration))
- .publicMethod(false)
- .overridingMethod(false)
- .isVarArgs(false);
+ return new AutoValue_FactoryMethodDescriptor.Builder().declaration(checkNotNull(declaration));
}
@AutoValue.Builder
abstract static class Builder {
abstract Builder declaration(AutoFactoryDeclaration declaration);
+
abstract Builder name(String name);
+
abstract Builder returnType(TypeMirror returnType);
+
abstract Builder publicMethod(boolean publicMethod);
+
abstract Builder overridingMethod(boolean overridingMethod);
+
abstract Builder passedParameters(Iterable<Parameter> passedParameters);
+
abstract Builder providedParameters(Iterable<Parameter> providedParameters);
+
abstract Builder creationParameters(Iterable<Parameter> creationParameters);
+
abstract Builder isVarArgs(boolean isVarargs);
+
+ abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);
+
abstract FactoryMethodDescriptor buildImpl();
FactoryMethodDescriptor build() {
FactoryMethodDescriptor descriptor = buildImpl();
- checkState(descriptor.creationParameters().equals(
- Sets.union(descriptor.passedParameters(), descriptor.providedParameters())));
+ checkState(
+ descriptor
+ .creationParameters()
+ .equals(Sets.union(descriptor.passedParameters(), descriptor.providedParameters())));
return descriptor;
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
index 53b99cb5..b7f9c3e4 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/FactoryWriter.java
@@ -19,19 +19,22 @@ import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotatio
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static java.util.Objects.requireNonNull;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
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 static javax.lang.model.element.Modifier.STATIC;
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.collect.FluentIterable;
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.AnnotationValues;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
@@ -43,13 +46,19 @@ import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.io.IOException;
+import java.lang.annotation.Target;
import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
@@ -71,14 +80,10 @@ final class FactoryWriter {
this.factoriesBeingCreated = factoriesBeingCreated;
}
- private static final Joiner ARGUMENT_JOINER = Joiner.on(", ");
-
- void writeFactory(FactoryDescriptor descriptor)
- throws IOException {
+ void writeFactory(FactoryDescriptor descriptor) throws IOException {
String factoryName = descriptor.name().className();
TypeSpec.Builder factory =
- classBuilder(factoryName)
- .addOriginatingElement(descriptor.declaration().targetType());
+ classBuilder(factoryName).addOriginatingElement(descriptor.declaration().targetType());
generatedAnnotationSpec(
elements,
sourceVersion,
@@ -145,7 +150,7 @@ final class FactoryWriter {
ImmutableSet<TypeVariableName> factoryTypeVariables) {
for (FactoryMethodDescriptor methodDescriptor : descriptor.methodDescriptors()) {
MethodSpec.Builder method =
- MethodSpec.methodBuilder(methodDescriptor.name())
+ methodBuilder(methodDescriptor.name())
.addTypeVariables(getMethodTypeVariables(methodDescriptor, factoryTypeVariables))
.returns(TypeName.get(methodDescriptor.returnType()))
.varargs(methodDescriptor.isVarArgs());
@@ -155,6 +160,8 @@ final class FactoryWriter {
if (methodDescriptor.publicMethod()) {
method.addModifiers(PUBLIC);
}
+ method.addExceptions(
+ methodDescriptor.exceptions().stream().map(TypeName::get).collect(toList()));
CodeBlock.Builder args = CodeBlock.builder();
method.addParameters(parameters(methodDescriptor.passedParameters()));
Iterator<Parameter> parameters = methodDescriptor.creationParameters().iterator();
@@ -168,7 +175,7 @@ final class FactoryWriter {
checkNotNull = false;
}
} else {
- ProviderField provider = descriptor.providers().get(parameter.key());
+ ProviderField provider = requireNonNull(descriptor.providers().get(parameter.key()));
argument = CodeBlock.of(provider.name());
if (parameter.isProvider()) {
// Providers are checked for nullness in the Factory's constructor.
@@ -190,8 +197,7 @@ final class FactoryWriter {
}
}
- private void addImplementationMethods(
- TypeSpec.Builder factory, FactoryDescriptor descriptor) {
+ private void addImplementationMethods(TypeSpec.Builder factory, FactoryDescriptor descriptor) {
for (ImplementationMethodDescriptor methodDescriptor :
descriptor.implementationMethodDescriptors()) {
MethodSpec.Builder implementationMethod =
@@ -202,18 +208,12 @@ final class FactoryWriter {
if (methodDescriptor.publicMethod()) {
implementationMethod.addModifiers(PUBLIC);
}
+ implementationMethod.addExceptions(
+ methodDescriptor.exceptions().stream().map(TypeName::get).collect(toList()));
implementationMethod.addParameters(parameters(methodDescriptor.passedParameters()));
implementationMethod.addStatement(
"return create($L)",
- FluentIterable.from(methodDescriptor.passedParameters())
- .transform(
- new Function<Parameter, String>() {
- @Override
- public String apply(Parameter parameter) {
- return parameter.name();
- }
- })
- .join(ARGUMENT_JOINER));
+ methodDescriptor.passedParameters().stream().map(Parameter::name).collect(joining(", ")));
factory.addMethod(implementationMethod.build());
}
}
@@ -225,17 +225,43 @@ final class FactoryWriter {
private ImmutableList<ParameterSpec> parameters(Iterable<Parameter> parameters) {
ImmutableList.Builder<ParameterSpec> builder = ImmutableList.builder();
for (Parameter parameter : parameters) {
- ParameterSpec.Builder parameterBuilder =
- ParameterSpec.builder(resolveTypeName(parameter.type().get()), parameter.name());
- for (AnnotationMirror annotation :
- Iterables.concat(parameter.nullable().asSet(), parameter.key().qualifier().asSet())) {
- parameterBuilder.addAnnotation(AnnotationSpec.get(annotation));
- }
- builder.add(parameterBuilder.build());
+ TypeName type = resolveTypeName(parameter.type().get());
+ // Remove TYPE_USE annotations, since resolveTypeName will already have included those in
+ // the TypeName it returns.
+ List<AnnotationSpec> annotations =
+ Stream.of(parameter.nullable(), parameter.key().qualifier())
+ .flatMap(Streams::stream)
+ .filter(a -> !isTypeUseAnnotation(a))
+ .map(AnnotationSpec::get)
+ .collect(toList());
+ ParameterSpec parameterSpec =
+ ParameterSpec.builder(type, parameter.name()).addAnnotations(annotations).build();
+ builder.add(parameterSpec);
}
return builder.build();
}
+ private static boolean isTypeUseAnnotation(AnnotationMirror mirror) {
+ Element annotationElement = mirror.getAnnotationType().asElement();
+ // This is basically equivalent to:
+ // Target target = annotationElement.getAnnotation(Target.class);
+ // return target != null
+ // && Arrays.asList(annotationElement.getAnnotation(Target.class)).contains(TYPE_USE);
+ // but that might blow up if the annotation is being compiled at the same time and has an
+ // undefined identifier in its @Target values. The rigmarole below avoids that problem.
+ Optional<AnnotationMirror> maybeTargetMirror =
+ Mirrors.getAnnotationMirror(annotationElement, Target.class);
+ return maybeTargetMirror
+ .map(
+ targetMirror ->
+ AnnotationValues.getEnums(
+ AnnotationMirrors.getAnnotationValue(targetMirror, "value"))
+ .stream()
+ .map(VariableElement::getSimpleName)
+ .anyMatch(name -> name.contentEquals("TYPE_USE")))
+ .orElse(false);
+ }
+
private static void addCheckNotNullMethod(
TypeSpec.Builder factory, FactoryDescriptor descriptor) {
if (shouldGenerateCheckNotNull(descriptor)) {
@@ -290,17 +316,20 @@ final class FactoryWriter {
* {@code @AutoFactory class Foo} has a constructor parameter of type {@code BarFactory} and
* {@code @AutoFactory class Bar} has a constructor parameter of type {@code FooFactory}. We did
* in fact find instances of this in Google's source base.
+ *
+ * <p>If the type has type annotations then include those in the returned {@link TypeName}.
*/
private TypeName resolveTypeName(TypeMirror type) {
- if (type.getKind() != TypeKind.ERROR) {
- return TypeName.get(type);
- }
- ImmutableSet<PackageAndClass> factoryNames = factoriesBeingCreated.get(type.toString());
- if (factoryNames.size() == 1) {
- PackageAndClass packageAndClass = Iterables.getOnlyElement(factoryNames);
- return ClassName.get(packageAndClass.packageName(), packageAndClass.className());
+ TypeName typeName = TypeName.get(type);
+ if (type.getKind() == TypeKind.ERROR) {
+ ImmutableSet<PackageAndClass> factoryNames = factoriesBeingCreated.get(type.toString());
+ if (factoryNames.size() == 1) {
+ PackageAndClass packageAndClass = Iterables.getOnlyElement(factoryNames);
+ typeName = ClassName.get(packageAndClass.packageName(), packageAndClass.className());
+ }
}
- return TypeName.get(type);
+ return typeName.annotated(
+ type.getAnnotationMirrors().stream().map(AnnotationSpec::get).collect(toList()));
}
private static ImmutableSet<TypeVariableName> getFactoryTypeVariables(
diff --git a/factory/src/main/java/com/google/auto/factory/processor/ImplementationMethodDescriptor.java b/factory/src/main/java/com/google/auto/factory/processor/ImplementationMethodDescriptor.java
index 9ddc249f..b2705b7e 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/ImplementationMethodDescriptor.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/ImplementationMethodDescriptor.java
@@ -22,21 +22,25 @@ import javax.lang.model.type.TypeMirror;
@AutoValue
abstract class ImplementationMethodDescriptor {
abstract String name();
+
abstract TypeMirror returnType();
+
abstract boolean publicMethod();
+
abstract ImmutableSet<Parameter> passedParameters();
+
abstract boolean isVarArgs();
+ abstract ImmutableSet<TypeMirror> exceptions();
+
static Builder builder() {
- return new AutoValue_ImplementationMethodDescriptor.Builder()
- .publicMethod(true)
- .isVarArgs(false);
+ return new AutoValue_ImplementationMethodDescriptor.Builder();
}
@AutoValue.Builder
- static abstract class Builder {
+ abstract static class Builder {
abstract Builder name(String name);
-
+
abstract Builder returnType(TypeMirror returnTypeElement);
abstract Builder publicMethod(boolean publicMethod);
@@ -49,6 +53,8 @@ abstract class ImplementationMethodDescriptor {
abstract Builder isVarArgs(boolean isVarargs);
+ abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);
+
abstract ImplementationMethodDescriptor build();
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Key.java b/factory/src/main/java/com/google/auto/factory/processor/Key.java
index 04bd4f37..728149eb 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Key.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Key.java
@@ -24,9 +24,8 @@ import com.google.auto.common.AnnotationMirrors;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.base.Equivalence;
-import com.google.common.base.Optional;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Optional;
import javax.inject.Qualifier;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.type.TypeMirror;
@@ -65,17 +64,15 @@ abstract class Key {
* <tr><td>{@code int} <td>{@code Integer}
* </table>
*/
- static Key create(
- TypeMirror type, Iterable<? extends AnnotationMirror> annotations, Types types) {
- ImmutableSet.Builder<AnnotationMirror> qualifiers = ImmutableSet.builder();
- for (AnnotationMirror annotation : annotations) {
- if (isAnnotationPresent(annotation.getAnnotationType().asElement(), Qualifier.class)) {
- qualifiers.add(annotation);
- }
- }
-
+ static Key create(TypeMirror type, Collection<AnnotationMirror> annotations, Types types) {
// TODO(gak): check for only one qualifier rather than using the first
- Optional<AnnotationMirror> qualifier = FluentIterable.from(qualifiers.build()).first();
+ Optional<AnnotationMirror> qualifier =
+ annotations.stream()
+ .filter(
+ annotation ->
+ isAnnotationPresent(
+ annotation.getAnnotationType().asElement(), Qualifier.class))
+ .findFirst();
TypeMirror keyType =
isProvider(type)
@@ -97,7 +94,7 @@ abstract class Key {
}
@Override
- public String toString() {
+ public final String toString() {
String typeQualifiedName = MoreTypes.asTypeElement(type().get()).toString();
return qualifier().isPresent()
? qualifier().get() + "/" + typeQualifiedName
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java b/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java
index c5b7d8bf..313fc9ee 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Mirrors.java
@@ -17,11 +17,11 @@ package com.google.auto.factory.processor;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Equivalence;
-import com.google.common.base.Optional;
import com.google.common.collect.ImmutableMap;
import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Optional;
import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
@@ -34,20 +34,23 @@ import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.SimpleElementVisitor6;
final class Mirrors {
- private Mirrors() { }
+ private Mirrors() {}
static Name getQualifiedName(DeclaredType type) {
- return type.asElement().accept(new SimpleElementVisitor6<Name, Void>() {
- @Override
- protected Name defaultAction(Element e, Void p) {
- throw new AssertionError("DeclaredTypes should be TypeElements");
- }
+ return type.asElement()
+ .accept(
+ new SimpleElementVisitor6<Name, Void>() {
+ @Override
+ protected Name defaultAction(Element e, Void p) {
+ throw new AssertionError("DeclaredTypes should be TypeElements");
+ }
- @Override
- public Name visitType(TypeElement e, Void p) {
- return e.getQualifiedName();
- }
- }, null);
+ @Override
+ public Name visitType(TypeElement e, Void p) {
+ return e.getQualifiedName();
+ }
+ },
+ null);
}
/** {@code true} if {@code type} is a {@link Provider}. */
@@ -62,8 +65,8 @@ final class Mirrors {
static ImmutableMap<String, AnnotationValue> simplifyAnnotationValueMap(
Map<? extends ExecutableElement, ? extends AnnotationValue> annotationValueMap) {
ImmutableMap.Builder<String, AnnotationValue> builder = ImmutableMap.builder();
- for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry
- : annotationValueMap.entrySet()) {
+ for (Entry<? extends ExecutableElement, ? extends AnnotationValue> entry :
+ annotationValueMap.entrySet()) {
builder.put(entry.getKey().getSimpleName().toString(), entry.getValue());
}
return builder.build();
@@ -73,15 +76,13 @@ final class Mirrors {
* Get the {@link AnnotationMirror} for the type {@code annotationType} present on the given
* {@link Element} if it exists.
*/
- static Optional<AnnotationMirror> getAnnotationMirror(Element element,
- Class<? extends Annotation> annotationType) {
+ static Optional<AnnotationMirror> getAnnotationMirror(
+ Element element, Class<? extends Annotation> annotationType) {
String annotationName = annotationType.getName();
- for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
- if (getQualifiedName(annotationMirror.getAnnotationType()).contentEquals(annotationName)) {
- return Optional.of(annotationMirror);
- }
- }
- return Optional.absent();
+ return element.getAnnotationMirrors().stream()
+ .filter(a -> getQualifiedName(a.getAnnotationType()).contentEquals(annotationName))
+ .<AnnotationMirror>map(x -> x) // get rid of wildcard <? extends AnnotationMirror>
+ .findFirst();
}
/**
@@ -91,9 +92,7 @@ final class Mirrors {
// TODO(ronshapiro): this is used in AutoFactory and Dagger, consider moving it into auto-common.
static <T> Optional<Equivalence.Wrapper<T>> wrapOptionalInEquivalence(
Equivalence<T> equivalence, Optional<T> optional) {
- return optional.isPresent()
- ? Optional.of(equivalence.wrap(optional.get()))
- : Optional.<Equivalence.Wrapper<T>>absent();
+ return optional.map(equivalence::wrap);
}
/**
@@ -102,8 +101,6 @@ final class Mirrors {
*/
static <T> Optional<T> unwrapOptionalEquivalence(
Optional<Equivalence.Wrapper<T>> wrappedOptional) {
- return wrappedOptional.isPresent()
- ? Optional.of(wrappedOptional.get().get())
- : Optional.<T>absent();
+ return wrappedOptional.map(Equivalence.Wrapper::get);
}
}
diff --git a/factory/src/main/java/com/google/auto/factory/processor/Parameter.java b/factory/src/main/java/com/google/auto/factory/processor/Parameter.java
index 781225a2..c5059ece 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/Parameter.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/Parameter.java
@@ -18,19 +18,20 @@ package com.google.auto.factory.processor;
import static com.google.auto.factory.processor.Mirrors.unwrapOptionalEquivalence;
import static com.google.auto.factory.processor.Mirrors.wrapOptionalInEquivalence;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.stream.Collectors.toList;
import com.google.auto.common.AnnotationMirrors;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.AutoValue;
import com.google.common.base.Equivalence;
-import com.google.common.base.Optional;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.List;
+import java.util.Optional;
import java.util.Set;
+import java.util.stream.Stream;
import javax.inject.Provider;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
@@ -63,6 +64,7 @@ abstract class Parameter {
abstract String name();
abstract Key key();
+
abstract Optional<Equivalence.Wrapper<AnnotationMirror>> nullableWrapper();
Optional<AnnotationMirror> nullable() {
@@ -71,15 +73,12 @@ abstract class Parameter {
private static Parameter forVariableElement(
VariableElement variable, TypeMirror type, Types types) {
- Optional<AnnotationMirror> nullable = Optional.absent();
- Iterable<? extends AnnotationMirror> annotations =
- Iterables.concat(variable.getAnnotationMirrors(), type.getAnnotationMirrors());
- for (AnnotationMirror annotation : annotations) {
- if (isNullable(annotation)) {
- nullable = Optional.of(annotation);
- break;
- }
- }
+ List<AnnotationMirror> annotations =
+ Stream.of(variable.getAnnotationMirrors(), type.getAnnotationMirrors())
+ .flatMap(List::stream)
+ .collect(toList());
+ Optional<AnnotationMirror> nullable =
+ annotations.stream().filter(Parameter::isNullable).findFirst();
Key key = Key.create(type, annotations, types);
return new AutoValue_Parameter(
@@ -108,7 +107,7 @@ abstract class Parameter {
Set<String> names = Sets.newHashSetWithExpectedSize(variables.size());
for (int i = 0; i < variables.size(); i++) {
Parameter parameter = forVariableElement(variables.get(i), variableTypes.get(i), types);
- checkArgument(names.add(parameter.name()));
+ checkArgument(names.add(parameter.name()), "Duplicate parameter name: %s", parameter.name());
builder.add(parameter);
}
ImmutableSet<Parameter> parameters = builder.build();
diff --git a/factory/src/main/java/com/google/auto/factory/processor/ProvidedChecker.java b/factory/src/main/java/com/google/auto/factory/processor/ProvidedChecker.java
index fe4c1fd2..bd88f837 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/ProvidedChecker.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/ProvidedChecker.java
@@ -26,6 +26,7 @@ import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementKindVisitor6;
+import org.checkerframework.checker.nullness.qual.Nullable;
final class ProvidedChecker {
private final Messager messager;
@@ -35,41 +36,54 @@ final class ProvidedChecker {
}
void checkProvidedParameter(Element element) {
- checkArgument(isAnnotationPresent(element, Provided.class), "%s not annoated with @Provided",
- element);
- element.accept(new ElementKindVisitor6<Void, Void>() {
- @Override
- protected Void defaultAction(Element e, Void p) {
- throw new AssertionError("Provided can only be applied to parameters");
- }
-
- @Override
- public Void visitVariableAsParameter(final VariableElement providedParameter, Void p) {
- providedParameter.getEnclosingElement().accept(new ElementKindVisitor6<Void, Void>() {
+ checkArgument(
+ isAnnotationPresent(element, Provided.class), "%s not annoated with @Provided", element);
+ element.accept(
+ new ElementKindVisitor6<@Nullable Void, @Nullable Void>() {
@Override
- protected Void defaultAction(Element e, Void p) {
- raiseError(providedParameter, "@%s may only be applied to constructor parameters");
- return null;
+ protected @Nullable Void defaultAction(Element e, @Nullable Void p) {
+ throw new AssertionError("Provided can only be applied to parameters");
}
@Override
- public Void visitExecutableAsConstructor(ExecutableElement constructor, Void p) {
- if (!(annotatedWithAutoFactory(constructor)
- || annotatedWithAutoFactory(constructor.getEnclosingElement()))) {
- raiseError(providedParameter,
- "@%s may only be applied to constructors requesting an auto-factory");
- }
+ public @Nullable Void visitVariableAsParameter(
+ VariableElement providedParameter, @Nullable Void p) {
+ providedParameter
+ .getEnclosingElement()
+ .accept(
+ new ElementKindVisitor6<@Nullable Void, @Nullable Void>() {
+ @Override
+ protected @Nullable Void defaultAction(Element e, @Nullable Void p) {
+ raiseError(
+ providedParameter, "@%s may only be applied to constructor parameters");
+ return null;
+ }
+
+ @Override
+ public @Nullable Void visitExecutableAsConstructor(
+ ExecutableElement constructor, @Nullable Void p) {
+ if (!(annotatedWithAutoFactory(constructor)
+ || annotatedWithAutoFactory(constructor.getEnclosingElement()))) {
+ raiseError(
+ providedParameter,
+ "@%s may only be applied to constructors requesting an auto-factory");
+ }
+ return null;
+ }
+ },
+ p);
return null;
}
- }, p);
- return null;
- }
- }, null);
+ },
+ null);
}
private void raiseError(VariableElement providedParameter, String messageFormat) {
- messager.printMessage(ERROR, String.format(messageFormat, Provided.class.getSimpleName()),
- providedParameter, Mirrors.getAnnotationMirror(providedParameter, Provided.class).get());
+ messager.printMessage(
+ ERROR,
+ String.format(messageFormat, Provided.class.getSimpleName()),
+ providedParameter,
+ Mirrors.getAnnotationMirror(providedParameter, Provided.class).get());
}
private static boolean annotatedWithAutoFactory(Element e) {
diff --git a/factory/src/main/java/com/google/auto/factory/processor/ProviderField.java b/factory/src/main/java/com/google/auto/factory/processor/ProviderField.java
index 855c1feb..99847ec0 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/ProviderField.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/ProviderField.java
@@ -21,13 +21,15 @@ import static com.google.auto.factory.processor.Mirrors.wrapOptionalInEquivalenc
import com.google.auto.common.AnnotationMirrors;
import com.google.auto.value.AutoValue;
import com.google.common.base.Equivalence;
-import com.google.common.base.Optional;
+import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
@AutoValue
abstract class ProviderField {
abstract String name();
+
abstract Key key();
+
abstract Optional<Equivalence.Wrapper<AnnotationMirror>> nullableWrapper();
Optional<AnnotationMirror> nullable() {
diff --git a/factory/src/main/java/com/google/auto/factory/processor/TypeVariables.java b/factory/src/main/java/com/google/auto/factory/processor/TypeVariables.java
index 1f894732..4bd546e8 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/TypeVariables.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/TypeVariables.java
@@ -38,8 +38,8 @@ final class TypeVariables {
return type.accept(ReferencedTypeVariables.INSTANCE, new HashSet<>());
}
- private static final class ReferencedTypeVariables extends
- SimpleTypeVisitor8<ImmutableSet<TypeVariable>, Set<Element>> {
+ private static final class ReferencedTypeVariables
+ extends SimpleTypeVisitor8<ImmutableSet<TypeVariable>, Set<Element>> {
private static final ReferencedTypeVariables INSTANCE = new ReferencedTypeVariables();
@@ -53,8 +53,7 @@ final class TypeVariables {
}
@Override
- public ImmutableSet<TypeVariable> visitDeclared(
- DeclaredType t, Set<Element> visited) {
+ public ImmutableSet<TypeVariable> visitDeclared(DeclaredType t, Set<Element> visited) {
if (!visited.add(t.asElement())) {
return ImmutableSet.of();
}
@@ -66,8 +65,7 @@ final class TypeVariables {
}
@Override
- public ImmutableSet<TypeVariable> visitTypeVariable(
- TypeVariable t, Set<Element> visited) {
+ public ImmutableSet<TypeVariable> visitTypeVariable(TypeVariable t, Set<Element> visited) {
if (!visited.add(t.asElement())) {
return ImmutableSet.of();
}
@@ -79,8 +77,7 @@ final class TypeVariables {
}
@Override
- public ImmutableSet<TypeVariable> visitUnion(
- UnionType t, Set<Element> visited) {
+ public ImmutableSet<TypeVariable> visitUnion(UnionType t, Set<Element> visited) {
ImmutableSet.Builder<TypeVariable> typeVariables = ImmutableSet.builder();
for (TypeMirror unionType : t.getAlternatives()) {
typeVariables.addAll(unionType.accept(this, visited));
@@ -89,8 +86,7 @@ final class TypeVariables {
}
@Override
- public ImmutableSet<TypeVariable> visitIntersection(
- IntersectionType t, Set<Element> visited) {
+ public ImmutableSet<TypeVariable> visitIntersection(IntersectionType t, Set<Element> visited) {
ImmutableSet.Builder<TypeVariable> typeVariables = ImmutableSet.builder();
for (TypeMirror intersectionType : t.getBounds()) {
typeVariables.addAll(intersectionType.accept(this, visited));
@@ -99,8 +95,7 @@ final class TypeVariables {
}
@Override
- public ImmutableSet<TypeVariable> visitWildcard(
- WildcardType t, Set<Element> visited) {
+ public ImmutableSet<TypeVariable> visitWildcard(WildcardType t, Set<Element> visited) {
ImmutableSet.Builder<TypeVariable> typeVariables = ImmutableSet.builder();
TypeMirror extendsBound = t.getExtendsBound();
if (extendsBound != null) {
diff --git a/factory/src/main/java/com/google/auto/factory/processor/package-info.java b/factory/src/main/java/com/google/auto/factory/processor/package-info.java
index 7339020a..4a7c4b10 100644
--- a/factory/src/main/java/com/google/auto/factory/processor/package-info.java
+++ b/factory/src/main/java/com/google/auto/factory/processor/package-info.java
@@ -15,4 +15,5 @@
* This package contains the annotation processor that implements the
* {@link com.google.auto.factory.AutoFactory} API.
*/
-package com.google.auto.factory.processor; \ No newline at end of file
+package com.google.auto.factory.processor;
+
diff --git a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryDeclarationTest.java b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryDeclarationTest.java
index 1b23a65d..7bef2334 100644
--- a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryDeclarationTest.java
+++ b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryDeclarationTest.java
@@ -24,7 +24,8 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class AutoFactoryDeclarationTest {
- @Test public void identifiers() {
+ @Test
+ public void identifiers() {
assertThat(isValidIdentifier("String")).isTrue();
assertThat(isValidIdentifier("9CantStartWithNumber")).isFalse();
assertThat(isValidIdentifier("enum")).isFalse();
diff --git a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
index 3088bb2e..0df4c9ca 100644
--- a/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
+++ b/factory/src/test/java/com/google/auto/factory/processor/AutoFactoryProcessorTest.java
@@ -15,15 +15,16 @@
*/
package com.google.auto.factory.processor;
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
-import static com.google.testing.compile.JavaSourcesSubject.assertThat;
-import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
+import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
+import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
import static java.nio.charset.StandardCharsets.UTF_8;
-import com.google.common.collect.ImmutableSet;
import com.google.common.io.Resources;
-import com.google.testing.compile.CompilationRule;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;
import java.io.IOException;
import java.io.UncheckedIOException;
@@ -31,415 +32,489 @@ import java.util.Collections;
import java.util.List;
import javax.lang.model.SourceVersion;
import javax.tools.JavaFileObject;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Functional tests for the {@link AutoFactoryProcessor}.
- */
+/** Functional tests for the {@link AutoFactoryProcessor}. */
@RunWith(JUnit4.class)
public class AutoFactoryProcessorTest {
+ private final Compiler javac = Compiler.javac().withProcessors(new AutoFactoryProcessor());
- @Rule public final CompilationRule compilationRule = new CompilationRule();
+ @Test
+ public void simpleClass() {
+ Compilation compilation = javac.compile(JavaFileObjects.forResource("good/SimpleClass.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassFactory.java"));
+ }
- @Test public void simpleClass() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/SimpleClass.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassFactory.java"));
+ @Test
+ public void simpleClassWithConstructorThrowsClause() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/SimpleClassThrows.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassThrowsFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassThrowsFactory.java"));
}
@Test
public void nestedClasses() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/NestedClasses.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(
- loadExpectedFile("expected/NestedClasses_SimpleNestedClassFactory.java"),
- loadExpectedFile("expected/NestedClassCustomNamedFactory.java"));
- }
-
- @Test public void simpleClassNonFinal() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/SimpleClassNonFinal.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassNonFinalFactory.java"));
- }
-
- @Test public void publicClass() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/PublicClass.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/PublicClassFactory.java"));
- }
-
- @Test public void simpleClassCustomName() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/SimpleClassCustomName.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/CustomNamedFactory.java"));
- }
-
- @Test public void simpleClassMixedDeps() {
- assertAbout(javaSources())
- .that(
- ImmutableSet.of(
- JavaFileObjects.forResource("good/SimpleClassMixedDeps.java"),
- JavaFileObjects.forResource("support/AQualifier.java")))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassMixedDepsFactory.java"));
- }
-
- @Test public void simpleClassPassedDeps() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/SimpleClassPassedDeps.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassPassedDepsFactory.java"));
- }
-
- @Test public void simpleClassProvidedDeps() {
- assertAbout(javaSources())
- .that(
- ImmutableSet.of(
- JavaFileObjects.forResource("support/AQualifier.java"),
- JavaFileObjects.forResource("support/BQualifier.java"),
- JavaFileObjects.forResource("good/SimpleClassProvidedDeps.java")))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassProvidedDepsFactory.java"));
+ Compilation compilation = javac.compile(JavaFileObjects.forResource("good/NestedClasses.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.NestedClasses_SimpleNestedClassFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/NestedClasses_SimpleNestedClassFactory.java"));
+ assertThat(compilation)
+ .generatedSourceFile("tests.NestedClassCustomNamedFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/NestedClassCustomNamedFactory.java"));
+ }
+
+ @Test
+ public void simpleClassNonFinal() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/SimpleClassNonFinal.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassNonFinalFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassNonFinalFactory.java"));
+ }
+
+ @Test
+ public void publicClass() {
+ Compilation compilation = javac.compile(JavaFileObjects.forResource("good/PublicClass.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.PublicClassFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/PublicClassFactory.java"));
+ }
+
+ @Test
+ public void simpleClassCustomName() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/SimpleClassCustomName.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.CustomNamedFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/CustomNamedFactory.java"));
+ }
+
+ @Test
+ public void simpleClassMixedDeps() {
+ Compilation compilation =
+ javac.compile(
+ JavaFileObjects.forResource("good/SimpleClassMixedDeps.java"),
+ JavaFileObjects.forResource("support/AQualifier.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassMixedDepsFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassMixedDepsFactory.java"));
+ }
+
+ @Test
+ public void simpleClassPassedDeps() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/SimpleClassPassedDeps.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassPassedDepsFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassPassedDepsFactory.java"));
+ }
+
+ @Test
+ public void simpleClassProvidedDeps() {
+ Compilation compilation =
+ javac.compile(
+ JavaFileObjects.forResource("support/AQualifier.java"),
+ JavaFileObjects.forResource("support/BQualifier.java"),
+ JavaFileObjects.forResource("good/SimpleClassProvidedDeps.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassProvidedDepsFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassProvidedDepsFactory.java"));
}
@Test
public void simpleClassProvidedProviderDeps() {
- assertAbout(javaSources())
- .that(
- ImmutableSet.of(
- JavaFileObjects.forResource("support/AQualifier.java"),
- JavaFileObjects.forResource("support/BQualifier.java"),
- JavaFileObjects.forResource("good/SimpleClassProvidedProviderDeps.java")))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassProvidedProviderDepsFactory.java"));
- }
-
- @Test public void constructorAnnotated() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/ConstructorAnnotated.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/ConstructorAnnotatedFactory.java"));
- }
-
- @Test public void constructorAnnotatedNonFinal() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/ConstructorAnnotatedNonFinal.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/ConstructorAnnotatedNonFinalFactory.java"));
- }
-
- @Test public void simpleClassImplementingMarker() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/SimpleClassImplementingMarker.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassImplementingMarkerFactory.java"));
- }
-
- @Test public void simpleClassImplementingSimpleInterface() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/SimpleClassImplementingSimpleInterface.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(
+ Compilation compilation =
+ javac.compile(
+ JavaFileObjects.forResource("support/AQualifier.java"),
+ JavaFileObjects.forResource("support/BQualifier.java"),
+ JavaFileObjects.forResource("good/SimpleClassProvidedProviderDeps.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassProvidedProviderDepsFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/SimpleClassProvidedProviderDepsFactory.java"));
+ }
+
+ @Test
+ public void constructorAnnotated() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/ConstructorAnnotated.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.ConstructorAnnotatedFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/ConstructorAnnotatedFactory.java"));
+ }
+
+ @Test
+ public void constructorWithThrowsClauseAnnotated() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/ConstructorAnnotatedThrows.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.ConstructorAnnotatedThrowsFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/ConstructorAnnotatedThrowsFactory.java"));
+ }
+
+ @Test
+ public void constructorAnnotatedNonFinal() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/ConstructorAnnotatedNonFinal.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.ConstructorAnnotatedNonFinalFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/ConstructorAnnotatedNonFinalFactory.java"));
+ }
+
+ @Test
+ public void simpleClassImplementingMarker() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/SimpleClassImplementingMarker.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassImplementingMarkerFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/SimpleClassImplementingMarkerFactory.java"));
+ }
+
+ @Test
+ public void simpleClassImplementingSimpleInterface() {
+ Compilation compilation =
+ javac.compile(
+ JavaFileObjects.forResource("good/SimpleClassImplementingSimpleInterface.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassImplementingSimpleInterfaceFactory")
+ .hasSourceEquivalentTo(
loadExpectedFile("expected/SimpleClassImplementingSimpleInterfaceFactory.java"));
}
- @Test public void mixedDepsImplementingInterfaces() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/MixedDepsImplementingInterfaces.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/MixedDepsImplementingInterfacesFactory.java"));
+ @Test
+ public void mixedDepsImplementingInterfaces() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/MixedDepsImplementingInterfaces.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.MixedDepsImplementingInterfacesFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/MixedDepsImplementingInterfacesFactory.java"));
}
- @Test public void failsWithMixedFinals() {
+ @Test
+ public void failsWithMixedFinals() {
JavaFileObject file = JavaFileObjects.forResource("bad/MixedFinals.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining(
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
"Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.")
- .in(file).onLine(24)
- .and().withErrorContaining(
+ .inFile(file)
+ .onLine(24);
+ assertThat(compilation)
+ .hadErrorContaining(
"Cannot mix allowSubclasses=true and allowSubclasses=false in one factory.")
- .in(file).onLine(27);
+ .inFile(file)
+ .onLine(27);
}
- @Test public void providedButNoAutoFactory() {
+ @Test
+ public void providedButNoAutoFactory() {
JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedButNoAutoFactory.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining(
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
"@Provided may only be applied to constructors requesting an auto-factory")
- .in(file).onLine(21).atColumn(38);
+ .inFile(file)
+ .onLineContaining("@Provided");
}
- @Test public void providedOnMethodParameter() {
+ @Test
+ public void providedOnMethodParameter() {
JavaFileObject file = JavaFileObjects.forResource("bad/ProvidedOnMethodParameter.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining(
- "@Provided may only be applied to constructor parameters")
- .in(file).onLine(21).atColumn(23);
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("@Provided may only be applied to constructor parameters")
+ .inFile(file)
+ .onLineContaining("@Provided");
}
- @Test public void invalidCustomName() {
+ @Test
+ public void invalidCustomName() {
JavaFileObject file = JavaFileObjects.forResource("bad/InvalidCustomName.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining("\"SillyFactory!\" is not a valid Java identifier")
- .in(file).onLine(20);
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("\"SillyFactory!\" is not a valid Java identifier")
+ .inFile(file)
+ .onLineContaining("SillyFactory!");
+ }
+
+ @Test
+ public void factoryExtendingAbstractClass() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/FactoryExtendingAbstractClass.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.FactoryExtendingAbstractClassFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/FactoryExtendingAbstractClassFactory.java"));
+ }
+
+ @Test
+ public void factoryWithConstructorThrowsClauseExtendingAbstractClass() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/FactoryExtendingAbstractClassThrows.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.FactoryExtendingAbstractClassThrowsFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/FactoryExtendingAbstractClassThrowsFactory.java"));
}
- @Test public void factoryExtendingAbstractClass() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/FactoryExtendingAbstractClass.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/FactoryExtendingAbstractClassFactory.java"));
+ @Test
+ public void factoryExtendingAbstractClass_withConstructorParams() {
+ JavaFileObject file =
+ JavaFileObjects.forResource("bad/FactoryExtendingAbstractClassWithConstructorParams.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory is not a"
+ + " valid supertype for a factory. Factory supertypes must have a no-arg"
+ + " constructor.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
}
- @Test public void factoryExtendingAbstractClass_withConstructorParams() {
+ @Test
+ public void factoryExtendingAbstractClass_multipleConstructors() {
JavaFileObject file =
- JavaFileObjects.forResource("good/FactoryExtendingAbstractClassWithConstructorParams.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining(
- "tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory "
- + "is not a valid supertype for a factory. "
- + "Factory supertypes must have a no-arg constructor.")
- .in(file).onLine(21);
- }
-
- @Test public void factoryExtendingAbstractClass_multipleConstructors() {
- JavaFileObject file = JavaFileObjects.forResource(
- "good/FactoryExtendingAbstractClassWithMultipleConstructors.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError();
- }
-
- @Test public void factoryExtendingInterface() {
+ JavaFileObjects.forResource(
+ "good/FactoryExtendingAbstractClassWithMultipleConstructors.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void factoryExtendingInterface() {
JavaFileObject file = JavaFileObjects.forResource("bad/InterfaceSupertype.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining("java.lang.Runnable is not a valid supertype for a factory. "
- + "Supertypes must be non-final classes.")
- .in(file).onLine(20);
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.lang.Runnable is not a valid supertype for a factory. Supertypes must be"
+ + " non-final classes.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
}
- @Test public void factoryExtendingEnum() {
+ @Test
+ public void factoryExtendingEnum() {
JavaFileObject file = JavaFileObjects.forResource("bad/EnumSupertype.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining(
- "java.util.concurrent.TimeUnit is not a valid supertype for a factory. "
- + "Supertypes must be non-final classes.")
- .in(file).onLine(21);
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.util.concurrent.TimeUnit is not a valid supertype for a factory. Supertypes must"
+ + " be non-final classes.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
}
- @Test public void factoryExtendingFinalClass() {
+ @Test
+ public void factoryExtendingFinalClass() {
JavaFileObject file = JavaFileObjects.forResource("bad/FinalSupertype.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .failsToCompile()
- .withErrorContaining("java.lang.Boolean is not a valid supertype for a factory. "
- + "Supertypes must be non-final classes.")
- .in(file).onLine(20);
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "java.lang.Boolean is not a valid supertype for a factory. Supertypes must be"
+ + " non-final classes.")
+ .inFile(file)
+ .onLineContaining("@AutoFactory");
}
- @Test public void factoryImplementingGenericInterfaceExtension() {
+ @Test
+ public void factoryImplementingGenericInterfaceExtension() {
JavaFileObject file =
JavaFileObjects.forResource("good/FactoryImplementingGenericInterfaceExtension.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.FactoryImplementingGenericInterfaceExtensionFactory")
+ .hasSourceEquivalentTo(
loadExpectedFile("expected/FactoryImplementingGenericInterfaceExtensionFactory.java"));
}
- @Test public void multipleFactoriesImpementingInterface() {
+ @Test
+ public void multipleFactoriesImpementingInterface() {
JavaFileObject file =
JavaFileObjects.forResource("good/MultipleFactoriesImplementingInterface.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(
- loadExpectedFile("expected/MultipleFactoriesImplementingInterface_ClassAFactory.java"),
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.MultipleFactoriesImplementingInterface_ClassAFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/MultipleFactoriesImplementingInterface_ClassAFactory.java"));
+ assertThat(compilation)
+ .generatedSourceFile("tests.MultipleFactoriesImplementingInterface_ClassBFactory")
+ .hasSourceEquivalentTo(
loadExpectedFile("expected/MultipleFactoriesImplementingInterface_ClassBFactory.java"));
}
- @Test public void classUsingQualifierWithArgs() {
- assertAbout(javaSources())
- .that(
- ImmutableSet.of(
- JavaFileObjects.forResource("support/QualifierWithArgs.java"),
- JavaFileObjects.forResource("good/ClassUsingQualifierWithArgs.java")))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/ClassUsingQualifierWithArgsFactory.java"));
+ @Test
+ public void classUsingQualifierWithArgs() {
+ Compilation compilation =
+ javac.compile(
+ JavaFileObjects.forResource("support/QualifierWithArgs.java"),
+ JavaFileObjects.forResource("good/ClassUsingQualifierWithArgs.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.ClassUsingQualifierWithArgsFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/ClassUsingQualifierWithArgsFactory.java"));
}
- @Test public void factoryImplementingInterfaceWhichRedeclaresCreateMethods() {
- JavaFileObject file =
- JavaFileObjects.forResource("good/FactoryImplementingCreateMethod.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(
+ @Test
+ public void factoryImplementingInterfaceWhichRedeclaresCreateMethods() {
+ JavaFileObject file = JavaFileObjects.forResource("good/FactoryImplementingCreateMethod.java");
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.FactoryImplementingCreateMethod_ConcreteClassFactory")
+ .hasSourceEquivalentTo(
loadExpectedFile("expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java"));
}
- @Test public void nullableParams() {
- assertAbout(javaSources())
- .that(
- ImmutableSet.of(
- JavaFileObjects.forResource("good/SimpleClassNullableParameters.java"),
- JavaFileObjects.forResource("support/AQualifier.java"),
- JavaFileObjects.forResource("support/BQualifier.java")))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassNullableParametersFactory.java"));
- }
-
- @Test public void customNullableType() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/CustomNullable.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/CustomNullableFactory.java"));
- }
-
- @Test public void checkerFrameworkNullableType() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/CheckerFrameworkNullable.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/CheckerFrameworkNullableFactory.java"));
- }
-
- @Test public void multipleProvidedParamsWithSameKey() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/MultipleProvidedParamsSameKey.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/MultipleProvidedParamsSameKeyFactory.java"));
- }
-
- @Test public void providerArgumentToCreateMethod() {
- assertAbout(javaSource())
- .that(JavaFileObjects.forResource("good/ProviderArgumentToCreateMethod.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/ProviderArgumentToCreateMethodFactory.java"));
- }
-
- @Test public void multipleFactoriesConflictingParameterNames() {
- assertThat(
+ @Test
+ public void nullableParams() {
+ Compilation compilation =
+ javac.compile(
+ JavaFileObjects.forResource("good/SimpleClassNullableParameters.java"),
+ JavaFileObjects.forResource("support/AQualifier.java"),
+ JavaFileObjects.forResource("support/BQualifier.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassNullableParametersFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/SimpleClassNullableParametersFactory.java"));
+ }
+
+ @Test
+ public void customNullableType() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/CustomNullable.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.CustomNullableFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/CustomNullableFactory.java"));
+ }
+
+ @Test
+ public void checkerFrameworkNullableType() {
+ // TYPE_USE annotations are pretty much unusable with annotation processors on Java 8 because
+ // of bugs that mean they only appear in the javax.lang.model API when the compiler feels like
+ // it. Checking for a java.specification.version that does not start with "1." eliminates 8 and
+ // any earlier version.
+ assume().that(JAVA_SPECIFICATION_VERSION.value()).doesNotMatch("1\\..*");
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/CheckerFrameworkNullable.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.CheckerFrameworkNullableFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/CheckerFrameworkNullableFactory.java"));
+ }
+
+ @Test
+ public void multipleProvidedParamsWithSameKey() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/MultipleProvidedParamsSameKey.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.MultipleProvidedParamsSameKeyFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/MultipleProvidedParamsSameKeyFactory.java"));
+ }
+
+ @Test
+ public void providerArgumentToCreateMethod() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/ProviderArgumentToCreateMethod.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.ProviderArgumentToCreateMethodFactory")
+ .hasSourceEquivalentTo(
+ loadExpectedFile("expected/ProviderArgumentToCreateMethodFactory.java"));
+ }
+
+ @Test
+ public void multipleFactoriesConflictingParameterNames() {
+ Compilation compilation =
+ javac.compile(
JavaFileObjects.forResource("good/MultipleFactoriesConflictingParameterNames.java"),
- JavaFileObjects.forResource("support/AQualifier.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(
+ JavaFileObjects.forResource("support/AQualifier.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.MultipleFactoriesConflictingParameterNamesFactory")
+ .hasSourceEquivalentTo(
loadExpectedFile("expected/MultipleFactoriesConflictingParameterNamesFactory.java"));
}
- @Test public void factoryVarargs() {
- assertThat(JavaFileObjects.forResource("good/SimpleClassVarargs.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/SimpleClassVarargsFactory.java"));
+ @Test
+ public void factoryVarargs() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/SimpleClassVarargs.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.SimpleClassVarargsFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassVarargsFactory.java"));
}
- @Test public void onlyPrimitives() {
- assertThat(JavaFileObjects.forResource("good/OnlyPrimitives.java"))
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/OnlyPrimitivesFactory.java"));
+ @Test
+ public void onlyPrimitives() {
+ Compilation compilation =
+ javac.compile(JavaFileObjects.forResource("good/OnlyPrimitives.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("tests.OnlyPrimitivesFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/OnlyPrimitivesFactory.java"));
}
@Test
public void defaultPackage() {
JavaFileObject file = JavaFileObjects.forResource("good/DefaultPackage.java");
- assertAbout(javaSource())
- .that(file)
- .processedWith(new AutoFactoryProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(loadExpectedFile("expected/DefaultPackageFactory.java"));
+ Compilation compilation = javac.compile(file);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("DefaultPackageFactory")
+ .hasSourceEquivalentTo(loadExpectedFile("expected/DefaultPackageFactory.java"));
}
private JavaFileObject loadExpectedFile(String resourceName) {
+ if (isJavaxAnnotationProcessingGeneratedAvailable()) {
+ return JavaFileObjects.forResource(resourceName);
+ }
try {
List<String> sourceLines = Resources.readLines(Resources.getResource(resourceName), UTF_8);
- if (!isJavaxAnnotationProcessingGeneratedAvailable()) {
- replaceGeneratedImport(sourceLines);
- }
+ replaceGeneratedImport(sourceLines);
return JavaFileObjects.forSourceLines(
resourceName.replace('/', '.').replace(".java", ""), sourceLines);
} catch (IOException e) {
@@ -457,8 +532,8 @@ public class AutoFactoryProcessorTest {
int lastImport = -1;
for (String line : sourceLines) {
if (line.startsWith("import ") && !line.startsWith("import static ")) {
- firstImport = Math.min(firstImport, i);
- lastImport = Math.max(lastImport, i);
+ firstImport = min(firstImport, i);
+ lastImport = max(lastImport, i);
}
i++;
}
diff --git a/factory/src/test/resources/good/FactoryExtendingAbstractClassWithConstructorParams.java b/factory/src/test/resources/bad/FactoryExtendingAbstractClassWithConstructorParams.java
index 98c5f667..7c7120bf 100644
--- a/factory/src/test/resources/good/FactoryExtendingAbstractClassWithConstructorParams.java
+++ b/factory/src/test/resources/bad/FactoryExtendingAbstractClassWithConstructorParams.java
@@ -20,9 +20,9 @@ import tests.FactoryExtendingAbstractClassWithConstructorParams.AbstractFactory;
@AutoFactory(extending = AbstractFactory.class)
final class FactoryExtendingAbstractClassWithConstructorParams {
- static abstract class AbstractFactory {
+ abstract static class AbstractFactory {
protected AbstractFactory(Object obj) {}
-
+
abstract FactoryExtendingAbstractClassWithConstructorParams newInstance();
}
}
diff --git a/factory/src/test/resources/bad/InvalidCustomName.java b/factory/src/test/resources/bad/InvalidCustomName.java
index 5734ee7f..6d0a2f91 100644
--- a/factory/src/test/resources/bad/InvalidCustomName.java
+++ b/factory/src/test/resources/bad/InvalidCustomName.java
@@ -18,4 +18,4 @@ package tests;
import com.google.auto.factory.AutoFactory;
@AutoFactory(className = "SillyFactory!")
-final class InvalidCustomName { }
+final class InvalidCustomName {}
diff --git a/factory/src/test/resources/expected/CheckerFrameworkNullableFactory.java b/factory/src/test/resources/expected/CheckerFrameworkNullableFactory.java
index 79175c7e..faa83971 100644
--- a/factory/src/test/resources/expected/CheckerFrameworkNullableFactory.java
+++ b/factory/src/test/resources/expected/CheckerFrameworkNullableFactory.java
@@ -15,6 +15,7 @@
*/
package tests;
+import java.util.Map;
import javax.annotation.processing.Generated;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -22,26 +23,34 @@ import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import org.checkerframework.checker.nullness.compatqual.NullableType;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
-)
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class CheckerFrameworkNullableFactory {
private final Provider<String> java_lang_StringProvider;
+ private final Provider<Map.@NullableType Entry<?, ?>> providedNestedNullableTypeProvider;
+
@Inject
CheckerFrameworkNullableFactory(
- Provider<String> java_lang_StringProvider) {
+ Provider<String> java_lang_StringProvider,
+ Provider<Map.@NullableType Entry<?, ?>> providedNestedNullableTypeProvider) {
this.java_lang_StringProvider = checkNotNull(java_lang_StringProvider, 1);
+ this.providedNestedNullableTypeProvider = checkNotNull(providedNestedNullableTypeProvider, 2);
}
CheckerFrameworkNullable create(
- @NullableDecl String nullableDecl, @NullableType String nullableType) {
+ @NullableDecl String nullableDecl,
+ @NullableType String nullableType,
+ Map.@NullableType Entry<?, ?> nestedNullableType) {
return new CheckerFrameworkNullable(
nullableDecl,
java_lang_StringProvider.get(),
nullableType,
- java_lang_StringProvider.get());
+ java_lang_StringProvider.get(),
+ nestedNullableType,
+ providedNestedNullableTypeProvider.get());
}
private static <T> T checkNotNull(T reference, int argumentIndex) {
diff --git a/factory/src/test/resources/expected/ClassUsingQualifierWithArgsFactory.java b/factory/src/test/resources/expected/ClassUsingQualifierWithArgsFactory.java
index b5bd89c1..8d889eed 100644
--- a/factory/src/test/resources/expected/ClassUsingQualifierWithArgsFactory.java
+++ b/factory/src/test/resources/expected/ClassUsingQualifierWithArgsFactory.java
@@ -20,14 +20,15 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class ClassUsingQualifierWithArgsFactory {
private final Provider<String> providedDepAProvider;
- @Inject ClassUsingQualifierWithArgsFactory(
- @QualifierWithArgs(name="Fred", count=3) Provider<String> providedDepAProvider) {
+ @Inject
+ ClassUsingQualifierWithArgsFactory(
+ @QualifierWithArgs(name = "Fred", count = 3) Provider<String> providedDepAProvider) {
this.providedDepAProvider = checkNotNull(providedDepAProvider, 1);
}
diff --git a/factory/src/test/resources/expected/ConstructorAnnotatedFactory.java b/factory/src/test/resources/expected/ConstructorAnnotatedFactory.java
index 6e9d242e..22349851 100644
--- a/factory/src/test/resources/expected/ConstructorAnnotatedFactory.java
+++ b/factory/src/test/resources/expected/ConstructorAnnotatedFactory.java
@@ -20,13 +20,14 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class ConstructorAnnotatedFactory {
private final Provider<Object> objProvider;
- @Inject ConstructorAnnotatedFactory(Provider<Object> objProvider) {
+ @Inject
+ ConstructorAnnotatedFactory(Provider<Object> objProvider) {
this.objProvider = checkNotNull(objProvider, 1);
}
diff --git a/factory/src/test/resources/expected/ConstructorAnnotatedNonFinalFactory.java b/factory/src/test/resources/expected/ConstructorAnnotatedNonFinalFactory.java
index f662642d..25ec894f 100644
--- a/factory/src/test/resources/expected/ConstructorAnnotatedNonFinalFactory.java
+++ b/factory/src/test/resources/expected/ConstructorAnnotatedNonFinalFactory.java
@@ -20,13 +20,14 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
class ConstructorAnnotatedNonFinalFactory {
private final Provider<Object> objProvider;
- @Inject ConstructorAnnotatedNonFinalFactory(Provider<Object> objProvider) {
+ @Inject
+ ConstructorAnnotatedNonFinalFactory(Provider<Object> objProvider) {
this.objProvider = checkNotNull(objProvider, 1);
}
diff --git a/factory/src/test/resources/expected/ConstructorAnnotatedThrowsFactory.java b/factory/src/test/resources/expected/ConstructorAnnotatedThrowsFactory.java
new file mode 100644
index 00000000..05b30fdf
--- /dev/null
+++ b/factory/src/test/resources/expected/ConstructorAnnotatedThrowsFactory.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 tests;
+
+import java.io.IOException;
+import javax.annotation.processing.Generated;
+import javax.inject.Inject;
+import javax.inject.Provider;
+
+@Generated(
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
+final class ConstructorAnnotatedThrowsFactory {
+ private final Provider<Object> objProvider;
+
+ @Inject
+ ConstructorAnnotatedThrowsFactory(Provider<Object> objProvider) {
+ this.objProvider = checkNotNull(objProvider, 1);
+ }
+
+ ConstructorAnnotatedThrows create() throws IOException, InterruptedException {
+ return new ConstructorAnnotatedThrows();
+ }
+
+ ConstructorAnnotatedThrows create(String s) {
+ return new ConstructorAnnotatedThrows(checkNotNull(s, 1));
+ }
+
+ ConstructorAnnotatedThrows create(int i) throws IOException {
+ return new ConstructorAnnotatedThrows(checkNotNull(objProvider.get(), 1), i);
+ }
+
+ ConstructorAnnotatedThrows create(char c) throws InterruptedException {
+ return new ConstructorAnnotatedThrows(checkNotNull(objProvider.get(), 1), c);
+ }
+
+ private static <T> T checkNotNull(T reference, int argumentIndex) {
+ if (reference == null) {
+ throw new NullPointerException(
+ "@AutoFactory method argument is null but is not marked @Nullable. Argument index: "
+ + argumentIndex);
+ }
+ return reference;
+ }
+}
diff --git a/factory/src/test/resources/expected/CustomNamedFactory.java b/factory/src/test/resources/expected/CustomNamedFactory.java
index c388387c..512c244c 100644
--- a/factory/src/test/resources/expected/CustomNamedFactory.java
+++ b/factory/src/test/resources/expected/CustomNamedFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class CustomNamedFactory {
- @Inject CustomNamedFactory() {}
+ @Inject
+ CustomNamedFactory() {}
SimpleClassCustomName create() {
return new SimpleClassCustomName();
diff --git a/factory/src/test/resources/expected/CustomNullableFactory.java b/factory/src/test/resources/expected/CustomNullableFactory.java
index 31bb5bfa..c8f2f286 100644
--- a/factory/src/test/resources/expected/CustomNullableFactory.java
+++ b/factory/src/test/resources/expected/CustomNullableFactory.java
@@ -20,9 +20,9 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class CustomNullableFactory {
private final Provider<Object> objectProvider;
diff --git a/factory/src/test/resources/expected/FactoryExtendingAbstractClassFactory.java b/factory/src/test/resources/expected/FactoryExtendingAbstractClassFactory.java
index c56afb0a..f35b414e 100644
--- a/factory/src/test/resources/expected/FactoryExtendingAbstractClassFactory.java
+++ b/factory/src/test/resources/expected/FactoryExtendingAbstractClassFactory.java
@@ -19,18 +19,20 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class FactoryExtendingAbstractClassFactory
extends FactoryExtendingAbstractClass.AbstractFactory {
- @Inject FactoryExtendingAbstractClassFactory() {}
+ @Inject
+ FactoryExtendingAbstractClassFactory() {}
FactoryExtendingAbstractClass create() {
return new FactoryExtendingAbstractClass();
}
- @Override public FactoryExtendingAbstractClass newInstance() {
+ @Override
+ public FactoryExtendingAbstractClass newInstance() {
return create();
}
}
diff --git a/factory/src/test/resources/expected/FactoryExtendingAbstractClassThrowsFactory.java b/factory/src/test/resources/expected/FactoryExtendingAbstractClassThrowsFactory.java
new file mode 100644
index 00000000..402f8946
--- /dev/null
+++ b/factory/src/test/resources/expected/FactoryExtendingAbstractClassThrowsFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 tests;
+
+import java.io.IOException;
+import javax.annotation.processing.Generated;
+import javax.inject.Inject;
+
+@Generated(
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
+final class FactoryExtendingAbstractClassThrowsFactory
+ extends FactoryExtendingAbstractClassThrows.AbstractFactory {
+ @Inject
+ FactoryExtendingAbstractClassThrowsFactory() {}
+
+ FactoryExtendingAbstractClassThrows create() throws IOException, InterruptedException {
+ return new FactoryExtendingAbstractClassThrows();
+ }
+
+ @Override
+ public FactoryExtendingAbstractClassThrows newInstance() throws Exception {
+ return create();
+ }
+}
diff --git a/factory/src/test/resources/expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java b/factory/src/test/resources/expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java
index f23dc9ab..2d8a392d 100644
--- a/factory/src/test/resources/expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java
+++ b/factory/src/test/resources/expected/FactoryImplementingCreateMethod_ConcreteClassFactory.java
@@ -20,9 +20,9 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class FactoryImplementingCreateMethod_ConcreteClassFactory
implements FactoryImplementingCreateMethod.FactoryInterfaceWithCreateMethod {
@@ -40,7 +40,8 @@ final class FactoryImplementingCreateMethod_ConcreteClassFactory
}
@Override
- public FactoryImplementingCreateMethod.ConcreteClass create(List<Integer> genericWithDifferentArgumentName) {
+ public FactoryImplementingCreateMethod.ConcreteClass create(
+ List<Integer> genericWithDifferentArgumentName) {
return new FactoryImplementingCreateMethod.ConcreteClass(
checkNotNull(genericWithDifferentArgumentName, 1));
}
diff --git a/factory/src/test/resources/expected/FactoryImplementingGenericInterfaceExtensionFactory.java b/factory/src/test/resources/expected/FactoryImplementingGenericInterfaceExtensionFactory.java
index 3bfc5262..9f2bf0ae 100644
--- a/factory/src/test/resources/expected/FactoryImplementingGenericInterfaceExtensionFactory.java
+++ b/factory/src/test/resources/expected/FactoryImplementingGenericInterfaceExtensionFactory.java
@@ -20,20 +20,23 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class FactoryImplementingGenericInterfaceExtensionFactory
implements FactoryImplementingGenericInterfaceExtension.MyFactory {
private final Provider<String> sProvider;
+
@Inject
FactoryImplementingGenericInterfaceExtensionFactory(Provider<String> sProvider) {
this.sProvider = checkNotNull(sProvider, 1);
}
+
FactoryImplementingGenericInterfaceExtension create(Integer i) {
return new FactoryImplementingGenericInterfaceExtension(
checkNotNull(sProvider.get(), 1), checkNotNull(i, 2));
}
+
@Override
public FactoryImplementingGenericInterfaceExtension make(Integer arg) {
return create(arg);
diff --git a/factory/src/test/resources/expected/MixedDepsImplementingInterfacesFactory.java b/factory/src/test/resources/expected/MixedDepsImplementingInterfacesFactory.java
index 19f2c138..ec4089b7 100644
--- a/factory/src/test/resources/expected/MixedDepsImplementingInterfacesFactory.java
+++ b/factory/src/test/resources/expected/MixedDepsImplementingInterfacesFactory.java
@@ -23,15 +23,18 @@ import javax.inject.Provider;
* @author Gregory Kick
*/
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
-)
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class MixedDepsImplementingInterfacesFactory
- implements MixedDepsImplementingInterfaces.FromInt, MixedDepsImplementingInterfaces.FromObject,
- MixedDepsImplementingInterfaces.MarkerA, MixedDepsImplementingInterfaces.MarkerB {
+ implements MixedDepsImplementingInterfaces.FromInt,
+ MixedDepsImplementingInterfaces.FromObject,
+ MixedDepsImplementingInterfaces.MarkerA,
+ MixedDepsImplementingInterfaces.MarkerB {
private final Provider<String> sProvider;
- @Inject MixedDepsImplementingInterfacesFactory(Provider<String> sProvider) {
+ @Inject
+ MixedDepsImplementingInterfacesFactory(Provider<String> sProvider) {
this.sProvider = checkNotNull(sProvider, 1);
}
@@ -43,11 +46,13 @@ final class MixedDepsImplementingInterfacesFactory
return new MixedDepsImplementingInterfaces(checkNotNull(o, 1));
}
- @Override public MixedDepsImplementingInterfaces fromInt(int i) {
+ @Override
+ public MixedDepsImplementingInterfaces fromInt(int i) {
return create(i);
}
- @Override public MixedDepsImplementingInterfaces fromObject(Object o) {
+ @Override
+ public MixedDepsImplementingInterfaces fromObject(Object o) {
return create(o);
}
diff --git a/factory/src/test/resources/expected/MultipleFactoriesConflictingParameterNamesFactory.java b/factory/src/test/resources/expected/MultipleFactoriesConflictingParameterNamesFactory.java
index fac6e13a..3eaf3afa 100644
--- a/factory/src/test/resources/expected/MultipleFactoriesConflictingParameterNamesFactory.java
+++ b/factory/src/test/resources/expected/MultipleFactoriesConflictingParameterNamesFactory.java
@@ -22,7 +22,7 @@ import javax.inject.Provider;
@Generated(
value = "com.google.auto.factory.processor.AutoFactoryProcessor",
comments = "https://github.com/google/auto/tree/master/factory"
-)
+ )
final class MultipleFactoriesConflictingParameterNamesFactory {
private final Provider<String> stringProvider;
diff --git a/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassAFactory.java b/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassAFactory.java
index 5ee2b2fe..6fcfb036 100644
--- a/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassAFactory.java
+++ b/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassAFactory.java
@@ -19,9 +19,9 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class MultipleFactoriesImplementingInterface_ClassAFactory
implements MultipleFactoriesImplementingInterface.Base.Factory {
@Inject
diff --git a/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassBFactory.java b/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassBFactory.java
index f6540683..56646891 100644
--- a/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassBFactory.java
+++ b/factory/src/test/resources/expected/MultipleFactoriesImplementingInterface_ClassBFactory.java
@@ -19,9 +19,9 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class MultipleFactoriesImplementingInterface_ClassBFactory
implements MultipleFactoriesImplementingInterface.Base.Factory {
@Inject
diff --git a/factory/src/test/resources/expected/MultipleProvidedParamsSameKeyFactory.java b/factory/src/test/resources/expected/MultipleProvidedParamsSameKeyFactory.java
index de7bad72..97cc8ac2 100644
--- a/factory/src/test/resources/expected/MultipleProvidedParamsSameKeyFactory.java
+++ b/factory/src/test/resources/expected/MultipleProvidedParamsSameKeyFactory.java
@@ -20,9 +20,9 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class MultipleProvidedParamsSameKeyFactory {
private final Provider<String> java_lang_StringProvider;
diff --git a/factory/src/test/resources/expected/NestedClassCustomNamedFactory.java b/factory/src/test/resources/expected/NestedClassCustomNamedFactory.java
index bf6a4681..fe7aa1a9 100644
--- a/factory/src/test/resources/expected/NestedClassCustomNamedFactory.java
+++ b/factory/src/test/resources/expected/NestedClassCustomNamedFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class NestedClassCustomNamedFactory {
- @Inject NestedClassCustomNamedFactory() {}
+ @Inject
+ NestedClassCustomNamedFactory() {}
NestedClasses.SimpleNestedClassWithCustomFactory create() {
return new NestedClasses.SimpleNestedClassWithCustomFactory();
diff --git a/factory/src/test/resources/expected/NestedClasses_SimpleNestedClassFactory.java b/factory/src/test/resources/expected/NestedClasses_SimpleNestedClassFactory.java
index f982e86f..41ecc52e 100644
--- a/factory/src/test/resources/expected/NestedClasses_SimpleNestedClassFactory.java
+++ b/factory/src/test/resources/expected/NestedClasses_SimpleNestedClassFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class NestedClasses_SimpleNestedClassFactory {
- @Inject NestedClasses_SimpleNestedClassFactory() {}
+ @Inject
+ NestedClasses_SimpleNestedClassFactory() {}
NestedClasses.SimpleNestedClass create() {
return new NestedClasses.SimpleNestedClass();
diff --git a/factory/src/test/resources/expected/OnlyPrimitivesFactory.java b/factory/src/test/resources/expected/OnlyPrimitivesFactory.java
index ec60c58e..b931a222 100644
--- a/factory/src/test/resources/expected/OnlyPrimitivesFactory.java
+++ b/factory/src/test/resources/expected/OnlyPrimitivesFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class OnlyPrimitivesFactory {
- @Inject OnlyPrimitivesFactory() {}
+ @Inject
+ OnlyPrimitivesFactory() {}
OnlyPrimitives create(int i, long l) {
return new OnlyPrimitives(i, l);
diff --git a/factory/src/test/resources/expected/ProviderArgumentToCreateMethodFactory.java b/factory/src/test/resources/expected/ProviderArgumentToCreateMethodFactory.java
index 4d1a4cf5..75a6291c 100644
--- a/factory/src/test/resources/expected/ProviderArgumentToCreateMethodFactory.java
+++ b/factory/src/test/resources/expected/ProviderArgumentToCreateMethodFactory.java
@@ -22,10 +22,11 @@ import javax.inject.Provider;
@Generated(
value = "com.google.auto.factory.processor.AutoFactoryProcessor",
comments = "https://github.com/google/auto/tree/master/factory"
-)
+ )
final class ProviderArgumentToCreateMethodFactory
- implements ProviderArgumentToCreateMethod.CustomCreator{
- @Inject ProviderArgumentToCreateMethodFactory() {}
+ implements ProviderArgumentToCreateMethod.CustomCreator {
+ @Inject
+ ProviderArgumentToCreateMethodFactory() {}
ProviderArgumentToCreateMethod create(Provider<String> stringProvider) {
return new ProviderArgumentToCreateMethod(checkNotNull(stringProvider, 1));
diff --git a/factory/src/test/resources/expected/PublicClassFactory.java b/factory/src/test/resources/expected/PublicClassFactory.java
index 06671dc6..9e5c113d 100644
--- a/factory/src/test/resources/expected/PublicClassFactory.java
+++ b/factory/src/test/resources/expected/PublicClassFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
public final class PublicClassFactory {
- @Inject public PublicClassFactory() {}
+ @Inject
+ public PublicClassFactory() {}
public PublicClass create() {
return new PublicClass();
diff --git a/factory/src/test/resources/expected/SimpleClassFactory.java b/factory/src/test/resources/expected/SimpleClassFactory.java
index 308d2cdc..4741b752 100644
--- a/factory/src/test/resources/expected/SimpleClassFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassFactory {
- @Inject SimpleClassFactory() {}
+ @Inject
+ SimpleClassFactory() {}
SimpleClass create() {
return new SimpleClass();
diff --git a/factory/src/test/resources/expected/SimpleClassImplementingMarkerFactory.java b/factory/src/test/resources/expected/SimpleClassImplementingMarkerFactory.java
index 6c611e99..17701387 100644
--- a/factory/src/test/resources/expected/SimpleClassImplementingMarkerFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassImplementingMarkerFactory.java
@@ -20,11 +20,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassImplementingMarkerFactory implements RandomAccess {
- @Inject SimpleClassImplementingMarkerFactory() {}
+ @Inject
+ SimpleClassImplementingMarkerFactory() {}
SimpleClassImplementingMarker create() {
return new SimpleClassImplementingMarker();
diff --git a/factory/src/test/resources/expected/SimpleClassImplementingSimpleInterfaceFactory.java b/factory/src/test/resources/expected/SimpleClassImplementingSimpleInterfaceFactory.java
index 720e7d0f..7dd91bea 100644
--- a/factory/src/test/resources/expected/SimpleClassImplementingSimpleInterfaceFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassImplementingSimpleInterfaceFactory.java
@@ -19,18 +19,20 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassImplementingSimpleInterfaceFactory
implements SimpleClassImplementingSimpleInterface.SimpleInterface {
- @Inject SimpleClassImplementingSimpleInterfaceFactory() {}
+ @Inject
+ SimpleClassImplementingSimpleInterfaceFactory() {}
SimpleClassImplementingSimpleInterface create() {
return new SimpleClassImplementingSimpleInterface();
}
- @Override public SimpleClassImplementingSimpleInterface newInstance() {
+ @Override
+ public SimpleClassImplementingSimpleInterface newInstance() {
return create();
}
}
diff --git a/factory/src/test/resources/expected/SimpleClassMixedDepsFactory.java b/factory/src/test/resources/expected/SimpleClassMixedDepsFactory.java
index ccdea61c..b69ea326 100644
--- a/factory/src/test/resources/expected/SimpleClassMixedDepsFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassMixedDepsFactory.java
@@ -20,14 +20,14 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassMixedDepsFactory {
private final Provider<String> providedDepAProvider;
- @Inject SimpleClassMixedDepsFactory(
- @AQualifier Provider<String> providedDepAProvider) {
+ @Inject
+ SimpleClassMixedDepsFactory(@AQualifier Provider<String> providedDepAProvider) {
this.providedDepAProvider = checkNotNull(providedDepAProvider, 1);
}
diff --git a/factory/src/test/resources/expected/SimpleClassNonFinalFactory.java b/factory/src/test/resources/expected/SimpleClassNonFinalFactory.java
index d323812e..5ab90306 100644
--- a/factory/src/test/resources/expected/SimpleClassNonFinalFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassNonFinalFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
class SimpleClassNonFinalFactory {
- @Inject SimpleClassNonFinalFactory() {}
+ @Inject
+ SimpleClassNonFinalFactory() {}
SimpleClassNonFinal create() {
return new SimpleClassNonFinal();
diff --git a/factory/src/test/resources/expected/SimpleClassNullableParametersFactory.java b/factory/src/test/resources/expected/SimpleClassNullableParametersFactory.java
index e3540386..5b955964 100644
--- a/factory/src/test/resources/expected/SimpleClassNullableParametersFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassNullableParametersFactory.java
@@ -21,9 +21,9 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassNullableParametersFactory {
private final Provider<String> providedNullableProvider;
diff --git a/factory/src/test/resources/expected/SimpleClassPassedDepsFactory.java b/factory/src/test/resources/expected/SimpleClassPassedDepsFactory.java
index 3260c36e..9cc8a166 100644
--- a/factory/src/test/resources/expected/SimpleClassPassedDepsFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassPassedDepsFactory.java
@@ -19,11 +19,12 @@ import javax.annotation.processing.Generated;
import javax.inject.Inject;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassPassedDepsFactory {
- @Inject SimpleClassPassedDepsFactory() {}
+ @Inject
+ SimpleClassPassedDepsFactory() {}
SimpleClassPassedDeps create(String depA, String depB) {
return new SimpleClassPassedDeps(checkNotNull(depA, 1), checkNotNull(depB, 2));
diff --git a/factory/src/test/resources/expected/SimpleClassProvidedDepsFactory.java b/factory/src/test/resources/expected/SimpleClassProvidedDepsFactory.java
index 05d1e5ab..52448aad 100644
--- a/factory/src/test/resources/expected/SimpleClassProvidedDepsFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassProvidedDepsFactory.java
@@ -20,9 +20,9 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassProvidedDepsFactory {
private final Provider<Integer> providedPrimitiveAProvider;
private final Provider<Integer> providedPrimitiveBProvider;
diff --git a/factory/src/test/resources/expected/SimpleClassProvidedProviderDepsFactory.java b/factory/src/test/resources/expected/SimpleClassProvidedProviderDepsFactory.java
index aafdcec2..7bf2372c 100644
--- a/factory/src/test/resources/expected/SimpleClassProvidedProviderDepsFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassProvidedProviderDepsFactory.java
@@ -20,9 +20,9 @@ import javax.inject.Inject;
import javax.inject.Provider;
@Generated(
- value = "com.google.auto.factory.processor.AutoFactoryProcessor",
- comments = "https://github.com/google/auto/tree/master/factory"
- )
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
final class SimpleClassProvidedProviderDepsFactory {
private final Provider<String> providedDepAProvider;
private final Provider<String> providedDepBProvider;
diff --git a/factory/src/test/resources/expected/SimpleClassThrowsFactory.java b/factory/src/test/resources/expected/SimpleClassThrowsFactory.java
new file mode 100644
index 00000000..eda503a4
--- /dev/null
+++ b/factory/src/test/resources/expected/SimpleClassThrowsFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 tests;
+
+import java.io.IOException;
+import javax.annotation.processing.Generated;
+import javax.inject.Inject;
+
+@Generated(
+ value = "com.google.auto.factory.processor.AutoFactoryProcessor",
+ comments = "https://github.com/google/auto/tree/master/factory"
+ )
+final class SimpleClassThrowsFactory {
+ @Inject
+ SimpleClassThrowsFactory() {}
+
+ SimpleClassThrows create() throws IOException, InterruptedException {
+ return new SimpleClassThrows();
+ }
+}
diff --git a/factory/src/test/resources/expected/SimpleClassVarargsFactory.java b/factory/src/test/resources/expected/SimpleClassVarargsFactory.java
index 51c7f466..ac7c4bdc 100644
--- a/factory/src/test/resources/expected/SimpleClassVarargsFactory.java
+++ b/factory/src/test/resources/expected/SimpleClassVarargsFactory.java
@@ -21,9 +21,10 @@ import javax.inject.Inject;
@Generated(
value = "com.google.auto.factory.processor.AutoFactoryProcessor",
comments = "https://github.com/google/auto/tree/master/factory"
-)
+ )
final class SimpleClassVarargsFactory implements SimpleClassVarargs.InterfaceWithVarargs {
- @Inject SimpleClassVarargsFactory() {}
+ @Inject
+ SimpleClassVarargsFactory() {}
SimpleClassVarargs create(String... args) {
return new SimpleClassVarargs(checkNotNull(args, 1));
diff --git a/factory/src/test/resources/good/CheckerFrameworkNullable.java b/factory/src/test/resources/good/CheckerFrameworkNullable.java
index 7f2a0fee..8b9cbc26 100644
--- a/factory/src/test/resources/good/CheckerFrameworkNullable.java
+++ b/factory/src/test/resources/good/CheckerFrameworkNullable.java
@@ -17,6 +17,7 @@ package tests;
import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
+import java.util.Map;
import org.checkerframework.checker.nullness.compatqual.NullableDecl;
import org.checkerframework.checker.nullness.compatqual.NullableType;
@@ -27,5 +28,7 @@ final class CheckerFrameworkNullable {
@NullableDecl String nullableDecl,
@Provided @NullableDecl String providedNullableDecl,
@NullableType String nullableType,
- @Provided @NullableType String providedNullableType) {}
+ @Provided @NullableType String providedNullableType,
+ Map.@NullableType Entry<?, ?> nestedNullableType,
+ @Provided Map.@NullableType Entry<?, ?> providedNestedNullableType) {}
}
diff --git a/factory/src/test/resources/good/ConstructorAnnotated.java b/factory/src/test/resources/good/ConstructorAnnotated.java
index fdc02f35..ddb154f4 100644
--- a/factory/src/test/resources/good/ConstructorAnnotated.java
+++ b/factory/src/test/resources/good/ConstructorAnnotated.java
@@ -19,9 +19,17 @@ import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
final class ConstructorAnnotated {
- @AutoFactory ConstructorAnnotated() {}
+ @AutoFactory
+ ConstructorAnnotated() {}
+
ConstructorAnnotated(Object obj) {}
- @AutoFactory ConstructorAnnotated(String s) {}
- @AutoFactory ConstructorAnnotated(@Provided Object obj, int i) {}
- @AutoFactory ConstructorAnnotated(@Provided Object obj, char c) {}
+
+ @AutoFactory
+ ConstructorAnnotated(String s) {}
+
+ @AutoFactory
+ ConstructorAnnotated(@Provided Object obj, int i) {}
+
+ @AutoFactory
+ ConstructorAnnotated(@Provided Object obj, char c) {}
}
diff --git a/factory/src/test/resources/good/ConstructorAnnotatedNonFinal.java b/factory/src/test/resources/good/ConstructorAnnotatedNonFinal.java
index 5bed1e60..1b10e79e 100644
--- a/factory/src/test/resources/good/ConstructorAnnotatedNonFinal.java
+++ b/factory/src/test/resources/good/ConstructorAnnotatedNonFinal.java
@@ -19,9 +19,17 @@ import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
final class ConstructorAnnotatedNonFinal {
- @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal() {}
+ @AutoFactory(allowSubclasses = true)
+ ConstructorAnnotatedNonFinal() {}
+
ConstructorAnnotatedNonFinal(Object obj) {}
- @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal(String s) {}
- @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal(@Provided Object obj, int i) {}
- @AutoFactory(allowSubclasses = true) ConstructorAnnotatedNonFinal(@Provided Object obj, char c) {}
+
+ @AutoFactory(allowSubclasses = true)
+ ConstructorAnnotatedNonFinal(String s) {}
+
+ @AutoFactory(allowSubclasses = true)
+ ConstructorAnnotatedNonFinal(@Provided Object obj, int i) {}
+
+ @AutoFactory(allowSubclasses = true)
+ ConstructorAnnotatedNonFinal(@Provided Object obj, char c) {}
}
diff --git a/factory/src/test/resources/good/ConstructorAnnotatedThrows.java b/factory/src/test/resources/good/ConstructorAnnotatedThrows.java
new file mode 100644
index 00000000..58a52d0b
--- /dev/null
+++ b/factory/src/test/resources/good/ConstructorAnnotatedThrows.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 tests;
+
+import com.google.auto.factory.AutoFactory;
+import com.google.auto.factory.Provided;
+import java.io.IOException;
+
+final class ConstructorAnnotatedThrows {
+ @AutoFactory
+ ConstructorAnnotatedThrows() throws IOException, InterruptedException {}
+
+ ConstructorAnnotatedThrows(Object obj) {}
+
+ @AutoFactory
+ ConstructorAnnotatedThrows(String s) {}
+
+ @AutoFactory
+ ConstructorAnnotatedThrows(@Provided Object obj, int i) throws IOException {}
+
+ @AutoFactory
+ ConstructorAnnotatedThrows(@Provided Object obj, char c) throws InterruptedException {}
+}
diff --git a/factory/src/test/resources/good/FactoryExtendingAbstractClass.java b/factory/src/test/resources/good/FactoryExtendingAbstractClass.java
index 5511e99e..bd3a4dc7 100644
--- a/factory/src/test/resources/good/FactoryExtendingAbstractClass.java
+++ b/factory/src/test/resources/good/FactoryExtendingAbstractClass.java
@@ -20,7 +20,7 @@ import tests.FactoryExtendingAbstractClass.AbstractFactory;
@AutoFactory(extending = AbstractFactory.class)
final class FactoryExtendingAbstractClass {
- static abstract class AbstractFactory {
+ abstract static class AbstractFactory {
abstract FactoryExtendingAbstractClass newInstance();
}
}
diff --git a/factory/src/test/resources/good/FactoryExtendingAbstractClassThrows.java b/factory/src/test/resources/good/FactoryExtendingAbstractClassThrows.java
new file mode 100644
index 00000000..2ac43f3f
--- /dev/null
+++ b/factory/src/test/resources/good/FactoryExtendingAbstractClassThrows.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 tests;
+
+import com.google.auto.factory.AutoFactory;
+import java.io.IOException;
+import tests.FactoryExtendingAbstractClassThrows.AbstractFactory;
+
+@AutoFactory(extending = AbstractFactory.class)
+final class FactoryExtendingAbstractClassThrows {
+ FactoryExtendingAbstractClassThrows() throws IOException, InterruptedException {}
+
+ abstract static class AbstractFactory {
+ abstract FactoryExtendingAbstractClassThrows newInstance() throws Exception;
+ }
+}
diff --git a/factory/src/test/resources/good/FactoryExtendingAbstractClassWithMultipleConstructors.java b/factory/src/test/resources/good/FactoryExtendingAbstractClassWithMultipleConstructors.java
index 43e94ce1..20e0b838 100644
--- a/factory/src/test/resources/good/FactoryExtendingAbstractClassWithMultipleConstructors.java
+++ b/factory/src/test/resources/good/FactoryExtendingAbstractClassWithMultipleConstructors.java
@@ -20,10 +20,11 @@ import tests.FactoryExtendingAbstractClassWithMultipleConstructors.AbstractFacto
@AutoFactory(extending = AbstractFactory.class)
final class FactoryExtendingAbstractClassWithMultipleConstructors {
- static abstract class AbstractFactory {
+ abstract static class AbstractFactory {
protected AbstractFactory(Object obj) {}
+
protected AbstractFactory() {}
-
+
abstract FactoryExtendingAbstractClassWithMultipleConstructors newInstance();
}
}
diff --git a/factory/src/test/resources/good/FactoryImplementingCreateMethod.java b/factory/src/test/resources/good/FactoryImplementingCreateMethod.java
index db15eefe..d2659667 100644
--- a/factory/src/test/resources/good/FactoryImplementingCreateMethod.java
+++ b/factory/src/test/resources/good/FactoryImplementingCreateMethod.java
@@ -26,7 +26,7 @@ final class FactoryImplementingCreateMethod {
Interface create();
Interface create(int a);
-
+
Interface create(List<Integer> generic);
}
diff --git a/factory/src/test/resources/good/MixedDepsImplementingInterfaces.java b/factory/src/test/resources/good/MixedDepsImplementingInterfaces.java
index c7435edd..05ee0df9 100644
--- a/factory/src/test/resources/good/MixedDepsImplementingInterfaces.java
+++ b/factory/src/test/resources/good/MixedDepsImplementingInterfaces.java
@@ -24,18 +24,18 @@ import com.google.auto.factory.Provided;
final class MixedDepsImplementingInterfaces {
@AutoFactory(implementing = {FromInt.class, MarkerA.class})
MixedDepsImplementingInterfaces(@Provided String s, int i) {}
-
+
@AutoFactory(implementing = {FromObject.class, MarkerB.class})
MixedDepsImplementingInterfaces(Object o) {}
interface FromInt {
MixedDepsImplementingInterfaces fromInt(int i);
}
-
+
interface FromObject {
MixedDepsImplementingInterfaces fromObject(Object o);
}
-
+
interface MarkerA {}
interface MarkerB {}
diff --git a/factory/src/test/resources/good/MultipleFactoriesImplementingInterface.java b/factory/src/test/resources/good/MultipleFactoriesImplementingInterface.java
index 2eecf1ae..f7709ec0 100644
--- a/factory/src/test/resources/good/MultipleFactoriesImplementingInterface.java
+++ b/factory/src/test/resources/good/MultipleFactoriesImplementingInterface.java
@@ -17,7 +17,7 @@ package tests;
import com.google.auto.factory.AutoFactory;
-class MultipleFactoriesImplementingInterface {
+class MultipleFactoriesImplementingInterface {
static interface Base {
static interface Factory {
public abstract Base abstractNonDefaultCreate();
@@ -25,8 +25,8 @@ class MultipleFactoriesImplementingInterface {
}
@AutoFactory(implementing = Base.Factory.class)
- static class ClassA implements Base { }
+ static class ClassA implements Base {}
@AutoFactory(implementing = Base.Factory.class)
static class ClassB implements Base {}
-}
+}
diff --git a/factory/src/test/resources/good/SimpleClassImplementingMarker.java b/factory/src/test/resources/good/SimpleClassImplementingMarker.java
index 24e3abc0..52a1fd5a 100644
--- a/factory/src/test/resources/good/SimpleClassImplementingMarker.java
+++ b/factory/src/test/resources/good/SimpleClassImplementingMarker.java
@@ -1,11 +1,11 @@
/*
* Copyright 2013 Google LLC
- *
+ *
* 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
@@ -17,5 +17,4 @@ import com.google.auto.factory.AutoFactory;
import java.util.RandomAccess;
@AutoFactory(implementing = RandomAccess.class)
-class SimpleClassImplementingMarker {
-}
+class SimpleClassImplementingMarker {}
diff --git a/factory/src/test/resources/good/SimpleClassProvidedDeps.java b/factory/src/test/resources/good/SimpleClassProvidedDeps.java
index ffcefd2a..fa9e0c41 100644
--- a/factory/src/test/resources/good/SimpleClassProvidedDeps.java
+++ b/factory/src/test/resources/good/SimpleClassProvidedDeps.java
@@ -1,11 +1,11 @@
/*
* Copyright 2013 Google LLC
- *
+ *
* 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
diff --git a/factory/src/test/resources/good/SimpleClassThrows.java b/factory/src/test/resources/good/SimpleClassThrows.java
new file mode 100644
index 00000000..67155952
--- /dev/null
+++ b/factory/src/test/resources/good/SimpleClassThrows.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 tests;
+
+import com.google.auto.factory.AutoFactory;
+import java.io.IOException;
+
+@AutoFactory
+final class SimpleClassThrows {
+ SimpleClassThrows() throws IOException, InterruptedException {}
+}
diff --git a/factory/src/test/resources/support/QualifierWithArgs.java b/factory/src/test/resources/support/QualifierWithArgs.java
index 81e3f84b..89f54eb6 100644
--- a/factory/src/test/resources/support/QualifierWithArgs.java
+++ b/factory/src/test/resources/support/QualifierWithArgs.java
@@ -29,5 +29,6 @@ import javax.inject.Qualifier;
@Retention(RUNTIME)
@interface QualifierWithArgs {
String name();
+
int count();
}
diff --git a/service/pom.xml b/service/pom.xml
index a64ca050..e29bdc37 100644
--- a/service/pom.xml
+++ b/service/pom.xml
@@ -37,8 +37,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
- <guava.version>27.0.1-jre</guava.version>
- <truth.version>1.0.1</truth.version>
+ <guava.version>30.1.1-jre</guava.version>
+ <truth.version>1.1.3</truth.version>
</properties>
<scm>
@@ -90,7 +90,7 @@
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
- <version>0.18</version>
+ <version>0.19</version>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
@@ -100,7 +100,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.12</version>
+ <version>4.13.2</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -111,7 +111,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -123,14 +123,14 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
</plugin>
</plugins>
</pluginManagement>
diff --git a/service/processor/pom.xml b/service/processor/pom.xml
index 22fc20e9..262493a9 100644
--- a/service/processor/pom.xml
+++ b/service/processor/pom.xml
@@ -49,7 +49,7 @@
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
- <version>0.10</version>
+ <version>1.1</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
diff --git a/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java b/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java
index 3bf42d91..f12299a5 100644
--- a/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java
+++ b/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java
@@ -17,8 +17,10 @@ package com.google.auto.service.processor;
import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.MoreElements.getAnnotationMirror;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
+import static com.google.common.base.Throwables.getStackTraceAsString;
+import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
@@ -28,8 +30,7 @@ import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -57,10 +58,11 @@ import javax.tools.StandardLocation;
* configuration files described in {@link java.util.ServiceLoader}.
* <p>
* Processor Options:<ul>
- * <li>debug - turns on debug statements</li>
+ * <li>{@code -Adebug} - turns on debug statements</li>
+ * <li>{@code -Averify=true} - turns on extra verification</li>
* </ul>
*/
-@SupportedOptions({ "debug", "verify" })
+@SupportedOptions({"debug", "verify"})
public class AutoServiceProcessor extends AbstractProcessor {
@VisibleForTesting
@@ -74,7 +76,7 @@ public class AutoServiceProcessor extends AbstractProcessor {
* {@code "com.google.apphosting.LocalRpcService" ->
* "com.google.apphosting.datastore.LocalDatastoreService"}
*/
- private Multimap<String, String> providers = HashMultimap.create();
+ private final Multimap<String, String> providers = HashMultimap.create();
@Override
public ImmutableSet<String> getSupportedAnnotationTypes() {
@@ -104,28 +106,24 @@ public class AutoServiceProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
try {
- return processImpl(annotations, roundEnv);
- } catch (Exception e) {
+ processImpl(annotations, roundEnv);
+ } catch (RuntimeException e) {
// We don't allow exceptions of any kind to propagate to the compiler
- StringWriter writer = new StringWriter();
- e.printStackTrace(new PrintWriter(writer));
- fatalError(writer.toString());
- return true;
+ fatalError(getStackTraceAsString(e));
}
+ return false;
}
- private boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ private void processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
generateConfigFiles();
} else {
processAnnotations(annotations, roundEnv);
}
-
- return true;
}
- private void processAnnotations(Set<? extends TypeElement> annotations,
- RoundEnvironment roundEnv) {
+ private void processAnnotations(
+ Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
@@ -134,7 +132,7 @@ public class AutoServiceProcessor extends AbstractProcessor {
for (Element e : elements) {
// TODO(gak): check for error trees?
- TypeElement providerImplementer = (TypeElement) e;
+ TypeElement providerImplementer = MoreElements.asType(e);
AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class).get();
Set<DeclaredType> providerInterfaces = getValueFieldOfClasses(annotationMirror);
if (providerInterfaces.isEmpty()) {
@@ -147,12 +145,14 @@ public class AutoServiceProcessor extends AbstractProcessor {
log("provider interface: " + providerType.getQualifiedName());
log("provider implementer: " + providerImplementer.getQualifiedName());
- if (checkImplementer(providerImplementer, providerType)) {
+ if (checkImplementer(providerImplementer, providerType, annotationMirror)) {
providers.put(getBinaryName(providerType), getBinaryName(providerImplementer));
} else {
- String message = "ServiceProviders must implement their service provider interface. "
- + providerImplementer.getQualifiedName() + " does not implement "
- + providerType.getQualifiedName();
+ String message =
+ "ServiceProviders must implement their service provider interface. "
+ + providerImplementer.getQualifiedName()
+ + " does not implement "
+ + providerType.getQualifiedName();
error(message, e, annotationMirror);
}
}
@@ -172,8 +172,8 @@ public class AutoServiceProcessor extends AbstractProcessor {
// before we attempt to get the resource in case the behavior
// of filer.getResource does change to match the spec, but there's
// no good way to resolve CLASS_OUTPUT without first getting a resource.
- FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "",
- resourceFile);
+ FileObject existingFile =
+ filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
log("Looking for existing resource file at " + existingFile.toUri());
Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
log("Existing service entries: " + oldServices);
@@ -187,19 +187,18 @@ public class AutoServiceProcessor extends AbstractProcessor {
log("Resource file did not already exist.");
}
- Set<String> newServices = new HashSet<String>(providers.get(providerInterface));
- if (allServices.containsAll(newServices)) {
+ Set<String> newServices = new HashSet<>(providers.get(providerInterface));
+ if (!allServices.addAll(newServices)) {
log("No new service entries being added.");
- return;
+ continue;
}
- allServices.addAll(newServices);
log("New service file contents: " + allServices);
- FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "",
- resourceFile);
- OutputStream out = fileObject.openOutputStream();
- ServicesFiles.writeServiceFile(allServices, out);
- out.close();
+ FileObject fileObject =
+ filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
+ try (OutputStream out = fileObject.openOutputStream()) {
+ ServicesFiles.writeServiceFile(allServices, out);
+ }
log("Wrote to: " + fileObject.toUri());
} catch (IOException e) {
fatalError("Unable to create " + resourceFile + ", " + e);
@@ -209,14 +208,17 @@ public class AutoServiceProcessor extends AbstractProcessor {
}
/**
- * Verifies {@link ServiceProvider} constraints on the concrete provider class.
- * Note that these constraints are enforced at runtime via the ServiceLoader,
- * we're just checking them at compile time to be extra nice to our users.
+ * Verifies {@link ServiceProvider} constraints on the concrete provider class. Note that these
+ * constraints are enforced at runtime via the ServiceLoader, we're just checking them at compile
+ * time to be extra nice to our users.
*/
- private boolean checkImplementer(TypeElement providerImplementer, TypeElement providerType) {
+ private boolean checkImplementer(
+ TypeElement providerImplementer,
+ TypeElement providerType,
+ AnnotationMirror annotationMirror) {
String verify = processingEnv.getOptions().get("verify");
- if (verify == null || !Boolean.valueOf(verify)) {
+ if (verify == null || !Boolean.parseBoolean(verify)) {
return true;
}
@@ -225,7 +227,37 @@ public class AutoServiceProcessor extends AbstractProcessor {
Types types = processingEnv.getTypeUtils();
- return types.isSubtype(providerImplementer.asType(), providerType.asType());
+ if (types.isSubtype(providerImplementer.asType(), providerType.asType())) {
+ return true;
+ }
+
+ // Maybe the provider has generic type, but the argument to @AutoService can't be generic.
+ // So we allow that with a warning, which can be suppressed with @SuppressWarnings("rawtypes").
+ // See https://github.com/google/auto/issues/870.
+ if (types.isSubtype(providerImplementer.asType(), types.erasure(providerType.asType()))) {
+ if (!rawTypesSuppressed(providerImplementer)) {
+ warning(
+ "Service provider "
+ + providerType
+ + " is generic, so it can't be named exactly by @AutoService."
+ + " If this is OK, add @SuppressWarnings(\"rawtypes\").",
+ providerImplementer,
+ annotationMirror);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean rawTypesSuppressed(Element element) {
+ for (; element != null; element = element.getEnclosingElement()) {
+ SuppressWarnings suppress = element.getAnnotation(SuppressWarnings.class);
+ if (suppress != null && Arrays.asList(suppress.value()).contains("rawtypes")) {
+ return true;
+ }
+ }
+ return false;
}
/**
@@ -241,19 +273,20 @@ public class AutoServiceProcessor extends AbstractProcessor {
Element enclosingElement = element.getEnclosingElement();
if (enclosingElement instanceof PackageElement) {
- PackageElement pkg = (PackageElement) enclosingElement;
+ PackageElement pkg = MoreElements.asPackage(enclosingElement);
if (pkg.isUnnamed()) {
return className;
}
return pkg.getQualifiedName() + "." + className;
}
- TypeElement typeElement = (TypeElement) enclosingElement;
+ TypeElement typeElement = MoreElements.asType(enclosingElement);
return getBinaryNameImpl(typeElement, typeElement.getSimpleName() + "$" + className);
}
/**
- * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code annotationMirror}.
+ * Returns the contents of a {@code Class[]}-typed "value" field in a given {@code
+ * annotationMirror}.
*/
private ImmutableSet<DeclaredType> getValueFieldOfClasses(AnnotationMirror annotationMirror) {
return getAnnotationValue(annotationMirror, "value")
@@ -261,16 +294,15 @@ public class AutoServiceProcessor extends AbstractProcessor {
new SimpleAnnotationValueVisitor8<ImmutableSet<DeclaredType>, Void>() {
@Override
public ImmutableSet<DeclaredType> visitType(TypeMirror typeMirror, Void v) {
- // TODO(ronshapiro): class literals may not always be declared types, i.e. int.class,
- // int[].class
+ // TODO(ronshapiro): class literals may not always be declared types, i.e.
+ // int.class, int[].class
return ImmutableSet.of(MoreTypes.asDeclared(typeMirror));
}
@Override
public ImmutableSet<DeclaredType> visitArray(
List<? extends AnnotationValue> values, Void v) {
- return values
- .stream()
+ return values.stream()
.flatMap(value -> value.accept(this, null).stream())
.collect(toImmutableSet());
}
@@ -284,6 +316,10 @@ public class AutoServiceProcessor extends AbstractProcessor {
}
}
+ private void warning(String msg, Element element, AnnotationMirror annotation) {
+ processingEnv.getMessager().printMessage(Kind.WARNING, msg, element, annotation);
+ }
+
private void error(String msg, Element element, AnnotationMirror annotation) {
processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation);
}
diff --git a/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java b/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java
index c61b3ba2..75d6cca7 100644
--- a/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java
+++ b/service/processor/src/main/java/com/google/auto/service/processor/ServicesFiles.java
@@ -35,7 +35,7 @@ import java.util.Set;
final class ServicesFiles {
public static final String SERVICES_PATH = "META-INF/services";
- private ServicesFiles() { }
+ private ServicesFiles() {}
/**
* Returns an absolute path to a service file given the class
@@ -96,4 +96,4 @@ final class ServicesFiles {
}
writer.flush();
}
-} \ No newline at end of file
+}
diff --git a/service/processor/src/main/java/com/google/auto/service/processor/package-info.java b/service/processor/src/main/java/com/google/auto/service/processor/package-info.java
index a9f0adb5..453c95e8 100644
--- a/service/processor/src/main/java/com/google/auto/service/processor/package-info.java
+++ b/service/processor/src/main/java/com/google/auto/service/processor/package-info.java
@@ -15,4 +15,4 @@
* This package contains the annotation processor that implements the
* {@link com.google.auto.service.AutoService} API.
*/
-package com.google.auto.service.processor; \ No newline at end of file
+package com.google.auto.service.processor;
diff --git a/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java b/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java
index d3e00a73..35615689 100644
--- a/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java
+++ b/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java
@@ -16,52 +16,132 @@
package com.google.auto.service.processor;
import static com.google.auto.service.processor.AutoServiceProcessor.MISSING_SERVICES_ERROR;
-import static com.google.testing.compile.JavaSourcesSubject.assertThat;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import com.google.common.io.Resources;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
import com.google.testing.compile.JavaFileObjects;
+import javax.tools.StandardLocation;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests the {@link AutoServiceProcessor}.
- */
+/** Tests the {@link AutoServiceProcessor}. */
@RunWith(JUnit4.class)
public class AutoServiceProcessorTest {
@Test
public void autoService() {
- assertThat(
- JavaFileObjects.forResource("test/SomeService.java"),
- JavaFileObjects.forResource("test/SomeServiceProvider1.java"),
- JavaFileObjects.forResource("test/SomeServiceProvider2.java"),
- JavaFileObjects.forResource("test/Enclosing.java"),
- JavaFileObjects.forResource("test/AnotherService.java"),
- JavaFileObjects.forResource("test/AnotherServiceProvider.java"))
- .processedWith(new AutoServiceProcessor())
- .compilesWithoutError()
- .and().generatesFiles(
- JavaFileObjects.forResource("META-INF/services/test.SomeService"),
- JavaFileObjects.forResource("META-INF/services/test.AnotherService"));
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .compile(
+ JavaFileObjects.forResource("test/SomeService.java"),
+ JavaFileObjects.forResource("test/SomeServiceProvider1.java"),
+ JavaFileObjects.forResource("test/SomeServiceProvider2.java"),
+ JavaFileObjects.forResource("test/Enclosing.java"),
+ JavaFileObjects.forResource("test/AnotherService.java"),
+ JavaFileObjects.forResource("test/AnotherServiceProvider.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedFile(StandardLocation.CLASS_OUTPUT, "META-INF/services/test.SomeService")
+ .hasContents(
+ Resources.asByteSource(Resources.getResource("META-INF/services/test.SomeService")));
+ assertThat(compilation)
+ .generatedFile(StandardLocation.CLASS_OUTPUT, "META-INF/services/test.AnotherService")
+ .hasContents(
+ Resources.asByteSource(Resources.getResource("META-INF/services/test.AnotherService")));
}
@Test
public void multiService() {
- assertThat(
- JavaFileObjects.forResource("test/SomeService.java"),
- JavaFileObjects.forResource("test/AnotherService.java"),
- JavaFileObjects.forResource("test/MultiServiceProvider.java"))
- .processedWith(new AutoServiceProcessor())
- .compilesWithoutError()
- .and().generatesFiles(
- JavaFileObjects.forResource("META-INF/services/test.SomeServiceMulti"),
- JavaFileObjects.forResource("META-INF/services/test.AnotherServiceMulti"));
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .compile(
+ JavaFileObjects.forResource("test/SomeService.java"),
+ JavaFileObjects.forResource("test/AnotherService.java"),
+ JavaFileObjects.forResource("test/MultiServiceProvider.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ // We have @AutoService({SomeService.class, AnotherService.class}) class MultiServiceProvider.
+ // So we expect META-INF/services/test.SomeService with contents that name MultiServiceProvider
+ // and likewise META-INF/services/test.AnotherService.
+ assertThat(compilation)
+ .generatedFile(StandardLocation.CLASS_OUTPUT, "META-INF/services/test.SomeService")
+ .contentsAsUtf8String()
+ .isEqualTo("test.MultiServiceProvider\n");
+ assertThat(compilation)
+ .generatedFile(StandardLocation.CLASS_OUTPUT, "META-INF/services/test.AnotherService")
+ .contentsAsUtf8String()
+ .isEqualTo("test.MultiServiceProvider\n");
}
@Test
public void badMultiService() {
- assertThat(JavaFileObjects.forResource("test/NoServices.java"))
- .processedWith(new AutoServiceProcessor())
- .failsToCompile()
- .withErrorContaining(MISSING_SERVICES_ERROR);
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .compile(JavaFileObjects.forResource("test/NoServices.java"));
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorContaining(MISSING_SERVICES_ERROR);
+ }
+
+ @Test
+ public void generic() {
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .compile(
+ JavaFileObjects.forResource("test/GenericService.java"),
+ JavaFileObjects.forResource("test/GenericServiceProvider.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedFile(StandardLocation.CLASS_OUTPUT, "META-INF/services/test.GenericService")
+ .contentsAsUtf8String()
+ .isEqualTo("test.GenericServiceProvider\n");
+ }
+
+ @Test
+ public void genericWithVerifyOption() {
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .withOptions("-Averify=true")
+ .compile(
+ JavaFileObjects.forResource("test/GenericService.java"),
+ JavaFileObjects.forResource("test/GenericServiceProvider.java"));
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .hadWarningContaining(
+ "Service provider test.GenericService is generic, so it can't be named exactly by"
+ + " @AutoService. If this is OK, add @SuppressWarnings(\"rawtypes\").");
+ }
+
+ @Test
+ public void genericWithVerifyOptionAndSuppressWarings() {
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .withOptions("-Averify=true")
+ .compile(
+ JavaFileObjects.forResource("test/GenericService.java"),
+ JavaFileObjects.forResource("test/GenericServiceProviderSuppressWarnings.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
+ public void nestedGenericWithVerifyOptionAndSuppressWarnings() {
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(new AutoServiceProcessor())
+ .withOptions("-Averify=true")
+ .compile(
+ JavaFileObjects.forResource("test/GenericService.java"),
+ JavaFileObjects.forResource("test/EnclosingGeneric.java"));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedFile(StandardLocation.CLASS_OUTPUT, "META-INF/services/test.GenericService")
+ .contentsAsUtf8String()
+ .isEqualTo("test.EnclosingGeneric$GenericServiceProvider\n");
}
}
diff --git a/service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti b/service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti
deleted file mode 100644
index f6ef36ac..00000000
--- a/service/processor/src/test/resources/META-INF/services/test.AnotherServiceMulti
+++ /dev/null
@@ -1 +0,0 @@
-test.MultiServiceProvider
diff --git a/service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti b/service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti
deleted file mode 100644
index f6ef36ac..00000000
--- a/service/processor/src/test/resources/META-INF/services/test.SomeServiceMulti
+++ /dev/null
@@ -1 +0,0 @@
-test.MultiServiceProvider
diff --git a/service/processor/src/test/resources/test/AnotherService.java b/service/processor/src/test/resources/test/AnotherService.java
index c096c223..de80f06d 100644
--- a/service/processor/src/test/resources/test/AnotherService.java
+++ b/service/processor/src/test/resources/test/AnotherService.java
@@ -15,4 +15,4 @@
*/
package test;
-interface AnotherService { } \ No newline at end of file
+interface AnotherService {}
diff --git a/service/processor/src/test/resources/test/AnotherServiceProvider.java b/service/processor/src/test/resources/test/AnotherServiceProvider.java
index c5e5c117..2a023e43 100644
--- a/service/processor/src/test/resources/test/AnotherServiceProvider.java
+++ b/service/processor/src/test/resources/test/AnotherServiceProvider.java
@@ -18,4 +18,4 @@ package test;
import com.google.auto.service.AutoService;
@AutoService(AnotherService.class)
-public class AnotherServiceProvider implements AnotherService { } \ No newline at end of file
+public class AnotherServiceProvider implements AnotherService {}
diff --git a/service/processor/src/test/resources/test/Enclosing.java b/service/processor/src/test/resources/test/Enclosing.java
index 26dd5852..24202a59 100644
--- a/service/processor/src/test/resources/test/Enclosing.java
+++ b/service/processor/src/test/resources/test/Enclosing.java
@@ -19,5 +19,5 @@ import com.google.auto.service.AutoService;
public class Enclosing {
@AutoService(SomeService.class)
- public static class NestedSomeServiceProvider implements SomeService { }
-} \ No newline at end of file
+ public static class NestedSomeServiceProvider implements SomeService {}
+}
diff --git a/service/processor/src/test/resources/test/EnclosingGeneric.java b/service/processor/src/test/resources/test/EnclosingGeneric.java
new file mode 100644
index 00000000..cddddacb
--- /dev/null
+++ b/service/processor/src/test/resources/test/EnclosingGeneric.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+/** Test for suppressing warnings about raw types on nested {@code @AutoService} classes. */
+@SuppressWarnings("rawtypes")
+public final class EnclosingGeneric {
+ /**
+ * This is technically a raw class reference, but should be suppressed by the
+ * {@code @SuppressWarnings} on the enclosing class.
+ */
+ @AutoService(GenericService.class)
+ public class GenericServiceProvider<T> implements GenericService<T> {}
+
+ private EnclosingGeneric() {}
+}
diff --git a/service/processor/src/test/resources/test/GenericService.java b/service/processor/src/test/resources/test/GenericService.java
new file mode 100644
index 00000000..5ed13ffc
--- /dev/null
+++ b/service/processor/src/test/resources/test/GenericService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 test;
+
+/**
+ * An interface with a type parameter, which by default will produce a warning with
+ * {@code @AutoService} if you compile with {@code -Averify=true}.
+ */
+public interface GenericService<T> {}
diff --git a/service/processor/src/test/resources/test/GenericServiceProvider.java b/service/processor/src/test/resources/test/GenericServiceProvider.java
new file mode 100644
index 00000000..84c5cba0
--- /dev/null
+++ b/service/processor/src/test/resources/test/GenericServiceProvider.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+/**
+ * An implementation of a service with a type parameter, which by default will produce a warning
+ * if you compile with {@code -Averify=true}.
+ */
+@AutoService(GenericService.class)
+public class GenericServiceProvider<T> implements GenericService<T> {}
diff --git a/service/processor/src/test/resources/test/GenericServiceProviderSuppressWarnings.java b/service/processor/src/test/resources/test/GenericServiceProviderSuppressWarnings.java
new file mode 100644
index 00000000..eb285dad
--- /dev/null
+++ b/service/processor/src/test/resources/test/GenericServiceProviderSuppressWarnings.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 Google LLC
+ *
+ * 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 test;
+
+import com.google.auto.service.AutoService;
+
+/**
+ * An implementation of a service with a type parameter, which will not produce a warning even if
+ * compiled with {@code -Averify=true}, because of the {@code @SuppressWarnings}.
+ */
+@AutoService(GenericService.class)
+@SuppressWarnings("rawtypes")
+public class GenericServiceProviderSuppressWarnings<T> implements GenericService<T> {}
diff --git a/service/processor/src/test/resources/test/SomeService.java b/service/processor/src/test/resources/test/SomeService.java
index d29c4097..d81cac40 100644
--- a/service/processor/src/test/resources/test/SomeService.java
+++ b/service/processor/src/test/resources/test/SomeService.java
@@ -15,4 +15,4 @@
*/
package test;
-interface SomeService { } \ No newline at end of file
+interface SomeService {}
diff --git a/service/processor/src/test/resources/test/SomeServiceProvider1.java b/service/processor/src/test/resources/test/SomeServiceProvider1.java
index 008136be..fc2c843c 100644
--- a/service/processor/src/test/resources/test/SomeServiceProvider1.java
+++ b/service/processor/src/test/resources/test/SomeServiceProvider1.java
@@ -18,4 +18,4 @@ package test;
import com.google.auto.service.AutoService;
@AutoService(SomeService.class)
-public class SomeServiceProvider1 implements SomeService { } \ No newline at end of file
+public class SomeServiceProvider1 implements SomeService {}
diff --git a/service/processor/src/test/resources/test/SomeServiceProvider2.java b/service/processor/src/test/resources/test/SomeServiceProvider2.java
index 5444996b..b7097d41 100644
--- a/service/processor/src/test/resources/test/SomeServiceProvider2.java
+++ b/service/processor/src/test/resources/test/SomeServiceProvider2.java
@@ -18,4 +18,4 @@ package test;
import com.google.auto.service.AutoService;
@AutoService(SomeService.class)
-public class SomeServiceProvider2 implements SomeService { } \ No newline at end of file
+public class SomeServiceProvider2 implements SomeService {}
diff --git a/util/generate-latest-docs.sh b/util/generate-latest-docs.sh
index 8617ba41..88c8e7fb 100755
--- a/util/generate-latest-docs.sh
+++ b/util/generate-latest-docs.sh
@@ -1,25 +1,25 @@
-# see http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/ for details
+#!/bin/bash
-if [ "$TRAVIS_REPO_SLUG" == "google/auto" ] && \
- [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \
- [ "$TRAVIS_PULL_REQUEST" == "false" ] && \
- [ "$TRAVIS_BRANCH" == "master" ]; then
- echo -e "Publishing javadoc...\n"
-
- mvn -f build-pom.xml javadoc:aggregate
- TARGET="$(pwd)/target"
+# Run by GitHub Actions (see .github/workflows/ci.yml)
- cd $HOME
- git clone --quiet --branch=gh-pages https://${GH_TOKEN}@github.com/google/auto gh-pages > /dev/null
-
- cd gh-pages
- git config --global user.email "travis@travis-ci.org"
- git config --global user.name "travis-ci"
- git rm -rf api/latest
- mv ${TARGET}/site/apidocs api/latest
- git add -A -f api/latest
- git commit -m "Latest javadoc on successful travis build $TRAVIS_BUILD_NUMBER auto-pushed to gh-pages"
- git push -fq origin gh-pages > /dev/null
+set -e
- echo -e "Published Javadoc to gh-pages.\n"
-fi
+echo -e "Publishing javadoc...\n"
+
+mvn -f build-pom.xml javadoc:aggregate
+TARGET="$(pwd)/target"
+
+cd $HOME
+git clone --quiet --branch=gh-pages "https://x-access-token:${GITHUB_TOKEN}@github.com/google/auto" gh-pages > /dev/null
+
+cd gh-pages
+git config --global user.name "$GITHUB_ACTOR"
+git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com"
+git rm -rf api/latest
+mkdir -p api # Just to make mv work if the directory is missing
+mv ${TARGET}/site/apidocs api/latest
+git add -A -f api/latest
+git commit -m "Latest javadoc on successful CI build auto-pushed to gh-pages"
+git push -fq origin gh-pages > /dev/null
+
+echo -e "Published Javadoc to gh-pages.\n"
diff --git a/util/publish-snapshot-on-commit.sh b/util/publish-snapshot-on-commit.sh
index 0b74c6bb..0166b059 100755
--- a/util/publish-snapshot-on-commit.sh
+++ b/util/publish-snapshot-on-commit.sh
@@ -1,12 +1,6 @@
-# see https://coderwall.com/p/9b_lfq
+#!/bin/bash
-if [ "$TRAVIS_REPO_SLUG" == "google/auto" ] && \
- [ "$TRAVIS_JDK_VERSION" == "oraclejdk7" ] && \
- [ "$TRAVIS_PULL_REQUEST" == "false" ] && \
- [ "$TRAVIS_BRANCH" == "master" ]; then
- echo -e "Publishing maven snapshot...\n"
+set -e
- mvn -f build-pom.xml clean source:jar deploy --settings="util/settings.xml" -DskipTests=true -Dmaven.javadoc.skip=true
-
- echo -e "Published maven snapshot"
-fi
+mvn -B dependency:go-offline test clean -U --quiet --fail-never -DskipTests=true -f build-pom.xml
+mvn -B -U source:jar deploy -DskipTests=true -f build-pom.xml
diff --git a/util/settings.xml b/util/settings.xml
deleted file mode 100644
index 91f444b2..00000000
--- a/util/settings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<settings>
- <servers>
- <server>
- <id>sonatype-nexus-snapshots</id>
- <username>${env.CI_DEPLOY_USERNAME}</username>
- <password>${env.CI_DEPLOY_PASSWORD}</password>
- </server>
- </servers>
-</settings>
diff --git a/value/Android.bp b/value/Android.bp
index 73a72154..2f554aaa 100644
--- a/value/Android.bp
+++ b/value/Android.bp
@@ -72,6 +72,23 @@ java_plugin {
}
java_plugin {
+ name: "auto_oneof_plugin",
+ static_libs: [
+ "libauto_value_plugin",
+
+ "auto_android_annotation_stubs",
+ "auto_common",
+ "auto_service_plugin",
+ "auto_value_extension",
+ "escapevelocity",
+ "guava",
+ "javapoet",
+ ],
+ processor_class: "com.google.auto.value.processor.AutoOneOfProcessor",
+ visibility: ["//visibility:public"],
+}
+
+java_plugin {
name: "auto_annotation_plugin",
static_libs: [
"libauto_value_plugin",
diff --git a/value/annotations/pom.xml b/value/annotations/pom.xml
index d6c03de5..8bc63ac9 100644
--- a/value/annotations/pom.xml
+++ b/value/annotations/pom.xml
@@ -54,6 +54,7 @@
<include>com/google/auto/value/*</include>
<include>com/google/auto/value/extension/memoized/*</include>
<include>com/google/auto/value/extension/serializable/*</include>
+ <include>com/google/auto/value/extension/toprettystring/*</include>
</includes>
</configuration>
</plugin>
diff --git a/value/pom.xml b/value/pom.xml
index dcf2cb1d..5405c7cd 100644
--- a/value/pom.xml
+++ b/value/pom.xml
@@ -37,8 +37,8 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
- <guava.version>28.1-jre</guava.version>
- <truth.version>1.0.1</truth.version>
+ <guava.version>30.1.1-jre</guava.version>
+ <truth.version>1.1.3</truth.version>
</properties>
<scm>
@@ -89,7 +89,7 @@
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
- <version>1.11.1</version>
+ <version>1.13.0</version>
</dependency>
<!-- test dependencies -->
@@ -107,7 +107,7 @@
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
- <version>0.18</version>
+ <version>0.19</version>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
@@ -122,7 +122,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
- <version>4.13</version>
+ <version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
@@ -138,7 +138,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -150,19 +150,19 @@
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
- <version>3.0.1</version>
+ <version>3.2.2</version>
<configuration>
<addTestClassPath>true</addTestClassPath>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
diff --git a/value/processor/pom.xml b/value/processor/pom.xml
index 5968352c..8892c4f6 100644
--- a/value/processor/pom.xml
+++ b/value/processor/pom.xml
@@ -40,23 +40,26 @@
<tag>HEAD</tag>
</scm>
+ <properties>
+ <auto-service.version>1.0</auto-service.version>
+ <errorprone.version>2.7.1</errorprone.version>
+ </properties>
+
<dependencies>
<dependency>
<groupId>com.google.auto</groupId>
<artifactId>auto-common</artifactId>
- <version>0.10</version>
+ <version>1.1</version>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
- <artifactId>auto-service</artifactId>
- <version>1.0-rc6</version>
- <scope>provided</scope>
+ <artifactId>auto-service-annotations</artifactId>
+ <version>${auto-service.version}</version>
</dependency>
<dependency>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
- <version>2.3.3</version>
- <scope>provided</scope>
+ <version>${errorprone.version}</version>
</dependency>
<dependency>
<groupId>com.google.escapevelocity</groupId>
@@ -66,13 +69,7 @@
<dependency>
<groupId>net.ltgt.gradle.incap</groupId>
<artifactId>incap</artifactId>
- <version>0.2</version>
- </dependency>
- <dependency>
- <groupId>net.ltgt.gradle.incap</groupId>
- <artifactId>incap-processor</artifactId>
- <version>0.2</version>
- <scope>provided</scope>
+ <version>0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@@ -90,6 +87,12 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>com.google.errorprone</groupId>
+ <artifactId>error_prone_type_annotations</artifactId>
+ <version>${errorprone.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<scope>test</scope>
@@ -122,7 +125,7 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>3.1.0</version>
+ <version>3.11.2</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -149,7 +152,55 @@
<include>com/google/auto/value/extension/memoized/processor/**/*.java</include>
<include>com/google/auto/value/extension/serializable/processor/**/*.java</include>
<include>com/google/auto/value/extension/serializable/serializer/**/*.java</include>
+ <include>com/google/auto/value/extension/toprettystring/processor/**/*.java</include>
</includes>
+ <compilerArgs>
+ <!-- This is something of a hack to allow tests to pass. Ideally we would build
+ TestStringSerializerFactory as a separate artifact, to avoid a problem when it
+ is built at the same time as @AutoValue classes that might end up finding it.
+ But by allowing a missing class to be ignored, we avoid crashing if there is a
+ META-INF/services entry for a class that the compiler has not yet generated. -->
+ <arg>-AallowedMissingSerializableExtensionClasses=.*TestStringSerializerFactory</arg>
+ </compilerArgs>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service</artifactId>
+ <version>${auto-service.version}</version>
+ </path>
+ <path>
+ <groupId>net.ltgt.gradle.incap</groupId>
+ <artifactId>incap-processor</artifactId>
+ <version>0.3</version>
+ </path>
+ </annotationProcessorPaths>
+ </configuration>
+ <executions>
+ <execution>
+ <id>default-testCompile</id>
+ <configuration>
+ <annotationProcessorPaths>
+ <path>
+ <groupId>com.google.auto.value</groupId>
+ <artifactId>auto-value</artifactId>
+ <version>${project.version}</version>
+ </path>
+ <path>
+ <groupId>com.google.auto.service</groupId>
+ <artifactId>auto-service</artifactId>
+ <version>${auto-service.version}</version>
+ </path>
+ </annotationProcessorPaths>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.22.2</version>
+ <configuration>
+ <argLine>${test.jvm.flags}</argLine>
</configuration>
</plugin>
<plugin>
@@ -217,5 +268,14 @@
<additionalparam>-Xdoclint:none</additionalparam>
</properties>
</profile>
+ <profile>
+ <id>open-modules</id>
+ <activation>
+ <jdk>[9,)</jdk>
+ </activation>
+ <properties>
+ <test.jvm.flags>--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</test.jvm.flags>
+ </properties>
+ </profile>
</profiles>
</project>
diff --git a/value/src/it/functional/pom.xml b/value/src/it/functional/pom.xml
index 750b9c43..d4ae1386 100644
--- a/value/src/it/functional/pom.xml
+++ b/value/src/it/functional/pom.xml
@@ -32,6 +32,7 @@
<version>1.7.4</version>
<name>Auto-Value Functional Integration Test</name>
<properties>
+ <kotlin.version>1.5.21</kotlin.version>
<exclude.tests>this-matches-nothing</exclude.tests>
</properties>
<dependencies>
@@ -48,7 +49,7 @@
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
- <version>1.0-rc6</version>
+ <version>1.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
@@ -62,7 +63,7 @@
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
- <version>2.8.2</version>
+ <version>2.9.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
@@ -90,21 +91,32 @@
<scope>test</scope>
</dependency>
<dependency>
+ <groupId>dev.gradleplugins</groupId>
+ <artifactId>gradle-test-kit</artifactId>
+ <version>6.8.3</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>3.1.0</version>
+ <version>3.11.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jdt</groupId>
<artifactId>ecj</artifactId>
- <version>3.20.0</version>
+ <version>3.25.0</version>
</dependency>
<dependency>
<groupId>com.google.escapevelocity</groupId>
<artifactId>escapevelocity</artifactId>
<version>0.9.1</version>
</dependency>
+ <dependency>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-stdlib</artifactId>
+ <version>${kotlin.version}</version>
+ </dependency>
</dependencies>
<build>
@@ -112,17 +124,49 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.jetbrains.kotlin</groupId>
+ <artifactId>kotlin-maven-plugin</artifactId>
+ <version>${kotlin.version}</version>
+ <executions>
+ <!--
+ The Kotlin configuration here is a bit unusual. JetBrains recommends
+ <https://kotlinlang.org/docs/maven.html#compile-kotlin-and-java-sources>
+ a fairly invasive reconfiguration of the maven-compiler-plugin (which compiles Java)
+ in order to ensure that the Kotlin compiler can run first. In our case, we have just
+ one Kotlin file that a test in Java accesses. So, even though it is in src/test/java,
+ we compile it in the `compile` goal, which means it is available to the Java test sources
+ when they are compiled in the `default-testCompile` goal.
+
+ Currently if you want to use JDK ≥ 16 then you must set
+ JAVA_TOOL_OPTIONS=__illegal-access=permit
+ except the two underscores should be dashes (which XML comments won't allow us to write).
+ This is a bug that will presumably be fixed in a forthcoming version.
+ -->
+ <execution>
+ <id>compile</id>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ <configuration>
+ <sourceDirs>
+ <sourceDir>${project.basedir}/src/test/java</sourceDir>
+ </sourceDirs>
+ </configuration>
+ </execution>
+ </executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
<configuration>
@@ -132,6 +176,7 @@
<arg>-Xlint:all</arg>
<arg>-encoding</arg>
<arg>utf8</arg>
+ <arg>-Acom.google.auto.value.AutoBuilderIsUnstable</arg>
</compilerArgs>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
@@ -143,12 +188,23 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
- <version>2.7</version>
+ <version>2.8.2</version>
<configuration>
<!-- Build/test, but don't deploy -->
<skip>true</skip>
</configuration>
</plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.22.2</version>
+ <configuration>
+ <argLine>${test.jvm.flags}</argLine>
+ <systemPropertyVariables>
+ <autoValueVersion>${project.version}</autoValueVersion>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
</plugins>
</build>
@@ -160,12 +216,12 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
<configuration>
@@ -195,5 +251,27 @@
<exclude.tests>**/AutoValueJava8Test.java</exclude.tests>
</properties>
</profile>
+ <profile>
+ <id>open-modules</id>
+ <activation>
+ <jdk>[9,)</jdk>
+ </activation>
+ <properties>
+ <test.jvm.flags>--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED</test.jvm.flags>
+ </properties>
+ </profile>
+ <profile>
+ <!-- Before JDK 11, parameter names from already-compiled classes are not reliably available
+ to the compiler even when they are present in class files. Since our Kotlin test file
+ obviously has to be compiled separately from the Java test that uses it,
+ AutoBuilderKotlinTest doesn't pass on earlier JDK versions. -->
+ <id>exclude-separate-compilation-parameter-names</id>
+ <activation>
+ <jdk>(,11)</jdk>
+ </activation>
+ <properties>
+ <exclude.tests>**/AutoBuilderKotlinTest.java</exclude.tests>
+ </properties>
+ </profile>
</profiles>
</project>
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoAnnotationTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoAnnotationTest.java
index ca1ef6b9..a04d41f3 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoAnnotationTest.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoAnnotationTest.java
@@ -15,20 +15,24 @@
*/
package com.google.auto.value;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import com.google.auto.value.annotations.Empty;
import com.google.auto.value.annotations.GwtArrays;
import com.google.auto.value.annotations.StringValues;
+import com.google.common.base.StandardSystemProperty;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.primitives.Ints;
import com.google.common.testing.EqualsTester;
+import com.google.common.testing.SerializableTester;
+import java.io.ObjectStreamClass;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -38,6 +42,7 @@ import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+import org.junit.AssumptionViolatedException;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -66,13 +71,41 @@ public class AutoAnnotationTest {
}
@Test
+ public void testEqualsParameterAnnotation() throws ReflectiveOperationException {
+ assume()
+ .that(Double.parseDouble(StandardSystemProperty.JAVA_SPECIFICATION_VERSION.value()))
+ .isAtLeast(8.0);
+ Class<? extends Annotation> jspecifyNullable;
+ try {
+ // We write this using .concat in order to hide it from rewriting rules.
+ jspecifyNullable =
+ Class.forName("org".concat(".jspecify.nullness.Nullable")).asSubclass(Annotation.class);
+ } catch (ClassNotFoundException e) {
+ throw new AssumptionViolatedException("No JSpecify @Nullable available", e);
+ }
+ @SuppressWarnings("GetClassOnAnnotation") // yes, I really want the implementation class
+ Class<? extends StringValues> autoAnnotationImpl = newStringValues(new String[0]).getClass();
+ Method equals = autoAnnotationImpl.getDeclaredMethod("equals", Object.class);
+ // The remaining faffing around with reflection is there because we have a Google-internal test
+ // that runs this code with -source 7 -target 7. We're really just doing this:
+ // assertThat(equals.getAnnotatedParameterTypes()[0].isAnnotationPresent(jspecifyNullable))
+ // .isTrue();
+ Method getAnnotatedParameterTypes = Method.class.getMethod("getAnnotatedParameterTypes");
+ Object[] annotatedParameterTypes = (Object[]) getAnnotatedParameterTypes.invoke(equals);
+ Method isAnnotationPresent =
+ annotatedParameterTypes[0].getClass().getMethod("isAnnotationPresent", Class.class);
+ assertThat(isAnnotationPresent.invoke(annotatedParameterTypes[0], jspecifyNullable))
+ .isEqualTo(true);
+ }
+
+ @Test
public void testArraysAreCloned() {
String[] array = {"Jekyll"};
StringValues stringValues = newStringValues(array);
array[0] = "Hyde";
- assertEquals("Jekyll", stringValues.value()[0]);
+ assertThat(stringValues.value()).asList().containsExactly("Jekyll");
stringValues.value()[0] = "Hyde";
- assertEquals("Jekyll", stringValues.value()[0]);
+ assertThat(stringValues.value()[0]).isEqualTo("Jekyll");
}
@Test
@@ -80,12 +113,12 @@ public class AutoAnnotationTest {
String[] strings = {"Jekyll"};
int[] ints = {2, 3, 5};
GwtArrays arrays = newGwtArrays(strings, ints);
- assertEquals(ImmutableList.of("Jekyll"), ImmutableList.copyOf(arrays.strings()));
- assertEquals(ImmutableList.of(2, 3, 5), Ints.asList(arrays.ints()));
+ assertThat(arrays.strings()).asList().containsExactly("Jekyll");
+ assertThat(arrays.ints()).asList().containsExactly(2, 3, 5).inOrder();
strings[0] = "Hyde";
ints[0] = -1;
- assertEquals(ImmutableList.of("Jekyll"), ImmutableList.copyOf(arrays.strings()));
- assertEquals(ImmutableList.of(2, 3, 5), Ints.asList(arrays.ints()));
+ assertThat(arrays.strings()).asList().containsExactly("Jekyll");
+ assertThat(arrays.ints()).asList().containsExactly(2, 3, 5).inOrder();
}
@AutoAnnotation
@@ -406,7 +439,39 @@ public class AutoAnnotationTest {
.testEquals();
}
+ @Test
+ public void testSerialization() {
+ Annotation[] instances = {EVERYTHING_FROM_AUTO, EVERYTHING_FROM_AUTO_COLLECTIONS};
+ for (Annotation instance : instances) {
+ SerializableTester.reserializeAndAssert(instance);
+ }
+ }
+
+ @Test
+ @SuppressWarnings("GetClassOnAnnotation") // yes, we really do want the implementation classes
+ public void testSerialVersionUid() {
+ Class<? extends Everything> everythingImpl = EVERYTHING_FROM_AUTO.getClass();
+ Class<? extends Everything> everythingFromCollectionsImpl =
+ EVERYTHING_FROM_AUTO_COLLECTIONS.getClass();
+ assertThat(everythingImpl).isNotEqualTo(everythingFromCollectionsImpl);
+ long everythingUid = ObjectStreamClass.lookup(everythingImpl).getSerialVersionUID();
+ long everythingFromCollectionsUid =
+ ObjectStreamClass.lookup(everythingFromCollectionsImpl).getSerialVersionUID();
+ // Two different implementations of the same annotation with the same members being provided
+ // (not defaulted) should have the same serialVersionUID. They won't be serial-compatible, of
+ // course, because their classes are different. So we're really just checking that the
+ // serialVersionUID depends only on the names and types of those members.
+ assertThat(everythingFromCollectionsUid).isEqualTo(everythingUid);
+ Class<? extends StringValues> stringValuesImpl = newStringValues(new String[0]).getClass();
+ long stringValuesUid = ObjectStreamClass.lookup(stringValuesImpl).getSerialVersionUID();
+ // The previous assertion would be vacuously true if every implementation had the same
+ // serialVersionUID, so check that that's not true.
+ assertThat(stringValuesUid).isNotEqualTo(everythingUid);
+ }
+
public static class IntList extends ArrayList<Integer> {
+ private static final long serialVersionUID = 1L;
+
IntList(Collection<Integer> c) {
super(c);
}
@@ -440,7 +505,7 @@ public class AutoAnnotationTest {
IntList intList = new IntList(ImmutableList.of(1, 2, 3));
IntArray actual = newIntArray(intList);
IntArray expected = AnnotatedWithIntArray.class.getAnnotation(IntArray.class);
- assertEquals(expected, actual);
+ assertThat(actual).isEqualTo(expected);
}
@Test
@@ -461,8 +526,8 @@ public class AutoAnnotationTest {
+ "@com.google.auto.value.annotations.StringValues([\"foo\", \"bar\"])"
+ "]"
+ ")";
- assertEquals(expected, EVERYTHING_FROM_AUTO.toString());
- assertEquals(expected, EVERYTHING_FROM_AUTO_COLLECTIONS.toString());
+ assertThat(EVERYTHING_FROM_AUTO.toString()).isEqualTo(expected);
+ assertThat(EVERYTHING_FROM_AUTO_COLLECTIONS.toString()).isEqualTo(expected);
}
@Test
@@ -475,7 +540,7 @@ public class AutoAnnotationTest {
String expected =
"@com.google.auto.value.annotations.StringValues("
+ "[\"\", \"\\r\\n\", \"hello, world\", \"Éamonn\", \"\\007\\uffef\"])";
- assertEquals(expected, instance.toString());
+ assertThat(instance.toString()).isEqualTo(expected);
}
@Retention(RetentionPolicy.RUNTIME)
@@ -498,7 +563,7 @@ public class AutoAnnotationTest {
ImmutableList.<Class<? extends Annotation>>of(AnnotationsAnnotation.class));
AnnotationsAnnotation fromReflect =
AnnotatedWithAnnotationsAnnotation.class.getAnnotation(AnnotationsAnnotation.class);
- assertEquals(fromReflect, generated);
+ assertThat(generated).isEqualTo(fromReflect);
}
@Retention(RetentionPolicy.RUNTIME)
@@ -520,7 +585,7 @@ public class AutoAnnotationTest {
newClassesAnnotation(Arrays.<Class<?>>asList(AnnotationsAnnotation.class));
ClassesAnnotation fromReflect =
AnnotatedWithClassesAnnotation.class.getAnnotation(ClassesAnnotation.class);
- assertEquals(fromReflect, generated);
+ assertThat(generated).isEqualTo(fromReflect);
}
@Retention(RetentionPolicy.RUNTIME)
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java
new file mode 100644
index 00000000..1dc346c9
--- /dev/null
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderKotlinTest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AutoBuilderKotlinTest {
+ @AutoBuilder(ofClass = KotlinData.class)
+ abstract static class KotlinDataBuilder {
+ static KotlinDataBuilder builder() {
+ return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataBuilder();
+ }
+
+ abstract KotlinDataBuilder setInt(int x);
+
+ abstract KotlinDataBuilder setString(String x);
+
+ abstract KotlinData build();
+ }
+
+ @Test
+ public void simpleKotlin() {
+ KotlinData x = KotlinDataBuilder.builder().setInt(23).setString("skidoo").build();
+ assertThat(x.getInt()).isEqualTo(23);
+ assertThat(x.getString()).isEqualTo("skidoo");
+ }
+
+ @AutoBuilder(ofClass = KotlinDataWithNullable.class)
+ abstract static class KotlinDataWithNullableBuilder {
+ static KotlinDataWithNullableBuilder builder() {
+ return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataWithNullableBuilder();
+ }
+
+ abstract KotlinDataWithNullableBuilder setAnInt(int x);
+
+ abstract KotlinDataWithNullableBuilder setAString(String x);
+
+ abstract KotlinDataWithNullable build();
+ }
+
+ @Test
+ public void kotlinWithNullable() {
+ KotlinDataWithNullable empty = KotlinDataWithNullableBuilder.builder().build();
+ assertThat(empty.getAnInt()).isNull();
+ assertThat(empty.getAString()).isNull();
+
+ KotlinDataWithNullable notEmpty =
+ KotlinDataWithNullableBuilder.builder().setAString("answer").setAnInt(42).build();
+ assertThat(notEmpty.getAString()).isEqualTo("answer");
+ assertThat(notEmpty.getAnInt()).isEqualTo(42);
+ }
+
+ @AutoBuilder(ofClass = KotlinDataWithDefaults.class)
+ abstract static class KotlinDataWithDefaultsBuilder {
+ static KotlinDataWithDefaultsBuilder builder() {
+ return new AutoBuilder_AutoBuilderKotlinTest_KotlinDataWithDefaultsBuilder();
+ }
+
+ abstract KotlinDataWithDefaultsBuilder setAnInt(int x);
+
+ abstract KotlinDataWithDefaultsBuilder setAString(String x);
+
+ abstract KotlinDataWithDefaults build();
+ }
+
+ @Test
+ public void kotlinWithDefaults() {
+ // AutoBuilder doesn't currently try to give the builder the same defaults as the Kotlin class,
+ // but we do at least check that the presence of defaults doesn't throw AutoBuilder off.
+ // When a constructor has default parameters, the Kotlin compiler generates an extra constructor
+ // with two extra parameters: an int bitmask saying which parameters were defaulted, and a
+ // DefaultConstructorMarker parameter to avoid clashing with another constructor that might have
+ // an extra int parameter for some other reason. If AutoBuilder found this constructor it might
+ // be confused, but fortunately the constructor is marked synthetic, and javax.lang.model
+ // doesn't show synthetic elements.
+ KotlinDataWithDefaults x =
+ KotlinDataWithDefaultsBuilder.builder().setAString("answer").setAnInt(42).build();
+ assertThat(x.getAString()).isEqualTo("answer");
+ assertThat(x.getAnInt()).isEqualTo(42);
+ }
+}
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java
new file mode 100644
index 00000000..952edaac
--- /dev/null
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoBuilderTest.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth8.assertThat;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.time.LocalTime;
+import java.util.AbstractSet;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AutoBuilderTest {
+ static class Simple {
+ private final int anInt;
+ private final String aString;
+
+ Simple(int anInt, String aString) {
+ this.anInt = anInt;
+ this.aString = aString;
+ }
+
+ static Simple of(int anInt, String aString) {
+ return new Simple(anInt, aString);
+ }
+
+ @Override
+ public boolean equals(Object x) {
+ if (x instanceof Simple) {
+ Simple that = (Simple) x;
+ return this.anInt == that.anInt && Objects.equals(this.aString, that.aString);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(anInt, aString);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("anInt", anInt)
+ .add("aString", aString)
+ .toString();
+ }
+
+ static Builder builder() {
+ return new AutoBuilder_AutoBuilderTest_Simple_Builder();
+ }
+
+ @AutoBuilder
+ abstract static class Builder {
+ abstract Builder setAnInt(int x);
+
+ abstract Builder setAString(String x);
+
+ abstract Simple build();
+ }
+ }
+
+ @Test
+ public void simple() {
+ Simple x = Simple.builder().setAnInt(23).setAString("skidoo").build();
+ assertThat(x).isEqualTo(new Simple(23, "skidoo"));
+ }
+
+ @AutoValue
+ abstract static class SimpleAuto {
+ abstract int getFoo();
+
+ abstract String getBar();
+
+ static Builder builder() {
+ return new AutoBuilder_AutoBuilderTest_SimpleAuto_Builder();
+ }
+
+ // There's no particular reason to do this since @AutoValue.Builder works just as well, but
+ // let's check anyway.
+ @AutoBuilder(ofClass = AutoValue_AutoBuilderTest_SimpleAuto.class)
+ abstract static class Builder {
+ abstract Builder setFoo(int x);
+
+ abstract Builder setBar(String x);
+
+ abstract AutoValue_AutoBuilderTest_SimpleAuto build();
+ }
+ }
+
+ @Test
+ public void simpleAuto() {
+ SimpleAuto x = SimpleAuto.builder().setFoo(23).setBar("skidoo").build();
+ assertThat(x.getFoo()).isEqualTo(23);
+ assertThat(x.getBar()).isEqualTo("skidoo");
+ }
+
+ enum Truthiness {
+ FALSY,
+ TRUTHY
+ }
+
+ @interface MyAnnotation {
+ String value();
+
+ int DEFAULT_ID = -1;
+
+ int id() default DEFAULT_ID;
+
+ Truthiness DEFAULT_TRUTHINESS = Truthiness.FALSY;
+
+ Truthiness truthiness() default Truthiness.FALSY;
+ }
+
+ // This method has a parameter for `truthiness`, even though that has a default, but it has no
+ // parameter for `id`, which also has a default.
+ @AutoAnnotation
+ static MyAnnotation myAnnotation(String value, Truthiness truthiness) {
+ return new AutoAnnotation_AutoBuilderTest_myAnnotation(value, truthiness);
+ }
+
+ @AutoBuilder(callMethod = "myAnnotation")
+ interface MyAnnotationBuilder {
+ MyAnnotationBuilder value(String x);
+
+ MyAnnotationBuilder truthiness(Truthiness x);
+
+ MyAnnotation build();
+ }
+
+ static MyAnnotationBuilder myAnnotationBuilder() {
+ return new AutoBuilder_AutoBuilderTest_MyAnnotationBuilder()
+ .truthiness(MyAnnotation.DEFAULT_TRUTHINESS);
+ }
+
+ @Test
+ public void simpleAutoAnnotation() {
+ MyAnnotation annotation1 = myAnnotationBuilder().value("foo").build();
+ assertThat(annotation1.value()).isEqualTo("foo");
+ assertThat(annotation1.id()).isEqualTo(MyAnnotation.DEFAULT_ID);
+ assertThat(annotation1.truthiness()).isEqualTo(MyAnnotation.DEFAULT_TRUTHINESS);
+ MyAnnotation annotation2 =
+ myAnnotationBuilder().value("bar").truthiness(Truthiness.TRUTHY).build();
+ assertThat(annotation2.value()).isEqualTo("bar");
+ assertThat(annotation2.id()).isEqualTo(MyAnnotation.DEFAULT_ID);
+ assertThat(annotation2.truthiness()).isEqualTo(Truthiness.TRUTHY);
+ }
+
+ static class Overload {
+ final int anInt;
+ final String aString;
+ final BigInteger aBigInteger;
+
+ Overload(int anInt, String aString) {
+ this(anInt, aString, BigInteger.ZERO);
+ }
+
+ Overload(int anInt, String aString, BigInteger aBigInteger) {
+ this.anInt = anInt;
+ this.aString = aString;
+ this.aBigInteger = aBigInteger;
+ }
+
+ @Override
+ public boolean equals(Object x) {
+ if (x instanceof Overload) {
+ Overload that = (Overload) x;
+ return this.anInt == that.anInt
+ && Objects.equals(this.aString, that.aString)
+ && Objects.equals(this.aBigInteger, that.aBigInteger);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(anInt, aString, aBigInteger);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(this)
+ .add("anInt", anInt)
+ .add("aString", aString)
+ .add("aBigInteger", aBigInteger)
+ .toString();
+ }
+
+ static Builder1 builder1() {
+ return new AutoBuilder_AutoBuilderTest_Overload_Builder1();
+ }
+
+ static Builder2 builder2() {
+ return new AutoBuilder_AutoBuilderTest_Overload_Builder2();
+ }
+
+ @AutoBuilder
+ interface Builder1 {
+ Builder1 setAnInt(int x);
+
+ Builder1 setAString(String x);
+
+ Overload build();
+ }
+
+ @AutoBuilder
+ interface Builder2 {
+ Builder2 setAnInt(int x);
+
+ Builder2 setAString(String x);
+
+ Builder2 setABigInteger(BigInteger x);
+
+ Overload build();
+ }
+ }
+
+ @Test
+ public void overloadedConstructor() {
+ Overload actual1 = Overload.builder1().setAnInt(23).setAString("skidoo").build();
+ Overload expected1 = new Overload(23, "skidoo");
+ assertThat(actual1).isEqualTo(expected1);
+
+ BigInteger big17 = BigInteger.valueOf(17);
+ Overload actual2 =
+ Overload.builder2().setAnInt(17).setAString("17").setABigInteger(big17).build();
+ Overload expected2 = new Overload(17, "17", big17);
+ assertThat(actual2).isEqualTo(expected2);
+ }
+
+ @AutoBuilder(callMethod = "of", ofClass = Simple.class)
+ interface SimpleStaticBuilder {
+ SimpleStaticBuilder anInt(int x);
+
+ SimpleStaticBuilder aString(String x);
+
+ Simple build();
+ }
+
+ static SimpleStaticBuilder simpleStaticBuilder() {
+ return new AutoBuilder_AutoBuilderTest_SimpleStaticBuilder();
+ }
+
+ @Test
+ public void staticMethod() {
+ Simple actual = simpleStaticBuilder().anInt(17).aString("17").build();
+ Simple expected = new Simple(17, "17");
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ // We can't be sure that the java.time package has parameter names, so we use this intermediary.
+ // Otherwise we could just write @AutoBuilder(callMethod = "of", ofClass = LocalTime.class).
+ // It's still interesting to test this as a realistic example.
+ static LocalTime localTimeOf(int hour, int minute, int second, int nanoOfSecond) {
+ return LocalTime.of(hour, minute, second, nanoOfSecond);
+ }
+
+ static LocalTimeBuilder localTimeBuilder() {
+ return new AutoBuilder_AutoBuilderTest_LocalTimeBuilder().nanoOfSecond(0);
+ }
+
+ @AutoBuilder(callMethod = "localTimeOf")
+ interface LocalTimeBuilder {
+ LocalTimeBuilder hour(int hour);
+
+ LocalTimeBuilder minute(int minute);
+
+ LocalTimeBuilder second(int second);
+
+ LocalTimeBuilder nanoOfSecond(int nanoOfSecond);
+
+ LocalTime build();
+ }
+
+ @Test
+ public void staticMethodOfContainingClass() {
+ LocalTime actual = localTimeBuilder().hour(12).minute(34).second(56).build();
+ LocalTime expected = LocalTime.of(12, 34, 56);
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ @Test
+ public void missingRequiredProperty() {
+ // This test is compiled at source level 7 by CompileWithEclipseTest, so we can't use
+ // assertThrows with a lambda.
+ try {
+ localTimeBuilder().hour(12).minute(34).build();
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessageThat().isEqualTo("Missing required properties: second");
+ }
+ }
+
+ static void throwException() throws IOException {
+ throw new IOException("oops");
+ }
+
+ static ThrowExceptionBuilder throwExceptionBuilder() {
+ return new AutoBuilder_AutoBuilderTest_ThrowExceptionBuilder();
+ }
+
+ @AutoBuilder(callMethod = "throwException")
+ interface ThrowExceptionBuilder {
+ void build() throws IOException;
+ }
+
+ @Test
+ public void emptyBuilderThrowsException() {
+ try {
+ throwExceptionBuilder().build();
+ fail();
+ } catch (IOException expected) {
+ assertThat(expected).hasMessageThat().isEqualTo("oops");
+ }
+ }
+
+ static class ListContainer {
+ private final ImmutableList<String> list;
+
+ ListContainer(ImmutableList<String> list) {
+ this.list = checkNotNull(list);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof ListContainer && list.equals(((ListContainer) o).list);
+ }
+
+ @Override
+ public int hashCode() {
+ return list.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return list.toString();
+ }
+
+ static Builder builder() {
+ return new AutoBuilder_AutoBuilderTest_ListContainer_Builder();
+ }
+
+ @AutoBuilder
+ interface Builder {
+ Builder setList(Iterable<String> list);
+
+ ImmutableList.Builder<String> listBuilder();
+
+ ListContainer build();
+ }
+ }
+
+ @Test
+ public void propertyBuilder() {
+ ListContainer expected = new ListContainer(ImmutableList.of("one", "two", "three"));
+ ListContainer actual1 =
+ ListContainer.builder().setList(ImmutableList.of("one", "two", "three")).build();
+ assertThat(actual1).isEqualTo(expected);
+
+ ListContainer.Builder builder2 = ListContainer.builder();
+ builder2.listBuilder().add("one", "two", "three");
+ assertThat(builder2.build()).isEqualTo(expected);
+
+ ListContainer.Builder builder3 = ListContainer.builder().setList(ImmutableList.of("one"));
+ builder3.listBuilder().add("two", "three");
+ assertThat(builder3.build()).isEqualTo(expected);
+
+ ListContainer.Builder builder4 = ListContainer.builder();
+ builder4.listBuilder();
+ try {
+ builder4.setList(ImmutableList.of("one", "two", "three"));
+ fail();
+ } catch (IllegalStateException e) {
+ assertThat(e).hasMessageThat().isEqualTo("Cannot set list after calling listBuilder()");
+ }
+ }
+
+ static <T> String concatList(ImmutableList<T> list) {
+ // We're avoiding streams for now so we compile this in Java 7 mode in CompileWithEclipseTest.
+ StringBuilder sb = new StringBuilder();
+ for (T element : list) {
+ sb.append(element);
+ }
+ return sb.toString();
+ }
+
+ @AutoBuilder(callMethod = "concatList")
+ interface ConcatListCaller<T> {
+ ImmutableList.Builder<T> listBuilder();
+
+ String call();
+ }
+
+ @Test
+ public void propertyBuilderWithoutSetter() {
+ ConcatListCaller<Integer> caller = new AutoBuilder_AutoBuilderTest_ConcatListCaller<>();
+ caller.listBuilder().add(1, 1, 2, 3, 5, 8);
+ String s = caller.call();
+ assertThat(s).isEqualTo("112358");
+ }
+
+ static <K, V extends Number> Map<K, V> singletonMap(K key, V value) {
+ return Collections.singletonMap(key, value);
+ }
+
+ static <K, V extends Number> SingletonMapBuilder<K, V> singletonMapBuilder() {
+ return new AutoBuilder_AutoBuilderTest_SingletonMapBuilder<>();
+ }
+
+ @AutoBuilder(callMethod = "singletonMap")
+ interface SingletonMapBuilder<K, V extends Number> {
+ SingletonMapBuilder<K, V> key(K key);
+
+ SingletonMapBuilder<K, V> value(V value);
+
+ Map<K, V> build();
+ }
+
+ @Test
+ public void genericStaticMethod() {
+ ImmutableMap<String, Integer> expected = ImmutableMap.of("17", 17);
+ SingletonMapBuilder<String, Integer> builder = singletonMapBuilder();
+ Map<String, Integer> actual = builder.key("17").value(17).build();
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ static class SingletonSet<E> extends AbstractSet<E> {
+ private final E element;
+
+ SingletonSet(E element) {
+ this.element = element;
+ }
+
+ @Override
+ public int size() {
+ return 1;
+ }
+
+ @Override
+ public Iterator<E> iterator() {
+ return new Iterator<E>() {
+ private boolean first = true;
+
+ @Override
+ public boolean hasNext() {
+ return first;
+ }
+
+ @Override
+ public E next() {
+ if (!first) {
+ throw new NoSuchElementException();
+ }
+ first = false;
+ return element;
+ }
+ };
+ }
+ }
+
+ @AutoBuilder(ofClass = SingletonSet.class)
+ interface SingletonSetBuilder<E> {
+ SingletonSetBuilder<E> setElement(E element);
+
+ SingletonSet<E> build();
+ }
+
+ static <E> SingletonSetBuilder<E> singletonSetBuilder() {
+ return new AutoBuilder_AutoBuilderTest_SingletonSetBuilder<>();
+ }
+
+ @Test
+ public void genericClass() {
+ ImmutableSet<String> expected = ImmutableSet.of("foo");
+ SingletonSetBuilder<String> builder = singletonSetBuilder();
+ Set<String> actual = builder.setElement("foo").build();
+ assertThat(actual).isEqualTo(expected);
+ }
+
+ static class TypedSingletonSet<E> extends SingletonSet<E> {
+ private final Class<?> type;
+
+ <T extends E> TypedSingletonSet(T element, Class<T> type) {
+ super(element);
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return type.getName() + super.toString();
+ }
+ }
+
+ @AutoBuilder(ofClass = TypedSingletonSet.class)
+ interface TypedSingletonSetBuilder<E, T extends E> {
+ TypedSingletonSetBuilder<E, T> setElement(T element);
+
+ TypedSingletonSetBuilder<E, T> setType(Class<T> type);
+
+ TypedSingletonSet<E> build();
+ }
+
+ static <E, T extends E> TypedSingletonSetBuilder<E, T> typedSingletonSetBuilder() {
+ return new AutoBuilder_AutoBuilderTest_TypedSingletonSetBuilder<>();
+ }
+
+ @Test
+ public void genericClassWithGenericConstructor() {
+ TypedSingletonSetBuilder<CharSequence, String> builder = typedSingletonSetBuilder();
+ TypedSingletonSet<CharSequence> set = builder.setElement("foo").setType(String.class).build();
+ assertThat(set.toString()).isEqualTo("java.lang.String[foo]");
+ }
+
+ static <T> ImmutableList<T> pair(T first, T second) {
+ return ImmutableList.of(first, second);
+ }
+
+ @AutoBuilder(callMethod = "pair")
+ interface PairBuilder<T> {
+ PairBuilder<T> setFirst(T x);
+
+ T getFirst();
+
+ PairBuilder<T> setSecond(T x);
+
+ Optional<T> getSecond();
+
+ ImmutableList<T> build();
+ }
+
+ static <T> PairBuilder<T> pairBuilder() {
+ return new AutoBuilder_AutoBuilderTest_PairBuilder<>();
+ }
+
+ @Test
+ public void genericGetters() {
+ PairBuilder<Number> builder = pairBuilder();
+ assertThat(builder.getSecond()).isEmpty();
+ builder.setSecond(2);
+ assertThat(builder.getSecond()).hasValue(2);
+ try {
+ builder.getFirst();
+ fail();
+ } catch (IllegalStateException expected) {
+ }
+ builder.setFirst(1.0);
+ assertThat(builder.getFirst()).isEqualTo(1.0);
+ assertThat(builder.build()).containsExactly(1.0, 2).inOrder();
+ }
+}
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoOneOfTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoOneOfTest.java
index 1b587282..ee337409 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoOneOfTest.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoOneOfTest.java
@@ -499,10 +499,7 @@ public class AutoOneOfTest {
ArrayValue string = ArrayValue.ofString("foo");
ArrayValue ints1 = ArrayValue.ofInts(new int[] {17, 23});
ArrayValue ints2 = ArrayValue.ofInts(new int[] {17, 23});
- new EqualsTester()
- .addEqualityGroup(string)
- .addEqualityGroup(ints1, ints2)
- .testEquals();
+ new EqualsTester().addEqualityGroup(string).addEqualityGroup(ints1, ints2).testEquals();
}
@Retention(RetentionPolicy.RUNTIME)
@@ -560,8 +557,11 @@ public class AutoOneOfTest {
@AutoOneOf(MaybeEmpty.Kind.class)
public abstract static class MaybeEmpty implements Serializable {
+ private static final long serialVersionUID = 1L;
+
public enum Kind {
- EMPTY, STRING,
+ EMPTY,
+ STRING,
}
public abstract Kind getKind();
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java
index 10812f8d..3f4e9bf5 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueJava8Test.java
@@ -15,9 +15,11 @@
*/
package com.google.auto.value;
+import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.Truth8.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
import static com.google.testing.compile.CompilationSubject.assertThat;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeTrue;
@@ -53,6 +55,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
+import org.junit.AssumptionViolatedException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -153,6 +156,19 @@ public class AutoValueJava8Test {
.isEqualTo("NullableProperties{nullableString=null, randomInt=23}");
}
+ @Test
+ public void testEqualsParameterIsAnnotated() throws NoSuchMethodException {
+ // Sadly we can't rely on JDK 8 to handle type annotations correctly.
+ // Some versions do, some don't. So skip the test unless we are on at least JDK 9.
+ double javaVersion = Double.parseDouble(JAVA_SPECIFICATION_VERSION.value());
+ assume().that(javaVersion).isAtLeast(9.0);
+ Method equals =
+ NullableProperties.create(null, 23).getClass().getMethod("equals", Object.class);
+ AnnotatedType[] parameterTypes = equals.getAnnotatedParameterTypes();
+ assertThat(parameterTypes).hasLength(1);
+ assertThat(parameterTypes[0].getAnnotation(Nullable.class)).isNotNull();
+ }
+
@AutoAnnotation
static Nullable nullable() {
return new AutoAnnotation_AutoValueJava8Test_nullable();
@@ -162,9 +178,7 @@ public class AutoValueJava8Test {
public void testNullablePropertyImplementationIsNullable() throws NoSuchMethodException {
Method method =
AutoValue_AutoValueJava8Test_NullableProperties.class.getDeclaredMethod("nullableString");
- assertThat(method.getAnnotatedReturnType().getAnnotations())
- .asList()
- .contains(nullable());
+ assertThat(method.getAnnotatedReturnType().getAnnotations()).asList().contains(nullable());
}
@Test
@@ -198,8 +212,9 @@ public class AutoValueJava8Test {
@Test
public void testExcludedNullablePropertyImplementation() throws NoSuchMethodException {
- Method method = AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class
- .getDeclaredMethod("nullableString");
+ Method method =
+ AutoValue_AutoValueJava8Test_NullablePropertiesNotCopied.class.getDeclaredMethod(
+ "nullableString");
assertThat(method.getAnnotatedReturnType().getAnnotations())
.asList()
.doesNotContain(nullable());
@@ -536,6 +551,35 @@ public class AutoValueJava8Test {
}
}
+ @AutoValue
+ abstract static class NoNullableRef {
+ abstract String foo();
+
+ static NoNullableRef of(String foo) {
+ return new AutoValue_AutoValueJava8Test_NoNullableRef(foo);
+ }
+ }
+
+ // Tests that we generate equals(@Nullable x) using JSpecify @Nullable if that annotation is
+ // available and there is no other @Nullable type annotation mentioned in the @AutoValue class.
+ // If there *are* other @Nullable type annotations, other test methods here will check that they
+ // are used instead.
+ @Test
+ public void testDefaultToJSpecifyNullable() throws ReflectiveOperationException {
+ Class<? extends Annotation> jspecifyNullable;
+ try {
+ // We write this using .concat in order to hide it from rewriting rules.
+ jspecifyNullable =
+ Class.forName("org".concat(".jspecify.nullness.Nullable")).asSubclass(Annotation.class);
+ } catch (ClassNotFoundException e) {
+ throw new AssumptionViolatedException("No JSpecify @Nullable available", e);
+ }
+ Class<? extends NoNullableRef> autoValueImpl = NoNullableRef.of("foo").getClass();
+ Method equals = autoValueImpl.getDeclaredMethod("equals", Object.class);
+ assertThat(equals.getAnnotatedParameterTypes()[0].isAnnotationPresent(jspecifyNullable))
+ .isTrue();
+ }
+
@Test
public void testBuilderWithUnprefixedGetter() {
assumeTrue(javacHandlesTypeAnnotationsCorrectly);
@@ -571,7 +615,7 @@ public class AutoValueJava8Test {
public abstract static class BuilderWithPrefixedGetters<T extends Comparable<T>> {
public abstract ImmutableList<T> getList();
- public abstract T getT();
+ public abstract @Nullable T getT();
@SuppressWarnings("mutable")
public abstract int @Nullable [] getInts();
@@ -586,7 +630,7 @@ public class AutoValueJava8Test {
public abstract static class Builder<T extends Comparable<T>> {
public abstract Builder<T> setList(ImmutableList<T> list);
- public abstract Builder<T> setT(T t);
+ public abstract Builder<T> setT(@Nullable T t);
public abstract Builder<T> setInts(int[] ints);
@@ -761,6 +805,7 @@ public class AutoValueJava8Test {
@AutoValue.Builder
abstract static class Builder {
abstract Builder maybeJustMaybe(Optional<String> maybe);
+
abstract OptionalOptional build();
}
}
@@ -795,6 +840,7 @@ public class AutoValueJava8Test {
@AutoValue.Builder
abstract static class Builder {
abstract Builder setPredicate(Predicate<? super Integer> predicate);
+
abstract OptionalExtends build();
}
}
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueTest.java
index 346bc53a..3a7e7bc4 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueTest.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/AutoValueTest.java
@@ -34,6 +34,7 @@ import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ImmutableTable;
+import com.google.common.collect.Ordering;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.SerializableTester;
import java.io.ObjectStreamClass;
@@ -52,6 +53,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -60,6 +62,7 @@ import java.util.NavigableMap;
import java.util.NavigableSet;
import java.util.NoSuchElementException;
import java.util.SortedMap;
+import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.annotation.Nullable;
@@ -70,6 +73,7 @@ import org.junit.runners.JUnit4;
/** @author emcmanus@google.com (Éamonn McManus) */
@RunWith(JUnit4.class)
+@SuppressWarnings({"AutoValueImmutableFields", "AutoValueFinalMethods", "TypeNameShadowing"})
public class AutoValueTest {
private static boolean omitIdentifiers;
@@ -180,12 +184,15 @@ public class AutoValueTest {
@AutoValue
abstract static class StrangeGetters {
abstract int get1st();
+
abstract int get_1st(); // by default we'll use _1st where identifiers are needed, so foil that.
@AutoValue.Builder
abstract static class Builder {
abstract Builder set1st(int x);
+
abstract Builder set_1st(int x);
+
abstract StrangeGetters build();
}
@@ -284,6 +291,8 @@ public class AutoValueTest {
@AutoValue
public abstract static class Serialize implements Serializable {
+ private static final long serialVersionUID = 1L;
+
public abstract int integer();
public abstract String string();
@@ -845,6 +854,8 @@ public class AutoValueTest {
@Test
public void testGenericClassWithHairyBounds() throws Exception {
class ComparableList<E> extends ArrayList<E> implements Comparable<ComparableList<E>> {
+ private static final long serialVersionUID = 1L;
+
@Override
public int compareTo(ComparableList<E> list) {
throw new UnsupportedOperationException();
@@ -1262,7 +1273,7 @@ public class AutoValueTest {
}
@AutoValue
- public abstract static class ComplexInheritance extends AbstractBase implements A, B {
+ public abstract static class ComplexInheritance extends AbstractBase implements IntfA, IntfB {
public static ComplexInheritance create(String name) {
return new AutoValue_AutoValueTest_ComplexInheritance(name);
}
@@ -1277,9 +1288,9 @@ public class AutoValueTest {
}
}
- interface A extends Base {}
+ interface IntfA extends Base {}
- interface B extends Base {}
+ interface IntfB extends Base {}
interface Base {
int answer();
@@ -1352,7 +1363,7 @@ public class AutoValueTest {
}
@AutoValue
- public abstract static class InheritTwice implements A, B {
+ public abstract static class InheritTwice implements IntfA, IntfB {
public static InheritTwice create(int answer) {
return new AutoValue_AutoValueTest_InheritTwice(answer);
}
@@ -1664,6 +1675,13 @@ public class AutoValueTest {
.build();
assertThat(suppliedDirectly.optionalString()).hasValue("foo");
assertThat(suppliedDirectly.optionalInteger()).hasValue(23);
+
+ try {
+ // The parameter is not marked @Nullable so this should fail.
+ OptionalPropertiesWithBuilder.builder().setOptionalString((String) null);
+ fail();
+ } catch (NullPointerException expected) {
+ }
}
@AutoValue
@@ -1874,6 +1892,40 @@ public class AutoValueTest {
assertEquals((Integer) 17, instance3.u());
}
+ public interface ToBuilder<BuilderT> {
+ BuilderT toBuilder();
+ }
+
+ @AutoValue
+ public abstract static class InheritedToBuilder<T, U>
+ implements ToBuilder<InheritedToBuilder.Builder<T, U>> {
+
+ public abstract T t();
+
+ public abstract U u();
+
+ public static <T, U> Builder<T, U> builder() {
+ return new AutoValue_AutoValueTest_InheritedToBuilder.Builder<T, U>();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder<T, U> {
+ public abstract Builder<T, U> setT(T t);
+
+ public abstract Builder<T, U> setU(U u);
+
+ public abstract InheritedToBuilder<T, U> build();
+ }
+ }
+
+ @Test
+ public void testInheritedToBuilder() {
+ InheritedToBuilder<Integer, String> x =
+ InheritedToBuilder.<Integer, String>builder().setT(17).setU("wibble").build();
+ InheritedToBuilder<Integer, String> y = x.toBuilder().setT(23).build();
+ assertThat(y.u()).isEqualTo("wibble");
+ }
+
@AutoValue
public abstract static class BuilderWithSet<T extends Comparable<T>> {
public abstract List<T> list();
@@ -2115,6 +2167,37 @@ public class AutoValueTest {
}
@AutoValue
+ public abstract static class BuilderWithPrefixedGettersAndUnprefixedSetters {
+ public abstract String getOAuth();
+
+ public abstract String getOBrien();
+
+ public static Builder builder() {
+ return new AutoValue_AutoValueTest_BuilderWithPrefixedGettersAndUnprefixedSetters.Builder();
+ }
+
+ @AutoValue.Builder
+ public abstract static class Builder {
+ public abstract Builder oAuth(String x);
+
+ public abstract Builder OBrien(String x);
+
+ public abstract BuilderWithPrefixedGettersAndUnprefixedSetters build();
+ }
+ }
+
+ @Test
+ public void testBuilderWithPrefixedGetterAndUnprefixedSetter() {
+ BuilderWithPrefixedGettersAndUnprefixedSetters x =
+ BuilderWithPrefixedGettersAndUnprefixedSetters.builder()
+ .oAuth("OAuth")
+ .OBrien("Flann")
+ .build();
+ assertThat(x.getOAuth()).isEqualTo("OAuth");
+ assertThat(x.getOBrien()).isEqualTo("Flann");
+ }
+
+ @AutoValue
public abstract static class BuilderWithPropertyBuilders<FooT extends Comparable<FooT>> {
public abstract ImmutableList<FooT> getFoos();
@@ -2223,6 +2306,7 @@ public class AutoValueTest {
@AutoValue.Builder
abstract static class Builder {
abstract ImmutableList.Builder<String> listBuilder();
+
abstract PropertyBuilderInheritsType build();
}
}
@@ -2820,6 +2904,8 @@ public class AutoValueTest {
}
public static class MyMap<K, V> extends HashMap<K, V> {
+ private static final long serialVersionUID = 1L;
+
public MyMap() {}
public MyMap(Map<K, V> map) {
@@ -2828,6 +2914,8 @@ public class AutoValueTest {
}
public static class MyMapBuilder<K, V> extends LinkedHashMap<K, V> {
+ private static final long serialVersionUID = 1L;
+
public MyMapBuilder() {}
public MyMapBuilder(Map<K, V> map) {
@@ -2873,6 +2961,8 @@ public class AutoValueTest {
}
public static class MyStringMap<V> extends MyMap<String, V> {
+ private static final long serialVersionUID = 1L;
+
public MyStringMap() {}
public MyStringMap(Map<String, V> map) {
@@ -2885,6 +2975,8 @@ public class AutoValueTest {
}
public static class MyStringMapBuilder<V> extends MyMapBuilder<String, V> {
+ private static final long serialVersionUID = 1L;
+
public MyStringMapBuilder() {}
public MyStringMapBuilder(Map<String, V> map) {
@@ -3258,6 +3350,7 @@ public class AutoValueTest {
@AutoValue.Builder
abstract static class Builder {
abstract Builder setMetrics(ImmutableSet<? extends Number> metrics);
+
abstract GenericExtends build();
}
}
@@ -3282,6 +3375,7 @@ public class AutoValueTest {
@AutoValue.Builder
abstract static class Builder {
abstract Builder setList(List<String> list);
+
abstract Child build();
}
}
@@ -3325,14 +3419,18 @@ public class AutoValueTest {
@SuppressWarnings("ClassCanBeStatic")
static class OuterWithTypeParam<T extends Number> {
class InnerWithTypeParam<U> {}
+
class InnerWithoutTypeParam {}
+
static class Nested {}
}
@AutoValue
abstract static class Nesty {
abstract OuterWithTypeParam<Double>.InnerWithTypeParam<String> innerWithTypeParam();
+
abstract OuterWithTypeParam<Double>.InnerWithoutTypeParam innerWithoutTypeParam();
+
abstract OuterWithTypeParam.Nested nested();
static Builder builder() {
@@ -3343,8 +3441,11 @@ public class AutoValueTest {
abstract static class Builder {
abstract Builder setInnerWithTypeParam(
OuterWithTypeParam<Double>.InnerWithTypeParam<String> x);
+
abstract Builder setInnerWithoutTypeParam(OuterWithTypeParam<Double>.InnerWithoutTypeParam x);
+
abstract Builder setNested(OuterWithTypeParam.Nested x);
+
abstract Nesty build();
}
}
@@ -3353,11 +3454,12 @@ public class AutoValueTest {
public void outerWithTypeParam() throws ReflectiveOperationException {
@SuppressWarnings("UseDiamond") // Currently we compile this with -source 6 in the Eclipse test.
OuterWithTypeParam<Double> outer = new OuterWithTypeParam<Double>();
- Nesty nesty = Nesty.builder()
- .setInnerWithTypeParam(outer.new InnerWithTypeParam<String>())
- .setInnerWithoutTypeParam(outer.new InnerWithoutTypeParam())
- .setNested(new OuterWithTypeParam.Nested())
- .build();
+ Nesty nesty =
+ Nesty.builder()
+ .setInnerWithTypeParam(outer.new InnerWithTypeParam<String>())
+ .setInnerWithoutTypeParam(outer.new InnerWithoutTypeParam())
+ .setNested(new OuterWithTypeParam.Nested())
+ .build();
Type originalReturnType =
Nesty.class.getDeclaredMethod("innerWithTypeParam").getGenericReturnType();
Type generatedReturnType =
@@ -3383,6 +3485,7 @@ public class AutoValueTest {
@MyAnnotation("thing")
abstract static class Builder {
abstract Builder setFoo(String x);
+
abstract BuilderAnnotationsNotCopied build();
}
}
@@ -3407,6 +3510,7 @@ public class AutoValueTest {
@MyAnnotation("thing")
abstract static class Builder {
abstract Builder setFoo(String x);
+
abstract BuilderAnnotationsCopied build();
}
}
@@ -3417,4 +3521,106 @@ public class AutoValueTest {
assertThat(builder.getClass().getAnnotations()).asList().containsExactly(myAnnotation("thing"));
assertThat(builder.setFoo("foo").build().foo()).isEqualTo("foo");
}
+
+ @AutoValue
+ @AutoValue.CopyAnnotations
+ @SuppressWarnings({"rawtypes", "unchecked"}) // deliberately checking handling of raw types
+ abstract static class DataWithSortedCollectionBuilders<K, V> {
+ abstract ImmutableSortedMap<K, V> anImmutableSortedMap();
+
+ abstract ImmutableSortedSet<V> anImmutableSortedSet();
+
+ abstract ImmutableSortedMap<Integer, V> nonGenericImmutableSortedMap();
+
+ abstract ImmutableSortedSet rawImmutableSortedSet();
+
+ abstract DataWithSortedCollectionBuilders.Builder<K, V> toBuilder();
+
+ static <K, V> DataWithSortedCollectionBuilders.Builder<K, V> builder() {
+ return new AutoValue_AutoValueTest_DataWithSortedCollectionBuilders.Builder<K, V>();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder<K, V> {
+ abstract DataWithSortedCollectionBuilders.Builder<K, V> anImmutableSortedMap(
+ SortedMap<K, V> anImmutableSortedMap);
+
+ abstract ImmutableSortedMap.Builder<K, V> anImmutableSortedMapBuilder(
+ Comparator<K> keyComparator);
+
+ abstract DataWithSortedCollectionBuilders.Builder<K, V> anImmutableSortedSet(
+ SortedSet<V> anImmutableSortedSet);
+
+ abstract ImmutableSortedSet.Builder<V> anImmutableSortedSetBuilder(Comparator<V> comparator);
+
+ abstract ImmutableSortedMap.Builder<Integer, V> nonGenericImmutableSortedMapBuilder(
+ Comparator<Integer> keyComparator);
+
+ abstract ImmutableSortedSet.Builder rawImmutableSortedSetBuilder(Comparator comparator);
+
+ abstract DataWithSortedCollectionBuilders<K, V> build();
+ }
+ }
+
+ @Test
+ @SuppressWarnings({"rawtypes", "unchecked"}) // deliberately checking handling of raw types
+ public void shouldGenerateBuildersWithComparators() {
+ Comparator<String> stringComparator =
+ new Comparator<String>() {
+ @Override
+ public int compare(String left, String right) {
+ return left.compareTo(right);
+ }
+ };
+
+ Comparator<Integer> intComparator =
+ new Comparator<Integer>() {
+ @Override
+ public int compare(Integer o1, Integer o2) {
+ return o1 - o2;
+ }
+ };
+
+ Comparator comparator =
+ new Comparator() {
+ @Override
+ public int compare(Object left, Object right) {
+ return String.valueOf(left).compareTo(String.valueOf(right));
+ }
+ };
+
+ AutoValueTest.DataWithSortedCollectionBuilders.Builder<String, Integer> builder =
+ AutoValueTest.DataWithSortedCollectionBuilders.builder();
+
+ builder
+ .anImmutableSortedMapBuilder(stringComparator)
+ .put("Charlie", 1)
+ .put("Alfa", 2)
+ .put("Bravo", 3);
+ builder.anImmutableSortedSetBuilder(intComparator).add(1, 5, 9, 3);
+ builder.nonGenericImmutableSortedMapBuilder(intComparator).put(9, 99).put(1, 11).put(3, 33);
+ builder.rawImmutableSortedSetBuilder(comparator).add("Bravo", "Charlie", "Alfa");
+
+ AutoValueTest.DataWithSortedCollectionBuilders<String, Integer> data = builder.build();
+
+ AutoValueTest.DataWithSortedCollectionBuilders.Builder<String, Integer> copiedBuilder =
+ data.toBuilder();
+ AutoValueTest.DataWithSortedCollectionBuilders<String, Integer> copiedData =
+ copiedBuilder.build();
+
+ assertThat(data.anImmutableSortedMap().keySet())
+ .containsExactly("Alfa", "Bravo", "Charlie")
+ .inOrder();
+ assertThat(data.anImmutableSortedSet()).containsExactly(1, 3, 5, 9).inOrder();
+ assertThat(data.nonGenericImmutableSortedMap().keySet()).containsExactly(1, 3, 9).inOrder();
+ assertThat(data.rawImmutableSortedSet()).containsExactly("Alfa", "Bravo", "Charlie").inOrder();
+
+ assertThat(copiedData).isEqualTo(data);
+
+ try {
+ builder.anImmutableSortedMapBuilder(Ordering.from(stringComparator).reverse());
+ fail("Calling property builder method a second time should have failed");
+ } catch (IllegalStateException expected) {
+ }
+ }
}
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/CompileWithEclipseTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/CompileWithEclipseTest.java
index 15188274..ca10fb45 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/CompileWithEclipseTest.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/CompileWithEclipseTest.java
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import com.google.auto.value.processor.AutoAnnotationProcessor;
+import com.google.auto.value.processor.AutoBuilderProcessor;
import com.google.auto.value.processor.AutoOneOfProcessor;
import com.google.auto.value.processor.AutoValueProcessor;
import com.google.common.collect.ImmutableList;
@@ -58,25 +59,28 @@ public class CompileWithEclipseTest {
@BeforeClass
public static void setSourceRoot() {
assertWithMessage("basedir property must be set - test must be run from Maven")
- .that(SOURCE_ROOT).isNotNull();
+ .that(SOURCE_ROOT)
+ .isNotNull();
}
public @Rule TemporaryFolder tmp = new TemporaryFolder();
private static final ImmutableSet<String> IGNORED_TEST_FILES =
- ImmutableSet.of("AutoValueNotEclipseTest.java", "CompileWithEclipseTest.java");
+ ImmutableSet.of(
+ "AutoValueNotEclipseTest.java", "CompileWithEclipseTest.java", "GradleTest.java");
private static final Predicate<File> JAVA_FILE =
f -> f.getName().endsWith(".java") && !IGNORED_TEST_FILES.contains(f.getName());
private static final Predicate<File> JAVA8_TEST =
- f -> f.getName().equals("AutoValueJava8Test.java")
- || f.getName().equals("AutoOneOfJava8Test.java")
- || f.getName().equals("EmptyExtension.java");
+ f ->
+ f.getName().equals("AutoValueJava8Test.java")
+ || f.getName().equals("AutoOneOfJava8Test.java")
+ || f.getName().equals("EmptyExtension.java");
@Test
- public void compileWithEclipseJava6() throws Exception {
- compileWithEclipse("6", JAVA_FILE.and(JAVA8_TEST.negate()));
+ public void compileWithEclipseJava7() throws Exception {
+ compileWithEclipse("7", JAVA_FILE.and(JAVA8_TEST.negate()));
}
@Test
@@ -103,17 +107,28 @@ public class CompileWithEclipseTest {
// fileManager.getLocation(SYSTEM_MODULES).
File rtJar = new File(JAVA_HOME.value() + "/lib/rt.jar");
if (rtJar.exists()) {
- List<File> bootClassPath = ImmutableList.<File>builder()
- .add(rtJar)
- .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH))
- .build();
+ List<File> bootClassPath =
+ ImmutableList.<File>builder()
+ .add(rtJar)
+ .addAll(fileManager.getLocation(StandardLocation.PLATFORM_CLASS_PATH))
+ .build();
fileManager.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath);
}
Iterable<? extends JavaFileObject> sourceFileObjects =
fileManager.getJavaFileObjectsFromFiles(sources);
String outputDir = tmp.getRoot().toString();
ImmutableList<String> options =
- ImmutableList.of("-d", outputDir, "-s", outputDir, "-source", version, "-target", version);
+ ImmutableList.of(
+ "-d",
+ outputDir,
+ "-s",
+ outputDir,
+ "-source",
+ version,
+ "-target",
+ version,
+ "-warn:-warningToken,-intfAnnotation",
+ "-Acom.google.auto.value.AutoBuilderIsUnstable");
JavaCompiler.CompilationTask task =
compiler.getTask(null, fileManager, null, options, null, sourceFileObjects);
// Explicitly supply an empty list of extensions for AutoValueProcessor, because otherwise this
@@ -121,7 +136,10 @@ public class CompileWithEclipseTest {
AutoValueProcessor autoValueProcessor = new AutoValueProcessor(ImmutableList.of());
ImmutableList<? extends Processor> processors =
ImmutableList.of(
- autoValueProcessor, new AutoOneOfProcessor(), new AutoAnnotationProcessor());
+ autoValueProcessor,
+ new AutoOneOfProcessor(),
+ new AutoAnnotationProcessor(),
+ new AutoBuilderProcessor());
task.setProcessors(processors);
assertWithMessage("Compilation should succeed").that(task.call()).isTrue();
}
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/GradleTest.java b/value/src/it/functional/src/test/java/com/google/auto/value/GradleTest.java
new file mode 100644
index 00000000..f4eb5388
--- /dev/null
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/GradleTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value;
+
+import static com.google.common.truth.Truth.assertThat;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.collect.ImmutableList;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.gradle.util.GradleVersion;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class GradleTest {
+ @Rule public TemporaryFolder fakeProject = new TemporaryFolder();
+
+ private static final String BUILD_GRADLE_TEXT =
+ String.join(
+ "\n",
+ "plugins {",
+ " id 'java-library'",
+ "}",
+ "repositories {",
+ " maven { url = uri('${localRepository}') }",
+ "}",
+ "dependencies {",
+ " compileOnlyApi "
+ + " 'com.google.auto.value:auto-value-annotations:${autoValueVersion}'",
+ " annotationProcessor 'com.google.auto.value:auto-value:${autoValueVersion}'",
+ "}");
+
+ private static final String FOO_TEXT =
+ String.join(
+ "\n",
+ "package com.example;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "abstract class Foo {",
+ " abstract String bar();",
+ "",
+ " static Foo of(String bar) {",
+ " return new AutoValue_Foo(bar);",
+ " }",
+ "}");
+
+ private static final Optional<File> GRADLE_INSTALLATION = getGradleInstallation();
+
+ @Test
+ public void basic() throws IOException {
+ String autoValueVersion = System.getProperty("autoValueVersion");
+ assertThat(autoValueVersion).isNotNull();
+ String localRepository = System.getProperty("localRepository");
+ assertThat(localRepository).isNotNull();
+
+ // Set up the fake Gradle project.
+ String buildGradleText = expandSystemProperties(BUILD_GRADLE_TEXT);
+ writeFile(fakeProject.newFile("build.gradle").toPath(), buildGradleText);
+ Path srcDir = fakeProject.newFolder("src", "main", "java", "com", "example").toPath();
+ writeFile(srcDir.resolve("Foo.java"), FOO_TEXT);
+
+ // Build it the first time.
+ BuildResult result1 = buildFakeProject();
+ assertThat(result1.getOutput())
+ .contains(
+ "Full recompilation is required because no incremental change information is"
+ + " available");
+ Path output =
+ fakeProject
+ .getRoot()
+ .toPath()
+ .resolve("build/classes/java/main/com/example/AutoValue_Foo.class");
+ assertThat(Files.exists(output)).isTrue();
+
+ // Add a source file to the project.
+ String barText = FOO_TEXT.replace("Foo", "Bar");
+ Path barFile = srcDir.resolve("Bar.java");
+ writeFile(barFile, barText);
+
+ // Build it a second time.
+ BuildResult result2 = buildFakeProject();
+ assertThat(result2.getOutput()).doesNotContain("Full recompilation is required");
+
+ // Remove the second source file and build a third time. If incremental annotation processing
+ // is not working, this will produce a message like this:
+ // Full recompilation is required because com.google.auto.value.processor.AutoValueProcessor
+ // is not incremental
+ Files.delete(barFile);
+ BuildResult result3 = buildFakeProject();
+ assertThat(result3.getOutput()).doesNotContain("Full recompilation is required");
+ }
+
+ private BuildResult buildFakeProject() throws IOException {
+ GradleRunner runner =
+ GradleRunner.create()
+ .withProjectDir(fakeProject.getRoot())
+ .withArguments("--info", "compileJava");
+ if (GRADLE_INSTALLATION.isPresent()) {
+ runner.withGradleInstallation(GRADLE_INSTALLATION.get());
+ } else {
+ runner.withGradleVersion(GradleVersion.current().getVersion());
+ }
+ return runner.build();
+ }
+
+ private static Optional<File> getGradleInstallation() {
+ String gradleHome = System.getenv("GRADLE_HOME");
+ if (gradleHome != null) {
+ File gradleHomeFile = new File(gradleHome);
+ if (gradleHomeFile.isDirectory()) {
+ return Optional.of(new File(gradleHome));
+ }
+ }
+ try {
+ Path gradleExecutable = Paths.get("/usr/bin/gradle");
+ Path gradleLink = gradleExecutable.resolveSibling(Files.readSymbolicLink(gradleExecutable));
+ if (!gradleLink.endsWith("bin/gradle")) {
+ return Optional.empty();
+ }
+ Path installationPath = gradleLink.getParent().getParent();
+ if (!Files.isDirectory(installationPath)) {
+ return Optional.empty();
+ }
+ Optional<Path> coreJar;
+ Pattern corePattern = Pattern.compile("gradle-core-([0-9]+)\\..*\\.jar");
+ try (Stream<Path> files = Files.walk(installationPath.resolve("lib"))) {
+ coreJar =
+ files
+ .filter(
+ p -> {
+ Matcher matcher = corePattern.matcher(p.getFileName().toString());
+ if (matcher.matches()) {
+ int version = Integer.parseInt(matcher.group(1));
+ if (version >= 5) {
+ return true;
+ }
+ }
+ return false;
+ })
+ .findFirst();
+ }
+ return coreJar.map(unused -> installationPath.toFile());
+ } catch (IOException e) {
+ return Optional.empty();
+ }
+ }
+
+ private static String expandSystemProperties(String s) {
+ for (String name : System.getProperties().stringPropertyNames()) {
+ String value = System.getProperty(name);
+ s = s.replace("${" + name + "}", value);
+ }
+ return s;
+ }
+
+ private static void writeFile(Path file, String text) throws IOException {
+ Files.write(file, ImmutableList.of(text), UTF_8);
+ }
+}
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt b/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt
new file mode 100644
index 00000000..f3318890
--- /dev/null
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/KotlinData.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value
+
+data class KotlinData(val int: Int, val string: String)
+
+data class KotlinDataWithNullable(val anInt: Int?, val aString: String?)
+
+data class KotlinDataWithDefaults(val anInt: Int = 23, val aString: String = "skidoo")
diff --git a/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java b/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java
index 0476906c..e6f7abf7 100644
--- a/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java
+++ b/value/src/it/functional/src/test/java/com/google/auto/value/gwt/EmptyExtension.java
@@ -99,8 +99,7 @@ public class EmptyExtension extends AutoValueExtension {
if (typeParameters.isEmpty()) {
return "";
}
- return typeParameters
- .stream()
+ return typeParameters.stream()
.map(e -> e.getSimpleName().toString())
.collect(joining(", ", "<", ">"));
}
diff --git a/value/src/it/gwtserializer/pom.xml b/value/src/it/gwtserializer/pom.xml
index eea89528..42cc2fe2 100644
--- a/value/src/it/gwtserializer/pom.xml
+++ b/value/src/it/gwtserializer/pom.xml
@@ -36,7 +36,7 @@
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt</artifactId>
- <version>2.8.2</version>
+ <version>2.9.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
@@ -94,17 +94,17 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
- <version>3.7.0</version>
+ <version>3.8.1</version>
<dependencies>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-java</artifactId>
- <version>0.9.4</version>
+ <version>1.0.7</version>
</dependency>
</dependencies>
<configuration>
- <source>1.7</source>
- <target>1.7</target>
+ <source>1.8</source>
+ <target>1.8</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>true</showDeprecation>
@@ -120,7 +120,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
- <version>3.0.2</version>
+ <version>3.2.0</version>
<executions>
<execution>
<!-- postpone resources:testResources until after compiler:testCompile to get generated sources -->
@@ -132,7 +132,7 @@
<plugin>
<groupId>net.ltgt.gwt.maven</groupId>
<artifactId>gwt-maven-plugin</artifactId>
- <version>1.0-rc-6</version>
+ <version>1.0.0</version>
<executions>
<execution>
<goals>
@@ -144,7 +144,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
- <version>2.7</version>
+ <version>2.8.2</version>
<configuration>
<!-- Build/test, but don't deploy -->
<skip>true</skip>
diff --git a/value/src/main/java/com/google/auto/value/AutoBuilder.java b/value/src/main/java/com/google/auto/value/AutoBuilder.java
new file mode 100644
index 00000000..b9709005
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/AutoBuilder.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Specifies that the annotated interface or abstract class should be implemented as a builder.
+ * This is still unstable; uses outside Google may break.
+ *
+ * <p>A simple example:
+ *
+ * <pre>
+ *
+ * {@code @}AutoBuilder(ofClass = Person.class)
+ * abstract class PersonBuilder {
+ * static PersonBuilder builder() {
+ * return new AutoBuilder_PersonBuilder();
+ * }
+ *
+ * abstract PersonBuilder setName(String name);
+ * abstract PersonBuilder setId(int id);
+ * abstract Person build();
+ * }</pre>
+ *
+ * @see <a
+ * href="https://github.com/google/auto/blob/master/value/userguide/autobuilder.md">AutoBuilder
+ * User's Guide</a>
+ */
+@Retention(RetentionPolicy.CLASS)
+@Target(ElementType.TYPE)
+public @interface AutoBuilder {
+ /**
+ * The static method from {@link #ofClass} to call when the build-method of the builder is called.
+ * By default this is empty, meaning that a constructor rather than a static method should be
+ * called. There can be more than one method with the given name, or more than one constructor, in
+ * which case the one to call is the one whose parameter names and types correspond to the
+ * abstract methods of the class or interface with the {@code @AutoBuilder} annotation.
+ */
+ String callMethod() default "";
+
+ /**
+ * The class or interface containing the constructor or static method that the generated builder
+ * will eventually call. By default this is the class or interface that <i>contains</i> the class
+ * or interface with the {@code @AutoBuilder} annotation.
+ */
+ Class<?> ofClass() default Void.class;
+}
diff --git a/value/src/main/java/com/google/auto/value/AutoValue.java b/value/src/main/java/com/google/auto/value/AutoValue.java
index 45a677c9..d7541f65 100644
--- a/value/src/main/java/com/google/auto/value/AutoValue.java
+++ b/value/src/main/java/com/google/auto/value/AutoValue.java
@@ -98,7 +98,7 @@ public @interface AutoValue {
* <p>If you want to copy annotations from your {@literal @}AutoValue-annotated class's methods to
* the generated fields in the AutoValue_... implementation, annotate your method
* with {@literal @}AutoValue.CopyAnnotations. For example, if Example.java is:<pre>
-
+ *
* {@code @}Immutable
* {@code @}AutoValue
* abstract class Example {
diff --git a/value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java b/value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java
index 2c8a3fb0..343645ae 100644
--- a/value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java
+++ b/value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java
@@ -121,12 +121,10 @@ public abstract class AutoValueExtension {
* example, if you have...
*
* <pre>
- * interface Parent<T> {
+ * {@code interface Parent<T>} {
* T bar();
* }
- *
- * {@code @AutoValue abstract class Foo implements Parent<String> {...}}
- * </pre>
+ * {@code @AutoValue abstract class Foo implements Parent<String> {...}}</pre>
*
* ...then the type of the {@code bar} property in {@code Foo} is actually {@code String}, but
* the {@code ExecutableElement} will be the the method in {@code Parent}, whose return type is
@@ -418,11 +416,11 @@ public abstract class AutoValueExtension {
* Context#abstractMethods()}.
*
* <p>For example, Android's {@code Parcelable} interface includes a <a
- * href="http://developer.android.com/reference/android/os/Parcelable.html#writeToParcel(android.os.Parcel,
- * int)">method</a> {@code void writeToParcel(Parcel, int)}. Normally AutoValue would not know
- * what to do with that abstract method. But an {@code AutoValueExtension} that understands {@code
- * Parcelable} can provide a useful implementation and return the {@code writeToParcel} method
- * here. That will prevent a warning about the method from AutoValue.
+ * href="http://developer.android.com/reference/android/os/Parcelable.html#writeToParcel(android.os.Parcel,int)">method</a>
+ * {@code void writeToParcel(Parcel, int)}. Normally AutoValue would not know what to do with that
+ * abstract method. But an {@code AutoValueExtension} that understands {@code Parcelable} can
+ * provide a useful implementation and return the {@code writeToParcel} method here. That will
+ * prevent a warning about the method from AutoValue.
*
* @param context the Context of the code generation for this class.
*/
diff --git a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java
index 0ca46bde..acbe1c03 100644
--- a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java
+++ b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizeExtension.java
@@ -19,18 +19,19 @@ import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
import static com.google.auto.common.MoreElements.getPackage;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
import static com.google.auto.value.extension.memoized.processor.ClassNames.MEMOIZED_NAME;
import static com.google.auto.value.extension.memoized.processor.MemoizedValidator.getAnnotationMirror;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static com.google.common.collect.Iterables.filter;
import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.common.collect.Sets.union;
import static com.squareup.javapoet.MethodSpec.constructorBuilder;
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static javax.lang.model.element.Modifier.ABSTRACT;
@@ -50,7 +51,6 @@ import com.google.auto.common.Visibility;
import com.google.auto.service.AutoService;
import com.google.auto.value.extension.AutoValueExtension;
import com.google.common.base.Equivalence.Wrapper;
-import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.FormatMethod;
@@ -65,9 +65,7 @@ import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import java.lang.annotation.Inherited;
-import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.Messager;
@@ -80,7 +78,6 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
@@ -126,13 +123,9 @@ public final class MemoizeExtension extends AutoValueExtension {
}
private static ImmutableSet<ExecutableElement> memoizedMethods(Context context) {
- ImmutableSet.Builder<ExecutableElement> memoizedMethods = ImmutableSet.builder();
- for (ExecutableElement method : methodsIn(context.autoValueClass().getEnclosedElements())) {
- if (getAnnotationMirror(method, MEMOIZED_NAME).isPresent()) {
- memoizedMethods.add(method);
- }
- }
- return memoizedMethods.build();
+ return methodsIn(context.autoValueClass().getEnclosedElements()).stream()
+ .filter(m -> getAnnotationMirror(m, MEMOIZED_NAME).isPresent())
+ .collect(toImmutableSet());
}
static final class Generator {
@@ -164,7 +157,7 @@ public final class MemoizeExtension extends AutoValueExtension {
classBuilder(className)
.superclass(superType())
.addAnnotations(copiedClassAnnotations(context.autoValueClass()))
- .addTypeVariables(typeVariableNames())
+ .addTypeVariables(annotatedTypeVariableNames())
.addModifiers(isFinal ? FINAL : ABSTRACT)
.addMethod(constructor());
generatedAnnotationSpec(elements, sourceVersion, MemoizeExtension.class)
@@ -183,6 +176,7 @@ public final class MemoizeExtension extends AutoValueExtension {
return JavaFile.builder(context.packageName(), generated.build()).build().toString();
}
+ // LINT.IfChange
private TypeName superType() {
ClassName superType = ClassName.get(context.packageName(), classToExtend);
ImmutableList<TypeVariableName> typeVariableNames = typeVariableNames();
@@ -193,23 +187,31 @@ public final class MemoizeExtension extends AutoValueExtension {
}
private ImmutableList<TypeVariableName> typeVariableNames() {
- ImmutableList.Builder<TypeVariableName> typeVariableNamesBuilder = ImmutableList.builder();
- for (TypeParameterElement typeParameter : context.autoValueClass().getTypeParameters()) {
- typeVariableNamesBuilder.add(TypeVariableName.get(typeParameter));
- }
- return typeVariableNamesBuilder.build();
+ return context.autoValueClass().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .collect(toImmutableList());
+ }
+
+ private ImmutableList<TypeVariableName> annotatedTypeVariableNames() {
+ return context.autoValueClass().getTypeParameters().stream()
+ .map(
+ p ->
+ TypeVariableName.get(p)
+ .annotated(
+ p.getAnnotationMirrors().stream()
+ .map(AnnotationSpec::get)
+ .collect(toImmutableList())))
+ .collect(toImmutableList());
}
private MethodSpec constructor() {
MethodSpec.Builder constructor = constructorBuilder();
- for (Map.Entry<String, TypeMirror> property : context.propertyTypes().entrySet()) {
- constructor.addParameter(annotatedType(property.getValue()), property.getKey() + "$");
- }
- List<String> namesWithDollars = new ArrayList<String>();
- for (String property : context.properties().keySet()) {
- namesWithDollars.add(property + "$");
- }
- constructor.addStatement("super($L)", Joiner.on(", ").join(namesWithDollars));
+ context
+ .propertyTypes()
+ .forEach((name, type) -> constructor.addParameter(annotatedType(type), name + "$"));
+ String superParams =
+ context.properties().keySet().stream().map(n -> n + "$").collect(joining(", "));
+ constructor.addStatement("super($L)", superParams);
return constructor.build();
}
@@ -250,6 +252,7 @@ public final class MemoizeExtension extends AutoValueExtension {
.build();
}
+ // LINT.IfChange
/**
* True if the given class name is in the com.google.auto.value package or a subpackage. False
* if the class name contains {@code Test}, since many AutoValue tests under
@@ -478,15 +481,15 @@ public final class MemoizeExtension extends AutoValueExtension {
return elements.overrides(method, objectMethod(methodName), context.autoValueClass());
}
- private ExecutableElement objectMethod(final String methodName) {
+ private ExecutableElement objectMethod(String methodName) {
TypeElement object = elements.getTypeElement(Object.class.getName());
- for (ExecutableElement method : methodsIn(object.getEnclosedElements())) {
- if (method.getSimpleName().contentEquals(methodName)) {
- return method;
- }
- }
- throw new IllegalArgumentException(
- String.format("No method in Object named \"%s\"", methodName));
+ return methodsIn(object.getEnclosedElements()).stream()
+ .filter(m -> m.getSimpleName().contentEquals(methodName))
+ .findFirst()
+ .orElseThrow(
+ () ->
+ new IllegalArgumentException(
+ String.format("No method in Object named \"%s\"", methodName)));
}
private boolean pullDownMethodAnnotation(AnnotationMirror annotation) {
@@ -594,9 +597,7 @@ public final class MemoizeExtension extends AutoValueExtension {
/** Translate a {@link TypeMirror} into a {@link TypeName}, including type annotations. */
private static TypeName annotatedType(TypeMirror type) {
List<AnnotationSpec> annotations =
- type.getAnnotationMirrors().stream()
- .map(AnnotationSpec::get)
- .collect(toList());
+ type.getAnnotationMirrors().stream().map(AnnotationSpec::get).collect(toList());
return TypeName.get(type).annotated(annotations);
}
}
diff --git a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizedValidator.java b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizedValidator.java
index 5a770500..d250fbc6 100644
--- a/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizedValidator.java
+++ b/value/src/main/java/com/google/auto/value/extension/memoized/processor/MemoizedValidator.java
@@ -37,8 +37,8 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
/**
- * An annotation {@link Processor} that reports errors for {@link Memoized @Memoized} methods that
- * are not inside {@code AutoValue}-annotated classes.
+ * An annotation {@link Processor} that reports errors for {@code @Memoized} methods that are not
+ * inside {@code AutoValue}-annotated classes.
*/
@AutoService(Processor.class)
@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
@@ -66,9 +66,7 @@ public final class MemoizedValidator extends AbstractProcessor {
}
private static boolean isAutoValue(Element element) {
- return element
- .getAnnotationMirrors()
- .stream()
+ return element.getAnnotationMirrors().stream()
.map(annotation -> MoreTypes.asTypeElement(annotation.getAnnotationType()))
.anyMatch(type -> type.getQualifiedName().contentEquals("com.google.auto.value.AutoValue"));
}
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/userguide/index.md b/value/src/main/java/com/google/auto/value/extension/serializable/g3doc/index.md
index e7661839..0282404b 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/userguide/index.md
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/g3doc/index.md
@@ -4,7 +4,6 @@
An [`AutoValue`] extension that enables `@AutoValue` classes with
un-serializable properties to be serializable.
-
## Usage
To use the [`SerializableAutoValueExtension`] with your `AutoValue` class, the
@@ -100,4 +99,3 @@ un-serializable types with [SerializerExtensions].
[`SerializableAutoValue`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/SerializableAutoValue.java
[`SerializableAutoValueExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/extension/SerializableAutoValueExtension.java
[SerializerExtensions]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/userguide/serializer-extension.md
-
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/userguide/serializer-extension.md b/value/src/main/java/com/google/auto/value/extension/serializable/g3doc/serializer-extension.md
index edfa3ca3..aaec7c02 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/userguide/serializer-extension.md
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/g3doc/serializer-extension.md
@@ -243,4 +243,3 @@ as-is.
[`SerializableAutoValueExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/extension/SerializableAutoValueExtension.java
[`SerializerExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerExtension.java
[`Serializer`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/Serializer.java
-
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtension.java b/value/src/main/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtension.java
index d3265d02..5143d8bf 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtension.java
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtension.java
@@ -15,9 +15,9 @@
*/
package com.google.auto.value.extension.serializable.processor;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreStreams.toImmutableMap;
import static com.google.auto.value.extension.serializable.processor.ClassNames.SERIALIZABLE_AUTO_VALUE_NAME;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-import static com.google.common.collect.ImmutableMap.toImmutableMap;
import static java.util.stream.Collectors.joining;
import com.google.auto.common.GeneratedAnnotationSpecs;
@@ -59,6 +59,7 @@ import javax.lang.model.type.TypeMirror;
* <li>The AutoValue class must implement {@link Serializable}.
* <li>Unserializable fields in the AutoValue class must be supported by a {@link
* com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension}.
+ * </ul>
*/
@AutoService(AutoValueExtension.class)
public final class SerializableAutoValueExtension extends AutoValueExtension {
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/SerializerFactoryLoader.java b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/SerializerFactoryLoader.java
index 12984b05..e81b5867 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/SerializerFactoryLoader.java
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/SerializerFactoryLoader.java
@@ -21,6 +21,8 @@ import com.google.auto.value.extension.serializable.serializer.interfaces.Serial
import com.google.auto.value.processor.SimpleServiceLoader;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
+import java.util.Optional;
+import java.util.regex.Pattern;
import javax.annotation.processing.ProcessingEnvironment;
import javax.tools.Diagnostic;
@@ -40,10 +42,18 @@ public final class SerializerFactoryLoader {
private static ImmutableList<SerializerExtension> loadExtensions(
ProcessingEnvironment processingEnv) {
+ // The below is a workaround for a test-building bug. We don't expect to support it indefinitely
+ // so don't depend on it.
+ String allowedMissingClasses =
+ processingEnv.getOptions().get("allowedMissingSerializableExtensionClasses");
+ Optional<Pattern> allowedMissingClassesPattern =
+ Optional.ofNullable(allowedMissingClasses).map(Pattern::compile);
try {
return ImmutableList.copyOf(
SimpleServiceLoader.load(
- SerializerExtension.class, SerializerFactoryLoader.class.getClassLoader()));
+ SerializerExtension.class,
+ SerializerFactoryLoader.class.getClassLoader(),
+ allowedMissingClassesPattern));
} catch (Throwable t) {
processingEnv
.getMessager()
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableListSerializerExtension.java b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableListSerializerExtension.java
index 7ff4f19d..a390adde 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableListSerializerExtension.java
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableListSerializerExtension.java
@@ -55,17 +55,22 @@ public final class ImmutableListSerializerExtension implements SerializerExtensi
return Optional.empty();
}
- return Optional.of(new ImmutableListSerializer(containedTypeSerializer, processingEnv));
+ return Optional.of(
+ new ImmutableListSerializer(containedTypeSerializer, factory, processingEnv));
}
private static class ImmutableListSerializer implements Serializer {
private final Serializer containedTypeSerializer;
+ private final SerializerFactory factory;
private final ProcessingEnvironment processingEnv;
ImmutableListSerializer(
- Serializer containedTypeSerializer, ProcessingEnvironment processingEnv) {
+ Serializer containedTypeSerializer,
+ SerializerFactory factory,
+ ProcessingEnvironment processingEnv) {
this.containedTypeSerializer = containedTypeSerializer;
+ this.factory = factory;
this.processingEnv = processingEnv;
}
@@ -81,7 +86,7 @@ public final class ImmutableListSerializerExtension implements SerializerExtensi
@Override
public CodeBlock toProxy(CodeBlock expression) {
- CodeBlock element = CodeBlock.of("value$$");
+ CodeBlock element = factory.newIdentifier("value");
return CodeBlock.of(
"$L.stream().map($T.wrapper($L -> $L)).collect($T.toImmutableList())",
expression,
@@ -93,7 +98,7 @@ public final class ImmutableListSerializerExtension implements SerializerExtensi
@Override
public CodeBlock fromProxy(CodeBlock expression) {
- CodeBlock element = CodeBlock.of("value$$");
+ CodeBlock element = factory.newIdentifier("value");
return CodeBlock.of(
"$L.stream().map($T.wrapper($L -> $L)).collect($T.toImmutableList())",
expression,
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableMapSerializerExtension.java b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableMapSerializerExtension.java
index 9d571e3b..8d67e103 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableMapSerializerExtension.java
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/ImmutableMapSerializerExtension.java
@@ -60,7 +60,7 @@ public final class ImmutableMapSerializerExtension implements SerializerExtensio
return Optional.of(
new ImmutableMapSerializer(
- keyType, valueType, keyTypeSerializer, valueTypeSerializer, processingEnv));
+ keyType, valueType, keyTypeSerializer, valueTypeSerializer, factory, processingEnv));
}
private static class ImmutableMapSerializer implements Serializer {
@@ -71,6 +71,7 @@ public final class ImmutableMapSerializerExtension implements SerializerExtensio
private final TypeMirror valueProxyType;
private final Serializer keyTypeSerializer;
private final Serializer valueTypeSerializer;
+ private final SerializerFactory factory;
private final ProcessingEnvironment processingEnv;
ImmutableMapSerializer(
@@ -78,6 +79,7 @@ public final class ImmutableMapSerializerExtension implements SerializerExtensio
TypeMirror valueType,
Serializer keyTypeSerializer,
Serializer valueTypeSerializer,
+ SerializerFactory factory,
ProcessingEnvironment processingEnv) {
this.keyType = keyType;
this.valueType = valueType;
@@ -85,6 +87,7 @@ public final class ImmutableMapSerializerExtension implements SerializerExtensio
this.valueProxyType = valueTypeSerializer.proxyFieldType();
this.keyTypeSerializer = keyTypeSerializer;
this.valueTypeSerializer = valueTypeSerializer;
+ this.factory = factory;
this.processingEnv = processingEnv;
}
@@ -117,13 +120,15 @@ public final class ImmutableMapSerializerExtension implements SerializerExtensio
generateValueMapFunction(valueProxyType, valueType, valueTypeSerializer::fromProxy));
}
- private static CodeBlock generateKeyMapFunction(
+ private CodeBlock generateKeyMapFunction(
TypeMirror originalType,
TypeMirror transformedType,
Function<CodeBlock, CodeBlock> proxyMap) {
- CodeBlock element = CodeBlock.of("element$$");
+ CodeBlock element = factory.newIdentifier("element");
+ CodeBlock value = factory.newIdentifier("value");
return CodeBlock.of(
- "value$$ -> $T.<$T, $T>wrapper($L -> $L).apply(value$$.getKey())",
+ "$1L -> $2T.<$3T, $4T>wrapper($5L -> $6L).apply($1L.getKey())",
+ value,
FunctionWithExceptions.class,
originalType,
transformedType,
@@ -131,13 +136,15 @@ public final class ImmutableMapSerializerExtension implements SerializerExtensio
proxyMap.apply(element));
}
- private static CodeBlock generateValueMapFunction(
+ private CodeBlock generateValueMapFunction(
TypeMirror originalType,
TypeMirror transformedType,
Function<CodeBlock, CodeBlock> proxyMap) {
- CodeBlock element = CodeBlock.of("element$$");
+ CodeBlock element = factory.newIdentifier("element");
+ CodeBlock value = factory.newIdentifier("value");
return CodeBlock.of(
- "value$$ -> $T.<$T, $T>wrapper($L -> $L).apply(value$$.getValue())",
+ "$1L -> $2T.<$3T, $4T>wrapper($5L -> $6L).apply($1L.getValue())",
+ value,
FunctionWithExceptions.class,
originalType,
transformedType,
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/SerializerFactoryImpl.java b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/SerializerFactoryImpl.java
index 57741f91..7c55289d 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/SerializerFactoryImpl.java
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/impl/SerializerFactoryImpl.java
@@ -19,7 +19,9 @@ import com.google.auto.value.extension.serializable.serializer.interfaces.Serial
import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerExtension;
import com.google.auto.value.extension.serializable.serializer.interfaces.SerializerFactory;
import com.google.common.collect.ImmutableList;
+import com.squareup.javapoet.CodeBlock;
import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.type.TypeMirror;
@@ -28,6 +30,7 @@ public final class SerializerFactoryImpl implements SerializerFactory {
private final ImmutableList<SerializerExtension> extensions;
private final ProcessingEnvironment env;
+ private final AtomicInteger idCount = new AtomicInteger();
public SerializerFactoryImpl(
ImmutableList<SerializerExtension> extensions, ProcessingEnvironment env) {
@@ -45,4 +48,9 @@ public final class SerializerFactoryImpl implements SerializerFactory {
}
return IdentitySerializerFactory.getSerializer(typeMirror);
}
+
+ @Override
+ public CodeBlock newIdentifier(String prefix) {
+ return CodeBlock.of("$L$$$L", prefix, idCount.incrementAndGet());
+ }
}
diff --git a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerFactory.java b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerFactory.java
index d05c88b2..2b542251 100644
--- a/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerFactory.java
+++ b/value/src/main/java/com/google/auto/value/extension/serializable/serializer/interfaces/SerializerFactory.java
@@ -15,6 +15,7 @@
*/
package com.google.auto.value.extension.serializable.serializer.interfaces;
+import com.squareup.javapoet.CodeBlock;
import javax.lang.model.type.TypeMirror;
/**
@@ -26,4 +27,16 @@ public interface SerializerFactory {
/** Returns a {@link Serializer} for the given {@link TypeMirror}. */
Serializer getSerializer(TypeMirror type);
+
+ /**
+ * Returns an identifier beginning with the given prefix and that is distinct from any identifier
+ * returned by another call to this method. The returned identifier will contain a {@code $},
+ * which should also mean it is distinct from identifiers in user code that are in scope.
+ *
+ * <p>The default implementation of this method throws {@link UnsupportedOperationException} for
+ * compatibility reasons.
+ */
+ default CodeBlock newIdentifier(String prefix) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/ToPrettyString.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/ToPrettyString.java
new file mode 100644
index 00000000..cd2762a2
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/ToPrettyString.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring;
+
+import static java.lang.annotation.ElementType.METHOD;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Target;
+import java.util.Collection;
+
+/**
+ * Annotates instance methods that return an easy-to-read {@link String} representing the instance.
+ * When the method is {@code abstract} and enclosed in an {@link com.google.auto.value.AutoValue}
+ * class, an implementation of the method will be automatically generated.
+ *
+ * <p>When generating an implementation of an {@code @ToPrettyString} method, each property of the
+ * {@code @AutoValue} type is individually printed in an easy-to-read format. If the type of the
+ * property itself has a {@code @ToPrettyString} method, that method will be called in assistance of
+ * computing the pretty string. Non-{@code @AutoValue} classes can contribute a pretty string
+ * representation by annotating a method with {@code @ToPrettyString}.
+ *
+ * <p>{@link Collection} and {@link Collection}-like types have special representations in generated
+ * pretty strings.
+ *
+ * <p>If no {@code @ToPrettyString} method is found on a type and the type is not one with a built
+ * in rendering, the {@link Object#toString()} value will be used instead.
+ *
+ * <p>{@code @ToPrettyString} is valid on overridden {@code toString()} and other methods alike.
+ *
+ * <h3>Example</h3>
+ *
+ * <pre>
+ * {@code @AutoValue}
+ * abstract class Pretty {
+ * abstract {@code List<String>} property();
+ *
+ * {@code @ToPrettyString}
+ * abstract String toPrettyString();
+ * }
+ *
+ * System.out.println(new AutoValue_Pretty(List.of("abc", "def", "has\nnewline)).toPrettyString())
+ * // Pretty{
+ * // property = [
+ * // abc,
+ * // def,
+ * // has
+ * // newline,
+ * // ]
+ * // }
+ * }</pre>
+ */
+@Documented
+@Target(METHOD)
+public @interface ToPrettyString {}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/Annotations.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/Annotations.java
new file mode 100644
index 00000000..255d6c9c
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/Annotations.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring.processor;
+
+import static com.google.auto.value.extension.toprettystring.processor.ClassNames.TO_PRETTY_STRING_NAME;
+
+import com.google.auto.common.MoreTypes;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** Extension methods for working with {@link AnnotationMirror}. */
+final class Annotations {
+ static Optional<AnnotationMirror> getAnnotationMirror(Element element, String annotationName) {
+ for (AnnotationMirror annotation : element.getAnnotationMirrors()) {
+ TypeElement annotationElement = MoreTypes.asTypeElement(annotation.getAnnotationType());
+ if (annotationElement.getQualifiedName().contentEquals(annotationName)) {
+ return Optional.of(annotation);
+ }
+ }
+ return Optional.empty();
+ }
+
+ static Optional<AnnotationMirror> toPrettyStringAnnotation(Element element) {
+ return getAnnotationMirror(element, TO_PRETTY_STRING_NAME);
+ }
+
+ private Annotations() {}
+}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ClassNames.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ClassNames.java
new file mode 100644
index 00000000..0cfd5772
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ClassNames.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring.processor;
+
+/** Names of classes that are referenced in the processor/extension. */
+final class ClassNames {
+ static final String TO_PRETTY_STRING_NAME =
+ "com.google.auto.value.extension.toprettystring.ToPrettyString";
+
+ private ClassNames() {}
+}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ExtensionClassTypeSpecBuilder.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ExtensionClassTypeSpecBuilder.java
new file mode 100644
index 00000000..e2381f7e
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ExtensionClassTypeSpecBuilder.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring.processor;
+
+import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
+import static com.google.auto.common.GeneratedAnnotationSpecs.generatedAnnotationSpec;
+import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
+import static com.google.auto.value.extension.toprettystring.processor.Annotations.getAnnotationMirror;
+import static com.google.common.collect.Sets.union;
+import static com.squareup.javapoet.MethodSpec.constructorBuilder;
+import static com.squareup.javapoet.TypeSpec.classBuilder;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+import static javax.lang.model.element.Modifier.ABSTRACT;
+import static javax.lang.model.element.Modifier.FINAL;
+
+import com.google.auto.common.MoreTypes;
+import com.google.auto.common.Visibility;
+import com.google.auto.value.extension.AutoValueExtension;
+import com.google.auto.value.extension.AutoValueExtension.Context;
+import com.google.common.base.Equivalence.Wrapper;
+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.MethodSpec;
+import com.squareup.javapoet.ParameterizedTypeName;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import com.squareup.javapoet.TypeVariableName;
+import java.lang.annotation.Inherited;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.QualifiedNameable;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+/**
+ * A factory for {@link TypeSpec}s used in {@link AutoValueExtension} implementations.
+ *
+ * <p>This is copied from {@link
+ * com.google.auto.value.extension.memoized.processor.MemoizeExtension} until we find a better
+ * location to consolidate the code.
+ */
+final class ExtensionClassTypeSpecBuilder {
+ private static final String AUTO_VALUE_PACKAGE_NAME = "com.google.auto.value.";
+ private static final String AUTO_VALUE_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoValue";
+ private static final String COPY_ANNOTATIONS_NAME = AUTO_VALUE_NAME + ".CopyAnnotations";
+
+ private final Context context;
+ private final String className;
+ private final String classToExtend;
+ private final boolean isFinal;
+ private final Types types;
+ private final Elements elements;
+ private final SourceVersion sourceVersion;
+
+ private ExtensionClassTypeSpecBuilder(
+ Context context, String className, String classToExtend, boolean isFinal) {
+ this.context = context;
+ this.className = className;
+ this.classToExtend = classToExtend;
+ this.isFinal = isFinal;
+ this.types = context.processingEnvironment().getTypeUtils();
+ this.elements = context.processingEnvironment().getElementUtils();
+ this.sourceVersion = context.processingEnvironment().getSourceVersion();
+ }
+
+ static TypeSpec.Builder extensionClassTypeSpecBuilder(
+ Context context, String className, String classToExtend, boolean isFinal) {
+ return new ExtensionClassTypeSpecBuilder(context, className, classToExtend, isFinal)
+ .extensionClassBuilder();
+ }
+
+ TypeSpec.Builder extensionClassBuilder() {
+ TypeSpec.Builder builder =
+ classBuilder(className)
+ .superclass(superType())
+ .addAnnotations(copiedClassAnnotations(context.autoValueClass()))
+ .addTypeVariables(annotatedTypeVariableNames())
+ .addModifiers(isFinal ? FINAL : ABSTRACT)
+ .addMethod(constructor());
+ generatedAnnotationSpec(elements, sourceVersion, ToPrettyStringExtension.class)
+ .ifPresent(builder::addAnnotation);
+ return builder;
+ }
+
+ private TypeName superType() {
+ ClassName superType = ClassName.get(context.packageName(), classToExtend);
+ ImmutableList<TypeVariableName> typeVariableNames = typeVariableNames();
+
+ return typeVariableNames.isEmpty()
+ ? superType
+ : ParameterizedTypeName.get(superType, typeVariableNames.toArray(new TypeName[] {}));
+ }
+
+ private ImmutableList<TypeVariableName> typeVariableNames() {
+ return context.autoValueClass().getTypeParameters().stream()
+ .map(TypeVariableName::get)
+ .collect(toImmutableList());
+ }
+
+ private ImmutableList<TypeVariableName> annotatedTypeVariableNames() {
+ return context.autoValueClass().getTypeParameters().stream()
+ .map(
+ p ->
+ TypeVariableName.get(p)
+ .annotated(
+ p.getAnnotationMirrors().stream()
+ .map(AnnotationSpec::get)
+ .collect(toImmutableList())))
+ .collect(toImmutableList());
+ }
+
+ private MethodSpec constructor() {
+ MethodSpec.Builder constructor = constructorBuilder();
+ context
+ .propertyTypes()
+ .forEach((name, type) -> constructor.addParameter(annotatedType(type), name + "$"));
+ String superParams =
+ context.properties().keySet().stream().map(n -> n + "$").collect(joining(", "));
+ constructor.addStatement("super($L)", superParams);
+ return constructor.build();
+ }
+
+ /**
+ * True if the given class name is in the com.google.auto.value package or a subpackage. False if
+ * the class name contains {@code Test}, since many AutoValue tests under com.google.auto.value
+ * define their own annotations.
+ */
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private boolean isInAutoValuePackage(String className) {
+ return className.startsWith(AUTO_VALUE_PACKAGE_NAME) && !className.contains("Test");
+ }
+
+ /**
+ * Returns the fully-qualified name of an annotation-mirror, e.g.
+ * "com.google.auto.value.AutoValue".
+ */
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private static String getAnnotationFqName(AnnotationMirror annotation) {
+ return ((QualifiedNameable) annotation.getAnnotationType().asElement())
+ .getQualifiedName()
+ .toString();
+ }
+
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private boolean annotationVisibleFrom(AnnotationMirror annotation, Element from) {
+ Element annotationElement = annotation.getAnnotationType().asElement();
+ Visibility visibility = Visibility.effectiveVisibilityOfElement(annotationElement);
+ switch (visibility) {
+ case PUBLIC:
+ return true;
+ case PROTECTED:
+ // If the annotation is protected, it must be inside another class, call it C. If our
+ // @AutoValue class is Foo then, for the annotation to be visible, either Foo must be in
+ // the same package as C or Foo must be a subclass of C. If the annotation is visible from
+ // Foo then it is also visible from our generated subclass AutoValue_Foo.
+ // The protected case only applies to method annotations. An annotation on the
+ // AutoValue_Foo class itself can't be protected, even if AutoValue_Foo ultimately
+ // inherits from the class that defines the annotation. The JLS says "Access is permitted
+ // only within the body of a subclass":
+ // https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.2.1
+ // AutoValue_Foo is a top-level class, so an annotation on it cannot be in the body of a
+ // subclass of anything.
+ return getPackage(annotationElement).equals(getPackage(from))
+ || types.isSubtype(from.asType(), annotationElement.getEnclosingElement().asType());
+ case DEFAULT:
+ return getPackage(annotationElement).equals(getPackage(from));
+ default:
+ return false;
+ }
+ }
+
+ /** Implements the semantics of {@code AutoValue.CopyAnnotations}; see its javadoc. */
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private ImmutableList<AnnotationMirror> annotationsToCopy(
+ Element autoValueType, Element typeOrMethod, Set<String> excludedAnnotations) {
+ ImmutableList.Builder<AnnotationMirror> result = ImmutableList.builder();
+ for (AnnotationMirror annotation : typeOrMethod.getAnnotationMirrors()) {
+ String annotationFqName = getAnnotationFqName(annotation);
+ // To be included, the annotation should not be in com.google.auto.value,
+ // and it should not be in the excludedAnnotations set.
+ if (!isInAutoValuePackage(annotationFqName)
+ && !excludedAnnotations.contains(annotationFqName)
+ && annotationVisibleFrom(annotation, autoValueType)) {
+ result.add(annotation);
+ }
+ }
+
+ return result.build();
+ }
+
+ /** Implements the semantics of {@code AutoValue.CopyAnnotations}; see its javadoc. */
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private ImmutableList<AnnotationSpec> copyAnnotations(
+ Element autoValueType, Element typeOrMethod, Set<String> excludedAnnotations) {
+ ImmutableList<AnnotationMirror> annotationsToCopy =
+ annotationsToCopy(autoValueType, typeOrMethod, excludedAnnotations);
+ return annotationsToCopy.stream().map(AnnotationSpec::get).collect(toImmutableList());
+ }
+
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private static boolean hasAnnotationMirror(Element element, String annotationName) {
+ return getAnnotationMirror(element, annotationName).isPresent();
+ }
+
+ /**
+ * Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of
+ * {@code TypeMirror} where each type is an annotation type.
+ */
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private ImmutableSet<TypeMirror> getExcludedAnnotationTypes(Element element) {
+ Optional<AnnotationMirror> maybeAnnotation =
+ getAnnotationMirror(element, COPY_ANNOTATIONS_NAME);
+ if (!maybeAnnotation.isPresent()) {
+ return ImmutableSet.of();
+ }
+
+ @SuppressWarnings("unchecked")
+ List<AnnotationValue> excludedClasses =
+ (List<AnnotationValue>) getAnnotationValue(maybeAnnotation.get(), "exclude").getValue();
+ return excludedClasses.stream()
+ .map(
+ annotationValue ->
+ MoreTypes.equivalence().wrap((TypeMirror) annotationValue.getValue()))
+ // TODO(b/122509249): Move TypeMirrorSet to common package instead of doing this.
+ .distinct()
+ .map(Wrapper::get)
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the contents of the {@code AutoValue.CopyAnnotations.exclude} element, as a set of
+ * strings that are fully-qualified class names.
+ */
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private Set<String> getExcludedAnnotationClassNames(Element element) {
+ return getExcludedAnnotationTypes(element).stream()
+ .map(MoreTypes::asTypeElement)
+ .map(typeElement -> typeElement.getQualifiedName().toString())
+ .collect(toSet());
+ }
+
+ // TODO(b/122509249): Move code copied from com.google.auto.value.processor to auto-common.
+ private static Set<String> getAnnotationsMarkedWithInherited(Element element) {
+ return element.getAnnotationMirrors().stream()
+ .filter(a -> isAnnotationPresent(a.getAnnotationType().asElement(), Inherited.class))
+ .map(ExtensionClassTypeSpecBuilder::getAnnotationFqName)
+ .collect(toSet());
+ }
+
+ private ImmutableList<AnnotationSpec> copiedClassAnnotations(TypeElement type) {
+ // Only copy annotations from a class if it has @AutoValue.CopyAnnotations.
+ if (hasAnnotationMirror(type, COPY_ANNOTATIONS_NAME)) {
+ Set<String> excludedAnnotations =
+ union(getExcludedAnnotationClassNames(type), getAnnotationsMarkedWithInherited(type));
+
+ return copyAnnotations(type, type, excludedAnnotations);
+ } else {
+ return ImmutableList.of();
+ }
+ }
+
+ /** Translate a {@link TypeMirror} into a {@link TypeName}, including type annotations. */
+ private static TypeName annotatedType(TypeMirror type) {
+ List<AnnotationSpec> annotations =
+ type.getAnnotationMirrors().stream().map(AnnotationSpec::get).collect(toList());
+
+ return TypeName.get(type).annotated(annotations);
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringExtension.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringExtension.java
new file mode 100644
index 00000000..134fcac4
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringExtension.java
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring.processor;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreTypes.asTypeElement;
+import static com.google.auto.value.extension.toprettystring.processor.ExtensionClassTypeSpecBuilder.extensionClassTypeSpecBuilder;
+import static com.google.auto.value.extension.toprettystring.processor.ToPrettyStringMethods.toPrettyStringMethod;
+import static com.google.auto.value.extension.toprettystring.processor.ToPrettyStringMethods.toPrettyStringMethods;
+import static com.google.common.collect.Iterables.getLast;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static com.google.common.collect.Sets.intersection;
+import static com.squareup.javapoet.MethodSpec.methodBuilder;
+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 com.google.auto.common.MoreTypes;
+import com.google.auto.service.AutoService;
+import com.google.auto.value.extension.AutoValueExtension;
+import com.google.auto.value.extension.toprettystring.processor.ToPrettyStringExtension.PrettyPrintableKind.KindVisitor;
+import com.google.common.base.Equivalence;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.CodeBlock;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.MethodSpec;
+import com.squareup.javapoet.TypeName;
+import com.squareup.javapoet.TypeSpec;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Supplier;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.SimpleTypeVisitor8;
+import javax.lang.model.util.Types;
+
+/**
+ * Generates implementations of {@link
+ * com.google.auto.value.extension.toprettystring.ToPrettyString} annotated methods in {@link
+ * com.google.auto.value.AutoValue} types.
+ */
+@AutoService(AutoValueExtension.class)
+public final class ToPrettyStringExtension extends AutoValueExtension {
+ private static final ImmutableSet<Modifier> INHERITED_VISIBILITY_MODIFIERS =
+ ImmutableSet.of(PUBLIC, PROTECTED);
+ private static final String INDENT = " ";
+ private static final String INDENT_METHOD_NAME = "$indent";
+ private static final CodeBlock KEY_VALUE_SEPARATOR = CodeBlock.of("$S", ": ");
+
+ @Override
+ public String generateClass(
+ Context context, String className, String classToExtend, boolean isFinal) {
+ TypeSpec type =
+ extensionClassTypeSpecBuilder(context, className, classToExtend, isFinal)
+ .addMethods(toPrettyStringMethodSpecs(context))
+ .build();
+ return JavaFile.builder(context.packageName(), type)
+ .skipJavaLangImports(true)
+ .build()
+ .toString();
+ }
+
+ private ImmutableList<MethodSpec> toPrettyStringMethodSpecs(Context context) {
+ ExecutableElement toPrettyStringMethod = getOnlyElement(toPrettyStringMethods(context));
+ MethodSpec.Builder method =
+ methodBuilder(toPrettyStringMethod.getSimpleName().toString())
+ .addAnnotation(Override.class)
+ .returns(ClassName.get(String.class))
+ .addModifiers(FINAL)
+ .addModifiers(
+ intersection(toPrettyStringMethod.getModifiers(), INHERITED_VISIBILITY_MODIFIERS));
+
+ method.addCode("return $S", context.autoValueClass().getSimpleName() + " {");
+ ToPrettyStringImplementation implementation = ToPrettyStringImplementation.create(context);
+ method.addCode(implementation.toStringCodeBlock.build());
+
+ if (!context.properties().isEmpty()) {
+ method.addCode(" + $S", "\n");
+ }
+ method.addCode(" + $S;\n", "}");
+
+ return ImmutableList.<MethodSpec>builder()
+ .add(method.build())
+ .addAll(implementation.delegateMethods.values())
+ .add(indentMethod())
+ .build();
+ }
+
+ private static MethodSpec indentMethod() {
+ return methodBuilder(INDENT_METHOD_NAME)
+ .addModifiers(PRIVATE, STATIC)
+ .returns(ClassName.get(String.class))
+ .addParameter(TypeName.INT, "level")
+ .addStatement("$1T builder = new $1T()", StringBuilder.class)
+ .beginControlFlow("for (int i = 0; i < level; i++)")
+ .addStatement("builder.append($S)", INDENT)
+ .endControlFlow()
+ .addStatement("return builder.toString()")
+ .build();
+ }
+
+ private static class ToPrettyStringImplementation {
+ private final Types types;
+ private final Elements elements;
+
+ private final CodeBlock.Builder toStringCodeBlock = CodeBlock.builder();
+ private final Map<Equivalence.Wrapper<TypeMirror>, MethodSpec> delegateMethods =
+ new LinkedHashMap<>();
+ private final Set<String> methodNames = new HashSet<>();
+
+ private ToPrettyStringImplementation(Context context) {
+ this.types = context.processingEnvironment().getTypeUtils();
+ this.elements = context.processingEnvironment().getElementUtils();
+ // do not submit: what about "inherited" static methods?
+ getLocalAndInheritedMethods(context.autoValueClass(), types, elements)
+ .forEach(method -> methodNames.add(method.getSimpleName().toString()));
+ }
+
+ static ToPrettyStringImplementation create(Context context) {
+ ToPrettyStringImplementation implemention = new ToPrettyStringImplementation(context);
+ context
+ .propertyTypes()
+ .forEach(
+ (propertyName, type) -> {
+ String methodName =
+ context.properties().get(propertyName).getSimpleName().toString();
+ implemention.toStringCodeBlock.add(
+ "\n + $S + $L + $S",
+ String.format("\n%s%s = ", INDENT, propertyName),
+ implemention.format(CodeBlock.of("$N()", methodName), CodeBlock.of("1"), type),
+ ",");
+ });
+ return implemention;
+ }
+
+ /**
+ * Returns {@code propertyAccess} formatted for use within the {@link
+ * com.google.auto.value.extension.toprettystring.ToPrettyString} implementation.
+ *
+ * <p>If a helper method is necessary for formatting, a {@link MethodSpec} will be added to
+ * {@link #delegateMethods}.
+ *
+ * @param propertyAccess a reference to the variable that should be formatted.
+ * @param indentAccess a reference to an {@code int} representing how many indent levels should
+ * be used for this property.
+ * @param type the type of the {@code propertyAccess}.
+ */
+ private CodeBlock format(CodeBlock propertyAccess, CodeBlock indentAccess, TypeMirror type) {
+ PrettyPrintableKind printableKind = type.accept(new KindVisitor(types, elements), null);
+ DelegateMethod delegateMethod = new DelegateMethod(propertyAccess, indentAccess);
+ switch (printableKind) {
+ case PRIMITIVE:
+ return propertyAccess;
+ case REGULAR_OBJECT:
+ return delegateMethod
+ .methodName("format")
+ .invocation(
+ elements.getTypeElement("java.lang.Object").asType(), () -> reindent("toString"));
+ case HAS_TO_PRETTY_STRING_METHOD:
+ ExecutableElement method =
+ toPrettyStringMethod(asTypeElement(type), types, elements).get();
+ return delegateMethod.invocation(type, () -> reindent(method.getSimpleName()));
+ case ARRAY:
+ TypeMirror componentType = MoreTypes.asArray(type).getComponentType();
+ return delegateMethod.invocation(type, () -> forEachLoopMethodBody(componentType));
+ case COLLECTION:
+ TypeMirror elementType =
+ getOnlyElement(resolvedTypeParameters(type, "java.util.Collection"));
+ return delegateMethod.invocation(
+ collectionOf(elementType), () -> forEachLoopMethodBody(elementType));
+ case IMMUTABLE_PRIMITIVE_ARRAY:
+ return delegateMethod.invocation(type, this::forLoopMethodBody);
+ case OPTIONAL:
+ case GUAVA_OPTIONAL:
+ TypeMirror optionalType = getOnlyElement(MoreTypes.asDeclared(type).getTypeArguments());
+ return delegateMethod.invocation(
+ type, () -> optionalMethodBody(optionalType, printableKind));
+ case MAP:
+ return formatMap(type, delegateMethod);
+ case MULTIMAP:
+ return formatMultimap(type, delegateMethod);
+ }
+ throw new AssertionError(printableKind);
+ }
+
+ private CodeBlock formatMap(TypeMirror type, DelegateMethod delegateMethod) {
+ ImmutableList<TypeMirror> typeParameters = resolvedTypeParameters(type, "java.util.Map");
+ TypeMirror keyType = typeParameters.get(0);
+ TypeMirror valueType = typeParameters.get(1);
+ return delegateMethod.invocation(
+ mapOf(keyType, valueType), () -> mapMethodBody(keyType, valueType));
+ }
+
+ private CodeBlock formatMultimap(TypeMirror type, DelegateMethod delegateMethod) {
+ ImmutableList<TypeMirror> typeParameters =
+ resolvedTypeParameters(type, "com.google.common.collect.Multimap");
+ TypeMirror keyType = typeParameters.get(0);
+ TypeMirror valueType = typeParameters.get(1);
+ return delegateMethod.invocation(
+ multimapOf(keyType, valueType),
+ () -> multimapMethodBody(keyType, collectionOf(valueType)));
+ }
+
+ /**
+ * Parameter object to simplify the branches of {@link #format(CodeBlock, CodeBlock,
+ * TypeMirror)} that call a delegate method.
+ */
+ private class DelegateMethod {
+
+ private final CodeBlock propertyAccess;
+ private final CodeBlock indentAccess;
+ private Optional<String> methodName = Optional.empty();
+
+ DelegateMethod(CodeBlock propertyAccess, CodeBlock indentAccess) {
+ this.propertyAccess = propertyAccess;
+ this.indentAccess = indentAccess;
+ }
+
+ DelegateMethod methodName(String methodName) {
+ this.methodName = Optional.of(methodName);
+ return this;
+ }
+
+ CodeBlock invocation(TypeMirror parameterType, Supplier<CodeBlock> methodBody) {
+ Equivalence.Wrapper<TypeMirror> key = MoreTypes.equivalence().wrap(parameterType);
+ // This doesn't use putIfAbsent because the methodBody supplier could recursively create
+ // new delegate methods. Map.putIfAbsent doesn't support reentrant calls.
+ if (!delegateMethods.containsKey(key)) {
+ delegateMethods.put(
+ key,
+ createMethod(
+ methodName.orElseGet(() -> newDelegateMethodName(parameterType)),
+ parameterType,
+ methodBody));
+ }
+ return CodeBlock.of(
+ "$N($L, $L)", delegateMethods.get(key).name, propertyAccess, indentAccess);
+ }
+
+ private String newDelegateMethodName(TypeMirror type) {
+ String prefix = "format" + nameForType(type);
+ String methodName = prefix;
+ for (int i = 2; !methodNames.add(methodName); i++) {
+ methodName = prefix + i;
+ }
+ return methodName;
+ }
+
+ private MethodSpec createMethod(
+ String methodName, TypeMirror type, Supplier<CodeBlock> methodBody) {
+ return methodBuilder(methodName)
+ .addModifiers(PRIVATE, STATIC)
+ .returns(ClassName.get(String.class))
+ .addParameter(TypeName.get(type), "value")
+ .addParameter(TypeName.INT, "indentLevel")
+ .beginControlFlow("if (value == null)")
+ .addStatement("return $S", "null")
+ .endControlFlow()
+ .addCode(methodBody.get())
+ .build();
+ }
+ }
+
+ private CodeBlock reindent(CharSequence methodName) {
+ return CodeBlock.builder()
+ .addStatement(
+ "return value.$1N().replace($2S, $2S + $3N(indentLevel))",
+ methodName,
+ "\n",
+ INDENT_METHOD_NAME)
+ .build();
+ }
+
+ private CodeBlock forEachLoopMethodBody(TypeMirror elementType) {
+ return loopMethodBody(
+ "[",
+ "]",
+ CodeBlock.of("for ($T element : value)", elementType),
+ format(CodeBlock.of("element"), CodeBlock.of("indentLevel + 1"), elementType));
+ }
+
+ private CodeBlock forLoopMethodBody() {
+ return loopMethodBody(
+ "[",
+ "]",
+ CodeBlock.of("for (int i = 0; i < value.length(); i++)"),
+ CodeBlock.of("value.get(i)"));
+ }
+
+ private CodeBlock mapMethodBody(TypeMirror keyType, TypeMirror valueType) {
+ return forEachMapEntryMethodBody(keyType, valueType, "value");
+ }
+
+ private CodeBlock multimapMethodBody(TypeMirror keyType, TypeMirror valueType) {
+ return forEachMapEntryMethodBody(keyType, valueType, "value.asMap()");
+ }
+
+ private CodeBlock forEachMapEntryMethodBody(
+ TypeMirror keyType, TypeMirror valueType, String propertyAccess) {
+ CodeBlock entryType = CodeBlock.of("$T<$T, $T>", Map.Entry.class, keyType, valueType);
+ return loopMethodBody(
+ "{",
+ "}",
+ CodeBlock.of("for ($L entry : $L.entrySet())", entryType, propertyAccess),
+ format(CodeBlock.of("entry.getKey()"), CodeBlock.of("indentLevel + 1"), keyType),
+ KEY_VALUE_SEPARATOR,
+ format(CodeBlock.of("entry.getValue()"), CodeBlock.of("indentLevel + 1"), valueType));
+ }
+
+ private CodeBlock loopMethodBody(
+ String openSymbol,
+ String closeSymbol,
+ CodeBlock loopDeclaration,
+ CodeBlock... appendedValues) {
+ ImmutableList<CodeBlock> allAppendedValues =
+ ImmutableList.<CodeBlock>builder()
+ .add(CodeBlock.of("$S", "\n"))
+ .add(CodeBlock.of("$N(indentLevel + 1)", INDENT_METHOD_NAME))
+ .add(appendedValues)
+ .add(CodeBlock.of("$S", ","))
+ .build();
+ return CodeBlock.builder()
+ .addStatement("$1T builder = new $1T().append($2S)", StringBuilder.class, openSymbol)
+ .addStatement("boolean hasElements = false")
+ .beginControlFlow("$L", loopDeclaration)
+ .addStatement(
+ "builder$L",
+ allAppendedValues.stream()
+ .map(value -> CodeBlock.of(".append($L)", value))
+ .collect(CodeBlock.joining("")))
+ .addStatement("hasElements = true")
+ .endControlFlow()
+ .beginControlFlow("if (hasElements)")
+ .addStatement("builder.append($S).append($N(indentLevel))", "\n", INDENT_METHOD_NAME)
+ .endControlFlow()
+ .addStatement("return builder.append($S).toString()", closeSymbol)
+ .build();
+ }
+
+ private CodeBlock optionalMethodBody(
+ TypeMirror optionalType, PrettyPrintableKind printableKind) {
+ return CodeBlock.builder()
+ .addStatement(
+ "return (value.isPresent() ? $L : $S)",
+ format(CodeBlock.of("value.get()"), CodeBlock.of("indentLevel"), optionalType),
+ printableKind.equals(PrettyPrintableKind.OPTIONAL) ? "<empty>" : "<absent>")
+ .build();
+ }
+
+ private ImmutableList<TypeMirror> resolvedTypeParameters(
+ TypeMirror propertyType, String interfaceName) {
+ return elements.getTypeElement(interfaceName).getTypeParameters().stream()
+ .map(p -> types.asMemberOf(MoreTypes.asDeclared(propertyType), p))
+ .collect(toImmutableList());
+ }
+
+ private DeclaredType collectionOf(TypeMirror elementType) {
+ return types.getDeclaredType(elements.getTypeElement("java.util.Collection"), elementType);
+ }
+
+ private DeclaredType mapOf(TypeMirror keyType, TypeMirror valueType) {
+ return types.getDeclaredType(elements.getTypeElement("java.util.Map"), keyType, valueType);
+ }
+
+ private DeclaredType multimapOf(TypeMirror keyType, TypeMirror valueType) {
+ return types.getDeclaredType(
+ elements.getTypeElement("com.google.common.collect.Multimap"), keyType, valueType);
+ }
+
+ /** Returns a valid Java identifier for method or variable of type {@code type}. */
+ private String nameForType(TypeMirror type) {
+ return type.accept(
+ new SimpleTypeVisitor8<String, Void>() {
+ @Override
+ public String visitDeclared(DeclaredType type, Void v) {
+ String simpleName = simpleNameForType(type);
+ if (type.getTypeArguments().isEmpty()) {
+ return simpleName;
+ }
+ ImmutableList<String> typeArgumentNames =
+ type.getTypeArguments().stream()
+ .map(t -> simpleNameForType(t))
+ .collect(toImmutableList());
+ if (isMapOrMultimap(type) && typeArgumentNames.size() == 2) {
+ return String.format(
+ "%sOf%sTo%s", simpleName, typeArgumentNames.get(0), typeArgumentNames.get(1));
+ }
+
+ List<String> parts = new ArrayList<>();
+ parts.add(simpleName);
+ parts.add("Of");
+ parts.addAll(typeArgumentNames.subList(0, typeArgumentNames.size() - 1));
+ if (typeArgumentNames.size() > 1) {
+ parts.add("And");
+ }
+ parts.add(getLast(typeArgumentNames));
+ return String.join("", parts);
+ }
+
+ @Override
+ protected String defaultAction(TypeMirror type, Void v) {
+ return simpleNameForType(type);
+ }
+ },
+ null);
+ }
+
+ boolean isMapOrMultimap(TypeMirror type) {
+ TypeMirror mapType = elements.getTypeElement("java.util.Map").asType();
+ if (types.isAssignable(type, types.erasure(mapType))) {
+ return true;
+ }
+ TypeElement multimapElement = elements.getTypeElement("com.google.common.collect.Multimap");
+ return multimapElement != null
+ && types.isAssignable(type, types.erasure(multimapElement.asType()));
+ }
+
+ private String simpleNameForType(TypeMirror type) {
+ return type.accept(
+ new SimpleTypeVisitor8<String, Void>() {
+ @Override
+ public String visitPrimitive(PrimitiveType primitiveType, Void v) {
+ return types.boxedClass(primitiveType).getSimpleName().toString();
+ }
+
+ @Override
+ public String visitArray(ArrayType arrayType, Void v) {
+ return arrayType.getComponentType().accept(this, null) + "Array";
+ }
+
+ @Override
+ public String visitDeclared(DeclaredType declaredType, Void v) {
+ return declaredType.asElement().getSimpleName().toString();
+ }
+
+ @Override
+ protected String defaultAction(TypeMirror typeMirror, Void v) {
+ throw new AssertionError(typeMirror);
+ }
+ },
+ null);
+ }
+ }
+
+ enum PrettyPrintableKind {
+ HAS_TO_PRETTY_STRING_METHOD,
+ REGULAR_OBJECT,
+ PRIMITIVE,
+ COLLECTION,
+ ARRAY,
+ IMMUTABLE_PRIMITIVE_ARRAY,
+ OPTIONAL,
+ GUAVA_OPTIONAL,
+ MAP,
+ MULTIMAP,
+ ;
+
+ private static final ImmutableMap<String, PrettyPrintableKind> KINDS_BY_EXACT_TYPE =
+ ImmutableMap.of(
+ "java.util.Optional", OPTIONAL,
+ "com.google.common.base.Optional", GUAVA_OPTIONAL,
+ "com.google.common.primitives.ImmutableIntArray", IMMUTABLE_PRIMITIVE_ARRAY,
+ "com.google.common.primitives.ImmutableLongArray", IMMUTABLE_PRIMITIVE_ARRAY,
+ "com.google.common.primitives.ImmutableDoubleArray", IMMUTABLE_PRIMITIVE_ARRAY);
+
+ private static final ImmutableMap<String, PrettyPrintableKind> KINDS_BY_SUPERTYPE =
+ ImmutableMap.of(
+ "java.util.Collection", COLLECTION,
+ "java.util.Map", MAP,
+ "com.google.common.collect.Multimap", MULTIMAP);
+
+ static class KindVisitor extends SimpleTypeVisitor8<PrettyPrintableKind, Void> {
+ private final Elements elements;
+ private final Types types;
+
+ KindVisitor(Types types, Elements elements) {
+ this.types = types;
+ this.elements = elements;
+ }
+
+ @Override
+ public PrettyPrintableKind visitPrimitive(PrimitiveType primitiveType, Void v) {
+ return PRIMITIVE;
+ }
+
+ @Override
+ public PrettyPrintableKind visitArray(ArrayType arrayType, Void v) {
+ return ARRAY;
+ }
+
+ @Override
+ public PrettyPrintableKind visitDeclared(DeclaredType declaredType, Void v) {
+ TypeElement typeElement = asTypeElement(declaredType);
+ if (toPrettyStringMethod(typeElement, types, elements).isPresent()) {
+ return HAS_TO_PRETTY_STRING_METHOD;
+ }
+ PrettyPrintableKind byExactType =
+ KINDS_BY_EXACT_TYPE.get(typeElement.getQualifiedName().toString());
+ if (byExactType != null) {
+ return byExactType;
+ }
+
+ for (Map.Entry<String, PrettyPrintableKind> entry : KINDS_BY_SUPERTYPE.entrySet()) {
+ TypeElement supertypeElement = elements.getTypeElement(entry.getKey());
+ if (supertypeElement != null
+ && types.isAssignable(declaredType, types.erasure(supertypeElement.asType()))) {
+ return entry.getValue();
+ }
+ }
+
+ return REGULAR_OBJECT;
+ }
+ }
+ }
+
+ @Override
+ public boolean applicable(Context context) {
+ return toPrettyStringMethods(context).size() == 1;
+ }
+
+ @Override
+ public ImmutableSet<ExecutableElement> consumeMethods(Context context) {
+ return toPrettyStringMethods(context);
+ }
+
+ @Override
+ public IncrementalExtensionType incrementalType(ProcessingEnvironment processingEnvironment) {
+ return IncrementalExtensionType.ISOLATING;
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringMethods.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringMethods.java
new file mode 100644
index 00000000..041a16d6
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringMethods.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring.processor;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
+import static com.google.auto.value.extension.toprettystring.processor.Annotations.toPrettyStringAnnotation;
+import static com.google.common.collect.MoreCollectors.toOptional;
+
+import com.google.auto.value.extension.AutoValueExtension.Context;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import java.util.Optional;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+
+final class ToPrettyStringMethods {
+ /**
+ * Returns the {@link com.google.auto.value.extension.toprettystring.ToPrettyString} annotated
+ * methods for an {@code @AutoValue} type.
+ */
+ static ImmutableSet<ExecutableElement> toPrettyStringMethods(Context context) {
+ return context.abstractMethods().stream()
+ .filter(method -> toPrettyStringAnnotation(method).isPresent())
+ .collect(toImmutableSet());
+ }
+
+ /**
+ * Returns the {@link com.google.auto.value.extension.toprettystring.ToPrettyString} annotated
+ * method for a type.
+ */
+ static ImmutableList<ExecutableElement> toPrettyStringMethods(
+ TypeElement element, Types types, Elements elements) {
+ return getLocalAndInheritedMethods(element, types, elements).stream()
+ .filter(method -> toPrettyStringAnnotation(method).isPresent())
+ .collect(toImmutableList());
+ }
+
+ /**
+ * Returns the {@link com.google.auto.value.extension.toprettystring.ToPrettyString} annotated
+ * method for a type.
+ */
+ static Optional<ExecutableElement> toPrettyStringMethod(
+ TypeElement element, Types types, Elements elements) {
+ return toPrettyStringMethods(element, types, elements).stream().collect(toOptional());
+ }
+
+ private ToPrettyStringMethods() {}
+}
diff --git a/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringValidator.java b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringValidator.java
new file mode 100644
index 00000000..b77a54d6
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/extension/toprettystring/processor/ToPrettyStringValidator.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring.processor;
+
+import static com.google.auto.value.extension.toprettystring.processor.ClassNames.TO_PRETTY_STRING_NAME;
+import static com.google.auto.value.extension.toprettystring.processor.ToPrettyStringMethods.toPrettyStringMethods;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toCollection;
+import static javax.lang.model.element.Modifier.STATIC;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+import static javax.tools.Diagnostic.Kind.ERROR;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.service.AutoService;
+import com.google.common.collect.ImmutableList;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
+
+/**
+ * An annotation processor that validates {@link
+ * com.google.auto.value.extension.toprettystring.ToPrettyString} usage.
+ */
+@AutoService(Processor.class)
+@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
+@SupportedAnnotationTypes(TO_PRETTY_STRING_NAME)
+public final class ToPrettyStringValidator extends AbstractProcessor {
+ @Override
+ public boolean process(
+ Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
+ Types types = processingEnv.getTypeUtils();
+ Elements elements = processingEnv.getElementUtils();
+ TypeElement toPrettyString = elements.getTypeElement(TO_PRETTY_STRING_NAME);
+
+ Set<ExecutableElement> annotatedMethods =
+ methodsIn(roundEnvironment.getElementsAnnotatedWith(toPrettyString));
+ for (ExecutableElement method : annotatedMethods) {
+ validateMethod(method, elements);
+ }
+
+ validateSingleToPrettyStringMethod(annotatedMethods, types, elements);
+
+ return false;
+ }
+
+ private void validateMethod(ExecutableElement method, Elements elements) {
+ ErrorReporter errorReporter = new ErrorReporter(method, processingEnv.getMessager());
+ if (method.getModifiers().contains(STATIC)) {
+ errorReporter.reportError("@ToPrettyString methods must be instance methods");
+ }
+
+ TypeMirror stringType = elements.getTypeElement("java.lang.String").asType();
+ if (!MoreTypes.equivalence().equivalent(method.getReturnType(), stringType)) {
+ errorReporter.reportError("@ToPrettyString methods must return String");
+ }
+
+ if (!method.getParameters().isEmpty()) {
+ errorReporter.reportError("@ToPrettyString methods cannot have parameters");
+ }
+ }
+
+ private void validateSingleToPrettyStringMethod(
+ Set<ExecutableElement> annotatedMethods, Types types, Elements elements) {
+ Set<TypeElement> enclosingTypes =
+ annotatedMethods.stream()
+ .map(Element::getEnclosingElement)
+ .map(MoreElements::asType)
+ .collect(toCollection(LinkedHashSet::new));
+ for (TypeElement enclosingType : enclosingTypes) {
+ ImmutableList<ExecutableElement> methods =
+ toPrettyStringMethods(enclosingType, types, elements);
+ if (methods.size() > 1) {
+ processingEnv
+ .getMessager()
+ .printMessage(
+ ERROR,
+ String.format(
+ "%s has multiple @ToPrettyString methods:%s",
+ enclosingType.getQualifiedName(), formatMethodList(methods)),
+ enclosingType);
+ }
+ }
+ }
+
+ private String formatMethodList(ImmutableList<ExecutableElement> methods) {
+ return methods.stream().map(this::formatMethodInList).collect(joining());
+ }
+
+ private String formatMethodInList(ExecutableElement method) {
+ return String.format(
+ "\n - %s.%s()",
+ MoreElements.asType(method.getEnclosingElement()).getQualifiedName(),
+ method.getSimpleName());
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ private static final class ErrorReporter {
+ private final ExecutableElement method;
+ private final Messager messager;
+
+ ErrorReporter(ExecutableElement method, Messager messager) {
+ this.method = method;
+ this.messager = messager;
+ }
+
+ void reportError(String error) {
+ messager.printMessage(ERROR, error, method);
+ }
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoAnnotationProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoAnnotationProcessor.java
index d3cd9bd5..3acf9332 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoAnnotationProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoAnnotationProcessor.java
@@ -17,14 +17,20 @@ package com.google.auto.value.processor;
import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation;
import static com.google.auto.value.processor.ClassNames.AUTO_ANNOTATION_NAME;
+import static com.google.common.collect.Maps.immutableEntry;
+import static java.util.Comparator.comparing;
+import static java.util.stream.Collectors.joining;
import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
import com.google.auto.common.SuperficialValidation;
import com.google.auto.service.AutoService;
import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.hash.Hashing;
import com.google.common.primitives.Primitives;
import com.google.errorprone.annotations.FormatMethod;
import java.io.IOException;
@@ -41,6 +47,7 @@ import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
@@ -74,11 +81,30 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
public class AutoAnnotationProcessor extends AbstractProcessor {
public AutoAnnotationProcessor() {}
+ private Elements elementUtils;
+ private Types typeUtils;
+ private Nullables nullables;
+ private TypeMirror javaLangObject;
+
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
+ @Override
+ public ImmutableSet<String> getSupportedOptions() {
+ return ImmutableSet.of(Nullables.NULLABLE_OPTION);
+ }
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ this.elementUtils = processingEnv.getElementUtils();
+ this.typeUtils = processingEnv.getTypeUtils();
+ this.nullables = new Nullables(processingEnv);
+ this.javaLangObject = elementUtils.getTypeElement("java.lang.Object").asType();
+ }
+
/**
* Issue a compilation error. This method does not throw an exception, since we want to continue
* processing and perhaps report other errors.
@@ -99,26 +125,10 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
return new AbortProcessingException();
}
- private Elements elementUtils;
- private Types typeUtils;
-
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- elementUtils = processingEnv.getElementUtils();
- typeUtils = processingEnv.getTypeUtils();
- boolean claimed =
- (annotations.size() == 1
- && annotations
- .iterator()
- .next()
- .getQualifiedName()
- .contentEquals(AUTO_ANNOTATION_NAME));
- if (claimed) {
- process(roundEnv);
- return true;
- } else {
- return false;
- }
+ process(roundEnv);
+ return false;
}
private void process(RoundEnvironment roundEnv) {
@@ -152,7 +162,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
Set<Class<?>> wrapperTypesUsedInCollections = wrapperTypesUsedInCollections(method);
ImmutableMap<String, ExecutableElement> memberMethods = getMemberMethods(annotationElement);
- TypeElement methodClass = (TypeElement) method.getEnclosingElement();
+ TypeElement methodClass = MoreElements.asType(method.getEnclosingElement());
String pkg = TypeSimplifier.packageNameOf(methodClass);
ImmutableMap<String, AnnotationValue> defaultValues = getDefaultValues(annotationElement);
@@ -169,9 +179,11 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
vars.generated = getGeneratedTypeName();
vars.members = members;
vars.params = parameters;
+ vars.equalsParameterType = equalsParameterType();
vars.pkg = pkg;
vars.wrapperTypesUsedInCollections = wrapperTypesUsedInCollections;
vars.gwtCompatible = isGwtCompatible(annotationElement);
+ vars.serialVersionUID = computeSerialVersionUid(members, parameters);
ImmutableMap<String, Integer> invariableHashes = invariableHashes(members, parameters.keySet());
vars.invariableHashSum = 0;
for (int h : invariableHashes.values()) {
@@ -191,6 +203,18 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
.orElse("");
}
+ private String equalsParameterType() {
+ // Unlike AutoValue, we don't currently try to guess a @Nullable based on the methods in your
+ // class. It's the default one or nothing.
+ ImmutableList<AnnotationMirror> equalsParameterAnnotations =
+ nullables
+ .appropriateNullableGivenMethods(ImmutableSet.of())
+ .map(ImmutableList::of)
+ .orElse(ImmutableList.of());
+ return TypeEncoder.encodeWithAnnotations(
+ javaLangObject, equalsParameterAnnotations, ImmutableSet.of());
+ }
+
/**
* Returns the hashCode of the given AnnotationValue, if that hashCode is guaranteed to be always
* the same. The hashCode of a String or primitive type never changes. The hashCode of a Class or
@@ -246,7 +270,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
private boolean methodsAreOverloaded(List<ExecutableElement> methods) {
boolean overloaded = false;
- Set<String> classNames = new HashSet<String>();
+ Set<String> classNames = new HashSet<>();
for (ExecutableElement method : methods) {
String qualifiedClassName =
fullyQualifiedName(
@@ -261,10 +285,10 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
}
private String generatedClassName(ExecutableElement method) {
- TypeElement type = (TypeElement) method.getEnclosingElement();
+ TypeElement type = MoreElements.asType(method.getEnclosingElement());
String name = type.getSimpleName().toString();
while (type.getEnclosingElement() instanceof TypeElement) {
- type = (TypeElement) type.getEnclosingElement();
+ type = MoreElements.asType(type.getEnclosingElement());
name = type.getSimpleName() + "_" + name;
}
return "AutoAnnotation_" + name + "_" + method.getSimpleName();
@@ -275,7 +299,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
if (returnTypeMirror.getKind() == TypeKind.DECLARED) {
Element returnTypeElement = typeUtils.asElement(method.getReturnType());
if (returnTypeElement.getKind() == ElementKind.ANNOTATION_TYPE) {
- return (TypeElement) returnTypeElement;
+ return MoreElements.asType(returnTypeElement);
}
}
throw abortWithError(
@@ -399,7 +423,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
if (memberType.getKind() != TypeKind.ARRAY) {
return false;
}
- TypeMirror arrayElementType = ((ArrayType) memberType).getComponentType();
+ TypeMirror arrayElementType = MoreTypes.asArray(memberType).getComponentType();
TypeMirror wrappedArrayElementType =
arrayElementType.getKind().isPrimitive()
? typeUtils.boxedClass((PrimitiveType) arrayElementType).asType()
@@ -416,7 +440,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
* like {@code List<Integer>}. This is needed because we will emit a helper method for each such
* type, for example to convert {@code Collection<Integer>} into {@code int[]}.
*/
- private Set<Class<?>> wrapperTypesUsedInCollections(ExecutableElement method) {
+ private ImmutableSet<Class<?>> wrapperTypesUsedInCollections(ExecutableElement method) {
TypeElement javaUtilCollection = elementUtils.getTypeElement(Collection.class.getName());
ImmutableSet.Builder<Class<?>> usedInCollections = ImmutableSet.builder();
for (Class<?> wrapper : Primitives.allWrapperTypes()) {
@@ -437,9 +461,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
}
private static boolean isGwtCompatible(TypeElement annotationElement) {
- return annotationElement
- .getAnnotationMirrors()
- .stream()
+ return annotationElement.getAnnotationMirrors().stream()
.map(mirror -> mirror.getAnnotationType().asElement())
.anyMatch(element -> element.getSimpleName().contentEquals("GwtCompatible"));
}
@@ -448,6 +470,43 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
return pkg.isEmpty() ? cls : pkg + "." + cls;
}
+ /**
+ * We compute a {@code serialVersionUID} for the generated class based on the names and types of
+ * the annotation members that the {@code @AutoAnnotation} method defines. These are exactly the
+ * names and types of the instance fields in the generated class. So in the common case where the
+ * annotation acquires a new member with a default value, if the {@code @AutoAnnotation} method is
+ * not changed then the generated class will acquire an implementation of the new member method
+ * which just returns the default value. The {@code serialVersionUID} will not change, which makes
+ * sense because the instance fields haven't changed, and instances that were serialized before
+ * the new member was added should deserialize fine. On the other hand, if you then add a
+ * parameter to the {@code @AutoAnnotation} method for the new member, the implementation class
+ * will acquire a new instance field, and we will compute a different {@code serialVersionUID}.
+ * That's because an instance serialized before that change would not have a value for the new
+ * instance field, which would end up zero or null. Users don't expect annotation methods to
+ * return null so that would be bad.
+ *
+ * <p>We could instead add a {@code readObject(ObjectInputStream)} method that would check that
+ * all of the instance fields are really present in the deserialized instance, and perhaps
+ * replace them with their default values from the annotation if not. That seems a lot more
+ * complicated than is justified, though, especially since the instance fields are final and
+ * would have to be set in the deserialized object through reflection.
+ */
+ private static long computeSerialVersionUid(
+ ImmutableMap<String, Member> members, ImmutableMap<String, Parameter> parameters) {
+ // TypeMirror.toString() isn't fully specified so it could potentially differ between
+ // implementations. Our member.getType() string comes from TypeEncoder and is predictable, but
+ // it includes `...` markers around fully-qualified type names, which are used to handle
+ // imports. So we remove those markers below.
+ String namesAndTypesString =
+ members.entrySet().stream()
+ .filter(e -> parameters.containsKey(e.getKey()))
+ .map(e -> immutableEntry(e.getKey(), e.getValue().getType().replace("`", "")))
+ .sorted(comparing(Map.Entry::getKey))
+ .map(e -> e.getKey() + ":" + e.getValue())
+ .collect(joining(";"));
+ return Hashing.murmur3_128().hashUnencodedChars(namesAndTypesString).asLong();
+ }
+
private void writeSourceFile(String className, String text, TypeElement originatingType) {
try {
JavaFileObject sourceFile =
@@ -491,7 +550,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
public String getComponentType() {
Preconditions.checkState(getTypeMirror().getKind() == TypeKind.ARRAY);
- ArrayType arrayType = (ArrayType) getTypeMirror();
+ ArrayType arrayType = MoreTypes.asArray(getTypeMirror());
return TypeEncoder.encode(arrayType.getComponentType());
}
@@ -513,12 +572,12 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
if (getTypeMirror().getKind() != TypeKind.ARRAY) {
return false;
}
- TypeMirror componentType = ((ArrayType) getTypeMirror()).getComponentType();
+ TypeMirror componentType = MoreTypes.asArray(getTypeMirror()).getComponentType();
if (componentType.getKind() != TypeKind.DECLARED) {
return false;
}
- DeclaredType declared = (DeclaredType) componentType;
- if (!((TypeElement) processingEnv.getTypeUtils().asElement(componentType))
+ DeclaredType declared = MoreTypes.asDeclared(componentType);
+ if (!MoreElements.asType(processingEnv.getTypeUtils().asElement(componentType))
.getQualifiedName()
.contentEquals("java.lang.Class")) {
return false;
@@ -530,7 +589,7 @@ public class AutoAnnotationProcessor extends AbstractProcessor {
if (parameter.getKind() != TypeKind.WILDCARD) {
return true; // for Class<Foo>
}
- WildcardType wildcard = (WildcardType) parameter;
+ WildcardType wildcard = MoreTypes.asWildcard(parameter);
// In theory, we should check if getExtendsBound() != Object, since '?' is equivalent to
// '? extends Object', but, experimentally, neither javac or ecj will sets getExtendsBound()
// to 'Object', so there isn't a point in checking.
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoAnnotationTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoAnnotationTemplateVars.java
index 4db59c78..11bc896c 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoAnnotationTemplateVars.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoAnnotationTemplateVars.java
@@ -35,6 +35,12 @@ class AutoAnnotationTemplateVars extends TemplateVars {
*/
Map<String, AutoAnnotationProcessor.Parameter> params;
+ /**
+ * A string representing the parameter type declaration of the equals(Object) method, including
+ * any annotations.
+ */
+ String equalsParameterType;
+
/** The encoded form of the {@code Generated} class, or empty if it is not available. */
String generated;
@@ -74,6 +80,12 @@ class AutoAnnotationTemplateVars extends TemplateVars {
/** The sum of the hash code contributions from the members in {@link #invariableHashes}. */
Integer invariableHashSum;
+ /**
+ * A computed {@code serialVersionUID} based on the names and types of the {@code @AutoAnnotation}
+ * method parameters.
+ */
+ Long serialVersionUID;
+
private static final Template TEMPLATE = parsedTemplateForResource("autoannotation.vm");
@Override
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
new file mode 100644
index 00000000..b6a578fc
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreElements.getPackage;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
+import static com.google.auto.value.processor.AutoValueProcessor.OMIT_IDENTIFIERS_OPTION;
+import static com.google.auto.value.processor.ClassNames.AUTO_BUILDER_NAME;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toCollection;
+import static java.util.stream.Collectors.toMap;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
+import static javax.lang.model.util.ElementFilter.methodsIn;
+
+import com.google.auto.common.AnnotationMirrors;
+import com.google.auto.common.AnnotationValues;
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.auto.common.Visibility;
+import com.google.auto.service.AutoService;
+import com.google.auto.value.processor.BuilderSpec.PropertyGetter;
+import com.google.auto.value.processor.MissingTypes.MissingTypeException;
+import com.google.common.base.Ascii;
+import com.google.common.base.VerifyException;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableSet;
+import java.util.Optional;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.stream.Stream;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.Processor;
+import javax.annotation.processing.SupportedAnnotationTypes;
+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.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
+import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
+
+/**
+ * Javac annotation processor (compiler plugin) for builders; user code never references this class.
+ *
+ * @see <a href="https://github.com/google/auto/tree/master/value">AutoValue User's Guide</a>
+ * @author Éamonn McManus
+ */
+@AutoService(Processor.class)
+@SupportedAnnotationTypes(AUTO_BUILDER_NAME)
+@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
+public class AutoBuilderProcessor extends AutoValueishProcessor {
+ private static final String ALLOW_OPTION = "com.google.auto.value.AutoBuilderIsUnstable";
+
+ public AutoBuilderProcessor() {
+ super(AUTO_BUILDER_NAME);
+ }
+
+ @Override
+ public Set<String> getSupportedOptions() {
+ return ImmutableSet.of(OMIT_IDENTIFIERS_OPTION, ALLOW_OPTION);
+ }
+
+ private TypeMirror javaLangVoid;
+
+ @Override
+ public synchronized void init(ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ javaLangVoid = elementUtils().getTypeElement("java.lang.Void").asType();
+ }
+
+ @Override
+ void processType(TypeElement autoBuilderType) {
+ if (!processingEnv.getOptions().containsKey(ALLOW_OPTION)) {
+ errorReporter()
+ .abortWithError(
+ autoBuilderType,
+ "Compile with -A%s to enable this UNSUPPORTED AND UNSTABLE prototype",
+ ALLOW_OPTION);
+ }
+ if (autoBuilderType.getKind() != ElementKind.CLASS
+ && autoBuilderType.getKind() != ElementKind.INTERFACE) {
+ errorReporter()
+ .abortWithError(
+ autoBuilderType,
+ "[AutoBuilderWrongType] @AutoBuilder only applies to classes and interfaces");
+ }
+ checkModifiersIfNested(autoBuilderType);
+ // The annotation is guaranteed to be present by the contract of Processor#process
+ AnnotationMirror autoBuilderAnnotation =
+ getAnnotationMirror(autoBuilderType, AUTO_BUILDER_NAME).get();
+ TypeElement ofClass = getOfClass(autoBuilderType, autoBuilderAnnotation);
+ checkModifiersIfNested(ofClass, autoBuilderType, "AutoBuilder ofClass");
+ String callMethod = findCallMethodValue(autoBuilderAnnotation);
+ ImmutableSet<ExecutableElement> methods =
+ abstractMethodsIn(
+ getLocalAndInheritedMethods(autoBuilderType, typeUtils(), elementUtils()));
+ ExecutableElement executable = findExecutable(ofClass, callMethod, autoBuilderType, methods);
+ BuilderSpec builderSpec = new BuilderSpec(ofClass, processingEnv, errorReporter());
+ BuilderSpec.Builder builder = builderSpec.new Builder(autoBuilderType);
+ TypeMirror builtType = builtType(executable);
+ Optional<BuilderMethodClassifier<VariableElement>> maybeClassifier =
+ BuilderMethodClassifierForAutoBuilder.classify(
+ methods, errorReporter(), processingEnv, executable, builtType, autoBuilderType);
+ if (!maybeClassifier.isPresent()) {
+ // We've already output one or more error messages.
+ return;
+ }
+ BuilderMethodClassifier<VariableElement> classifier = maybeClassifier.get();
+ Map<String, String> propertyToGetterName =
+ Maps.transformValues(classifier.builderGetters(), PropertyGetter::getName);
+ AutoBuilderTemplateVars vars = new AutoBuilderTemplateVars();
+ vars.props = propertySet(executable, propertyToGetterName);
+ builder.defineVars(vars, classifier);
+ vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION);
+ String generatedClassName = generatedClassName(autoBuilderType, "AutoBuilder_");
+ vars.builderName = TypeSimplifier.simpleNameOf(generatedClassName);
+ vars.builtType = TypeEncoder.encode(builtType);
+ vars.build = build(executable);
+ vars.types = typeUtils();
+ vars.toBuilderConstructor = false;
+ vars.toBuilderMethods = ImmutableList.of();
+ defineSharedVarsForType(autoBuilderType, ImmutableSet.of(), vars);
+ String text = vars.toText();
+ text = TypeEncoder.decode(text, processingEnv, vars.pkg, autoBuilderType.asType());
+ text = Reformatter.fixup(text);
+ writeSourceFile(generatedClassName, text, autoBuilderType);
+ }
+
+ private ImmutableSet<Property> propertySet(
+ ExecutableElement executable, Map<String, String> propertyToGetterName) {
+ // Fix any parameter names that are reserved words in Java. Java source code can't have
+ // such parameter names, but Kotlin code might, for example.
+ Map<VariableElement, String> identifiers =
+ executable.getParameters().stream()
+ .collect(toMap(v -> v, v -> v.getSimpleName().toString()));
+ fixReservedIdentifiers(identifiers);
+ return executable.getParameters().stream()
+ .map(
+ v ->
+ newProperty(
+ v, identifiers.get(v), propertyToGetterName.get(v.getSimpleName().toString())))
+ .collect(toImmutableSet());
+ }
+
+ private Property newProperty(VariableElement var, String identifier, String getterName) {
+ String name = var.getSimpleName().toString();
+ TypeMirror type = var.asType();
+ Optional<String> nullableAnnotation = nullableAnnotationFor(var, var.asType());
+ return new Property(
+ name, identifier, TypeEncoder.encode(type), type, nullableAnnotation, getterName);
+ }
+
+ private ExecutableElement findExecutable(
+ TypeElement ofClass,
+ String callMethod,
+ TypeElement autoBuilderType,
+ ImmutableSet<ExecutableElement> methods) {
+ List<ExecutableElement> executables =
+ findRelevantExecutables(ofClass, callMethod, autoBuilderType);
+ String description =
+ callMethod.isEmpty() ? "constructor" : "static method named \"" + callMethod + "\"";
+ switch (executables.size()) {
+ case 0:
+ throw errorReporter()
+ .abortWithError(
+ autoBuilderType,
+ "[AutoBuilderNoVisible] No visible %s for %s",
+ description,
+ ofClass);
+ case 1:
+ return executables.get(0);
+ default:
+ return matchingExecutable(autoBuilderType, executables, methods, description);
+ }
+ }
+
+ private ImmutableList<ExecutableElement> findRelevantExecutables(
+ TypeElement ofClass, String callMethod, TypeElement autoBuilderType) {
+ List<? extends Element> elements = ofClass.getEnclosedElements();
+ Stream<ExecutableElement> relevantExecutables =
+ callMethod.isEmpty()
+ ? constructorsIn(elements).stream()
+ : methodsIn(elements).stream()
+ .filter(m -> m.getSimpleName().contentEquals(callMethod))
+ .filter(m -> m.getModifiers().contains(Modifier.STATIC));
+ return relevantExecutables
+ .filter(c -> visibleFrom(c, getPackage(autoBuilderType)))
+ .collect(toImmutableList());
+ }
+
+ private ExecutableElement matchingExecutable(
+ TypeElement autoBuilderType,
+ List<ExecutableElement> executables,
+ ImmutableSet<ExecutableElement> methods,
+ String description) {
+ // There's more than one visible executable (constructor or method). We try to find the one that
+ // corresponds to the methods in the @AutoBuilder interface. This is a bit approximate. We're
+ // basically just looking for an executable where all the parameter names correspond to setters
+ // or property builders in the interface. We might find out after choosing one that it is wrong
+ // for whatever reason (types don't match, spurious methods, etc). But it is likely that if the
+ // names are all accounted for in the methods, and if there's no other matching executable with
+ // more parameters, then this is indeed the one we want. If we later get errors when we try to
+ // analyze the interface in detail, those are probably legitimate errors and not because we
+ // picked the wrong executable.
+ ImmutableList<ExecutableElement> matches =
+ executables.stream().filter(x -> executableMatches(x, methods)).collect(toImmutableList());
+ switch (matches.size()) {
+ case 0:
+ throw errorReporter()
+ .abortWithError(
+ autoBuilderType,
+ "[AutoBuilderNoMatch] Property names do not correspond to the parameter names of"
+ + " any %s:\n%s",
+ description,
+ executableListString(executables));
+ case 1:
+ return matches.get(0);
+ default:
+ // More than one match, let's see if we can find the best one.
+ }
+ int max = matches.stream().mapToInt(c -> c.getParameters().size()).max().getAsInt();
+ ImmutableList<ExecutableElement> maxMatches =
+ matches.stream().filter(c -> c.getParameters().size() == max).collect(toImmutableList());
+ if (maxMatches.size() > 1) {
+ throw errorReporter()
+ .abortWithError(
+ autoBuilderType,
+ "[AutoBuilderAmbiguous] Property names correspond to more than one %s:\n%s",
+ description,
+ executableListString(maxMatches));
+ }
+ return maxMatches.get(0);
+ }
+
+ private String executableListString(List<ExecutableElement> executables) {
+ return executables.stream()
+ .map(AutoBuilderProcessor::executableString)
+ .collect(joining("\n ", " ", ""));
+ }
+
+ static String executableString(ExecutableElement executable) {
+ Element nameSource =
+ executable.getKind() == ElementKind.CONSTRUCTOR
+ ? executable.getEnclosingElement()
+ : executable;
+ return nameSource.getSimpleName()
+ + executable.getParameters().stream()
+ .map(v -> v.asType() + " " + v.getSimpleName())
+ .collect(joining(", ", "(", ")"));
+ }
+
+ private boolean executableMatches(
+ ExecutableElement executable, ImmutableSet<ExecutableElement> methods) {
+ // Start with the complete set of parameter names and remove them one by one as we find
+ // corresponding methods. We ignore case, under the assumption that it is unlikely that a case
+ // difference is going to allow a candidate to match when another one is better.
+ // A parameter named foo could be matched by methods like this:
+ // X foo(Y)
+ // X setFoo(Y)
+ // X fooBuilder()
+ // X fooBuilder(Y)
+ // There are further constraints, including on the types X and Y, that will later be imposed by
+ // BuilderMethodClassifier, but here we just require that there be at least one method with
+ // one of these shapes for foo.
+ NavigableSet<String> parameterNames =
+ executable.getParameters().stream()
+ .map(v -> v.getSimpleName().toString())
+ .collect(toCollection(() -> new TreeSet<>(String.CASE_INSENSITIVE_ORDER)));
+ for (ExecutableElement method : methods) {
+ String name = method.getSimpleName().toString();
+ if (name.endsWith("Builder")) {
+ String property = name.substring(0, name.length() - "Builder".length());
+ parameterNames.remove(property);
+ }
+ if (method.getParameters().size() == 1) {
+ parameterNames.remove(name);
+ if (name.startsWith("set")) {
+ parameterNames.remove(name.substring(3));
+ }
+ }
+ if (parameterNames.isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean visibleFrom(Element element, PackageElement fromPackage) {
+ Visibility visibility = Visibility.effectiveVisibilityOfElement(element);
+ switch (visibility) {
+ case PUBLIC:
+ return true;
+ case PROTECTED:
+ // We care about whether the constructor is visible from the generated class. The generated
+ // class is never going to be a subclass of the class containing the constructor, so
+ // protected and default access are equivalent.
+ case DEFAULT:
+ return getPackage(element).equals(fromPackage);
+ default:
+ return false;
+ }
+ }
+
+ private TypeMirror builtType(ExecutableElement executable) {
+ switch (executable.getKind()) {
+ case CONSTRUCTOR:
+ return executable.getEnclosingElement().asType();
+ case METHOD:
+ return executable.getReturnType();
+ default:
+ throw new VerifyException("Unexpected executable kind " + executable.getKind());
+ }
+ }
+
+ private String build(ExecutableElement executable) {
+ TypeElement enclosing = MoreElements.asType(executable.getEnclosingElement());
+ String type = TypeEncoder.encodeRaw(enclosing.asType());
+ switch (executable.getKind()) {
+ case CONSTRUCTOR:
+ boolean generic = !enclosing.getTypeParameters().isEmpty();
+ String typeParams = generic ? "<>" : "";
+ return "new " + type + typeParams;
+ case METHOD:
+ return type + "." + executable.getSimpleName();
+ default:
+ throw new VerifyException("Unexpected executable kind " + executable.getKind());
+ }
+ }
+
+ private static final ElementKind ELEMENT_KIND_RECORD = elementKindRecord();
+
+ private static ElementKind elementKindRecord() {
+ try {
+ Field record = ElementKind.class.getField("RECORD");
+ return (ElementKind) record.get(null);
+ } catch (ReflectiveOperationException e) {
+ // OK: we must be on a JDK version that predates this.
+ return null;
+ }
+ }
+
+ private TypeElement getOfClass(
+ TypeElement autoBuilderType, AnnotationMirror autoBuilderAnnotation) {
+ TypeElement ofClassValue = findOfClassValue(autoBuilderAnnotation);
+ boolean isDefault = typeUtils().isSameType(ofClassValue.asType(), javaLangVoid);
+ if (!isDefault) {
+ return ofClassValue;
+ }
+ Element enclosing = autoBuilderType.getEnclosingElement();
+ ElementKind enclosingKind = enclosing.getKind();
+ if (enclosing.getKind() != ElementKind.CLASS && enclosingKind != ELEMENT_KIND_RECORD) {
+ errorReporter()
+ .abortWithError(
+ autoBuilderType,
+ "[AutoBuilderEnclosing] @AutoBuilder must specify ofClass=Something.class or it"
+ + " must be nested inside the class to be built; actually nested inside %s %s.",
+ Ascii.toLowerCase(enclosingKind.name()),
+ enclosing);
+ }
+ return MoreElements.asType(enclosing);
+ }
+
+ private TypeElement findOfClassValue(AnnotationMirror autoBuilderAnnotation) {
+ AnnotationValue ofClassValue =
+ AnnotationMirrors.getAnnotationValue(autoBuilderAnnotation, "ofClass");
+ Object value = ofClassValue.getValue();
+ if (value instanceof TypeMirror) {
+ TypeMirror ofClassType = (TypeMirror) value;
+ switch (ofClassType.getKind()) {
+ case DECLARED:
+ return MoreTypes.asTypeElement(ofClassType);
+ case ERROR:
+ throw new MissingTypeException(MoreTypes.asError(ofClassType));
+ default:
+ break;
+ }
+ }
+ throw new MissingTypeException(null);
+ }
+
+ private String findCallMethodValue(AnnotationMirror autoBuilderAnnotation) {
+ AnnotationValue callMethodValue =
+ AnnotationMirrors.getAnnotationValue(autoBuilderAnnotation, "callMethod");
+ return AnnotationValues.getString(callMethodValue);
+ }
+
+ @Override
+ Optional<String> nullableAnnotationForMethod(ExecutableElement propertyMethod) {
+ // TODO(b/183005059): implement
+ return Optional.empty();
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoBuilderTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoBuilderTemplateVars.java
new file mode 100644
index 00000000..8c08a481
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/AutoBuilderTemplateVars.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import com.google.escapevelocity.Template;
+
+class AutoBuilderTemplateVars extends AutoValueOrBuilderTemplateVars {
+ private static final Template TEMPLATE = parsedTemplateForResource("autobuilder.vm");
+
+ @Override
+ Template parsedTemplate() {
+ return TEMPLATE;
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java
index d73c1c29..711b138c 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java
@@ -58,7 +58,7 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
@AutoService(Processor.class)
@SupportedAnnotationTypes(AUTO_ONE_OF_NAME)
@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
-public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor {
+public class AutoOneOfProcessor extends AutoValueishProcessor {
public AutoOneOfProcessor() {
super(AUTO_ONE_OF_NAME);
}
@@ -69,6 +69,11 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor {
}
@Override
+ public ImmutableSet<String> getSupportedOptions() {
+ return ImmutableSet.of(Nullables.NULLABLE_OPTION);
+ }
+
+ @Override
void processType(TypeElement autoOneOfType) {
if (autoOneOfType.getKind() != ElementKind.CLASS) {
errorReporter()
@@ -123,19 +128,9 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor {
}
private DeclaredType mirrorForKindType(TypeElement autoOneOfType) {
- Optional<AnnotationMirror> oneOfAnnotation =
- getAnnotationMirror(autoOneOfType, AUTO_ONE_OF_NAME);
- if (!oneOfAnnotation.isPresent()) {
- // This shouldn't happen unless the compilation environment is buggy,
- // but it has happened in the past and can crash the compiler.
- errorReporter()
- .abortWithError(
- autoOneOfType,
- "[AutoOneOfCompilerBug] annotation processor for @AutoOneOf was invoked with a type"
- + " that does not have that annotation; this is probably a compiler bug");
- }
- AnnotationValue kindValue =
- AnnotationMirrors.getAnnotationValue(oneOfAnnotation.get(), "value");
+ // The annotation is guaranteed to be present by the contract of Processor#process
+ AnnotationMirror oneOfAnnotation = getAnnotationMirror(autoOneOfType, AUTO_ONE_OF_NAME).get();
+ AnnotationValue kindValue = AnnotationMirrors.getAnnotationValue(oneOfAnnotation, "value");
Object value = kindValue.getValue();
if (value instanceof TypeMirror) {
TypeMirror kindType = (TypeMirror) value;
@@ -162,9 +157,7 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor {
Map<String, String> transformedPropertyNames =
propertyNames.stream().collect(toMap(this::transformName, s -> s));
Map<String, Element> transformedEnumConstants =
- kindElement
- .getEnclosedElements()
- .stream()
+ kindElement.getEnclosedElements().stream()
.filter(e -> e.getKind().equals(ElementKind.ENUM_CONSTANT))
.collect(toMap(e -> transformName(e.getSimpleName().toString()), e -> e));
@@ -215,8 +208,7 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor {
TypeMirror kindMirror,
ImmutableSet<ExecutableElement> abstractMethods) {
Set<ExecutableElement> kindGetters =
- abstractMethods
- .stream()
+ abstractMethods.stream()
.filter(e -> sameType(kindMirror, e.getReturnType()))
.filter(e -> e.getParameters().isEmpty())
.collect(toSet());
@@ -273,14 +265,15 @@ public class AutoOneOfProcessor extends AutoValueOrOneOfProcessor {
AutoOneOfTemplateVars vars,
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes,
ExecutableElement kindGetter) {
- vars.props = propertySet(
- propertyMethodsAndTypes, ImmutableListMultimap.of(), ImmutableListMultimap.of());
+ vars.props =
+ propertySet(
+ propertyMethodsAndTypes, ImmutableListMultimap.of(), ImmutableListMultimap.of());
vars.kindGetter = kindGetter.getSimpleName().toString();
vars.kindType = TypeEncoder.encode(kindGetter.getReturnType());
TypeElement javaIoSerializable = elementUtils().getTypeElement("java.io.Serializable");
vars.serializable =
- javaIoSerializable != null // just in case
- && typeUtils().isAssignable(type.asType(), javaIoSerializable.asType());
+ javaIoSerializable != null // just in case
+ && typeUtils().isAssignable(type.asType(), javaIoSerializable.asType());
}
@Override
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoOneOfTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoOneOfTemplateVars.java
index 1e15771f..06b24e2c 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoOneOfTemplateVars.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoOneOfTemplateVars.java
@@ -25,7 +25,7 @@ import java.util.Map;
* @author emcmanus@google.com (Éamonn McManus)
*/
@SuppressWarnings("unused") // the fields in this class are only read via reflection
-class AutoOneOfTemplateVars extends AutoValueOrOneOfTemplateVars {
+class AutoOneOfTemplateVars extends AutoValueishTemplateVars {
/**
* The properties defined by the parent class's abstract methods. The elements of this set are in
* the same order as the original abstract method declarations in the AutoOneOf class.
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueBuilderProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueBuilderProcessor.java
index 62dfeb35..ac6c8ecb 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueBuilderProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueBuilderProcessor.java
@@ -15,7 +15,7 @@
*/
package com.google.auto.value.processor;
-import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.hasAnnotationMirror;
+import static com.google.auto.value.processor.AutoValueishProcessor.hasAnnotationMirror;
import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_BUILDER_NAME;
import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_NAME;
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java
new file mode 100644
index 00000000..86cf4974
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueOrBuilderTemplateVars.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2012 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import com.google.auto.value.processor.AutoValueishProcessor.Property;
+import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.ImmutableSet;
+import java.util.Optional;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.util.Types;
+
+/**
+ * Variables to substitute into the autovalue.vm or builder.vm template.
+ *
+ * @author emcmanus@google.com (Éamonn McManus)
+ */
+@SuppressWarnings("unused") // the fields in this class are only read via reflection
+abstract class AutoValueOrBuilderTemplateVars extends AutoValueishTemplateVars {
+ /**
+ * The properties defined by the parent class's abstract methods. The elements of this set are in
+ * the same order as the original abstract method declarations in the AutoValue class.
+ */
+ ImmutableSet<Property> props;
+
+ /**
+ * The simple name of the generated builder, or empty if there is no builder. This is just
+ * {@code Builder} for AutoValue, since it is nested inside the {@code AutoValue_Foo} class. But
+ * it is {@code AutoBuilder_Foo} for AutoBuilder.
+ */
+ String builderName = "";
+
+ /**
+ * The name of the builder type as it should appear in source code, or empty if there is no
+ * builder type. If class {@code Address} contains {@code @AutoValue.Builder} class Builder then
+ * this will typically be {@code "Address.Builder"}.
+ */
+ String builderTypeName = "";
+
+ /**
+ * The formal generic signature of the {@code AutoValue.Builder} class. This is empty, or contains
+ * type variables with optional bounds, for example {@code <K, V extends K>}.
+ */
+ String builderFormalTypes = "";
+
+ /**
+ * The generic signature used by the generated builder subclass for its superclass reference. This
+ * is empty, or contains only type variables with no bounds, for example {@code <K, V>}.
+ */
+ String builderActualTypes = "";
+
+ /** True if the builder being implemented is an interface, false if it is an abstract class. */
+ Boolean builderIsInterface = false;
+
+ /**
+ * The full spelling of any annotations to add to the generated builder subclass, or an empty list
+ * if there are none. A non-empty value might look something like {@code
+ * @`java.lang.SuppressWarnings`("Immutable")}. The {@code ``} marks are explained in
+ * {@link TypeEncoder}.
+ */
+ ImmutableList<String> builderAnnotations = ImmutableList.of();
+
+ /** The builder's build method, often {@code "build"}. */
+ Optional<SimpleMethod> buildMethod = Optional.empty();
+
+ /** The type that will be built by the {@code build()} method of a builder. */
+ String builtType;
+
+ /**
+ * The constructor or method invocation that the {@code build()} method of a builder should use,
+ * without any parameters. This might be {@code "new Foo"} or {@code "Foo.someMethod"}.
+ */
+ String build;
+
+ /**
+ * A multimap from property names (like foo) to the corresponding setters. The same property may
+ * be set by more than one setter. For example, an ImmutableList might be set by {@code
+ * setFoo(ImmutableList<String>)} and {@code setFoo(String[])}.
+ */
+ ImmutableMultimap<String, BuilderSpec.PropertySetter> builderSetters = ImmutableMultimap.of();
+
+ /**
+ * A map from property names to information about the associated property builder. A property
+ * called foo (defined by a method foo() or getFoo()) can have a property builder called
+ * fooBuilder(). The type of foo must be a type that has an associated builder following certain
+ * conventions. Guava immutable types such as ImmutableList follow those conventions, as do many
+ * {@code @AutoValue} types.
+ */
+ ImmutableMap<String, PropertyBuilder> builderPropertyBuilders = ImmutableMap.of();
+
+ /**
+ * Properties that are required to be set. A property must be set explicitly except in the
+ * following cases:
+ *
+ * <ul>
+ * <li>it is {@code @Nullable} (in which case it defaults to null);
+ * <li>it is {@code Optional} (in which case it defaults to empty);
+ * <li>it has a property-builder method (in which case it defaults to empty).
+ * </ul>
+ */
+ ImmutableSet<Property> builderRequiredProperties = ImmutableSet.of();
+
+ /**
+ * A map from property names to information about the associated property getter. A property
+ * called foo (defined by a method foo() or getFoo()) can have a property getter method with the
+ * same name (foo() or getFoo()) and either the same return type or an Optional (or OptionalInt,
+ * etc) wrapping it.
+ */
+ ImmutableMap<String, BuilderSpec.PropertyGetter> builderGetters = ImmutableMap.of();
+
+ /**
+ * True if the generated builder should have a second constructor with a parameter of the built
+ * class. The constructor produces a new builder that starts off with the values from the
+ * parameter.
+ */
+ Boolean toBuilderConstructor;
+
+ /**
+ * Any {@code toBuilder()} methods, that is methods that return the builder type. AutoBuilder does
+ * not currently support this, but it's included in these shared variables to simplify the
+ * template.
+ */
+ ImmutableList<SimpleMethod> toBuilderMethods;
+
+ /**
+ * Whether to include identifiers in strings in the generated code. If false, exception messages
+ * will not mention properties by name, and {@code toString()} will include neither property names
+ * nor the name of the {@code @AutoValue} class.
+ */
+ Boolean identifiers;
+
+ /**
+ * True if the generated class should be final (there are no extensions that will generate
+ * subclasses)
+ */
+ Boolean isFinal = false;
+
+ /** The type utilities returned by {@link ProcessingEnvironment#getTypeUtils()}. */
+ Types types;
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
index aafffd7c..ab7da924 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java
@@ -16,11 +16,12 @@
package com.google.auto.value.processor;
import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
+import static com.google.auto.common.MoreStreams.toImmutableList;
import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_NAME;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
+import static java.util.Comparator.naturalOrder;
import static java.util.stream.Collectors.joining;
-import static java.util.stream.Collectors.toList;
import com.google.auto.service.AutoService;
import com.google.auto.value.extension.AutoValueExtension;
@@ -32,11 +33,9 @@ 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.Iterables;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -64,8 +63,8 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
@AutoService(Processor.class)
@SupportedAnnotationTypes(AUTO_VALUE_NAME)
@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.DYNAMIC)
-public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
- private static final String OMIT_IDENTIFIERS_OPTION = "com.google.auto.value.OmitIdentifiers";
+public class AutoValueProcessor extends AutoValueishProcessor {
+ static final String OMIT_IDENTIFIERS_OPTION = "com.google.auto.value.OmitIdentifiers";
// We moved MemoizeExtension to a different package, which had an unexpected effect:
// now if an old version of AutoValue is in the class path, ServiceLoader can pick up both the
@@ -100,10 +99,9 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
@VisibleForTesting
static ImmutableList<AutoValueExtension> extensionsFromLoader(ClassLoader loader) {
- return ImmutableList.copyOf(
- Iterables.filter(
- SimpleServiceLoader.load(AutoValueExtension.class, loader),
- ext -> !ext.getClass().getName().equals(OLD_MEMOIZE_EXTENSION)));
+ return SimpleServiceLoader.load(AutoValueExtension.class, loader).stream()
+ .filter(ext -> !ext.getClass().getName().equals(OLD_MEMOIZE_EXTENSION))
+ .collect(toImmutableList());
}
@Override
@@ -131,14 +129,17 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
}
@Override
- public Set<String> getSupportedOptions() {
+ public ImmutableSet<String> getSupportedOptions() {
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
AutoValueExtension.IncrementalExtensionType incrementalType =
extensions.stream()
.map(e -> e.incrementalType(processingEnv))
- .min(Comparator.naturalOrder())
+ .min(naturalOrder())
.orElse(AutoValueExtension.IncrementalExtensionType.ISOLATING);
- builder.add(OMIT_IDENTIFIERS_OPTION).addAll(optionsFor(incrementalType));
+ builder
+ .add(OMIT_IDENTIFIERS_OPTION)
+ .add(Nullables.NULLABLE_OPTION)
+ .addAll(optionsFor(incrementalType));
for (AutoValueExtension extension : extensions) {
builder.addAll(extension.getSupportedOptions());
}
@@ -164,15 +165,6 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
@Override
void processType(TypeElement type) {
- if (!hasAnnotationMirror(type, AUTO_VALUE_NAME)) {
- // This shouldn't happen unless the compilation environment is buggy,
- // but it has happened in the past and can crash the compiler.
- errorReporter()
- .abortWithError(
- type,
- "[AutoValueCompilerBug] annotation processor for @AutoValue was invoked with a type"
- + " that does not have that annotation; this is probably a compiler bug");
- }
if (type.getKind() != ElementKind.CLASS) {
errorReporter()
.abortWithError(type, "[AutoValueNotClass] @AutoValue only applies to classes");
@@ -216,7 +208,7 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
Optional<BuilderSpec.Builder> builder = builderSpec.getBuilder();
ImmutableSet<ExecutableElement> toBuilderMethods;
if (builder.isPresent()) {
- toBuilderMethods = builder.get().toBuilderMethods(typeUtils(), abstractMethods);
+ toBuilderMethods = builder.get().toBuilderMethods(typeUtils(), type, abstractMethods);
} else {
toBuilderMethods = ImmutableSet.of();
}
@@ -226,8 +218,9 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
ImmutableMap<String, ExecutableElement> properties =
propertyNameToMethodMap(propertyMethodsAndTypes.keySet());
- ExtensionContext context = new ExtensionContext(
- processingEnv, type, properties, propertyMethodsAndTypes, abstractMethods);
+ ExtensionContext context =
+ new ExtensionContext(
+ processingEnv, type, properties, propertyMethodsAndTypes, abstractMethods);
ImmutableList<AutoValueExtension> applicableExtensions = applicableExtensions(type, context);
ImmutableSet<ExecutableElement> consumedMethods =
methodsConsumedByExtensions(
@@ -240,21 +233,23 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
propertyMethodsAndTypes =
propertyMethodsIn(immutableSetDifference(abstractMethods, toBuilderMethods), type);
properties = propertyNameToMethodMap(propertyMethodsAndTypes.keySet());
- context = new ExtensionContext(
- processingEnv, type, properties, propertyMethodsAndTypes, allAbstractMethods);
+ context =
+ new ExtensionContext(
+ processingEnv, type, properties, propertyMethodsAndTypes, allAbstractMethods);
}
ImmutableSet<ExecutableElement> propertyMethods = propertyMethodsAndTypes.keySet();
boolean extensionsPresent = !applicableExtensions.isEmpty();
validateMethods(type, abstractMethods, toBuilderMethods, propertyMethods, extensionsPresent);
- String finalSubclass = generatedSubclassName(type, 0);
+ String finalSubclass = TypeSimplifier.simpleNameOf(generatedSubclassName(type, 0));
AutoValueTemplateVars vars = new AutoValueTemplateVars();
- vars.finalSubclass = TypeSimplifier.simpleNameOf(finalSubclass);
vars.types = processingEnv.getTypeUtils();
vars.identifiers = !processingEnv.getOptions().containsKey(OMIT_IDENTIFIERS_OPTION);
defineSharedVarsForType(type, methods, vars);
defineVarsForType(type, vars, toBuilderMethods, propertyMethodsAndTypes, builder);
+ vars.builtType = vars.origClass + vars.actualTypes;
+ vars.build = "new " + finalSubclass + vars.actualTypes;
// If we've encountered problems then we might end up invoking extensions with inconsistent
// state. Anyway we probably don't want to generate code which is likely to provoke further
@@ -276,7 +271,7 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
text = Reformatter.fixup(text);
writeSourceFile(subclass, text, type);
GwtSerialization gwtSerialization = new GwtSerialization(gwtCompatibility, processingEnv, type);
- gwtSerialization.maybeWriteGwtSerializer(vars);
+ gwtSerialization.maybeWriteGwtSerializer(vars, finalSubclass);
}
// Invokes each of the given extensions to generate its subclass, and returns the number of
@@ -431,22 +426,22 @@ public class AutoValueProcessor extends AutoValueOrOneOfProcessor {
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsAndTypes,
Optional<BuilderSpec.Builder> maybeBuilder) {
ImmutableSet<ExecutableElement> propertyMethods = propertyMethodsAndTypes.keySet();
- // We can't use ImmutableList.toImmutableList() for obscure Google-internal reasons.
vars.toBuilderMethods =
- ImmutableList.copyOf(toBuilderMethods.stream().map(SimpleMethod::new).collect(toList()));
+ toBuilderMethods.stream().map(SimpleMethod::new).collect(toImmutableList());
+ vars.toBuilderConstructor = !vars.toBuilderMethods.isEmpty();
ImmutableListMultimap<ExecutableElement, AnnotationMirror> annotatedPropertyFields =
propertyFieldAnnotationMap(type, propertyMethods);
ImmutableListMultimap<ExecutableElement, AnnotationMirror> annotatedPropertyMethods =
propertyMethodAnnotationMap(type, propertyMethods);
vars.props =
propertySet(propertyMethodsAndTypes, annotatedPropertyFields, annotatedPropertyMethods);
- vars.serialVersionUID = getSerialVersionUID(type);
// Check for @AutoValue.Builder and add appropriate variables if it is present.
maybeBuilder.ifPresent(
builder -> {
ImmutableBiMap<ExecutableElement, String> methodToPropertyName =
propertyNameToMethodMap(propertyMethods).inverse();
- builder.defineVars(vars, methodToPropertyName);
+ builder.defineVarsForAutoValue(vars, methodToPropertyName);
+ vars.builderName = "Builder";
vars.builderAnnotations = copiedClassAnnotations(builder.builderType());
});
}
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoValueTemplateVars.java
index 8f855bd0..e53b92e9 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueTemplateVars.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueTemplateVars.java
@@ -15,15 +15,7 @@
*/
package com.google.auto.value.processor;
-import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMultimap;
-import com.google.common.collect.ImmutableSet;
import com.google.escapevelocity.Template;
-import java.util.Optional;
-import javax.annotation.processing.ProcessingEnvironment;
-import javax.lang.model.util.Types;
/**
* The variables to substitute into the autovalue.vm template.
@@ -31,22 +23,7 @@ import javax.lang.model.util.Types;
* @author emcmanus@google.com (Éamonn McManus)
*/
@SuppressWarnings("unused") // the fields in this class are only read via reflection
-class AutoValueTemplateVars extends AutoValueOrOneOfTemplateVars {
- /**
- * The properties defined by the parent class's abstract methods. The elements of this set are in
- * the same order as the original abstract method declarations in the AutoValue class.
- */
- ImmutableSet<AutoValueOrOneOfProcessor.Property> props;
-
- /**
- * Whether to include identifiers in strings in the generated code. If false, exception messages
- * will not mention properties by name, and {@code toString()} will include neither property names
- * nor the name of the {@code @AutoValue} class.
- */
- Boolean identifiers;
-
- /** The type utilities returned by {@link ProcessingEnvironment#getTypeUtils()}. */
- Types types;
+class AutoValueTemplateVars extends AutoValueOrBuilderTemplateVars {
/**
* The encoding of the {@code @GwtCompatible} annotation to add to this class, or an empty string
@@ -56,100 +33,14 @@ class AutoValueTemplateVars extends AutoValueOrOneOfTemplateVars {
*/
String gwtCompatibleAnnotation;
- /** The text of the serialVersionUID constant, or empty if there is none. */
- String serialVersionUID;
-
/** The simple name of the generated subclass. */
String subclass;
/**
- * The simple name of the final generated subclass. For {@code @AutoValue public static class Foo
- * {}} this should always be "AutoValue_Foo".
- */
- String finalSubclass;
-
- /**
- * True if the generated class should be final (there are no extensions that will generate
- * subclasses)
- */
- Boolean isFinal = false;
-
- /**
* The modifiers (for example {@code final} or {@code abstract}) for the generated subclass,
* followed by a space if they are not empty.
*/
String modifiers;
- /**
- * The name of the builder type as it should appear in source code, or empty if there is no
- * builder type. If class {@code Address} contains {@code @AutoValue.Builder} class Builder then
- * this will typically be {@code "Address.Builder"}.
- */
- String builderTypeName = "";
-
- /**
- * The formal generic signature of the {@code AutoValue.Builder} class. This is empty, or contains
- * type variables with optional bounds, for example {@code <K, V extends K>}.
- */
- String builderFormalTypes = "";
- /**
- * The generic signature used by the generated builder subclass for its superclass reference. This
- * is empty, or contains only type variables with no bounds, for example {@code <K, V>}.
- */
- String builderActualTypes = "";
-
- /** True if the builder being implemented is an interface, false if it is an abstract class. */
- Boolean builderIsInterface = false;
-
- /**
- * The full spelling of any annotations to add to the generated builder subclass, or an empty list
- * if there are none. A non-empty value might look something like {@code
- * @`java.lang.SuppressWarnings`("Immutable")}. The {@code ``} marks are explained in
- * {@link TypeEncoder}.
- */
- ImmutableList<String> builderAnnotations = ImmutableList.of();
-
- /** The builder's build method, often {@code "build"}. */
- Optional<SimpleMethod> buildMethod = Optional.empty();
-
- /**
- * A multimap from property names (like foo) to the corresponding setters. The same property may
- * be set by more than one setter. For example, an ImmutableList might be set by {@code
- * setFoo(ImmutableList<String>)} and {@code setFoo(String[])}.
- */
- ImmutableMultimap<String, BuilderSpec.PropertySetter> builderSetters = ImmutableMultimap.of();
-
- /**
- * A map from property names to information about the associated property builder. A property
- * called foo (defined by a method foo() or getFoo()) can have a property builder called
- * fooBuilder(). The type of foo must be a type that has an associated builder following certain
- * conventions. Guava immutable types such as ImmutableList follow those conventions, as do many
- * {@code @AutoValue} types.
- */
- ImmutableMap<String, PropertyBuilder> builderPropertyBuilders = ImmutableMap.of();
-
- /**
- * Properties that are required to be set. A property must be set explicitly except in the
- * following cases:
- *
- * <ul>
- * <li>it is {@code @Nullable} (in which case it defaults to null);
- * <li>it is {@code Optional} (in which case it defaults to empty);
- * <li>it has a property-builder method (in which case it defaults to empty).
- * </ul>
- */
- ImmutableSet<AutoValueProcessor.Property> builderRequiredProperties = ImmutableSet.of();
-
- /**
- * A map from property names to information about the associated property getter. A property
- * called foo (defined by a method foo() or getFoo()) can have a property getter method with the
- * same name (foo() or getFoo()) and either the same return type or an Optional (or OptionalInt,
- * etc) wrapping it.
- */
- ImmutableMap<String, BuilderSpec.PropertyGetter> builderGetters = ImmutableMap.of();
-
- /** Any {@code toBuilder()} methods, that is methods that return the builder type. */
- ImmutableList<SimpleMethod> toBuilderMethods;
-
private static final Template TEMPLATE = parsedTemplateForResource("autovalue.vm");
@Override
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
index 36a82bd4..93f2f79e 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
@@ -19,6 +19,8 @@ import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.GeneratedAnnotations.generatedAnnotation;
import static com.google.auto.common.MoreElements.getPackage;
import static com.google.auto.common.MoreElements.isAnnotationPresent;
+import static com.google.auto.common.MoreStreams.toImmutableList;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_PACKAGE_NAME;
import static com.google.auto.value.processor.ClassNames.COPY_ANNOTATIONS_NAME;
import static com.google.common.collect.Iterables.getOnlyElement;
@@ -56,6 +58,7 @@ import java.util.Optional;
import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Predicate;
+import java.util.stream.IntStream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
@@ -82,11 +85,12 @@ import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
/**
- * Shared code between AutoValueProcessor and AutoOneOfProcessor.
+ * Shared code between {@link AutoValueProcessor}, {@link AutoOneOfProcessor}, and {@link
+ * AutoBuilderProcessor}.
*
* @author emcmanus@google.com (Éamonn McManus)
*/
-abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
+abstract class AutoValueishProcessor extends AbstractProcessor {
private final String annotationClassName;
/**
@@ -96,7 +100,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
*/
private final List<String> deferredTypeNames = new ArrayList<>();
- AutoValueOrOneOfProcessor(String annotationClassName) {
+ AutoValueishProcessor(String annotationClassName) {
this.annotationClassName = annotationClassName;
}
@@ -106,11 +110,13 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
private String simpleAnnotationName;
private ErrorReporter errorReporter;
+ private Nullables nullables;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
errorReporter = new ErrorReporter(processingEnv);
+ nullables = new Nullables(processingEnv);
}
final ErrorReporter errorReporter() {
@@ -149,30 +155,26 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
public static class Property {
private final String name;
private final String identifier;
- private final ExecutableElement method;
private final String type;
- private final ImmutableList<String> fieldAnnotations;
- private final ImmutableList<String> methodAnnotations;
+ private final TypeMirror typeMirror;
private final Optional<String> nullableAnnotation;
private final Optionalish optional;
+ private final String getter;
Property(
String name,
String identifier,
- ExecutableElement method,
String type,
- ImmutableList<String> fieldAnnotations,
- ImmutableList<String> methodAnnotations,
- Optional<String> nullableAnnotation) {
+ TypeMirror typeMirror,
+ Optional<String> nullableAnnotation,
+ String getter) {
this.name = name;
this.identifier = identifier;
- this.method = method;
this.type = type;
- this.fieldAnnotations = fieldAnnotations;
- this.methodAnnotations = methodAnnotations;
+ this.typeMirror = typeMirror;
this.nullableAnnotation = nullableAnnotation;
- TypeMirror propertyType = method.getReturnType();
- this.optional = Optionalish.createIfOptional(propertyType);
+ this.optional = Optionalish.createIfOptional(typeMirror);
+ this.getter = getter;
}
/**
@@ -196,16 +198,8 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
return name;
}
- /**
- * Returns the name of the getter method for this property as defined by the {@code @AutoValue}
- * class. For property {@code foo}, this will be {@code foo} or {@code getFoo} or {@code isFoo}.
- */
- public String getGetter() {
- return method.getSimpleName().toString();
- }
-
public TypeMirror getTypeMirror() {
- return method.getReturnType();
+ return typeMirror;
}
public String getType() {
@@ -213,23 +207,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
}
public TypeKind getKind() {
- return method.getReturnType().getKind();
- }
-
- /**
- * Returns the annotations (in string form) that should be applied to the property's field
- * declaration.
- */
- public List<String> getFieldAnnotations() {
- return fieldAnnotations;
- }
-
- /**
- * Returns the annotations (in string form) that should be applied to the property's method
- * implementation.
- */
- public List<String> getMethodAnnotations() {
- return methodAnnotations;
+ return typeMirror.getKind();
}
/**
@@ -257,13 +235,66 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
return nullableAnnotation.isPresent();
}
+ /**
+ * Returns the name of the getter method for this property as defined by the {@code @AutoValue}
+ * or {@code @AutoBuilder} class. For property {@code foo}, this will be {@code foo} or {@code
+ * getFoo} or {@code isFoo}. For AutoValue, this will also be the name of a getter method in a
+ * builder; in the case of AutoBuilder it will only be that and may be null.
+ */
+ public String getGetter() {
+ return getter;
+ }
+ }
+
+ /** A {@link Property} that corresponds to an abstract getter method in the source. */
+ public static class GetterProperty extends Property {
+ private final ExecutableElement method;
+ private final ImmutableList<String> fieldAnnotations;
+ private final ImmutableList<String> methodAnnotations;
+
+ GetterProperty(
+ String name,
+ String identifier,
+ ExecutableElement method,
+ String type,
+ ImmutableList<String> fieldAnnotations,
+ ImmutableList<String> methodAnnotations,
+ Optional<String> nullableAnnotation) {
+ super(
+ name,
+ identifier,
+ type,
+ method.getReturnType(),
+ nullableAnnotation,
+ method.getSimpleName().toString());
+ this.method = method;
+ this.fieldAnnotations = fieldAnnotations;
+ this.methodAnnotations = methodAnnotations;
+ }
+
+ /**
+ * Returns the annotations (in string form) that should be applied to the property's field
+ * declaration.
+ */
+ public List<String> getFieldAnnotations() {
+ return fieldAnnotations;
+ }
+
+ /**
+ * Returns the annotations (in string form) that should be applied to the property's method
+ * implementation.
+ */
+ public List<String> getMethodAnnotations() {
+ return methodAnnotations;
+ }
+
public String getAccess() {
return SimpleMethod.access(method);
}
@Override
public boolean equals(Object obj) {
- return obj instanceof Property && ((Property) obj).method.equals(method);
+ return obj instanceof GetterProperty && ((GetterProperty) obj).method.equals(method);
}
@Override
@@ -289,8 +320,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
}
simpleAnnotationName = annotationType.getSimpleName().toString();
List<TypeElement> deferredTypes =
- deferredTypeNames
- .stream()
+ deferredTypeNames.stream()
.map(name -> elementUtils().getTypeElement(name))
.collect(toList());
if (roundEnv.processingOver()) {
@@ -383,7 +413,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
(propertyMethod, returnType) -> {
String propertyType =
TypeEncoder.encodeWithAnnotations(
- returnType, getExcludedAnnotationTypes(propertyMethod));
+ returnType, ImmutableList.of(), getExcludedAnnotationTypes(propertyMethod));
String propertyName = methodToPropertyName.get(propertyMethod);
String identifier = methodToIdentifier.get(propertyMethod);
ImmutableList<String> fieldAnnotations =
@@ -393,7 +423,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
ImmutableList<String> methodAnnotations = annotationStrings(methodAnnotationMirrors);
Optional<String> nullableAnnotation = nullableAnnotationForMethod(propertyMethod);
Property p =
- new Property(
+ new GetterProperty(
propertyName,
identifier,
propertyMethod,
@@ -411,11 +441,9 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
return props.build();
}
- /** Defines the template variables that are shared by AutoValue and AutoOneOf. */
+ /** Defines the template variables that are shared by AutoValue, AutoOneOf, and AutoBuilder. */
final void defineSharedVarsForType(
- TypeElement type,
- ImmutableSet<ExecutableElement> methods,
- AutoValueOrOneOfTemplateVars vars) {
+ TypeElement type, ImmutableSet<ExecutableElement> methods, AutoValueishTemplateVars vars) {
vars.pkg = TypeSimplifier.packageNameOf(type);
vars.origClass = TypeSimplifier.classNameOf(type);
vars.simpleClassName = TypeSimplifier.simpleNameOf(vars.origClass);
@@ -423,7 +451,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
generatedAnnotation(elementUtils(), processingEnv.getSourceVersion())
.map(annotation -> TypeEncoder.encode(annotation.asType()))
.orElse("");
- vars.formalTypes = TypeEncoder.formalTypeParametersString(type);
+ vars.formalTypes = TypeEncoder.typeParametersString(type.getTypeParameters());
vars.actualTypes = TypeSimplifier.actualTypeParametersString(type);
vars.wildcardTypes = wildcardTypeParametersString(type);
vars.annotations = copiedClassAnnotations(type);
@@ -432,14 +460,17 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
vars.toString = methodsToGenerate.containsKey(ObjectMethod.TO_STRING);
vars.equals = methodsToGenerate.containsKey(ObjectMethod.EQUALS);
vars.hashCode = methodsToGenerate.containsKey(ObjectMethod.HASH_CODE);
- vars.equalsParameterType = equalsParameterType(methodsToGenerate);
+ Optional<AnnotationMirror> nullable = nullables.appropriateNullableGivenMethods(methods);
+ vars.equalsParameterType = equalsParameterType(methodsToGenerate, nullable);
+ vars.serialVersionUID = getSerialVersionUID(type);
}
/** Returns the spelling to be used in the generated code for the given list of annotations. */
static ImmutableList<String> annotationStrings(List<? extends AnnotationMirror> annotations) {
// TODO(b/68008628): use ImmutableList.toImmutableList() when that works.
- return ImmutableList.copyOf(
- annotations.stream().map(AnnotationOutput::sourceFormForAnnotation).collect(toList()));
+ return annotations.stream()
+ .map(AnnotationOutput::sourceFormForAnnotation)
+ .collect(toImmutableList());
}
/**
@@ -454,7 +485,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
static String generatedClassName(TypeElement type, String prefix) {
String name = type.getSimpleName().toString();
while (type.getEnclosingElement() instanceof TypeElement) {
- type = (TypeElement) type.getEnclosingElement();
+ type = MoreElements.asType(type.getEnclosingElement());
name = type.getSimpleName() + "_" + name;
}
String pkg = TypeSimplifier.packageNameOf(type);
@@ -566,12 +597,9 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
}
private static OptionalInt nullableAnnotationIndex(List<? extends AnnotationMirror> annotations) {
- for (int i = 0; i < annotations.size(); i++) {
- if (isNullable(annotations.get(i))) {
- return OptionalInt.of(i);
- }
- }
- return OptionalInt.empty();
+ return IntStream.range(0, annotations.size())
+ .filter(i -> isNullable(annotations.get(i)))
+ .findFirst();
}
private static boolean isNullable(AnnotationMirror annotation) {
@@ -583,21 +611,19 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
* includes {@code isFoo} methods if they return {@code boolean}. This corresponds to JavaBeans
* conventions.
*/
- static ImmutableSet<ExecutableElement> prefixedGettersIn(Iterable<ExecutableElement> methods) {
- ImmutableSet.Builder<ExecutableElement> getters = ImmutableSet.builder();
- for (ExecutableElement method : methods) {
- String name = method.getSimpleName().toString();
- // Note that getfoo() (without a capital) is still a getter.
- boolean get = name.startsWith("get") && !name.equals("get");
- boolean is =
- name.startsWith("is")
- && !name.equals("is")
- && method.getReturnType().getKind() == TypeKind.BOOLEAN;
- if (get || is) {
- getters.add(method);
- }
- }
- return getters.build();
+ static ImmutableSet<ExecutableElement> prefixedGettersIn(Collection<ExecutableElement> methods) {
+ return methods.stream()
+ .filter(AutoValueishProcessor::isPrefixedGetter)
+ .collect(toImmutableSet());
+ }
+
+ static boolean isPrefixedGetter(ExecutableElement method) {
+ String name = method.getSimpleName().toString();
+ // Note that getfoo() (without a capital) is still a getter.
+ return (name.startsWith("get") && !name.equals("get"))
+ || (name.startsWith("is")
+ && !name.equals("is")
+ && method.getReturnType().getKind() == TypeKind.BOOLEAN);
}
/**
@@ -611,7 +637,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
* anyway, so the special behaviour is not useful, and of course it behaves poorly with examples
* like {@code OAuth}.
*/
- private static String nameWithoutPrefix(String name) {
+ static String nameWithoutPrefix(String name) {
if (name.startsWith("get")) {
name = name.substring(3);
} else {
@@ -622,27 +648,33 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
}
/**
- * Checks that, if the given {@code @AutoValue} or {@code @AutoOneOf} class is nested, it is
- * static and not private. This check is not necessary for correctness, since the generated code
- * would not compile if the check fails, but it produces better error messages for the user.
+ * Checks that, if the given {@code @AutoValue}, {@code @AutoOneOf}, or {@code @AutoBuilder} class
+ * is nested, it is static and not private. This check is not necessary for correctness, since the
+ * generated code would not compile if the check fails, but it produces better error messages for
+ * the user.
*/
final void checkModifiersIfNested(TypeElement type) {
+ checkModifiersIfNested(type, type, simpleAnnotationName);
+ }
+
+ final void checkModifiersIfNested(TypeElement type, TypeElement reportedType, String what) {
ElementKind enclosingKind = type.getEnclosingElement().getKind();
if (enclosingKind.isClass() || enclosingKind.isInterface()) {
if (type.getModifiers().contains(Modifier.PRIVATE)) {
errorReporter.abortWithError(
- type, "[AutoValuePrivate] @%s class must not be private", simpleAnnotationName);
+ reportedType, "[%sPrivate] @%s class must not be private", simpleAnnotationName, what);
} else if (Visibility.effectiveVisibilityOfElement(type).equals(Visibility.PRIVATE)) {
// The previous case, where the class itself is private, is much commoner so it deserves
// its own error message, even though it would be caught by the test here too.
errorReporter.abortWithError(
- type,
- "[AutoValueInPrivate] @%s class must not be nested in a private class",
- simpleAnnotationName);
+ reportedType,
+ "[%sInPrivate] @%s class must not be nested in a private class",
+ simpleAnnotationName,
+ what);
}
if (!type.getModifiers().contains(Modifier.STATIC)) {
errorReporter.abortWithError(
- type, "[AutoValueInner] Nested @%s class must be static", simpleAnnotationName);
+ reportedType, "[%sInner] Nested @%s class must be static", simpleAnnotationName, what);
}
}
// In principle type.getEnclosingElement() could be an ExecutableElement (for a class
@@ -693,7 +725,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
ObjectMethod override = objectMethodToOverride(method);
boolean canGenerate =
method.getModifiers().contains(Modifier.ABSTRACT)
- || isJavaLangObject((TypeElement) method.getEnclosingElement());
+ || isJavaLangObject(MoreElements.asType(method.getEnclosingElement()));
if (!override.equals(ObjectMethod.NONE) && canGenerate) {
methodsToGenerate.put(override, method);
}
@@ -705,14 +737,25 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
* Returns the encoded parameter type of the {@code equals(Object)} method that is to be
* generated, or an empty string if the method is not being generated. The parameter type includes
* any type annotations, for example {@code @Nullable}.
+ *
+ * @param methodsToGenerate the Object methods that are being generated
+ * @param nullable the type of a {@code @Nullable} type annotation that we have found, if any
*/
- static String equalsParameterType(Map<ObjectMethod, ExecutableElement> methodsToGenerate) {
+ static String equalsParameterType(
+ Map<ObjectMethod, ExecutableElement> methodsToGenerate, Optional<AnnotationMirror> nullable) {
ExecutableElement equals = methodsToGenerate.get(ObjectMethod.EQUALS);
if (equals == null) {
return ""; // this will not be referenced because no equals method will be generated
}
TypeMirror parameterType = equals.getParameters().get(0).asType();
- return TypeEncoder.encodeWithAnnotations(parameterType);
+ // Add @Nullable if we know one and the parameter doesn't already have one.
+ // The @Nullable we add will be a type annotation, but if the parameter already has @Nullable
+ // then that might be a type annotation or an annotation on the parameter.
+ ImmutableList<AnnotationMirror> extraAnnotations =
+ nullable.isPresent() && !nullableAnnotationFor(equals, parameterType).isPresent()
+ ? ImmutableList.of(nullable.get())
+ : ImmutableList.of();
+ return TypeEncoder.encodeWithAnnotations(parameterType, extraAnnotations, ImmutableSet.of());
}
/**
@@ -751,8 +794,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
* {@code toString()}.
*/
ImmutableMap<ExecutableElement, TypeMirror> propertyMethodsIn(
- Set<ExecutableElement> abstractMethods,
- TypeElement autoValueOrOneOfType) {
+ Set<ExecutableElement> abstractMethods, TypeElement autoValueOrOneOfType) {
DeclaredType declaredType = MoreTypes.asDeclared(autoValueOrOneOfType.asType());
ImmutableSet.Builder<ExecutableElement> properties = ImmutableSet.builder();
for (ExecutableElement method : abstractMethods) {
@@ -778,7 +820,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
TypeMirror type = getter.getReturnType();
if (type.getKind() == TypeKind.ARRAY) {
TypeMirror componentType = MoreTypes.asArray(type).getComponentType();
- if (componentType.getKind().isPrimitive()) {
+ if (componentType.getKind().isPrimitive()) {
warnAboutPrimitiveArrays(autoValueClass, getter);
} else {
errorReporter.reportError(
@@ -829,12 +871,13 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
@Override
public Boolean visitArray(List<? extends AnnotationValue> list, Void p) {
return list.stream().map(AnnotationValue::getValue).anyMatch("mutable"::equals);
- }
+ }
}
/**
- * Returns a string like {@code "1234L"} if {@code type instanceof Serializable} and defines
- * {@code serialVersionUID = 1234L}; otherwise {@code ""}.
+ * Returns a string like {@code "private static final long serialVersionUID = 1234L"} if {@code
+ * type instanceof Serializable} and defines {@code serialVersionUID = 1234L}; otherwise {@code
+ * ""}.
*/
final String getSerialVersionUID(TypeElement type) {
TypeMirror serializable = elementUtils().getTypeElement(Serializable.class.getName()).asType();
@@ -846,7 +889,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
if (field.getModifiers().containsAll(Arrays.asList(Modifier.STATIC, Modifier.FINAL))
&& field.asType().getKind() == TypeKind.LONG
&& value != null) {
- return value + "L";
+ return "private static final long serialVersionUID = " + value + "L;";
} else {
errorReporter.reportError(
field, "serialVersionUID must be a static final long compile-time constant");
@@ -889,7 +932,25 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
// Only copy annotations from a class if it has @AutoValue.CopyAnnotations.
if (hasAnnotationMirror(type, COPY_ANNOTATIONS_NAME)) {
Set<String> excludedAnnotations =
- union(getExcludedAnnotationClassNames(type), getAnnotationsMarkedWithInherited(type));
+ ImmutableSet.<String>builder()
+ .addAll(getExcludedAnnotationClassNames(type))
+ .addAll(getAnnotationsMarkedWithInherited(type))
+ //
+ // Kotlin classes have an intrinsic @Metadata annotation generated
+ // onto them by kotlinc. This annotation is specific to the annotated
+ // class and should not be implicitly copied. Doing so can mislead
+ // static analysis or metaprogramming tooling that reads the data
+ // contained in these annotations.
+ //
+ // It may be surprising to see AutoValue classes written in Kotlin
+ // when they could be written as Kotlin data classes, but this can
+ // come up in cases where consumers rely on AutoValue features or
+ // extensions that are not available in data classes.
+ //
+ // See: https://github.com/google/auto/issues/1087
+ //
+ .add(ClassNames.KOTLIN_METADATA_NAME)
+ .build();
return copyAnnotations(type, type, excludedAnnotations);
} else {
@@ -919,8 +980,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
@SuppressWarnings("unchecked")
List<AnnotationValue> excludedClasses =
(List<AnnotationValue>) getAnnotationValue(maybeAnnotation.get(), "exclude").getValue();
- return excludedClasses
- .stream()
+ return excludedClasses.stream()
.map(annotationValue -> (DeclaredType) annotationValue.getValue())
.collect(toCollection(TypeMirrorSet::new));
}
@@ -930,17 +990,14 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
* strings that are fully-qualified class names.
*/
private Set<String> getExcludedAnnotationClassNames(Element element) {
- return getExcludedAnnotationTypes(element)
- .stream()
+ return getExcludedAnnotationTypes(element).stream()
.map(MoreTypes::asTypeElement)
.map(typeElement -> typeElement.getQualifiedName().toString())
.collect(toSet());
}
private static Set<String> getAnnotationsMarkedWithInherited(Element element) {
- return element
- .getAnnotationMirrors()
- .stream()
+ return element.getAnnotationMirrors().stream()
.filter(a -> isAnnotationPresent(a.getAnnotationType().asElement(), Inherited.class))
.map(a -> getAnnotationFqName(a))
.collect(toSet());
@@ -1007,9 +1064,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
Set<String> returnTypeAnnotations =
getReturnTypeAnnotations(method, this::annotationAppliesToFields);
Set<String> nonFieldAnnotations =
- method
- .getAnnotationMirrors()
- .stream()
+ method.getAnnotationMirrors().stream()
.map(a -> a.getAnnotationType().asElement())
.map(MoreElements::asType)
.filter(a -> !annotationAppliesToFields(a))
@@ -1027,10 +1082,7 @@ abstract class AutoValueOrOneOfProcessor extends AbstractProcessor {
private Set<String> getReturnTypeAnnotations(
ExecutableElement method, Predicate<TypeElement> typeFilter) {
- return method
- .getReturnType()
- .getAnnotationMirrors()
- .stream()
+ return method.getReturnType().getAnnotationMirrors().stream()
.map(a -> a.getAnnotationType().asElement())
.map(MoreElements::asType)
.filter(typeFilter)
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfTemplateVars.java b/value/src/main/java/com/google/auto/value/processor/AutoValueishTemplateVars.java
index 9fdbea4f..9ff2891a 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueOrOneOfTemplateVars.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueishTemplateVars.java
@@ -18,12 +18,12 @@ package com.google.auto.value.processor;
import com.google.common.collect.ImmutableList;
/**
- * The variables to substitute into the autovalue.vm or autooneof.vm template.
+ * The variables to substitute into the autovalue.vm, autooneof.vm, or builder.vm templates.
*
* @author emcmanus@google.com (Éamonn McManus)
*/
@SuppressWarnings("unused") // the fields in this class are only read via reflection
-abstract class AutoValueOrOneOfTemplateVars extends TemplateVars {
+abstract class AutoValueishTemplateVars extends TemplateVars {
/** Whether to generate an equals(Object) method. */
Boolean equals;
@@ -81,4 +81,11 @@ abstract class AutoValueOrOneOfTemplateVars extends TemplateVars {
* wildcard, for example {@code <?, ?>}.
*/
String wildcardTypes;
+
+ /**
+ * The text of the complete serialVersionUID declaration, or empty if there is none. When
+ * non-empty, it will be something like {@code private static final long serialVersionUID =
+ * 123L;}.
+ */
+ String serialVersionUID;
}
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
index 81751e30..51773e6f 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifier.java
@@ -15,8 +15,7 @@
*/
package com.google.auto.value.processor;
-import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.nullableAnnotationFor;
-import static com.google.common.collect.Sets.difference;
+import static com.google.auto.value.processor.AutoValueishProcessor.nullableAnnotationFor;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
@@ -38,7 +37,9 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
+import java.util.stream.Stream;
import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
@@ -54,19 +55,35 @@ import javax.lang.model.util.Types;
/**
* Classifies methods inside builder types, based on their names and parameter and return types.
*
+ * @param <E> the kind of {@link Element} that the corresponding properties are defined by. This is
+ * {@link ExecutableElement} for AutoValue, where properties are defined by abstract methods,
+ * and {@link VariableElement} for AutoBuilder, where they are defined by constructor or method
+ * parameters.
* @author Éamonn McManus
*/
-class BuilderMethodClassifier {
+abstract class BuilderMethodClassifier<E extends Element> {
private static final Equivalence<TypeMirror> TYPE_EQUIVALENCE = MoreTypes.equivalence();
private final ErrorReporter errorReporter;
private final Types typeUtils;
private final Elements elementUtils;
- private final TypeElement autoValueClass;
+ private final TypeMirror builtType;
private final TypeElement builderType;
- private final ImmutableBiMap<ExecutableElement, String> getterToPropertyName;
- private final ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType;
- private final ImmutableMap<String, ExecutableElement> getterNameToGetter;
+
+ /**
+ * Property types, rewritten to refer to type variables in the builder. For example, suppose you
+ * have {@code @AutoValue abstract class Foo<T>} with a getter {@code abstract T bar()} and a
+ * builder {@code @AutoValue.Builder interface Builder<T>} with a setter {@code abstract
+ * Builder<T> setBar(T t)}. Then the {@code T} of {@code Foo<T>} and the {@code T} of {@code
+ * Foo.Builder<T>} are two separate variables. Originally {@code bar()} returned the {@code T} of
+ * {@code Foo<T>}, but in this map we have rewritten it to be the {@code T} of {@code
+ * Foo.Builder<T>}.
+ *
+ * <p>Importantly, this rewrite <b>loses type annotations</b>, so when those are important we must
+ * be careful to look at the original type as reported by the {@link #originalPropertyType}
+ * method.
+ */
+ private final ImmutableMap<String, TypeMirror> rewrittenPropertyTypes;
private final Set<ExecutableElement> buildMethods = new LinkedHashSet<>();
private final Map<String, BuilderSpec.PropertyGetter> builderGetters = new LinkedHashMap<>();
@@ -79,76 +96,27 @@ class BuilderMethodClassifier {
private boolean settersPrefixed;
- private BuilderMethodClassifier(
+ BuilderMethodClassifier(
ErrorReporter errorReporter,
ProcessingEnvironment processingEnv,
- TypeElement autoValueClass,
+ TypeMirror builtType,
TypeElement builderType,
- ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
- ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType) {
+ ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
this.errorReporter = errorReporter;
this.typeUtils = processingEnv.getTypeUtils();
this.elementUtils = processingEnv.getElementUtils();
- this.autoValueClass = autoValueClass;
+ this.builtType = builtType;
this.builderType = builderType;
- this.getterToPropertyName = getterToPropertyName;
- this.getterToPropertyType = getterToPropertyType;
- ImmutableMap.Builder<String, ExecutableElement> getterToPropertyNameBuilder =
- ImmutableMap.builder();
- for (ExecutableElement getter : getterToPropertyName.keySet()) {
- getterToPropertyNameBuilder.put(getter.getSimpleName().toString(), getter);
- }
- this.getterNameToGetter = getterToPropertyNameBuilder.build();
+ this.rewrittenPropertyTypes = rewrittenPropertyTypes;
this.eclipseHack = new EclipseHack(processingEnv);
}
/**
- * Classifies the given methods from a builder type and its ancestors.
- *
- * @param methods the abstract methods in {@code builderType} and its ancestors.
- * @param errorReporter where to report errors.
- * @param processingEnv the ProcessingEnvironment for annotation processing.
- * @param autoValueClass the {@code AutoValue} class containing the builder.
- * @param builderType the builder class or interface within {@code autoValueClass}.
- * @param getterToPropertyName a map from getter methods to the properties they get.
- * @param getterToPropertyType a map from getter methods to their return types. The return types
- * here use type parameters from the builder class (if any) rather than from the {@code
- * AutoValue} class, even though the getter methods are in the latter.
- * @param autoValueHasToBuilder true if the containing {@code @AutoValue} class has a {@code
- * toBuilder()} method.
- * @return an {@code Optional} that contains the results of the classification if it was
- * successful or nothing if it was not.
- */
- static Optional<BuilderMethodClassifier> classify(
- Iterable<ExecutableElement> methods,
- ErrorReporter errorReporter,
- ProcessingEnvironment processingEnv,
- TypeElement autoValueClass,
- TypeElement builderType,
- ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
- ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType,
- boolean autoValueHasToBuilder) {
- BuilderMethodClassifier classifier =
- new BuilderMethodClassifier(
- errorReporter,
- processingEnv,
- autoValueClass,
- builderType,
- getterToPropertyName,
- getterToPropertyType);
- if (classifier.classifyMethods(methods, autoValueHasToBuilder)) {
- return Optional.of(classifier);
- } else {
- return Optional.empty();
- }
- }
-
- /**
* Returns a multimap from the name of a property to the methods that set it. If the property is
* defined by an abstract method in the {@code @AutoValue} class called {@code foo()} or {@code
* getFoo()} then the name of the property is {@code foo} and there will be an entry in the map
- * where the key is {@code "foo"} and the value describes a method in the builder called
- * {@code foo} or {@code setFoo}.
+ * where the key is {@code "foo"} and the value describes a method in the builder called {@code
+ * foo} or {@code setFoo}.
*/
ImmutableMultimap<String, PropertySetter> propertyNameToSetters() {
return ImmutableMultimap.copyOf(
@@ -179,8 +147,7 @@ class BuilderMethodClassifier {
}
/** Classifies the given methods and sets the state of this object based on what is found. */
- private boolean classifyMethods(
- Iterable<ExecutableElement> methods, boolean autoValueHasToBuilder) {
+ boolean classifyMethods(Iterable<ExecutableElement> methods, boolean autoValueHasToBuilder) {
int startErrorCount = errorReporter.errorCount();
for (ExecutableElement method : methods) {
classifyMethod(method);
@@ -198,49 +165,48 @@ class BuilderMethodClassifier {
} else {
errorReporter.reportError(
propertyNameToUnprefixedSetters.values().iterator().next().getSetter(),
- "[AutoValueSetNotSet] If any setter methods use the setFoo convention then all must");
+ "[%sSetNotSet] If any setter methods use the setFoo convention then all must",
+ autoWhat());
return false;
}
- getterToPropertyName.forEach(
- (getter, property) -> {
- TypeMirror propertyType = getterToPropertyType.get(getter);
- boolean hasSetter = propertyNameToSetter.containsKey(property);
- PropertyBuilder propertyBuilder = propertyNameToPropertyBuilder.get(property);
- boolean hasBuilder = propertyBuilder != null;
- if (hasBuilder) {
- // If property bar of type Bar has a barBuilder() that returns BarBuilder, then it must
- // be possible to make a BarBuilder from a Bar if either (1) the @AutoValue class has a
- // toBuilder() or (2) there is also a setBar(Bar). Making BarBuilder from Bar is
- // possible if Bar either has a toBuilder() method or BarBuilder has an addAll or putAll
- // method that accepts a Bar argument.
- boolean canMakeBarBuilder =
- (propertyBuilder.getBuiltToBuilder() != null
- || propertyBuilder.getCopyAll() != null);
- boolean needToMakeBarBuilder = (autoValueHasToBuilder || hasSetter);
- if (needToMakeBarBuilder && !canMakeBarBuilder) {
- errorReporter.reportError(
- propertyBuilder.getPropertyBuilderMethod(),
- "[AutoValueCantMakeBuilder] Property builder method returns %1$s but there is no"
- + " way to make that type from %2$s: %2$s does not have a non-static"
- + " toBuilder() method that returns %1$s, and %1$s does not have a method"
- + " addAll or putAll that accepts an argument of type %2$s",
- propertyBuilder.getBuilderTypeMirror(),
- propertyType);
- }
- } else if (!hasSetter) {
- // We have neither barBuilder() nor setBar(Bar), so we should complain.
- String setterName = settersPrefixed ? prefixWithSet(property) : property;
- errorReporter.reportError(
- builderType,
- "[AutoValueBuilderMissingMethod] Expected a method with this signature: %s%s"
- + " %s(%s), or a %sBuilder() method",
- builderType,
- typeParamsString(),
- setterName,
- propertyType,
- property);
- }
- });
+ for (String property : rewrittenPropertyTypes.keySet()) {
+ TypeMirror propertyType = rewrittenPropertyTypes.get(property);
+ boolean hasSetter = propertyNameToSetter.containsKey(property);
+ PropertyBuilder propertyBuilder = propertyNameToPropertyBuilder.get(property);
+ boolean hasBuilder = propertyBuilder != null;
+ if (hasBuilder) {
+ // If property bar of type Bar has a barBuilder() that returns BarBuilder, then it must
+ // be possible to make a BarBuilder from a Bar if either (1) the @AutoValue class has a
+ // toBuilder() or (2) there is also a setBar(Bar). Making BarBuilder from Bar is
+ // possible if Bar either has a toBuilder() method or BarBuilder has an addAll or putAll
+ // method that accepts a Bar argument.
+ boolean canMakeBarBuilder =
+ (propertyBuilder.getBuiltToBuilder() != null || propertyBuilder.getCopyAll() != null);
+ boolean needToMakeBarBuilder = (autoValueHasToBuilder || hasSetter);
+ if (needToMakeBarBuilder && !canMakeBarBuilder) {
+ errorReporter.reportError(
+ propertyBuilder.getPropertyBuilderMethod(),
+ "[AutoValueCantMakeBuilder] Property builder method returns %1$s but there is no"
+ + " way to make that type from %2$s: %2$s does not have a non-static"
+ + " toBuilder() method that returns %1$s, and %1$s does not have a method"
+ + " addAll or putAll that accepts an argument of type %2$s",
+ propertyBuilder.getBuilderTypeMirror(),
+ propertyType);
+ }
+ } else if (!hasSetter) {
+ // We have neither barBuilder() nor setBar(Bar), so we should complain.
+ String setterName = settersPrefixed ? prefixWithSet(property) : property;
+ errorReporter.reportError(
+ builderType,
+ "[%sBuilderMissingMethod] Expected a method with this signature: %s"
+ + " %s(%s), or a %sBuilder() method",
+ autoWhat(),
+ builderType.asType(),
+ setterName,
+ propertyType,
+ property);
+ }
+ }
return errorReporter.errorCount() == startErrorCount;
}
@@ -255,7 +221,7 @@ class BuilderMethodClassifier {
break;
default:
errorReporter.reportError(
- method, "[AutoValueBuilderArgs] Builder methods must have 0 or 1 parameters");
+ method, "[%sBuilderArgs] Builder methods must have 0 or 1 parameters", autoWhat());
}
}
@@ -268,26 +234,26 @@ class BuilderMethodClassifier {
* ImmutableList<String> foos()} or {@code getFoos()}.
*/
private void classifyMethodNoArgs(ExecutableElement method) {
- String methodName = method.getSimpleName().toString();
- TypeMirror returnType = builderMethodReturnType(method);
-
- ExecutableElement getter = getterNameToGetter.get(methodName);
- if (getter != null) {
- classifyGetter(method, getter);
+ Optional<String> getterProperty = propertyForBuilderGetter(method);
+ if (getterProperty.isPresent()) {
+ classifyGetter(method, getterProperty.get());
return;
}
+ String methodName = method.getSimpleName().toString();
+ TypeMirror returnType = builderMethodReturnType(method);
+
if (methodName.endsWith("Builder")) {
String property = methodName.substring(0, methodName.length() - "Builder".length());
- if (getterToPropertyName.containsValue(property)) {
+ if (rewrittenPropertyTypes.containsKey(property)) {
PropertyBuilderClassifier propertyBuilderClassifier =
new PropertyBuilderClassifier(
errorReporter,
typeUtils,
elementUtils,
this,
- getterToPropertyName,
- getterToPropertyType,
+ this::propertyIsNullable,
+ rewrittenPropertyTypes,
eclipseHack);
Optional<PropertyBuilder> propertyBuilder =
propertyBuilderClassifier.makePropertyBuilder(method, property);
@@ -298,22 +264,24 @@ class BuilderMethodClassifier {
}
}
- if (TYPE_EQUIVALENCE.equivalent(returnType, autoValueClass.asType())) {
+ if (TYPE_EQUIVALENCE.equivalent(returnType, builtType)) {
buildMethods.add(method);
} else {
errorReporter.reportError(
method,
- "[AutoValueBuilderNoArg] Method without arguments should be a build method returning"
- + " %1$s%2$s, or a getter method with the same name and type as a getter method of"
- + " %1$s, or fooBuilder() where foo() or getFoo() is a getter method of %1$s",
- autoValueClass,
- typeParamsString());
+ "[%1$sBuilderNoArg] Method without arguments should be a build method returning"
+ + " %2$s, or a getter method with the same name and type as %3$s,"
+ + " or fooBuilder() where %4$s is %3$s",
+ // "where foo() or getFoo() is a method in..." or "where foo is a parameter of..."
+ autoWhat(),
+ builtType,
+ getterMustMatch(),
+ fooBuilderMustMatch());
}
}
- private void classifyGetter(ExecutableElement builderGetter, ExecutableElement originalGetter) {
- String propertyName = getterToPropertyName.get(originalGetter);
- TypeMirror originalGetterType = getterToPropertyType.get(originalGetter);
+ private void classifyGetter(ExecutableElement builderGetter, String propertyName) {
+ TypeMirror originalGetterType = rewrittenPropertyTypes.get(propertyName);
TypeMirror builderGetterType = builderMethodReturnType(builderGetter);
String builderGetterTypeString = TypeEncoder.encodeWithAnnotations(builderGetterType);
if (TYPE_EQUIVALENCE.equivalent(builderGetterType, originalGetterType)) {
@@ -344,51 +312,76 @@ class BuilderMethodClassifier {
builderGetter,
"[AutoValueBuilderReturnType] Method matches a property of %1$s but has return type %2$s"
+ " instead of %3$s or an Optional wrapping of %3$s",
- autoValueClass,
+ builtType,
builderGetterType,
originalGetterType);
}
/**
- * Classifies a method given that it has one argument. Currently, a method with one argument can
- * only be a setter, meaning that it must look like {@code foo(T)} or {@code setFoo(T)}, where the
- * {@code AutoValue} class has a property called {@code foo} of type {@code T}.
+ * Classifies a method given that it has one argument. A method with one argument can be:
+ *
+ * <ul>
+ * <li>a setter, meaning that it looks like {@code foo(T)} or {@code setFoo(T)}, where the
+ * {@code AutoValue} class has a property called {@code foo} of type {@code T};
+ * <li>a property builder with one argument, meaning it looks like {@code
+ * ImmutableSortedSet.Builder<V> foosBuilder(Comparator<V>)}, where the {@code AutoValue}
+ * class has a property called {@code foos} with a type whose builder can be made with an
+ * argument of the given type.
+ * </ul>
*/
private void classifyMethodOneArg(ExecutableElement method) {
+ if (classifyPropertyBuilderOneArg(method)) {
+ return;
+ }
String methodName = method.getSimpleName().toString();
- Map<String, ExecutableElement> propertyNameToGetter = getterToPropertyName.inverse();
+ ImmutableMap<String, E> propertyElements = propertyElements();
String propertyName = null;
- ExecutableElement valueGetter = propertyNameToGetter.get(methodName);
+ E propertyElement = propertyElements.get(methodName);
Multimap<String, PropertySetter> propertyNameToSetters = null;
- if (valueGetter != null) {
+ if (propertyElement != null) {
propertyNameToSetters = propertyNameToUnprefixedSetters;
propertyName = methodName;
- } else if (valueGetter == null && methodName.startsWith("set") && methodName.length() > 3) {
+ } else if (methodName.startsWith("set") && methodName.length() > 3) {
propertyNameToSetters = propertyNameToPrefixedSetters;
propertyName = PropertyNames.decapitalizeLikeJavaBeans(methodName.substring(3));
- valueGetter = propertyNameToGetter.get(propertyName);
- if (valueGetter == null) {
+ propertyElement = propertyElements.get(propertyName);
+ if (propertyElement == null) {
// If our property is defined by a getter called getOAuth() then it is called "OAuth"
- // because of Introspector.decapitalize. Therefore we want Introspector.decapitalize to
- // be used for the setter too, so that you can write setOAuth(x). Meanwhile if the property
- // is defined by a getter called oAuth() then it is called "oAuth", but you would still
- // expect to be able to set it using setOAuth(x). Hence the second try using a decapitalize
- // method without the quirky two-leading-capitals rule.
+ // because of JavaBeans rules. Therefore we want JavaBeans rules to be used for the setter
+ // too, so that you can write setOAuth(x). Meanwhile if the property is defined by a getter
+ // called oAuth() then it is called "oAuth", but you would still expect to be able to set it
+ // using setOAuth(x). Hence the second try using a decapitalize method without the quirky
+ // two-leading-capitals rule.
propertyName = PropertyNames.decapitalizeNormally(methodName.substring(3));
- valueGetter = propertyNameToGetter.get(propertyName);
+ propertyElement = propertyElements.get(propertyName);
+ }
+ } else {
+ // We might also have an unprefixed setter, so the getter is called OAuth() or getOAuth() and
+ // the setter is called oAuth(x), where again JavaBeans rules imply that it should be called
+ // OAuth(x). Iterating over the properties here is a bit clunky but this case should be
+ // unusual.
+ propertyNameToSetters = propertyNameToUnprefixedSetters;
+ for (Map.Entry<String, E> entry : propertyElements.entrySet()) {
+ if (methodName.equals(PropertyNames.decapitalizeNormally(entry.getKey()))) {
+ propertyName = entry.getKey();
+ propertyElement = entry.getValue();
+ break;
+ }
}
}
- if (valueGetter == null || propertyNameToSetters == null) {
+ if (propertyElement == null || propertyNameToSetters == null) {
// The second disjunct isn't needed but convinces control-flow checkers that
// propertyNameToSetters can't be null when we call put on it below.
errorReporter.reportError(
method,
- "[AutoValueBuilderWhatProp] Method does not correspond to a property of %s",
- autoValueClass);
+ "[%sBuilderWhatProp] Method %s does not correspond to %s",
+ autoWhat(),
+ methodName,
+ getterMustMatch());
checkForFailedJavaBean(method);
return;
}
- Optional<Copier> function = getSetterFunction(valueGetter, method);
+ Optional<Copier> function = getSetterFunction(propertyElement, method);
if (function.isPresent()) {
DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderType.asType());
ExecutableType methodMirror =
@@ -399,28 +392,44 @@ class BuilderMethodClassifier {
propertyName, new PropertySetter(method, parameterType, function.get()));
} else {
errorReporter.reportError(
- method, "Setter methods must return %s%s", builderType, typeParamsString());
+ method,
+ "[%sBuilderRet] Setter methods must return %s",
+ autoWhat(),
+ builderType.asType());
}
}
}
- // A frequent source of problems is where the JavaBeans conventions have been followed for
- // most but not all getters. Then AutoValue considers that they haven't been followed at all,
- // so you might have a property called getFoo where you thought it was called just foo, and
- // you might not understand why your setter called setFoo is rejected (it would have to be called
- // setGetFoo).
- private void checkForFailedJavaBean(ExecutableElement rejectedSetter) {
- ImmutableSet<ExecutableElement> allGetters = getterToPropertyName.keySet();
- ImmutableSet<ExecutableElement> prefixedGetters =
- AutoValueProcessor.prefixedGettersIn(allGetters);
- if (prefixedGetters.size() < allGetters.size()
- && prefixedGetters.size() >= allGetters.size() / 2) {
- errorReporter.reportNote(
- rejectedSetter,
- "This might be because you are using the getFoo() convention"
- + " for some but not all methods. These methods don't follow the convention: %s",
- difference(allGetters, prefixedGetters));
+ /**
+ * Classifies a method given that it has one argument and is a property builder with a parameter,
+ * like {@code ImmutableSortedSet.Builder<String> foosBuilder(Comparator<String>)}.
+ *
+ * @param method A method to classify
+ * @return true if method has been classified successfully
+ */
+ private boolean classifyPropertyBuilderOneArg(ExecutableElement method) {
+ String methodName = method.getSimpleName().toString();
+ if (!methodName.endsWith("Builder")) {
+ return false;
}
+ String property = methodName.substring(0, methodName.length() - "Builder".length());
+ if (!rewrittenPropertyTypes.containsKey(property)) {
+ return false;
+ }
+ PropertyBuilderClassifier propertyBuilderClassifier =
+ new PropertyBuilderClassifier(
+ errorReporter,
+ typeUtils,
+ elementUtils,
+ this,
+ this::propertyIsNullable,
+ rewrittenPropertyTypes,
+ eclipseHack);
+ Optional<PropertyBuilder> maybePropertyBuilder =
+ propertyBuilderClassifier.makePropertyBuilder(method, property);
+ maybePropertyBuilder.ifPresent(
+ propertyBuilder -> propertyNameToPropertyBuilder.put(property, propertyBuilder));
+ return maybePropertyBuilder.isPresent();
}
/**
@@ -431,12 +440,12 @@ class BuilderMethodClassifier {
* using a method like {@code ImmutableList.copyOf} or {@code Optional.of}, when the returned
* function will be something like {@code s -> "Optional.of(" + s + ")"}.
*/
- private Optional<Copier> getSetterFunction(
- ExecutableElement valueGetter, ExecutableElement setter) {
+ private Optional<Copier> getSetterFunction(E propertyElement, ExecutableElement setter) {
VariableElement parameterElement = Iterables.getOnlyElement(setter.getParameters());
boolean nullableParameter =
nullableAnnotationFor(parameterElement, parameterElement.asType()).isPresent();
- TypeMirror targetType = getterToPropertyType.get(valueGetter);
+ String property = propertyElements().inverse().get(propertyElement);
+ TypeMirror targetType = rewrittenPropertyTypes.get(property);
ExecutableType finalSetter =
MoreTypes.asExecutable(
typeUtils.asMemberOf(MoreTypes.asDeclared(builderType.asType()), setter));
@@ -448,14 +457,14 @@ class BuilderMethodClassifier {
&& typeUtils.isAssignable(targetType, parameterType)) {
if (nullableParameter) {
boolean nullableProperty =
- nullableAnnotationFor(valueGetter, valueGetter.getReturnType()).isPresent();
+ nullableAnnotationFor(propertyElement, originalPropertyType(propertyElement))
+ .isPresent();
if (!nullableProperty) {
errorReporter.reportError(
setter,
- "[AutoValueNullNotNull] Parameter of setter method is @Nullable but property method"
- + " %s.%s() is not",
- autoValueClass,
- valueGetter.getSimpleName());
+ "[%sNullNotNull] Parameter of setter method is @Nullable but %s is not",
+ autoWhat(),
+ propertyString(propertyElement));
return Optional.empty();
}
}
@@ -465,12 +474,15 @@ class BuilderMethodClassifier {
// Parameter type is not equal to property type, but might be convertible with copyOf.
ImmutableList<ExecutableElement> copyOfMethods = copyOfMethods(targetType, nullableParameter);
if (!copyOfMethods.isEmpty()) {
- return getConvertingSetterFunction(copyOfMethods, valueGetter, setter, parameterType);
+ return getConvertingSetterFunction(copyOfMethods, propertyElement, setter, parameterType);
}
errorReporter.reportError(
setter,
- "[AutoValueGetVsSet] Parameter type %s of setter method should be %s to match getter %s.%s",
- parameterType, targetType, autoValueClass, valueGetter.getSimpleName());
+ "[%sGetVsSet] Parameter type %s of setter method should be %s to match %s",
+ autoWhat(),
+ parameterType,
+ targetType,
+ propertyString(propertyElement));
return Optional.empty();
}
@@ -481,10 +493,11 @@ class BuilderMethodClassifier {
*/
private Optional<Copier> getConvertingSetterFunction(
ImmutableList<ExecutableElement> copyOfMethods,
- ExecutableElement valueGetter,
+ E propertyElement,
ExecutableElement setter,
TypeMirror parameterType) {
- DeclaredType targetType = MoreTypes.asDeclared(getterToPropertyType.get(valueGetter));
+ String property = propertyElements().inverse().get(propertyElement);
+ DeclaredType targetType = MoreTypes.asDeclared(rewrittenPropertyTypes.get(property));
for (ExecutableElement copyOfMethod : copyOfMethods) {
Optional<Copier> function =
getConvertingSetterFunction(copyOfMethod, targetType, parameterType);
@@ -495,12 +508,12 @@ class BuilderMethodClassifier {
String targetTypeSimpleName = targetType.asElement().getSimpleName().toString();
errorReporter.reportError(
setter,
- "[AutoValueGetVsSetOrConvert] Parameter type %s of setter method should be %s to match"
- + " getter %s.%s, or it should be a type that can be passed to %s.%s to produce %s",
+ "[%sGetVsSetOrConvert] Parameter type %s of setter method should be %s to match %s, or it"
+ + " should be a type that can be passed to %s.%s to produce %s",
+ autoWhat(),
parameterType,
targetType,
- autoValueClass,
- valueGetter.getSimpleName(),
+ propertyString(propertyElement),
targetTypeSimpleName,
copyOfMethods.get(0).getSimpleName(),
targetType);
@@ -635,7 +648,76 @@ class BuilderMethodClassifier {
return "set" + Character.toUpperCase(propertyName.charAt(0)) + propertyName.substring(1);
}
- private String typeParamsString() {
- return TypeSimplifier.actualTypeParametersString(autoValueClass);
+ /**
+ * True if the given property is nullable, either because its type has a {@code @Nullable} type
+ * annotation, or because its getter method has a {@code @Nullable} method annotation.
+ */
+ private boolean propertyIsNullable(String property) {
+ E propertyElement = propertyElements().get(property);
+ return Stream.of(propertyElement, originalPropertyType(propertyElement))
+ .flatMap(ac -> ac.getAnnotationMirrors().stream())
+ .map(a -> a.getAnnotationType().asElement().getSimpleName())
+ .anyMatch(n -> n.contentEquals("Nullable"));
}
+
+ /**
+ * Returns a map from property names to the corresponding source program elements. For AutoValue,
+ * these elements are the abstract getter methods in the {@code @AutoValue} class. For
+ * AutoBuilder, they are the parameters of the constructor or method that the generated builder
+ * will call.
+ */
+ abstract ImmutableBiMap<String, E> propertyElements();
+
+ /**
+ * Returns the property type as it appears on the original source program element. This can be
+ * different from the type stored in {@link #rewrittenPropertyTypes} since that one will refer to
+ * type variables in the builder rather than in the original class. Also, {@link
+ * #rewrittenPropertyTypes} will not have type annotations even if they were present on the
+ * original element, so {@code originalPropertyType} is the right thing to use for those.
+ */
+ abstract TypeMirror originalPropertyType(E propertyElement);
+
+ /**
+ * A string identifying the given property element, which is a method for AutoValue or a parameter
+ * for AutoBuilder.
+ */
+ abstract String propertyString(E propertyElement);
+
+ /**
+ * Returns the name of the property that the given no-arg builder method queries, if
+ * any. For example, if your {@code @AutoValue} class has a method {@code abstract String
+ * getBar()} then an abstract method in its builder with the same signature will query the {@code
+ * bar} property.
+ */
+ abstract Optional<String> propertyForBuilderGetter(ExecutableElement method);
+
+ /**
+ * Checks for failed JavaBean usage when a method that looks like a setter doesn't actually match
+ * anything, and emits a compiler Note if detected. A frequent source of problems is where the
+ * JavaBeans conventions have been followed for most but not all getters. Then AutoValue considers
+ * that they haven't been followed at all, so you might have a property called getFoo where you
+ * thought it was called just foo, and you might not understand why your setter called setFoo is
+ * rejected (it would have to be called setGetFoo).
+ *
+ * <p>This is not relevant for AutoBuilder, which uses parameter names rather than getters. The
+ * parameter names are unambiguously the same as the property names.
+ */
+ abstract void checkForFailedJavaBean(ExecutableElement rejectedSetter);
+
+ /**
+ * A string describing what sort of Auto this is, {@code "AutoValue"} or {@code "AutoBuilder"}.
+ */
+ abstract String autoWhat();
+
+ /**
+ * A string describing what a builder getter must match: a property method for AutoValue, a
+ * parameter for AutoBuilder.
+ */
+ abstract String getterMustMatch();
+
+ /**
+ * A string describing what a property builder for property {@code foo} must match, {@code foo()
+ * or getFoo()} for AutoValue, {@code foo} for AutoBuilder.
+ */
+ abstract String fooBuilderMustMatch();
}
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java
new file mode 100644
index 00000000..55a31983
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoBuilder.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import static com.google.auto.common.MoreStreams.toImmutableBiMap;
+import static com.google.auto.common.MoreStreams.toImmutableMap;
+
+import com.google.auto.common.MoreElements;
+import com.google.auto.common.MoreTypes;
+import com.google.common.base.Equivalence;
+import com.google.common.base.VerifyException;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.util.Types;
+
+class BuilderMethodClassifierForAutoBuilder extends BuilderMethodClassifier<VariableElement> {
+ private final ExecutableElement executable;
+ private final ImmutableBiMap<VariableElement, String> paramToPropertyName;
+
+ private BuilderMethodClassifierForAutoBuilder(
+ ErrorReporter errorReporter,
+ ProcessingEnvironment processingEnv,
+ ExecutableElement executable,
+ TypeMirror builtType,
+ TypeElement builderType,
+ ImmutableBiMap<VariableElement, String> paramToPropertyName,
+ ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
+ super(errorReporter, processingEnv, builtType, builderType, rewrittenPropertyTypes);
+ this.executable = executable;
+ this.paramToPropertyName = paramToPropertyName;
+ }
+
+ /**
+ * Classifies the given methods from a builder type and its ancestors.
+ *
+ * @param methods the abstract methods in {@code builderType} and its ancestors.
+ * @param errorReporter where to report errors.
+ * @param processingEnv the ProcessingEnvironment for annotation processing.
+ * @param executable the constructor or static method that AutoBuilder will call.
+ * @param builtType the type to be built.
+ * @param builderType the builder class or interface within {@code ofClass}.
+ * @return an {@code Optional} that contains the results of the classification if it was
+ * successful or nothing if it was not.
+ */
+ static Optional<BuilderMethodClassifier<VariableElement>> classify(
+ Iterable<ExecutableElement> methods,
+ ErrorReporter errorReporter,
+ ProcessingEnvironment processingEnv,
+ ExecutableElement executable,
+ TypeMirror builtType,
+ TypeElement builderType) {
+ ImmutableBiMap<VariableElement, String> paramToPropertyName =
+ executable.getParameters().stream()
+ .collect(toImmutableBiMap(v -> v, v -> v.getSimpleName().toString()));
+ ImmutableMap<String, TypeMirror> rewrittenPropertyTypes =
+ rewriteParameterTypes(executable, builderType, errorReporter, processingEnv.getTypeUtils());
+ BuilderMethodClassifier<VariableElement> classifier =
+ new BuilderMethodClassifierForAutoBuilder(
+ errorReporter,
+ processingEnv,
+ executable,
+ builtType,
+ builderType,
+ paramToPropertyName,
+ rewrittenPropertyTypes);
+ if (classifier.classifyMethods(methods, false)) {
+ return Optional.of(classifier);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ // Rewrites the parameter types of the executable so they use the type variables of the builder
+ // where appropriate.
+ //
+ // Suppose we have something like this:
+ //
+ // static <E> Set<E> singletonSet(E elem) {...}
+ //
+ // @AutoBuilder(callMethod = "singletonSet")
+ // interface SingletonSetBuilder<E> {
+ // SingletonSetBuilder<E> setElem(E elem);
+ // Set<E> build();
+ // }
+ //
+ // We want to check that the type of the setter `setElem` matches the type of the
+ // parameter it is setting. But in fact it doesn't: the type of the setter is
+ // E-of-SingletonSetBuilder while the type of the parameter is E-of-singletonSet. So we
+ // need to rewrite any type variables mentioned in parameters so that they use the corresponding
+ // types from the builder. We want to return a map where "elem" is mapped to
+ // E-of-SingletonSetBuilder, even though the `elem` that we get from the parameters of
+ // singletonSet is going to be E-of-singletonSet. And we also want that to work if the parameter
+ // is something more complicated, like List<? extends E>.
+ //
+ // For the corresponding situation with AutoValue, we have a way of dodging the problem somewhat.
+ // For an @AutoValue class Foo<E> with a builder Builder<E>, we can craft a DeclaredType
+ // Foo<E> where the E comes from Builder<E>, and we can use Types.asMemberOf to determine the
+ // return types of methods (which are what we want to rewrite in that case). But that doesn't
+ // work here because singletonSet is static and Types.asMemberOf would have no effect on it.
+ //
+ // So instead we take the type of each parameter and feed it through a TypeVisitor that rewrites
+ // type variables, rewriting from E-of-singletonSet to E-of-SingletonSetBuilder. Then we can use
+ // Types.isSameType or Types.isAssignable and it will work as we expect.
+ //
+ // In principle a similar situation arises with the return type Set<E> of singletonSet versus
+ // the return type Set<E> of SingletonSetBuilder.build(). But in fact we only use
+ // MoreTypes.equivalence to compare those, and that returns true for distinct type variables if
+ // they have the same name and bounds.
+ private static ImmutableMap<String, TypeMirror> rewriteParameterTypes(
+ ExecutableElement executable,
+ TypeElement builderType,
+ ErrorReporter errorReporter,
+ Types typeUtils) {
+ ImmutableList<TypeParameterElement> executableTypeParams = executableTypeParams(executable);
+ List<? extends TypeParameterElement> builderTypeParams = builderType.getTypeParameters();
+ if (!BuilderSpec.sameTypeParameters(executableTypeParams, builderTypeParams)) {
+ errorReporter.abortWithError(
+ builderType,
+ "[AutoBuilderTypeParams] Builder type parameters %s must match type parameters %s of %s",
+ TypeEncoder.typeParametersString(builderTypeParams),
+ TypeEncoder.typeParametersString(executableTypeParams),
+ AutoBuilderProcessor.executableString(executable));
+ }
+ if (executableTypeParams.isEmpty()) {
+ // Optimization for a common case. No point in doing all that type visiting if we have no
+ // variables to substitute.
+ return executable.getParameters().stream()
+ .collect(toImmutableMap(v -> v.getSimpleName().toString(), Element::asType));
+ }
+ Map<Equivalence.Wrapper<TypeVariable>, TypeMirror> typeVariables = new LinkedHashMap<>();
+ for (int i = 0; i < executableTypeParams.size(); i++) {
+ TypeVariable from = MoreTypes.asTypeVariable(executableTypeParams.get(i).asType());
+ TypeVariable to = MoreTypes.asTypeVariable(builderTypeParams.get(i).asType());
+ typeVariables.put(MoreTypes.equivalence().wrap(from), to);
+ }
+ Function<TypeVariable, TypeMirror> substitute =
+ v -> typeVariables.get(MoreTypes.equivalence().wrap(v));
+ return executable.getParameters().stream()
+ .collect(
+ toImmutableMap(
+ v -> v.getSimpleName().toString(),
+ v -> TypeVariables.substituteTypeVariables(v.asType(), substitute, typeUtils)));
+ }
+
+ private static ImmutableList<TypeParameterElement> executableTypeParams(
+ ExecutableElement executable) {
+ switch (executable.getKind()) {
+ case CONSTRUCTOR:
+ // A constructor can have its own type parameters, in addition to any that its containing
+ // class has. That's pretty unusual, but we allow it, requiring the builder to have type
+ // parameters that are the concatenation of the class's and the constructor's.
+ TypeElement container = MoreElements.asType(executable.getEnclosingElement());
+ return ImmutableList.<TypeParameterElement>builder()
+ .addAll(container.getTypeParameters())
+ .addAll(executable.getTypeParameters())
+ .build();
+ case METHOD:
+ return ImmutableList.copyOf(executable.getTypeParameters());
+ default:
+ throw new VerifyException("Unexpected executable kind " + executable.getKind());
+ }
+ }
+
+ @Override
+ Optional<String> propertyForBuilderGetter(ExecutableElement method) {
+ String methodName = method.getSimpleName().toString();
+ if (paramToPropertyName.containsValue(methodName)) {
+ return Optional.of(methodName);
+ }
+ if (AutoValueishProcessor.isPrefixedGetter(method)) {
+ int prefixLength = methodName.startsWith("get") ? 3 : 2; // "get" or "is"
+ String unprefixed = methodName.substring(prefixLength);
+ String propertyName = PropertyNames.decapitalizeLikeJavaBeans(unprefixed);
+ if (paramToPropertyName.containsValue(propertyName)) {
+ return Optional.of(propertyName);
+ }
+ propertyName = PropertyNames.decapitalizeNormally(unprefixed);
+ if (paramToPropertyName.containsValue(propertyName)) {
+ return Optional.of(propertyName);
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ void checkForFailedJavaBean(ExecutableElement rejectedSetter) {}
+
+ @Override
+ ImmutableBiMap<String, VariableElement> propertyElements() {
+ return paramToPropertyName.inverse();
+ }
+
+ @Override
+ TypeMirror originalPropertyType(VariableElement propertyElement) {
+ return propertyElement.asType();
+ }
+
+ @Override
+ String propertyString(VariableElement propertyElement) {
+ return "parameter \""
+ + propertyElement.getSimpleName()
+ + "\" of "
+ + AutoBuilderProcessor.executableString(executable);
+ }
+
+ @Override
+ String autoWhat() {
+ return "AutoBuilder";
+ }
+
+ @Override
+ String getterMustMatch() {
+ return "a parameter of " + AutoBuilderProcessor.executableString(executable);
+ }
+
+ @Override
+ String fooBuilderMustMatch() {
+ return "foo";
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java
new file mode 100644
index 00000000..dde449bb
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderMethodClassifierForAutoValue.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import static com.google.common.collect.Sets.difference;
+
+import com.google.auto.common.MoreElements;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.util.Optional;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.TypeMirror;
+
+class BuilderMethodClassifierForAutoValue extends BuilderMethodClassifier<ExecutableElement> {
+ private final ErrorReporter errorReporter;
+ private final ImmutableBiMap<ExecutableElement, String> getterToPropertyName;
+ private final ImmutableMap<String, ExecutableElement> getterNameToGetter;
+ private final TypeMirror builtType;
+
+ private BuilderMethodClassifierForAutoValue(
+ ErrorReporter errorReporter,
+ ProcessingEnvironment processingEnv,
+ TypeMirror builtType,
+ TypeElement builderType,
+ ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
+ ImmutableMap<String, TypeMirror> rewrittenPropertyTypes) {
+ super(errorReporter, processingEnv, builtType, builderType, rewrittenPropertyTypes);
+ this.errorReporter = errorReporter;
+ this.getterToPropertyName = getterToPropertyName;
+ this.getterNameToGetter =
+ Maps.uniqueIndex(getterToPropertyName.keySet(), m -> m.getSimpleName().toString());
+ this.builtType = builtType;
+ }
+
+ /**
+ * Classifies the given methods from a builder type and its ancestors.
+ *
+ * @param methods the abstract methods in {@code builderType} and its ancestors.
+ * @param errorReporter where to report errors.
+ * @param processingEnv the {@link ProcessingEnvironment} for annotation processing.
+ * @param autoValueClass the {@code AutoValue} class containing the builder.
+ * @param builderType the builder class or interface within {@code autoValueClass}.
+ * @param getterToPropertyName a map from getter methods to the properties they get.
+ * @param rewrittenPropertyTypes a map from property names to types. The types here use type
+ * parameters from the builder class (if any) rather than from the {@code AutoValue} class,
+ * even though the getter methods are in the latter.
+ * @param autoValueHasToBuilder true if the containing {@code @AutoValue} class has a {@code
+ * toBuilder()} method.
+ * @return an {@code Optional} that contains the results of the classification if it was
+ * successful or nothing if it was not.
+ */
+ static Optional<BuilderMethodClassifier<ExecutableElement>> classify(
+ Iterable<ExecutableElement> methods,
+ ErrorReporter errorReporter,
+ ProcessingEnvironment processingEnv,
+ TypeElement autoValueClass,
+ TypeElement builderType,
+ ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
+ ImmutableMap<String, TypeMirror> rewrittenPropertyTypes,
+ boolean autoValueHasToBuilder) {
+ BuilderMethodClassifier<ExecutableElement> classifier =
+ new BuilderMethodClassifierForAutoValue(
+ errorReporter,
+ processingEnv,
+ autoValueClass.asType(),
+ builderType,
+ getterToPropertyName,
+ rewrittenPropertyTypes);
+ if (classifier.classifyMethods(methods, autoValueHasToBuilder)) {
+ return Optional.of(classifier);
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ TypeMirror originalPropertyType(ExecutableElement propertyElement) {
+ return propertyElement.getReturnType();
+ }
+
+ @Override
+ String propertyString(ExecutableElement propertyElement) {
+ TypeElement type = MoreElements.asType(propertyElement.getEnclosingElement());
+ return "property method "
+ + type.getQualifiedName()
+ + "."
+ + propertyElement.getSimpleName()
+ + "()";
+ }
+
+ @Override
+ ImmutableBiMap<String, ExecutableElement> propertyElements() {
+ return getterToPropertyName.inverse();
+ }
+
+ @Override
+ Optional<String> propertyForBuilderGetter(ExecutableElement method) {
+ String methodName = method.getSimpleName().toString();
+ return Optional.ofNullable(getterNameToGetter.get(methodName)).map(getterToPropertyName::get);
+ }
+
+ @Override
+ void checkForFailedJavaBean(ExecutableElement rejectedSetter) {
+ ImmutableSet<ExecutableElement> allGetters = getterToPropertyName.keySet();
+ ImmutableSet<ExecutableElement> prefixedGetters =
+ AutoValueProcessor.prefixedGettersIn(allGetters);
+ if (prefixedGetters.size() < allGetters.size()
+ && prefixedGetters.size() >= allGetters.size() / 2) {
+ errorReporter.reportNote(
+ rejectedSetter,
+ "This might be because you are using the getFoo() convention"
+ + " for some but not all methods. These methods don't follow the convention: %s",
+ difference(allGetters, prefixedGetters));
+ }
+ }
+
+ @Override
+ String autoWhat() {
+ return "AutoValue";
+ }
+
+ @Override
+ String getterMustMatch() {
+ return "a property method of " + builtType;
+ }
+
+ @Override
+ String fooBuilderMustMatch() {
+ return "foo() or getFoo()";
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java
index 7e5b17c9..9f45d172 100644
--- a/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java
+++ b/value/src/main/java/com/google/auto/value/processor/BuilderSpec.java
@@ -16,8 +16,9 @@
package com.google.auto.value.processor;
import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
-import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.hasAnnotationMirror;
-import static com.google.auto.value.processor.AutoValueOrOneOfProcessor.nullableAnnotationFor;
+import static com.google.auto.common.MoreStreams.toImmutableSet;
+import static com.google.auto.value.processor.AutoValueishProcessor.hasAnnotationMirror;
+import static com.google.auto.value.processor.AutoValueishProcessor.nullableAnnotationFor;
import static com.google.auto.value.processor.ClassNames.AUTO_VALUE_BUILDER_NAME;
import static com.google.common.collect.Sets.immutableEnumSet;
import static java.util.stream.Collectors.toList;
@@ -25,16 +26,16 @@ import static java.util.stream.Collectors.toSet;
import static javax.lang.model.util.ElementFilter.methodsIn;
import static javax.lang.model.util.ElementFilter.typesIn;
+import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.extension.AutoValueExtension;
-import com.google.auto.value.processor.AutoValueOrOneOfProcessor.Property;
+import com.google.auto.value.processor.AutoValueishProcessor.Property;
import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -49,6 +50,7 @@ import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
@@ -117,7 +119,7 @@ class BuilderSpec {
private final TypeElement builderTypeElement;
private ImmutableSet<ExecutableElement> toBuilderMethods;
private ExecutableElement buildMethod;
- private BuilderMethodClassifier classifier;
+ private BuilderMethodClassifier<?> classifier;
Builder(TypeElement builderTypeElement) {
this.builderTypeElement = builderTypeElement;
@@ -142,14 +144,23 @@ class BuilderSpec {
@Override
public Optional<ExecutableElement> buildMethod() {
- return methodsIn(builderTypeElement.getEnclosedElements()).stream()
+ Types typeUtils = processingEnv.getTypeUtils();
+ DeclaredType builderTypeMirror = MoreTypes.asDeclared(builderTypeElement.asType());
+ return MoreElements.getLocalAndInheritedMethods(
+ builderTypeElement, typeUtils, processingEnv.getElementUtils())
+ .stream()
.filter(
m ->
m.getSimpleName().contentEquals("build")
&& !m.getModifiers().contains(Modifier.PRIVATE)
&& !m.getModifiers().contains(Modifier.STATIC)
- && m.getParameters().isEmpty()
- && erasedTypeIs(m.getReturnType(), autoValueClass))
+ && m.getParameters().isEmpty())
+ .filter(
+ m -> {
+ ExecutableType methodMirror =
+ MoreTypes.asExecutable(typeUtils.asMemberOf(builderTypeMirror, m));
+ return erasedTypeIs(methodMirror.getReturnType(), autoValueClass);
+ })
.findFirst();
}
@@ -186,7 +197,8 @@ class BuilderSpec {
* Finds any methods in the set that return the builder type. If the builder has type parameters
* {@code <A, B>}, then the return type of the method must be {@code Builder<A, B>} with the
* same parameter names. We enforce elsewhere that the names and bounds of the builder
- * parameters must be the same as those of the @AutoValue class. Here's a correct example:
+ * parameters must be the same as those of the {@code @AutoValue} class. Here's a correct
+ * example:
*
* <pre>
* {@code @AutoValue abstract class Foo<A extends Number, B> {
@@ -201,18 +213,25 @@ class BuilderSpec {
* <p>We currently impose that there cannot be more than one such method.
*/
ImmutableSet<ExecutableElement> toBuilderMethods(
- Types typeUtils, Set<ExecutableElement> abstractMethods) {
+ Types typeUtils, TypeElement autoValueType, Set<ExecutableElement> abstractMethods) {
List<String> builderTypeParamNames =
builderTypeElement.getTypeParameters().stream()
.map(e -> e.getSimpleName().toString())
.collect(toList());
+ DeclaredType autoValueTypeMirror = MoreTypes.asDeclared(autoValueType.asType());
ImmutableSet.Builder<ExecutableElement> methods = ImmutableSet.builder();
for (ExecutableElement method : abstractMethods) {
- if (builderTypeElement.equals(typeUtils.asElement(method.getReturnType()))) {
+ if (!method.getParameters().isEmpty()) {
+ continue;
+ }
+ ExecutableType methodMirror =
+ MoreTypes.asExecutable(typeUtils.asMemberOf(autoValueTypeMirror, method));
+ TypeMirror returnTypeMirror = methodMirror.getReturnType();
+ if (builderTypeElement.equals(typeUtils.asElement(returnTypeMirror))) {
methods.add(method);
- DeclaredType returnType = MoreTypes.asDeclared(method.getReturnType());
+ DeclaredType returnType = MoreTypes.asDeclared(returnTypeMirror);
List<String> typeArguments =
returnType.getTypeArguments().stream()
.filter(t -> t.getKind().equals(TypeKind.TYPEVAR))
@@ -237,11 +256,12 @@ class BuilderSpec {
return builderMethods;
}
- void defineVars(
- AutoValueTemplateVars vars,
+ void defineVarsForAutoValue(
+ AutoValueOrBuilderTemplateVars vars,
ImmutableBiMap<ExecutableElement, String> getterToPropertyName) {
- Iterable<ExecutableElement> builderMethods = abstractMethods(builderTypeElement);
- boolean autoValueHasToBuilder = !toBuilderMethods.isEmpty();
+ Iterable<ExecutableElement> builderMethods =
+ abstractMethods(builderTypeElement, processingEnv);
+ boolean autoValueHasToBuilder = toBuilderMethods != null && !toBuilderMethods.isEmpty();
ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType =
TypeVariables.rewriteReturnTypes(
processingEnv.getElementUtils(),
@@ -249,32 +269,40 @@ class BuilderSpec {
getterToPropertyName.keySet(),
autoValueClass,
builderTypeElement);
- Optional<BuilderMethodClassifier> optionalClassifier =
- BuilderMethodClassifier.classify(
+ ImmutableMap.Builder<String, TypeMirror> rewrittenPropertyTypes = ImmutableMap.builder();
+ getterToPropertyType.forEach(
+ (getter, type) -> rewrittenPropertyTypes.put(getterToPropertyName.get(getter), type));
+ Optional<BuilderMethodClassifier<ExecutableElement>> optionalClassifier =
+ BuilderMethodClassifierForAutoValue.classify(
builderMethods,
errorReporter,
processingEnv,
autoValueClass,
builderTypeElement,
getterToPropertyName,
- getterToPropertyType,
+ rewrittenPropertyTypes.build(),
autoValueHasToBuilder);
if (!optionalClassifier.isPresent()) {
return;
}
for (ExecutableElement method : methodsIn(builderTypeElement.getEnclosedElements())) {
if (method.getSimpleName().contentEquals("builder")
- && method.getModifiers().contains(Modifier.STATIC)
- && method.getAnnotationMirrors().isEmpty()) {
- // For now we ignore methods with annotations, because for example we do want to allow
- // Jackson's @JsonCreator.
+ && method.getModifiers().contains(Modifier.STATIC)
+ && method.getAnnotationMirrors().isEmpty()
+ && !(vars instanceof AutoBuilderTemplateVars)) {
+ // For now we don't warn for methods with annotations, because for example we do want to
+ // allow Jackson's @JsonCreator. We also don't warn if this is an @AutoBuilder.
errorReporter.reportWarning(
method,
"[AutoValueBuilderInBuilder] Static builder() method should be in the containing"
+ " class");
}
}
- this.classifier = optionalClassifier.get();
+ defineVars(vars, optionalClassifier.get());
+ }
+
+ void defineVars(AutoValueOrBuilderTemplateVars vars, BuilderMethodClassifier<?> classifier) {
+ this.classifier = classifier;
Set<ExecutableElement> buildMethods = classifier.buildMethods();
if (buildMethods.size() != 1) {
Set<? extends Element> errorElements =
@@ -282,8 +310,8 @@ class BuilderSpec {
for (Element buildMethod : errorElements) {
errorReporter.reportError(
buildMethod,
- "[AutoValueBuilderBuild] Builder must have a single no-argument method returning"
- + " %s%s",
+ "[AutoValueBuilderBuild] Builder must have a single no-argument method, typically"
+ + " called build(), that returns %s%s",
autoValueClass,
typeParamsString());
}
@@ -292,7 +320,8 @@ class BuilderSpec {
this.buildMethod = Iterables.getOnlyElement(buildMethods);
vars.builderIsInterface = builderTypeElement.getKind() == ElementKind.INTERFACE;
vars.builderTypeName = TypeSimplifier.classNameOf(builderTypeElement);
- vars.builderFormalTypes = TypeEncoder.formalTypeParametersString(builderTypeElement);
+ vars.builderFormalTypes =
+ TypeEncoder.typeParametersString(builderTypeElement.getTypeParameters());
vars.builderActualTypes = TypeSimplifier.actualTypeParametersString(builderTypeElement);
vars.buildMethod = Optional.of(new SimpleMethod(buildMethod));
vars.builderGetters = classifier.builderGetters();
@@ -301,15 +330,12 @@ class BuilderSpec {
vars.builderPropertyBuilders =
ImmutableMap.copyOf(classifier.propertyNameToPropertyBuilder());
- Set<Property> required = new LinkedHashSet<>(vars.props);
- for (Property property : vars.props) {
- if (property.isNullable()
- || property.getOptional() != null
- || vars.builderPropertyBuilders.containsKey(property.getName())) {
- required.remove(property);
- }
- }
- vars.builderRequiredProperties = ImmutableSet.copyOf(required);
+ vars.builderRequiredProperties =
+ vars.props.stream()
+ .filter(p -> !p.isNullable())
+ .filter(p -> p.getOptional() == null)
+ .filter(p -> !vars.builderPropertyBuilders.containsKey(p.getName()))
+ .collect(toImmutableSet());
}
}
@@ -325,6 +351,7 @@ class BuilderSpec {
* five) then {@code Optional<T>} can be the corresponding boxed type.
*/
public static class PropertyGetter {
+ private final String name;
private final String access;
private final String type;
private final Optionalish optional;
@@ -342,11 +369,17 @@ class BuilderSpec {
* {@code Optional<T>} rather than {@code T}, as explained above.
*/
PropertyGetter(ExecutableElement method, String type, Optionalish optional) {
+ this.name = method.getSimpleName().toString();
this.access = SimpleMethod.access(method);
this.type = type;
this.optional = optional;
}
+ // Not accessed from templates so doesn't have to be public.
+ String getName() {
+ return name;
+ }
+
public String getAccess() {
return access;
}
@@ -361,8 +394,8 @@ class BuilderSpec {
}
/**
- * Specifies how to copy a parameter value into the target type. This might be the identity, or
- * it might be something like {@code ImmutableList.of(...)} or {@code Optional.ofNullable(...)}.
+ * Specifies how to copy a parameter value into the target type. This might be the identity, or it
+ * might be something like {@code ImmutableList.of(...)} or {@code Optional.ofNullable(...)}.
*/
static class Copier {
static final Copier IDENTITY = acceptingNull(x -> x);
@@ -452,7 +485,7 @@ class BuilderSpec {
return nullableAnnotation;
}
- public String copy(AutoValueProcessor.Property property) {
+ public String copy(Property property) {
String copy = copier.copy.apply(property.toString());
if (property.isNullable() && !copier.acceptsNull) {
copy = String.format("(%s == null ? null : %s)", property, copy);
@@ -487,19 +520,24 @@ class BuilderSpec {
}
private static boolean sameTypeParameters(TypeElement a, TypeElement b) {
- int nTypeParameters = a.getTypeParameters().size();
- if (nTypeParameters != b.getTypeParameters().size()) {
+ return sameTypeParameters(a.getTypeParameters(), b.getTypeParameters());
+ }
+
+ static boolean sameTypeParameters(
+ List<? extends TypeParameterElement> aParams, List<? extends TypeParameterElement> bParams) {
+ int nTypeParameters = aParams.size();
+ if (nTypeParameters != bParams.size()) {
return false;
}
for (int i = 0; i < nTypeParameters; i++) {
- TypeParameterElement aParam = a.getTypeParameters().get(i);
- TypeParameterElement bParam = b.getTypeParameters().get(i);
+ TypeParameterElement aParam = aParams.get(i);
+ TypeParameterElement bParam = bParams.get(i);
if (!aParam.getSimpleName().equals(bParam.getSimpleName())) {
return false;
}
- Set<TypeMirror> autoValueBounds = new TypeMirrorSet(aParam.getBounds());
- Set<TypeMirror> builderBounds = new TypeMirrorSet(bParam.getBounds());
- if (!autoValueBounds.equals(builderBounds)) {
+ Set<TypeMirror> aBounds = new TypeMirrorSet(aParam.getBounds());
+ Set<TypeMirror> bBounds = new TypeMirrorSet(bParam.getBounds());
+ if (!aBounds.equals(bBounds)) {
return false;
}
}
@@ -512,7 +550,8 @@ class BuilderSpec {
* then this method will throw an exception that will cause us to defer processing of the current
* class until a later annotation-processing round.
*/
- private ImmutableSet<ExecutableElement> abstractMethods(TypeElement typeElement) {
+ static ImmutableSet<ExecutableElement> abstractMethods(
+ TypeElement typeElement, ProcessingEnvironment processingEnv) {
Set<ExecutableElement> methods =
getLocalAndInheritedMethods(
typeElement, processingEnv.getTypeUtils(), processingEnv.getElementUtils());
diff --git a/value/src/main/java/com/google/auto/value/processor/ClassNames.java b/value/src/main/java/com/google/auto/value/processor/ClassNames.java
index d23cfd28..76c50710 100644
--- a/value/src/main/java/com/google/auto/value/processor/ClassNames.java
+++ b/value/src/main/java/com/google/auto/value/processor/ClassNames.java
@@ -28,5 +28,7 @@ final class ClassNames {
static final String AUTO_ONE_OF_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoOneOf";
static final String AUTO_VALUE_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoValue";
static final String AUTO_VALUE_BUILDER_NAME = AUTO_VALUE_NAME + ".Builder";
+ static final String AUTO_BUILDER_NAME = AUTO_VALUE_PACKAGE_NAME + "AutoBuilder";
static final String COPY_ANNOTATIONS_NAME = AUTO_VALUE_NAME + ".CopyAnnotations";
+ static final String KOTLIN_METADATA_NAME = "kotlin.Metadata";
}
diff --git a/value/src/main/java/com/google/auto/value/processor/ErrorReporter.java b/value/src/main/java/com/google/auto/value/processor/ErrorReporter.java
index e2a3d839..3ec9a0ef 100644
--- a/value/src/main/java/com/google/auto/value/processor/ErrorReporter.java
+++ b/value/src/main/java/com/google/auto/value/processor/ErrorReporter.java
@@ -81,9 +81,12 @@ class ErrorReporter {
* @param e the element to which it pertains
* @param format the format string for the text of the error
* @param args arguments for the format string
+ * @return This method does not return, but is declared with an exception return type so you
+ * can write {@code throw abortWithError(...)} to tell the compiler that.
+ * @throws AbortProcessingException always
*/
@FormatMethod
- void abortWithError(Element e, String format, Object... args) {
+ AbortProcessingException abortWithError(Element e, String format, Object... args) {
reportError(e, format, args);
throw new AbortProcessingException();
}
diff --git a/value/src/main/java/com/google/auto/value/processor/GwtCompatibility.java b/value/src/main/java/com/google/auto/value/processor/GwtCompatibility.java
index 72207541..fae4e092 100644
--- a/value/src/main/java/com/google/auto/value/processor/GwtCompatibility.java
+++ b/value/src/main/java/com/google/auto/value/processor/GwtCompatibility.java
@@ -62,9 +62,7 @@ class GwtCompatibility {
annotationArguments = "";
} else {
annotationArguments =
- getElementValues(annotation)
- .entrySet()
- .stream()
+ getElementValues(annotation).entrySet().stream()
.map(e -> e.getKey().getSimpleName() + " = " + e.getValue())
.collect(joining(", ", "(", ")"));
}
diff --git a/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java b/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java
index cf928d5c..30ad0926 100644
--- a/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java
+++ b/value/src/main/java/com/google/auto/value/processor/GwtSerialization.java
@@ -18,6 +18,7 @@ package com.google.auto.value.processor;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
+import com.google.auto.value.processor.AutoValueishProcessor.GetterProperty;
import com.google.auto.value.processor.PropertyBuilderClassifier.PropertyBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Multimap;
@@ -80,12 +81,14 @@ class GwtSerialization {
* com.example.AutoValue_Foo_CustomFieldSerializer.
*
* @param autoVars the template variables defined for this type.
+ * @param finalSubclass the simple name of the AutoValue class being generated, AutoValue_Foo
+ * in the example.
*/
- void maybeWriteGwtSerializer(AutoValueTemplateVars autoVars) {
+ void maybeWriteGwtSerializer(AutoValueTemplateVars autoVars, String finalSubclass) {
if (shouldWriteGwtSerializer()) {
GwtTemplateVars vars = new GwtTemplateVars();
vars.pkg = autoVars.pkg;
- vars.subclass = autoVars.finalSubclass;
+ vars.subclass = finalSubclass;
vars.formalTypes = autoVars.formalTypes;
vars.actualTypes = autoVars.actualTypes;
vars.useBuilder = !autoVars.builderTypeName.isEmpty();
@@ -95,7 +98,8 @@ class GwtSerialization {
String className =
(vars.pkg.isEmpty() ? "" : vars.pkg + ".") + vars.subclass + "_CustomFieldSerializer";
vars.serializerClass = TypeSimplifier.simpleNameOf(className);
- vars.props = autoVars.props.stream().map(Property::new).collect(toList());
+ vars.props =
+ autoVars.props.stream().map(p -> new Property((GetterProperty) p)).collect(toList());
vars.classHashString = computeClassHash(autoVars.props, vars.pkg);
String text = vars.toText();
text = TypeEncoder.decode(text, processingEnv, vars.pkg, type.asType());
@@ -104,10 +108,10 @@ class GwtSerialization {
}
public static class Property {
- private final AutoValueProcessor.Property property;
+ private final GetterProperty property;
private final boolean isCastingUnchecked;
- Property(AutoValueProcessor.Property property) {
+ Property(GetterProperty property) {
this.property = property;
this.isCastingUnchecked = TypeSimplifier.isCastingUnchecked(property.getTypeMirror());
}
@@ -242,14 +246,14 @@ class GwtSerialization {
.printMessage(
Diagnostic.Kind.WARNING, "Could not write generated class " + className + ": " + e);
// A warning rather than an error for the reason explained in
- // AutoValueOrOneOfProcessor.writeSourceFile.
+ // AutoValueishProcessor.writeSourceFile.
}
}
// Compute a hash that is guaranteed to change if the names, types, or order of the fields
// change. We use TypeEncoder so that we can get a defined string for types, since
// TypeMirror.toString() isn't guaranteed to remain the same.
- private String computeClassHash(Iterable<AutoValueProcessor.Property> props, String pkg) {
+ private String computeClassHash(Iterable<AutoValueishProcessor.Property> props, String pkg) {
CRC32 crc = new CRC32();
String encodedType = TypeEncoder.encode(type.asType()) + ":";
String decodedType = TypeEncoder.decode(encodedType, processingEnv, "", null);
@@ -259,7 +263,7 @@ class GwtSerialization {
decodedType = pkg + "." + decodedType;
}
crc.update(decodedType.getBytes(UTF_8));
- for (AutoValueProcessor.Property prop : props) {
+ for (AutoValueishProcessor.Property prop : props) {
String encodedProp = prop + ":" + TypeEncoder.encode(prop.getTypeMirror()) + ";";
String decodedProp = TypeEncoder.decode(encodedProp, processingEnv, pkg, null);
crc.update(decodedProp.getBytes(UTF_8));
diff --git a/value/src/main/java/com/google/auto/value/processor/Nullables.java b/value/src/main/java/com/google/auto/value/processor/Nullables.java
new file mode 100644
index 00000000..c07656f1
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/Nullables.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import static java.util.stream.Collectors.toList;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.SimpleTypeVisitor8;
+
+class Nullables {
+ /**
+ * If set to a non-empty string, defines which {@code @Nullable} type annotation should be used by
+ * default. If set to an empty string, does not insert {@code @Nullable} unless it is referenced
+ * in the {@code @AutoValue} methods. If unset, defaults to {@value #DEFAULT_NULLABLE}.
+ */
+ static final String NULLABLE_OPTION = "com.google.auto.value.NullableTypeAnnotation";
+
+ // We write this using .concat in order to hide it from rewriting rules.
+ private static final String DEFAULT_NULLABLE = "org".concat(".jspecify.nullness.Nullable");
+
+ private final Optional<AnnotationMirror> defaultNullable;
+
+ Nullables(ProcessingEnvironment processingEnv) {
+ // -Afoo without `=` sets "foo" to null in the getOptions() map.
+ String nullableOption =
+ Strings.nullToEmpty(
+ processingEnv.getOptions().getOrDefault(NULLABLE_OPTION, DEFAULT_NULLABLE));
+ this.defaultNullable =
+ (!nullableOption.isEmpty()
+ && processingEnv.getSourceVersion().ordinal() >= SourceVersion.RELEASE_8.ordinal())
+ ? Optional.ofNullable(processingEnv.getElementUtils().getTypeElement(nullableOption))
+ .map(t -> annotationMirrorOf(MoreTypes.asDeclared(t.asType())))
+ : Optional.empty();
+ }
+
+ private static AnnotationMirror annotationMirrorOf(DeclaredType annotationType) {
+ return new AnnotationMirror() {
+ @Override
+ public DeclaredType getAnnotationType() {
+ return annotationType;
+ }
+
+ @Override
+ public ImmutableMap<? extends ExecutableElement, ? extends AnnotationValue>
+ getElementValues() {
+ return ImmutableMap.of();
+ }
+ };
+ }
+
+ /**
+ * Returns the type of a {@code @Nullable} type-annotation, if one is found anywhere in the
+ * signatures of the given methods.
+ */
+ @VisibleForTesting
+ static Optional<AnnotationMirror> nullableMentionedInMethods(
+ Collection<ExecutableElement> methods) {
+ return methods.stream()
+ .flatMap(
+ method ->
+ Stream.concat(
+ Stream.of(method.getReturnType()),
+ method.getParameters().stream().map(Element::asType)))
+ .map(Nullables::nullableIn)
+ .filter(Optional::isPresent)
+ .findFirst()
+ .orElse(Optional.empty());
+ }
+
+ /**
+ * Returns the type of an appropriate {@code @Nullable} type-annotation, given a set of methods
+ * that are known to be in the same compilation as the code being generated. If one of those
+ * methods contains an appropriate {@code @Nullable} annotation on a parameter or return type,
+ * this method will return that. Otherwise, if the <a href="http://jspecify.org">JSpecify</a>
+ * {@code @Nullable} is available, this method will return it. Otherwise, this method will return
+ * an empty {@code Optional}.
+ */
+ Optional<AnnotationMirror> appropriateNullableGivenMethods(
+ Collection<ExecutableElement> methods) {
+ return nullableMentionedInMethods(methods).map(Optional::of).orElse(defaultNullable);
+ }
+
+ private static Optional<AnnotationMirror> nullableIn(TypeMirror type) {
+ return new NullableFinder().visit(type);
+ }
+
+ private static Optional<AnnotationMirror> nullableIn(
+ List<? extends AnnotationMirror> annotations) {
+ return annotations.stream()
+ .filter(a -> a.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable"))
+ .map(a -> (AnnotationMirror) a) // get rid of the pesky wildcard
+ .findFirst();
+ }
+
+ private static class NullableFinder extends SimpleTypeVisitor8<Optional<AnnotationMirror>, Void> {
+ private final TypeMirrorSet visiting = new TypeMirrorSet();
+
+ NullableFinder() {
+ super(Optional.empty());
+ }
+
+ // Primitives can't be @Nullable so we don't check that.
+
+ @Override
+ public Optional<AnnotationMirror> visitDeclared(DeclaredType t, Void unused) {
+ if (!visiting.add(t)) {
+ return Optional.empty();
+ }
+ return nullableIn(t.getAnnotationMirrors())
+ .map(Optional::of)
+ .orElseGet(() -> visitAll(t.getTypeArguments()));
+ }
+
+ @Override
+ public Optional<AnnotationMirror> visitTypeVariable(TypeVariable t, Void unused) {
+ if (!visiting.add(t)) {
+ return Optional.empty();
+ }
+ return nullableIn(t.getAnnotationMirrors())
+ .map(Optional::of)
+ .orElseGet(() -> visitAll(ImmutableList.of(t.getUpperBound(), t.getLowerBound())));
+ }
+
+ @Override
+ public Optional<AnnotationMirror> visitArray(ArrayType t, Void unused) {
+ return nullableIn(t.getAnnotationMirrors())
+ .map(Optional::of)
+ .orElseGet(() -> visit(t.getComponentType()));
+ }
+
+ @Override
+ public Optional<AnnotationMirror> visitWildcard(WildcardType t, Void unused) {
+ return nullableIn(t.getAnnotationMirrors())
+ .map(Optional::of)
+ .orElseGet(
+ () ->
+ visitAll(
+ Stream.of(t.getExtendsBound(), t.getSuperBound())
+ .filter(Objects::nonNull)
+ .collect(toList())));
+ }
+
+ @Override
+ public Optional<AnnotationMirror> visitIntersection(IntersectionType t, Void unused) {
+ return nullableIn(t.getAnnotationMirrors())
+ .map(Optional::of)
+ .orElseGet(() -> visitAll(t.getBounds()));
+ }
+
+ private Optional<AnnotationMirror> visitAll(List<? extends TypeMirror> types) {
+ return types.stream()
+ .map(this::visit)
+ .filter(Optional::isPresent)
+ .findFirst()
+ .orElse(Optional.empty());
+ }
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java b/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java
index 2565cddc..5d0168e4 100644
--- a/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/PropertyBuilderClassifier.java
@@ -15,17 +15,19 @@
*/
package com.google.auto.value.processor;
+import static java.util.stream.Collectors.collectingAndThen;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toMap;
+
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
+import java.util.function.Function;
+import java.util.function.Predicate;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
@@ -51,25 +53,25 @@ class PropertyBuilderClassifier {
private final ErrorReporter errorReporter;
private final Types typeUtils;
private final Elements elementUtils;
- private final BuilderMethodClassifier builderMethodClassifier;
- private final ImmutableBiMap<ExecutableElement, String> getterToPropertyName;
- private final ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType;
+ private final BuilderMethodClassifier<?> builderMethodClassifier;
+ private final Predicate<String> propertyIsNullable;
+ private final ImmutableMap<String, TypeMirror> propertyTypes;
private final EclipseHack eclipseHack;
PropertyBuilderClassifier(
ErrorReporter errorReporter,
Types typeUtils,
Elements elementUtils,
- BuilderMethodClassifier builderMethodClassifier,
- ImmutableBiMap<ExecutableElement, String> getterToPropertyName,
- ImmutableMap<ExecutableElement, TypeMirror> getterToPropertyType,
+ BuilderMethodClassifier<?> builderMethodClassifier,
+ Predicate<String> propertyIsNullable,
+ ImmutableMap<String, TypeMirror> propertyTypes,
EclipseHack eclipseHack) {
this.errorReporter = errorReporter;
this.typeUtils = typeUtils;
this.elementUtils = elementUtils;
this.builderMethodClassifier = builderMethodClassifier;
- this.getterToPropertyName = getterToPropertyName;
- this.getterToPropertyType = getterToPropertyType;
+ this.propertyIsNullable = propertyIsNullable;
+ this.propertyTypes = propertyTypes;
this.eclipseHack = eclipseHack;
}
@@ -115,6 +117,14 @@ class PropertyBuilderClassifier {
return propertyBuilderMethod;
}
+ /** The property builder method parameters, for example {@code Comparator<T> comparator} */
+ public String getPropertyBuilderMethodParameters() {
+ return propertyBuilderMethod.getParameters().stream()
+ .map(
+ parameter -> TypeEncoder.encode(parameter.asType()) + " " + parameter.getSimpleName())
+ .collect(joining(", "));
+ }
+
public String getAccess() {
return SimpleMethod.access(propertyBuilderMethod);
}
@@ -206,8 +216,7 @@ class PropertyBuilderClassifier {
TypeElement barBuilderTypeElement = MoreTypes.asTypeElement(barBuilderTypeMirror);
Map<String, ExecutableElement> barBuilderNoArgMethods = noArgMethodsOf(barBuilderTypeElement);
- ExecutableElement barGetter = getterToPropertyName.inverse().get(property);
- TypeMirror barTypeMirror = getterToPropertyType.get(barGetter);
+ TypeMirror barTypeMirror = propertyTypes.get(property);
if (barTypeMirror.getKind() != TypeKind.DECLARED) {
errorReporter.reportError(
method,
@@ -216,10 +225,10 @@ class PropertyBuilderClassifier {
property);
return Optional.empty();
}
- if (isNullable(barGetter)) {
+ if (propertyIsNullable.test(property)) {
errorReporter.reportError(
- barGetter,
- "[AutoValueNullBuilder] Property %s has a property builder so it cannot be @Nullable",
+ method,
+ "[AutoValueNullBuilder] Property %s is @Nullable so it cannot have a property builder",
property);
}
TypeElement barTypeElement = MoreTypes.asTypeElement(barTypeMirror);
@@ -251,8 +260,13 @@ class PropertyBuilderClassifier {
return Optional.empty();
}
- Optional<ExecutableElement> maybeBuilderMaker =
- builderMaker(barNoArgMethods, barBuilderTypeElement);
+ Optional<ExecutableElement> maybeBuilderMaker;
+ if (method.getParameters().isEmpty()) {
+ maybeBuilderMaker = noArgBuilderMaker(barNoArgMethods, barBuilderTypeElement);
+ } else {
+ Map<String, ExecutableElement> barOneArgMethods = oneArgumentMethodsOf(barTypeElement);
+ maybeBuilderMaker = oneArgBuilderMaker(barOneArgMethods, barBuilderTypeElement);
+ }
if (!maybeBuilderMaker.isPresent()) {
errorReporter.reportError(
method,
@@ -268,10 +282,14 @@ class PropertyBuilderClassifier {
String barBuilderType = TypeEncoder.encodeWithAnnotations(barBuilderTypeMirror);
String rawBarType = TypeEncoder.encodeRaw(barTypeMirror);
+ String arguments =
+ method.getParameters().isEmpty()
+ ? "()"
+ : "(" + method.getParameters().get(0).getSimpleName() + ")";
String initializer =
(builderMaker.getKind() == ElementKind.CONSTRUCTOR)
- ? "new " + barBuilderType + "()"
- : rawBarType + "." + builderMaker.getSimpleName() + "()";
+ ? "new " + barBuilderType + arguments
+ : rawBarType + "." + builderMaker.getSimpleName() + arguments;
String builtToBuilder = null;
String copyAll = null;
ExecutableElement toBuilder = barNoArgMethods.get("toBuilder");
@@ -324,41 +342,75 @@ class PropertyBuilderClassifier {
private static final ImmutableSet<String> BUILDER_METHOD_NAMES =
ImmutableSet.of("naturalOrder", "builder", "newBuilder");
- // (2) `BarBuilder must have a public no-arg constructor, or `Bar` must have a visible static
- // method `naturalOrder(), `builder()`, or `newBuilder()` that returns `BarBuilder`.
- private Optional<ExecutableElement> builderMaker(
+ // (2) `BarBuilder` must have a public no-arg constructor, or `Bar` must have a visible static
+ // method `naturalOrder(), `builder()`, or `newBuilder()` that returns `BarBuilder`; or,
+ // if we have a foosBuilder(T) method, then `BarBuilder` must have a public constructor with
+ // a single parameter assignable from T, or a visible static `builder(T)` method.
+ private Optional<ExecutableElement> noArgBuilderMaker(
Map<String, ExecutableElement> barNoArgMethods, TypeElement barBuilderTypeElement) {
- for (String builderMethodName : BUILDER_METHOD_NAMES) {
- ExecutableElement method = barNoArgMethods.get(builderMethodName);
- if (method != null
- && method.getModifiers().contains(Modifier.STATIC)
- && typeUtils.isSameType(
- typeUtils.erasure(method.getReturnType()),
- typeUtils.erasure(barBuilderTypeElement.asType()))) {
- // TODO(emcmanus): check visibility. We don't want to require public for @AutoValue
- // builders. By not checking visibility we risk accepting something as a builder maker
- // and then failing when the generated code tries to call Bar.builder(). But the risk
- // seems small.
- return Optional.of(method);
- }
+ return builderMaker(BUILDER_METHOD_NAMES, barNoArgMethods, barBuilderTypeElement, 0);
+ }
+
+ private static final ImmutableSet<String> ONE_ARGUMENT_BUILDER_METHOD_NAMES =
+ ImmutableSet.of("builder");
+
+ private Optional<ExecutableElement> oneArgBuilderMaker(
+ Map<String, ExecutableElement> barOneArgMethods, TypeElement barBuilderTypeElement) {
+
+ return builderMaker(
+ ONE_ARGUMENT_BUILDER_METHOD_NAMES, barOneArgMethods, barBuilderTypeElement, 1);
+ }
+
+ private Optional<ExecutableElement> builderMaker(
+ ImmutableSet<String> methodNamesToCheck,
+ Map<String, ExecutableElement> methods,
+ TypeElement barBuilderTypeElement,
+ int argumentCount) {
+ Optional<ExecutableElement> maybeMethod =
+ methodNamesToCheck.stream()
+ .map(methods::get)
+ .filter(Objects::nonNull)
+ .filter(method -> method.getModifiers().contains(Modifier.STATIC))
+ .filter(
+ method ->
+ typeUtils.isSameType(
+ typeUtils.erasure(method.getReturnType()),
+ typeUtils.erasure(barBuilderTypeElement.asType())))
+ .findFirst();
+
+ if (maybeMethod.isPresent()) {
+ // TODO(emcmanus): check visibility. We don't want to require public for @AutoValue
+ // builders. By not checking visibility we risk accepting something as a builder maker
+ // and then failing when the generated code tries to call Bar.builder(). But the risk
+ // seems small.
+ return maybeMethod;
}
- return ElementFilter.constructorsIn(barBuilderTypeElement.getEnclosedElements())
- .stream()
- .filter(c -> c.getParameters().isEmpty())
+
+ return ElementFilter.constructorsIn(barBuilderTypeElement.getEnclosedElements()).stream()
+ .filter(c -> c.getParameters().size() == argumentCount)
.filter(c -> c.getModifiers().contains(Modifier.PUBLIC))
.findFirst();
}
private Map<String, ExecutableElement> noArgMethodsOf(TypeElement type) {
- // Can't easily use ImmutableMap here because getAllMembers could return more than one method
- // with the same name.
- Map<String, ExecutableElement> methods = new LinkedHashMap<>();
- for (ExecutableElement method : ElementFilter.methodsIn(elementUtils.getAllMembers(type))) {
- if (method.getParameters().isEmpty() && !isStaticInterfaceMethodNotIn(method, type)) {
- methods.put(method.getSimpleName().toString(), method);
- }
- }
- return methods;
+ return methodsOf(type, 0);
+ }
+
+ private ImmutableMap<String, ExecutableElement> oneArgumentMethodsOf(TypeElement type) {
+ return methodsOf(type, 1);
+ }
+
+ private ImmutableMap<String, ExecutableElement> methodsOf(TypeElement type, int argumentCount) {
+ return ElementFilter.methodsIn(elementUtils.getAllMembers(type)).stream()
+ .filter(method -> method.getParameters().size() == argumentCount)
+ .filter(method -> !isStaticInterfaceMethodNotIn(method, type))
+ .collect(
+ collectingAndThen(
+ toMap(
+ method -> method.getSimpleName().toString(),
+ Function.identity(),
+ (method1, method2) -> method1),
+ ImmutableMap::copyOf));
}
// Work around an Eclipse compiler bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=547185
@@ -399,18 +451,4 @@ class PropertyBuilderClassifier {
})
.findFirst();
}
-
- private static boolean isNullable(ExecutableElement getter) {
- List<List<? extends AnnotationMirror>> annotationLists =
- ImmutableList.of(
- getter.getAnnotationMirrors(), getter.getReturnType().getAnnotationMirrors());
- for (List<? extends AnnotationMirror> annotations : annotationLists) {
- for (AnnotationMirror annotation : annotations) {
- if (annotation.getAnnotationType().asElement().getSimpleName().contentEquals("Nullable")) {
- return true;
- }
- }
- }
- return false;
- }
}
diff --git a/value/src/main/java/com/google/auto/value/processor/Reformatter.java b/value/src/main/java/com/google/auto/value/processor/Reformatter.java
index 69265174..89b6d0d1 100644
--- a/value/src/main/java/com/google/auto/value/processor/Reformatter.java
+++ b/value/src/main/java/com/google/auto/value/processor/Reformatter.java
@@ -15,6 +15,8 @@
*/
package com.google.auto.value.processor;
+import com.google.common.base.CharMatcher;
+
/**
* Postprocessor that runs over the output of the template engine in order to make it look nicer.
* Mostly, this involves removing surplus horizontal and vertical space.
@@ -22,6 +24,14 @@ package com.google.auto.value.processor;
* @author emcmanus@google.com (Éamonn McManus)
*/
class Reformatter {
+ /**
+ * Characters that might start a continuation line. Since Google Style requires splitting before
+ * operators, we expect that a continuation line will begin with one of these (or be inside
+ * parentheses). We've omitted {@code /} from this list for now since none of our templates splits
+ * before one, and otherwise we'd have to handle {@code //} and {@code /*}.
+ */
+ private static final CharMatcher OPERATORS = CharMatcher.anyOf("+-*%&|^<>=?:.").precomputed();
+
static String fixup(String s) {
StringBuilder out = new StringBuilder();
JavaScanner scanner = new JavaScanner(s);
@@ -49,27 +59,43 @@ class Reformatter {
case ' ':
// This token is a string of consecutive spaces that is not at the start of a line.
// Consecutive spaces at the start of a line are attached to the previous newline, and
- // we don't expect the first line to start with spaces. So we are going to compress this
+ // we delete spaces at the start of the first line. So we are going to compress this
// into just one space, and we are going to delete it entirely if it follows '(' or
// precedes a newline or one of the punctuation characters here.
- if (s.charAt(previous) != '(' && "\n.,;)".indexOf(s.charAt(end)) < 0) {
+ if (start > 0 && s.charAt(previous) != '(' && "\n.,;)".indexOf(s.charAt(end)) < 0) {
out.append(' ');
}
continue;
case '\n':
// This token is a newline plus any following spaces (the indentation of the next line).
- // If it is followed by something other than a newline then we will output it. Otherwise,
- // it is part of a sequence of newlines but it is not the last one. If this is a context
- // where we delete blank lines, or if this is not the first new line in the sequence, or
- // if we are at the start of the file, we will delete this one. Otherwise we will output a
- // single newline with no following indentation. Contexts where we delete blank lines are
- // inside parentheses or inside more than one set of braces.
+ // If it is followed by something other than a newline then we will output the
+ // newline, and replace the following spaces by our computed indentation. Otherwise, the
+ // token is part of a sequence of newlines but it is not the last one. If this is a
+ // context where we delete blank lines, or if this is not the first new line in the
+ // sequence, or if we are at the start of the file, we will delete this one. Otherwise we
+ // will output a single newline with no following indentation. Contexts where we delete
+ // blank lines are inside parentheses or inside more than one set of braces.
if (end < len && s.charAt(end) != '\n') {
- if (out.length() == 0) {
- // Omit newlines at the very start of the file.
- start++;
+ // Omit newlines at the very start of the file. Also delete newline+indent between
+ // ( and ), since that shows up in some places where we output one parameter per line,
+ // when there are no parameters.
+ char prev = s.charAt(previous);
+ char next = s.charAt(end);
+ if (out.length() > 0 && (prev != '(' || next != ')')) {
+ out.append('\n');
+ // Replace any space after the newline with our computed indentation. The algorithm
+ // here is simplistic but works OK for our current templates.
+ int indent = braces * 2;
+ if (parens > 0 || OPERATORS.matches(next)) {
+ indent += 4;
+ } else if (next == '}') {
+ indent -= 2;
+ }
+ for (int i = 0; i < indent; i++) {
+ out.append(' ');
+ }
}
- break; // Output the newline and its following indentation.
+ continue;
}
if (parens == 0 && braces < 2 && s.charAt(previous) != '\n' && out.length() > 0) {
out.append('\n');
@@ -82,4 +108,6 @@ class Reformatter {
}
return out.toString();
}
+
+ private Reformatter() {}
}
diff --git a/value/src/main/java/com/google/auto/value/processor/SimpleMethod.java b/value/src/main/java/com/google/auto/value/processor/SimpleMethod.java
index 03510231..c144bc04 100644
--- a/value/src/main/java/com/google/auto/value/processor/SimpleMethod.java
+++ b/value/src/main/java/com/google/auto/value/processor/SimpleMethod.java
@@ -15,6 +15,8 @@
*/
package com.google.auto.value.processor;
+import static java.util.stream.Collectors.joining;
+
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
@@ -31,10 +33,12 @@ import javax.lang.model.element.Modifier;
public final class SimpleMethod {
private final String access;
private final String name;
+ private final String throwsString;
SimpleMethod(ExecutableElement method) {
this.access = access(method);
this.name = method.getSimpleName().toString();
+ this.throwsString = throwsString(method);
}
public String getAccess() {
@@ -45,6 +49,10 @@ public final class SimpleMethod {
return name;
}
+ public String getThrows() {
+ return throwsString;
+ }
+
/**
* Returns an appropriate string to be used in code for the access specification of the given
* method. This will be {@code public} or {@code protected} followed by a space, or the empty
@@ -60,4 +68,12 @@ public final class SimpleMethod {
return "";
}
}
+
+ private static String throwsString(ExecutableElement method) {
+ if (method.getThrownTypes().isEmpty()) {
+ return "";
+ }
+ return "throws "
+ + method.getThrownTypes().stream().map(TypeEncoder::encode).collect(joining(", "));
+ }
}
diff --git a/value/src/main/java/com/google/auto/value/processor/SimpleServiceLoader.java b/value/src/main/java/com/google/auto/value/processor/SimpleServiceLoader.java
index 55f9ae4e..23f6674c 100644
--- a/value/src/main/java/com/google/auto/value/processor/SimpleServiceLoader.java
+++ b/value/src/main/java/com/google/auto/value/processor/SimpleServiceLoader.java
@@ -15,20 +15,23 @@
*/
package com.google.auto.value.processor;
+import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Streams;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
-import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.ServiceConfigurationError;
+import java.util.regex.Pattern;
/**
* A replacement for {@link java.util.ServiceLoader} that avoids certain long-standing bugs. This
@@ -43,6 +46,11 @@ public final class SimpleServiceLoader {
private SimpleServiceLoader() {}
public static <T> ImmutableList<T> load(Class<? extends T> service, ClassLoader loader) {
+ return load(service, loader, Optional.empty());
+ }
+
+ public static <T> ImmutableList<T> load(
+ Class<? extends T> service, ClassLoader loader, Optional<Pattern> allowedMissingClasses) {
String resourceName = "META-INF/services/" + service.getName();
List<URL> resourceUrls;
try {
@@ -50,49 +58,64 @@ public final class SimpleServiceLoader {
} catch (IOException e) {
throw new ServiceConfigurationError("Could not look up " + resourceName, e);
}
- ImmutableList.Builder<T> providers = ImmutableList.builder();
+ ImmutableSet.Builder<Class<? extends T>> providerClasses = ImmutableSet.builder();
for (URL resourceUrl : resourceUrls) {
try {
- providers.addAll(providersFromUrl(resourceUrl, service, loader));
+ providerClasses.addAll(
+ providerClassesFromUrl(resourceUrl, service, loader, allowedMissingClasses));
} catch (IOException e) {
throw new ServiceConfigurationError("Could not read " + resourceUrl, e);
}
}
+ ImmutableList.Builder<T> providers = ImmutableList.builder();
+ for (Class<? extends T> providerClass : providerClasses.build()) {
+ try {
+ T provider = providerClass.getConstructor().newInstance();
+ providers.add(provider);
+ } catch (ReflectiveOperationException e) {
+ throw new ServiceConfigurationError("Could not construct " + providerClass.getName(), e);
+ }
+ }
return providers.build();
}
- private static <T> ImmutableList<T> providersFromUrl(
- URL resourceUrl, Class<T> service, ClassLoader loader) throws IOException {
- ImmutableList.Builder<T> providers = ImmutableList.builder();
+ private static <T> ImmutableSet<Class<? extends T>> providerClassesFromUrl(
+ URL resourceUrl,
+ Class<? extends T> service,
+ ClassLoader loader,
+ Optional<Pattern> allowedMissingClasses)
+ throws IOException {
+ ImmutableSet.Builder<Class<? extends T>> providerClasses = ImmutableSet.builder();
URLConnection urlConnection = resourceUrl.openConnection();
urlConnection.setUseCaches(false);
+ List<String> lines;
try (InputStream in = urlConnection.getInputStream();
- BufferedReader reader =
- new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
- for (String line : reader.lines().collect(toList())) {
- Optional<String> maybeClassName = parseClassName(line);
- if (maybeClassName.isPresent()) {
- String className = maybeClassName.get();
- Class<?> c;
- try {
- c = Class.forName(className, false, loader);
- } catch (ClassNotFoundException e) {
- throw new ServiceConfigurationError("Could not load " + className, e);
- }
- if (!service.isAssignableFrom(c)) {
- throw new ServiceConfigurationError(
- "Class " + className + " is not assignable to " + service.getName());
- }
- try {
- Object provider = c.getConstructor().newInstance();
- providers.add(service.cast(provider));
- } catch (ReflectiveOperationException e) {
- throw new ServiceConfigurationError("Could not construct " + className, e);
- }
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in, UTF_8))) {
+ lines = reader.lines().collect(toList());
+ }
+ List<String> classNames =
+ lines.stream()
+ .map(SimpleServiceLoader::parseClassName)
+ .flatMap(Streams::stream)
+ .collect(toList());
+ for (String className : classNames) {
+ Class<?> c;
+ try {
+ c = Class.forName(className, false, loader);
+ } catch (ClassNotFoundException e) {
+ if (allowedMissingClasses.isPresent()
+ && allowedMissingClasses.get().matcher(className).matches()) {
+ continue;
}
+ throw new ServiceConfigurationError("Could not load " + className, e);
+ }
+ if (!service.isAssignableFrom(c)) {
+ throw new ServiceConfigurationError(
+ "Class " + className + " is not assignable to " + service.getName());
}
- return providers.build();
+ providerClasses.add(c.asSubclass(service));
}
+ return providerClasses.build();
}
private static Optional<String> parseClassName(String line) {
diff --git a/value/src/main/java/com/google/auto/value/processor/TemplateVars.java b/value/src/main/java/com/google/auto/value/processor/TemplateVars.java
index 235f0ada..d9e3337b 100644
--- a/value/src/main/java/com/google/auto/value/processor/TemplateVars.java
+++ b/value/src/main/java/com/google/auto/value/processor/TemplateVars.java
@@ -99,7 +99,7 @@ abstract class TemplateVars {
return parsedTemplate().evaluate(vars);
}
- private Map<String, Object> toVars() {
+ private ImmutableMap<String, Object> toVars() {
Map<String, Object> vars = new TreeMap<>();
for (Field field : fields) {
Object value = fieldValue(field, this);
@@ -114,6 +114,11 @@ abstract class TemplateVars {
return ImmutableMap.copyOf(vars);
}
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + toVars();
+ }
+
static Template parsedTemplateForResource(String resourceName) {
try {
return Template.parseFrom(resourceName, TemplateVars::readerFromResource);
diff --git a/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java b/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java
index d1bbbab5..81968a50 100644
--- a/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java
+++ b/value/src/main/java/com/google/auto/value/processor/TypeEncoder.java
@@ -22,10 +22,12 @@ import static java.util.stream.Collectors.toList;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.auto.value.processor.MissingTypes.MissingTypeException;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
+import java.util.function.Function;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.TypeElement;
@@ -97,20 +99,36 @@ final class TypeEncoder {
* covers the details of annotation encoding.
*/
static String encodeWithAnnotations(TypeMirror type) {
- return encodeWithAnnotations(type, ImmutableSet.of());
+ return encodeWithAnnotations(type, ImmutableList.of(), ImmutableSet.of());
}
/**
* Encodes the given type and its type annotations. The class comment for {@link TypeEncoder}
* covers the details of annotation encoding.
*
+ * @param extraAnnotations additional type annotations to include with the type
* @param excludedAnnotationTypes annotations not to include in the encoding. For example, if
* {@code com.example.Nullable} is in this set then the encoding will not include this
* {@code @Nullable} annotation.
*/
- static String encodeWithAnnotations(TypeMirror type, Set<TypeMirror> excludedAnnotationTypes) {
+ static String encodeWithAnnotations(
+ TypeMirror type,
+ ImmutableList<AnnotationMirror> extraAnnotations,
+ Set<TypeMirror> excludedAnnotationTypes) {
StringBuilder sb = new StringBuilder();
- return new AnnotatedEncodingTypeVisitor(excludedAnnotationTypes).visit2(type, sb).toString();
+ // A function that is equivalent to t.getAnnotationMirrors() except when the t in question is
+ // our starting type. In that case we also add extraAnnotations to the result.
+ Function<TypeMirror, List<? extends AnnotationMirror>> getTypeAnnotations =
+ t ->
+ (t == type)
+ ? ImmutableList.<AnnotationMirror>builder()
+ .addAll(t.getAnnotationMirrors())
+ .addAll(extraAnnotations)
+ .build()
+ : t.getAnnotationMirrors();
+ return new AnnotatedEncodingTypeVisitor(excludedAnnotationTypes, getTypeAnnotations)
+ .visit2(type, sb)
+ .toString();
}
/**
@@ -145,9 +163,11 @@ final class TypeEncoder {
}
/**
- * Returns the formal type parameters of the given type. If we have {@code @AutoValue abstract
- * class Foo<T extends SomeClass>} then this method will return an encoding of {@code <T extends
- * SomeClass>} for {@code Foo}. Likewise it will return an encoding of the angle-bracket part of:
+ * Returns a string representing the given type parameters as they would appear in a class
+ * declaration. For example, if we have {@code @AutoValue abstract
+ * class Foo<T extends SomeClass>} then if we call {@link TypeElement#getTypeParameters()} on
+ * the representation of {@code Foo}, this method will return an encoding of {@code <T extends
+ * SomeClass>}. Likewise it will return an encoding of the angle-bracket part of:
* <br>
* {@code Foo<SomeClass>}<br>
* {@code Foo<T extends Number>}<br>
@@ -161,8 +181,7 @@ final class TypeEncoder {
* {@code <E extends `java.lang.Enum`<E>>}<br>
* {@code <K, V extends `java.lang.Comparable`<? extends K>>}.
*/
- static String formalTypeParametersString(TypeElement type) {
- List<? extends TypeParameterElement> typeParameters = type.getTypeParameters();
+ static String typeParametersString(List<? extends TypeParameterElement> typeParameters) {
if (typeParameters.isEmpty()) {
return "";
} else {
@@ -308,9 +327,13 @@ final class TypeEncoder {
*/
private static class AnnotatedEncodingTypeVisitor extends EncodingTypeVisitor {
private final Set<TypeMirror> excludedAnnotationTypes;
+ private final Function<TypeMirror, List<? extends AnnotationMirror>> getTypeAnnotations;
- AnnotatedEncodingTypeVisitor(Set<TypeMirror> excludedAnnotationTypes) {
+ AnnotatedEncodingTypeVisitor(
+ Set<TypeMirror> excludedAnnotationTypes,
+ Function<TypeMirror, List<? extends AnnotationMirror>> getTypeAnnotations) {
this.excludedAnnotationTypes = excludedAnnotationTypes;
+ this.getTypeAnnotations = getTypeAnnotations;
}
private void appendAnnotationsWithExclusions(
@@ -330,7 +353,7 @@ final class TypeEncoder {
@Override
public StringBuilder visitPrimitive(PrimitiveType type, StringBuilder sb) {
- appendAnnotationsWithExclusions(type.getAnnotationMirrors(), sb);
+ appendAnnotationsWithExclusions(getTypeAnnotations.apply(type), sb);
// We can't just append type.toString(), because that will also have the annotation, but
// without encoding.
return sb.append(type.getKind().toString().toLowerCase());
@@ -338,7 +361,7 @@ final class TypeEncoder {
@Override
public StringBuilder visitTypeVariable(TypeVariable type, StringBuilder sb) {
- appendAnnotationsWithExclusions(type.getAnnotationMirrors(), sb);
+ appendAnnotationsWithExclusions(getTypeAnnotations.apply(type), sb);
return sb.append(type.asElement().getSimpleName());
}
@@ -350,7 +373,7 @@ final class TypeEncoder {
@Override
public StringBuilder visitArray(ArrayType type, StringBuilder sb) {
visit2(type.getComponentType(), sb);
- List<? extends AnnotationMirror> annotationMirrors = type.getAnnotationMirrors();
+ List<? extends AnnotationMirror> annotationMirrors = getTypeAnnotations.apply(type);
if (!annotationMirrors.isEmpty()) {
sb.append(" ");
appendAnnotationsWithExclusions(annotationMirrors, sb);
@@ -360,7 +383,7 @@ final class TypeEncoder {
@Override
public StringBuilder visitDeclared(DeclaredType type, StringBuilder sb) {
- List<? extends AnnotationMirror> annotationMirrors = type.getAnnotationMirrors();
+ List<? extends AnnotationMirror> annotationMirrors = getTypeAnnotations.apply(type);
if (annotationMirrors.isEmpty()) {
super.visitDeclared(type, sb);
} else {
diff --git a/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java b/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java
index 290e59e0..e7bab774 100644
--- a/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java
+++ b/value/src/main/java/com/google/auto/value/processor/TypeSimplifier.java
@@ -140,8 +140,7 @@ final class TypeSimplifier {
if (typeParameters.isEmpty()) {
return "";
} else {
- return typeParameters
- .stream()
+ return typeParameters.stream()
.map(e -> e.getSimpleName().toString())
.collect(joining(", ", "<", ">"));
}
@@ -264,8 +263,7 @@ final class TypeSimplifier {
* {@code Map.Entry} everywhere rather than {@code Entry}.
*/
private static Set<TypeMirror> topLevelTypes(Types typeUtil, Set<TypeMirror> types) {
- return types
- .stream()
+ return types.stream()
.map(typeMirror -> MoreElements.asType(typeUtil.asElement(typeMirror)))
.map(typeElement -> topLevelType(typeElement).asType())
.collect(toCollection(TypeMirrorSet::new));
diff --git a/value/src/main/java/com/google/auto/value/processor/TypeVariables.java b/value/src/main/java/com/google/auto/value/processor/TypeVariables.java
index d664ff46..ae27f91b 100644
--- a/value/src/main/java/com/google/auto/value/processor/TypeVariables.java
+++ b/value/src/main/java/com/google/auto/value/processor/TypeVariables.java
@@ -15,6 +15,8 @@
*/
package com.google.auto.value.processor;
+import static com.google.auto.common.MoreStreams.toImmutableMap;
+
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
import com.google.common.base.Equivalence;
@@ -24,6 +26,7 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
@@ -38,9 +41,7 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.SimpleTypeVisitor8;
import javax.lang.model.util.Types;
-/**
- * Methods for handling type variables.
- */
+/** Methods for handling type variables. */
final class TypeVariables {
private TypeVariables() {}
@@ -60,13 +61,13 @@ final class TypeVariables {
* }
* </pre>
*
- * We want to be able to check that the parameter type of {@code setFoo} is the same as the
- * return type of {@code getFoo}. But in fact it isn't, because the {@code T} of {@code Foo<T>}
- * is not the same as the {@code T} of {@code Foo.Builder<T>}. So we create a parallel
- * {@code Foo<T>} where the {@code T} <i>is</i> the one from {@code Foo.Builder<T>}. That way the
- * types do correspond. This method then returns the return types of the given methods as they
- * appear in that parallel class, meaning the type given for {@code getFoo()} is the {@code T} of
- * {@code Foo.Builder<T>}.
+ * We want to be able to check that the parameter type of {@code setFoo} is the same as the return
+ * type of {@code getFoo}. But in fact it isn't, because the {@code T} of {@code Foo<T>} is not
+ * the same as the {@code T} of {@code Foo.Builder<T>}. So we create a parallel {@code Foo<T>}
+ * where the {@code T} <i>is</i> the one from {@code Foo.Builder<T>}. That way the types do
+ * correspond. This method then returns the return types of the given methods as they appear in
+ * that parallel class, meaning the type given for {@code getFoo()} is the {@code T} of {@code
+ * Foo.Builder<T>}.
*
* <p>We do the rewrite this way around (applying the type parameter from {@code Foo.Builder} to
* {@code Foo}) because if we hit one of the historical Eclipse bugs with {@link Types#asMemberOf}
@@ -101,14 +102,12 @@ final class TypeVariables {
}
DeclaredType parallelSource = typeUtils.getDeclaredType(sourceType, targetTypeParameterMirrors);
return methods.stream()
- .collect(
- ImmutableMap.toImmutableMap(
- m -> m, m -> eclipseHack.methodReturnType(m, parallelSource)));
+ .collect(toImmutableMap(m -> m, m -> eclipseHack.methodReturnType(m, parallelSource)));
}
/**
- * Tests whether a given parameter can be given to a static method like
- * {@code ImmutableMap.copyOf} to produce a value that can be assigned to the given target type.
+ * Tests whether a given parameter can be given to a static method like {@code
+ * ImmutableMap.copyOf} to produce a value that can be assigned to the given target type.
*
* <p>For example, suppose we have this method in {@code ImmutableMap}:<br>
* {@code static <K, V> ImmutableMap<K, V> copyOf(Map<? extends K, ? extends V>)}<br>
@@ -122,14 +121,14 @@ final class TypeVariables {
* We will infer {@code K=String}, {@code V=Number} based on the target type, and then rewrite the
* formal parameter type from<br>
* {@code Map<? extends K, ? extends V>} to<br>
- * {@code Map<? extends String, ? extends Number>}. Then we can check whether
- * {@code actualParameter} is assignable to that.
+ * {@code Map<? extends String, ? extends Number>}. Then we can check whether {@code
+ * actualParameter} is assignable to that.
*
* <p>The logic makes some simplifying assumptions, which are met for the {@code copyOf} and
* {@code of} methods that we use this for. The method must be static, it must have exactly one
* parameter, and it must have type parameters without bounds that are the same as the type
- * parameters of its return type. We can see that these assumptions are met for the
- * {@code ImmutableMap.copyOf} example above.
+ * parameters of its return type. We can see that these assumptions are met for the {@code
+ * ImmutableMap.copyOf} example above.
*/
static boolean canAssignStaticMethodResult(
ExecutableElement method,
@@ -152,8 +151,10 @@ final class TypeVariables {
TypeVariable v = MoreTypes.asTypeVariable(typeParameters.get(i).asType());
typeVariables.put(MoreTypes.equivalence().wrap(v), targetTypeArguments.get(i));
}
+ Function<TypeVariable, TypeMirror> substitute =
+ v -> typeVariables.get(MoreTypes.equivalence().wrap(v));
TypeMirror formalParameterType = method.getParameters().get(0).asType();
- SubstitutionVisitor substitutionVisitor = new SubstitutionVisitor(typeVariables, typeUtils);
+ SubstitutionVisitor substitutionVisitor = new SubstitutionVisitor(substitute, typeUtils);
TypeMirror substitutedParameterType = substitutionVisitor.visit(formalParameterType, null);
if (substitutedParameterType.getKind().equals(TypeKind.WILDCARD)) {
// If the target type is Optional<? extends Foo> then <T> T Optional.of(T) will give us
@@ -167,31 +168,38 @@ final class TypeVariables {
return typeUtils.isAssignable(actualParameterType, substitutedParameterType);
}
+ static TypeMirror substituteTypeVariables(
+ TypeMirror input, Function<TypeVariable, TypeMirror> substitute, Types typeUtils) {
+ SubstitutionVisitor substitutionVisitor = new SubstitutionVisitor(substitute, typeUtils);
+ return substitutionVisitor.visit(input, null);
+ }
+
/**
* Rewrites types such that references to type variables in the given map are replaced by the
* values of those variables.
*/
private static class SubstitutionVisitor extends SimpleTypeVisitor8<TypeMirror, Void> {
- private final Map<Equivalence.Wrapper<TypeVariable>, TypeMirror> typeVariables;
+ private final Function<TypeVariable, TypeMirror> substitute;
private final Types typeUtils;
- SubstitutionVisitor(
- Map<Equivalence.Wrapper<TypeVariable>, TypeMirror> typeVariables,
- Types typeUtils) {
- this.typeVariables = ImmutableMap.copyOf(typeVariables);
+ SubstitutionVisitor(Function<TypeVariable, TypeMirror> substitute, Types typeUtils) {
+ this.substitute = substitute;
this.typeUtils = typeUtils;
}
- @Override protected TypeMirror defaultAction(TypeMirror t, Void p) {
+ @Override
+ protected TypeMirror defaultAction(TypeMirror t, Void p) {
return t;
}
- @Override public TypeMirror visitTypeVariable(TypeVariable t, Void p) {
- TypeMirror substituted = typeVariables.get(MoreTypes.equivalence().wrap(t));
+ @Override
+ public TypeMirror visitTypeVariable(TypeVariable t, Void p) {
+ TypeMirror substituted = substitute.apply(t);
return (substituted == null) ? t : substituted;
}
- @Override public TypeMirror visitDeclared(DeclaredType t, Void p) {
+ @Override
+ public TypeMirror visitDeclared(DeclaredType t, Void p) {
List<? extends TypeMirror> typeArguments = t.getTypeArguments();
TypeMirror[] substitutedTypeArguments = new TypeMirror[typeArguments.size()];
for (int i = 0; i < typeArguments.size(); i++) {
@@ -201,7 +209,8 @@ final class TypeVariables {
MoreElements.asType(t.asElement()), substitutedTypeArguments);
}
- @Override public TypeMirror visitWildcard(WildcardType t, Void p) {
+ @Override
+ public TypeMirror visitWildcard(WildcardType t, Void p) {
TypeMirror ext = visitOrNull(t.getExtendsBound());
if (ext != null && ext.getKind().equals(TypeKind.WILDCARD)) {
// An example of where this happens is if we have this method in ImmutableSet:
@@ -216,7 +225,8 @@ final class TypeVariables {
return typeUtils.getWildcardType(ext, visitOrNull(t.getSuperBound()));
}
- @Override public TypeMirror visitArray(ArrayType t, Void p) {
+ @Override
+ public TypeMirror visitArray(ArrayType t, Void p) {
TypeMirror comp = visit(t.getComponentType());
if (comp.getKind().equals(TypeKind.WILDCARD)) {
// An example of where this happens is if we have this method in ImmutableSet:
diff --git a/value/src/main/java/com/google/auto/value/processor/autoannotation.vm b/value/src/main/java/com/google/auto/value/processor/autoannotation.vm
index a953edab..5cdad629 100644
--- a/value/src/main/java/com/google/auto/value/processor/autoannotation.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autoannotation.vm
@@ -45,7 +45,8 @@ package $pkg;
#else
// Generated by com.google.auto.value.processor.AutoAnnotationProcessor
#end
-final class $className implements $annotationName {
+final class $className implements $annotationName, `java.io.Serializable` {
+ private static final long serialVersionUID = ${serialVersionUID}L;
## Fields
@@ -212,7 +213,7 @@ final class $className implements $annotationName {
#end
@`java.lang.Override`
- public boolean equals(Object o) {
+ public boolean equals($equalsParameterType o) {
if (o == this) {
return true;
}
diff --git a/value/src/main/java/com/google/auto/value/processor/autobuilder.vm b/value/src/main/java/com/google/auto/value/processor/autobuilder.vm
new file mode 100644
index 00000000..01f61b9c
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/autobuilder.vm
@@ -0,0 +1,41 @@
+## Copyright 2014 Google LLC
+##
+## 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.
+
+## Template for each generated AutoValue_Foo class.
+## This template uses the Apache Velocity Template Language (VTL).
+## The variables ($pkg, $props, and so on) are defined by the fields of AutoBuilderTemplateVars.
+##
+## Comments, like this one, begin with ##. The comment text extends up to and including the newline
+## character at the end of the line. So comments also serve to join a line to the next one.
+## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there.
+## That does mean that we sometimes need an extra blank line after such a directive.
+##
+## Post-processing will remove unwanted spaces and blank lines, but will not join two lines.
+## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to
+## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not.
+
+#if (!$pkg.empty)
+package $pkg;
+#end
+
+## The following line will be replaced by the required imports during post-processing.
+`import`
+
+#if (!$generated.empty)
+@${generated}("com.google.auto.value.processor.AutoBuilderProcessor")
+#else
+// Generated by com.google.auto.value.processor.AutoBuilderProcessor
+#end
+#set($autoBuilder = true)
+#parse("builder.vm")
diff --git a/value/src/main/java/com/google/auto/value/processor/autooneof.vm b/value/src/main/java/com/google/auto/value/processor/autooneof.vm
index 0249d9eb..328f6a59 100644
--- a/value/src/main/java/com/google/auto/value/processor/autooneof.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autooneof.vm
@@ -94,19 +94,23 @@ final class $generatedClass {
#end
+#if (!$props.empty)
// Parent class that each implementation will inherit from.
private abstract static class Parent_$formalTypes extends $origClass$actualTypes {
-#foreach ($p in $props)
+ $serialVersionUID
+
+ #foreach ($p in $props)
@`java.lang.Override`
$p.access $p.type ${p.getter}() {
throw new UnsupportedOperationException(${kindGetter}().toString());
}
-#end
+ #end
}
+#end
#foreach ($p in $props)
@@ -120,6 +124,8 @@ final class $generatedClass {
// Implementation when the contained property is "${p}".
private static final class Impl_$p$formalTypes extends Parent_$actualTypes {
+ $serialVersionUID
+
#if ($p.type == "void")
// There is only one instance of this class.
diff --git a/value/src/main/java/com/google/auto/value/processor/autovalue.vm b/value/src/main/java/com/google/auto/value/processor/autovalue.vm
index 6f50e934..86cfe493 100644
--- a/value/src/main/java/com/google/auto/value/processor/autovalue.vm
+++ b/value/src/main/java/com/google/auto/value/processor/autovalue.vm
@@ -177,9 +177,7 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes {
}
#end
-#if (!$serialVersionUID.empty)
- private static final long serialVersionUID = $serialVersionUID;
-#end
+ $serialVersionUID
#if ($builderTypeName != "")
@@ -194,241 +192,8 @@ ${modifiers}class $subclass$formalTypes extends $origClass$actualTypes {
## BUILDER CLASS
- #foreach ($a in $builderAnnotations)
-
- $a##
- #end
-
- static #if ($isFinal) final #end class Builder${builderFormalTypes} ##
- #if ($builderIsInterface) implements #else extends #end
- ${builderTypeName}${builderActualTypes} {
-
- #foreach ($p in $props)
-
- #if ($p.kind.primitive)
-
- private $types.boxedClass($p.typeMirror).simpleName $p;
-
- #else
-
- #if ($builderPropertyBuilders[$p.name])
- ## If you have ImmutableList.Builder<String> stringsBuilder() then we define two fields:
- ## private ImmutableList.Builder<String> stringsBuilder$;
- ## private ImmutableList<String> strings;
-
- private ${builderPropertyBuilders[$p.name].builderType} ##
- ${builderPropertyBuilders[$p.name].name};
-
- #end
-
- private $p.type $p #if ($p.optional && !$p.nullable) = $p.optional.empty #end ;
-
- #end
- #end
-
- Builder() {
- }
-
- #if (!$toBuilderMethods.empty)
-
- private Builder(${origClass}${actualTypes} source) {
-
- #foreach ($p in $props)
-
- this.$p = source.${p.getter}();
-
- #end
-
- }
-
- #end
-
- #foreach ($p in $props)
-
- ## The following is either null or an instance of PropertyBuilderClassifier.PropertyBuilder
- #set ($propertyBuilder = $builderPropertyBuilders[$p.name])
-
- ## Setter and/or property builder
-
- #foreach ($setter in $builderSetters[$p.name])
-
- @`java.lang.Override`
- ${setter.access}${builderTypeName}${builderActualTypes} ##
- ${setter.name}(${setter.nullableAnnotation}$setter.parameterType $p) {
-
- ## Omit null check for primitive, or @Nullable, or if we are going to be calling a copy method
- ## such as Optional.of, which will have its own null check if appropriate.
- #if (!$setter.primitiveParameter && !$p.nullable && ${setter.copy($p)} == $p)
-
- #if ($identifiers)
-
- if ($p == null) {
- throw new NullPointerException("Null $p.name");
- }
- #else
- ## Just throw NullPointerException with no message if it's null.
- ## The Object cast has no effect on the code but silences an ErrorProne warning.
-
- ((`java.lang.Object`) ${p}).getClass();
- #end
-
- #end
-
- #if ($propertyBuilder)
-
- if (${propertyBuilder.name} != null) {
- throw new IllegalStateException(#if ($identifiers)"Cannot set $p after calling ${p.name}Builder()"#end);
- }
-
- #end
-
- this.$p = ${setter.copy($p)};
- return this;
- }
-
- #end
-
- #if ($propertyBuilder)
-
- @`java.lang.Override`
- ${propertyBuilder.access}$propertyBuilder.builderType ${p.name}Builder() {
- if (${propertyBuilder.name} == null) {
-
- ## This is the first time someone has asked for the builder. If the property it sets already
- ## has a value (because it came from a toBuilder() call on the AutoValue class, or because
- ## there is also a setter for this property) then we copy that value into the builder.
- ## Otherwise the builder starts out empty.
- ## If we have neither a setter nor a toBuilder() method, then the builder always starts
- ## off empty.
-
- #if ($builderSetters[$p.name].empty && $toBuilderMethods.empty)
-
- ${propertyBuilder.name} = ${propertyBuilder.initializer};
-
- #else
-
- if ($p == null) {
- ${propertyBuilder.name} = ${propertyBuilder.initializer};
- } else {
-
- #if (${propertyBuilder.builtToBuilder})
-
- ${propertyBuilder.name} = ${p}.${propertyBuilder.builtToBuilder}();
-
- #else
-
- ${propertyBuilder.name} = ${propertyBuilder.initializer};
- ${propertyBuilder.name}.${propertyBuilder.copyAll}($p);
-
- #end
-
- $p = null;
- }
-
- #end
-
- }
- return $propertyBuilder.name;
- }
-
- #end
-
- ## Getter
-
- #if ($builderGetters[$p.name])
-
- @`java.lang.Override`
- ${p.nullableAnnotation}${builderGetters[$p.name].access}$builderGetters[$p.name].type ${p.getter}() {
- #if ($builderGetters[$p.name].optional)
-
- if ($p == null) {
- return $builderGetters[$p.name].optional.empty;
- } else {
- return ${builderGetters[$p.name].optional.rawType}.of($p);
- }
-
- #else
- #if ($builderRequiredProperties.contains($p))
-
- if ($p == null) {
- throw new IllegalStateException(#if ($identifiers)"Property \"$p.name\" has not been set"#end);
- }
-
- #end
-
- #if ($propertyBuilder)
-
- if (${propertyBuilder.name} != null) {
- return ${propertyBuilder.name}.build();
- }
- if ($p == null) {
- ${propertyBuilder.beforeInitDefault}
- $p = ${propertyBuilder.initDefault};
- }
-
- #end
-
- return $p;
-
- #end
-
- }
-
- #end
- #end
-
- @`java.lang.Override`
- ${buildMethod.get().access}${origClass}${actualTypes} ${buildMethod.get().name}() {
-
- #foreach ($p in $props)
- #set ($propertyBuilder = $builderPropertyBuilders[$p.name])
- #if ($propertyBuilder)
-
- if (${propertyBuilder.name} != null) {
- this.$p = ${propertyBuilder.name}.build();
- } else if (this.$p == null) {
- ${propertyBuilder.beforeInitDefault}
- this.$p = ${propertyBuilder.initDefault};
- }
-
- #end
- #end
-
- #if (!$builderRequiredProperties.empty)
- #if ($identifiers) ## build a friendly message showing all missing properties
-
- `java.lang.String` missing = "";
-
- #foreach ($p in $builderRequiredProperties)
-
- if (this.$p == null) {
- missing += " $p.name";
- }
-
- #end
-
- if (!missing.isEmpty()) {
- throw new IllegalStateException("Missing required properties:" + missing);
- }
-
- #else ## just throw an exception if anything is missing
-
- if (#foreach ($p in $builderRequiredProperties)##
- this.$p == null##
- #if ($foreach.hasNext) || #end
- #end) {
- throw new IllegalStateException();
- }
- #end
- #end
-
- return new ${finalSubclass}${actualTypes}(
- #foreach ($p in $props)
-
- this.$p #if ($foreach.hasNext) , #end
- #end );
- }
- }
+ #set($autoBuilder = false)
+ #parse("builder.vm")
#end
}
diff --git a/value/src/main/java/com/google/auto/value/processor/builder.vm b/value/src/main/java/com/google/auto/value/processor/builder.vm
new file mode 100644
index 00000000..630330ca
--- /dev/null
+++ b/value/src/main/java/com/google/auto/value/processor/builder.vm
@@ -0,0 +1,285 @@
+## Copyright 2014 Google LLC
+##
+## 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.
+##
+##
+##
+## Template for AutoValue and AutoBuilder builders.
+## This template uses the Apache Velocity Template Language (VTL).
+## The variables ($isFinal, $props, and so on) are defined by the fields of AutoValueOrBuilderTemplateVars.
+##
+## Comments, like this one, begin with ##. The comment text extends up to and including the newline
+## character at the end of the line. So comments also serve to join a line to the next one.
+## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there.
+## That does mean that we sometimes need an extra blank line after such a directive.
+##
+## Post-processing will remove unwanted spaces and blank lines, but will not join two lines.
+## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to
+## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not.
+##
+#foreach ($a in $builderAnnotations)
+$a
+#end
+#if (!$autoBuilder) static #end##
+#if ($isFinal) final #end##
+class ${builderName}${builderFormalTypes} ##
+#if ($builderIsInterface) implements #else extends #end
+ ${builderTypeName}${builderActualTypes} {
+
+#foreach ($p in $props)
+
+ #if ($p.kind.primitive)
+
+ private $types.boxedClass($p.typeMirror).simpleName $p;
+
+ #else
+
+ #if ($builderPropertyBuilders[$p.name])
+ ## If you have ImmutableList.Builder<String> stringsBuilder() then we define two fields:
+ ## private ImmutableList.Builder<String> stringsBuilder$;
+ ## private ImmutableList<String> strings;
+
+ private ${builderPropertyBuilders[$p.name].builderType} ##
+ ${builderPropertyBuilders[$p.name].name};
+
+ #end
+
+ private $p.type $p #if ($p.optional && !$p.nullable) = $p.optional.empty #end ;
+
+ #end
+#end
+
+ ${builderName}() {
+ }
+
+#if ($toBuilderConstructor)
+
+ private ${builderName}(${origClass}${actualTypes} source) {
+
+ #foreach ($p in $props)
+
+ this.$p = source.${p.getter}();
+
+ #end
+
+ }
+
+#end
+
+#foreach ($p in $props)
+
+ ## The following is either null or an instance of PropertyBuilderClassifier.PropertyBuilder
+ #set ($propertyBuilder = $builderPropertyBuilders[$p.name])
+
+ ## Setter and/or property builder
+
+ #foreach ($setter in $builderSetters[$p.name])
+
+ @`java.lang.Override`
+ ${setter.access}${builderTypeName}${builderActualTypes} ##
+ ${setter.name}(${setter.nullableAnnotation}$setter.parameterType $p) {
+
+ ## Omit null check for primitive, or @Nullable, or if we are going to be calling a copy method
+ ## such as Optional.of, which will have its own null check if appropriate.
+ #if (!$setter.primitiveParameter && !$p.nullable && ${setter.copy($p)} == $p)
+
+ #if ($identifiers)
+
+ if ($p == null) {
+ throw new NullPointerException("Null $p.name");
+ }
+ #else
+ ## Just throw NullPointerException with no message if it's null.
+ ## The Object cast has no effect on the code but silences an ErrorProne warning.
+
+ ((`java.lang.Object`) ${p}).getClass();
+ #end
+
+ #end
+
+ #if ($propertyBuilder)
+
+ if (${propertyBuilder.name} != null) {
+ throw new IllegalStateException(#if ($identifiers)"Cannot set $p after calling ${p.name}Builder()"#end);
+ }
+
+ #end
+
+ this.$p = ${setter.copy($p)};
+ return this;
+ }
+
+ #end
+
+ #if ($propertyBuilder)
+
+ @`java.lang.Override`
+ ${propertyBuilder.access}$propertyBuilder.builderType ${p.name}Builder($propertyBuilder.propertyBuilderMethodParameters) {
+ if (${propertyBuilder.name} == null) {
+
+ ## This is the first time someone has asked for the builder. If the property it sets already
+ ## has a value (because it came from a toBuilder() call on the AutoValue class, or because
+ ## there is also a setter for this property) then we copy that value into the builder.
+ ## Otherwise the builder starts out empty.
+ ## If we have neither a setter nor a toBuilder() method, then the builder always starts
+ ## off empty.
+
+ #if ($builderSetters[$p.name].empty && $toBuilderMethods.empty)
+
+ ${propertyBuilder.name} = ${propertyBuilder.initializer};
+
+ #else
+
+ if ($p == null) {
+ ${propertyBuilder.name} = ${propertyBuilder.initializer};
+ } else {
+
+ #if (${propertyBuilder.builtToBuilder})
+
+ ${propertyBuilder.name} = ${p}.${propertyBuilder.builtToBuilder}();
+
+ #else
+
+ ${propertyBuilder.name} = ${propertyBuilder.initializer};
+ ${propertyBuilder.name}.${propertyBuilder.copyAll}($p);
+
+ #end
+
+ $p = null;
+ }
+
+ #end
+
+ } #if (!$propertyBuilder.propertyBuilderMethodParameters.empty) else {
+ ## This check only happens if the property-builder method has a parameter.
+ ## We don't know if the existing builder was created with the same parameter,
+ ## so we throw to avoid possibly giving you a builder that is different from
+ ## the one you asked for.
+
+ throw new IllegalStateException("Property builder for $p.name is already defined");
+ }
+ #end
+
+ return $propertyBuilder.name;
+ }
+
+ #end
+
+ ## Getter
+
+ #if ($builderGetters[$p.name])
+
+ @`java.lang.Override`
+ ${p.nullableAnnotation}${builderGetters[$p.name].access}$builderGetters[$p.name].type ${p.getter}() {
+ #if ($builderGetters[$p.name].optional)
+
+ if ($p == null) {
+ return $builderGetters[$p.name].optional.empty;
+ } else {
+ return ${builderGetters[$p.name].optional.rawType}.of($p);
+ }
+
+ #else
+ #if ($builderRequiredProperties.contains($p))
+
+ if ($p == null) {
+ throw new IllegalStateException(#if ($identifiers)"Property \"$p.name\" has not been set"#end);
+ }
+
+ #end
+
+ #if ($propertyBuilder)
+
+ if (${propertyBuilder.name} != null) {
+ return ${propertyBuilder.name}.build();
+ }
+ if ($p == null) {
+ ${propertyBuilder.beforeInitDefault}
+ $p = ${propertyBuilder.initDefault};
+ }
+
+ #end
+
+ return $p;
+
+ #end
+
+ }
+
+ #end
+#end
+
+## build() method
+
+ @`java.lang.Override`
+ ${buildMethod.get().access}${builtType} ${buildMethod.get().name}() ${buildMethod.get().throws} {
+
+#foreach ($p in $props)
+ #set ($propertyBuilder = $builderPropertyBuilders[$p.name])
+ #if ($propertyBuilder)
+
+ if (${propertyBuilder.name} != null) {
+ this.$p = ${propertyBuilder.name}.build();
+ } else if (this.$p == null) {
+ ${propertyBuilder.beforeInitDefault}
+ this.$p = ${propertyBuilder.initDefault};
+ }
+
+ #end
+#end
+
+#if (!$builderRequiredProperties.empty)
+ if (#foreach ($p in $builderRequiredProperties)##
+ this.$p == null##
+ #if ($foreach.hasNext)
+
+ || #end
+ #end) {
+
+ #if ($identifiers) ## build a friendly message showing all missing properties
+ #if ($builderRequiredProperties.size() == 1)
+
+ `java.lang.String` missing = " $builderRequiredProperties.iterator().next()";
+
+ #else
+
+ `java.lang.StringBuilder` missing = new `java.lang.StringBuilder`();
+
+ #foreach ($p in $builderRequiredProperties)
+
+ if (this.$p == null) {
+ missing.append(" $p.name");
+ }
+
+ #end
+ #end
+
+ throw new IllegalStateException("Missing required properties:" + missing);
+
+ #else ## just throw an exception if anything is missing
+
+ throw new IllegalStateException();
+
+ #end
+
+ }
+
+#end
+
+ #if ($builtType != "void") return #end ${build}(
+#foreach ($p in $props)
+
+ this.$p #if ($foreach.hasNext) , #end
+#end );
+ }
+}
diff --git a/value/src/main/java/com/google/auto/value/processor/equalshashcode.vm b/value/src/main/java/com/google/auto/value/processor/equalshashcode.vm
index 094f6c3c..03e25c2b 100644
--- a/value/src/main/java/com/google/auto/value/processor/equalshashcode.vm
+++ b/value/src/main/java/com/google/auto/value/processor/equalshashcode.vm
@@ -49,7 +49,7 @@
this.$p == that.${p.getter}() ##
#elseif ($p.kind == "ARRAY")
`java.util.Arrays`.equals(this.$p, ##
- (that instanceof $subclass) ? (($subclass) that).$p : that.${p.getter}()) ##
+ (that instanceof $subclass) ? (($subclass$wildcardTypes) that).$p : that.${p.getter}()) ##
#elseif ($p.nullable)
(this.$p == null ? that.${p.getter}() == null : this.${p}.equals(that.${p.getter}())) ##
#else
diff --git a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java
index 18c77368..5d1462d0 100644
--- a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java
+++ b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedMethodSubject.java
@@ -51,9 +51,6 @@ final class MemoizedMethodSubject extends Subject {
javac()
.withProcessors(new AutoValueProcessor(ImmutableList.of(new MemoizeExtension())))
.compile(file);
- assertThat(compilation)
- .hadErrorContaining(error)
- .inFile(file)
- .onLineContaining(actual);
+ assertThat(compilation).hadErrorContaining(error).inFile(file).onLineContaining(actual);
}
}
diff --git a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java
index 7bd61ac7..5beb686b 100644
--- a/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java
+++ b/value/src/test/java/com/google/auto/value/extension/memoized/MemoizedTest.java
@@ -22,6 +22,8 @@ import com.google.auto.value.AutoValue;
import com.google.auto.value.AutoValue.CopyAnnotations;
import com.google.auto.value.extension.memoized.MemoizedTest.HashCodeEqualsOptimization.EqualsCounter;
import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.Immutable;
+import com.google.errorprone.annotations.ImmutableTypeParameter;
import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@@ -123,7 +125,9 @@ public class MemoizedTest {
@org.checkerframework.checker.nullness.qual.Nullable
String nullableWithTypeAnnotation() {
nullableWithTypeAnnotationCount++;
- return "nullable derived " + stringWithTypeAnnotation() + " "
+ return "nullable derived "
+ + stringWithTypeAnnotation()
+ + " "
+ nullableWithTypeAnnotationCount;
}
@@ -234,8 +238,9 @@ public class MemoizedTest {
@Before
public void setUp() {
- value = new AutoValue_MemoizedTest_Value(
- "string", "stringWithTypeAnnotation", new HashCodeAndToStringCounter());
+ value =
+ new AutoValue_MemoizedTest_Value(
+ "string", "stringWithTypeAnnotation", new HashCodeAndToStringCounter());
listValue = new AutoValue_MemoizedTest_ListValue<Integer, String>(0, "hello");
}
@@ -375,8 +380,9 @@ public class MemoizedTest {
Method nullable =
AutoValue_MemoizedTest_Value.class.getDeclaredMethod("nullableWithTypeAnnotation");
AnnotatedType returnType = nullable.getAnnotatedReturnType();
- assertThat(returnType.isAnnotationPresent(
- org.checkerframework.checker.nullness.qual.Nullable.class))
+ assertThat(
+ returnType.isAnnotationPresent(
+ org.checkerframework.checker.nullness.qual.Nullable.class))
.isTrue();
}
@@ -387,11 +393,13 @@ public class MemoizedTest {
// [1] @org.checkerframework.checker.nullness.qual.Nullable String stringWithTypeAnnotation,
// [2] HashCodeAndToStringCounter counter
// We don't currently copy @javax.annotation.Nullable because it is not a TYPE_USE annotation.
- Constructor<?> constructor = AutoValue_MemoizedTest_Value.class.getDeclaredConstructor(
- String.class, String.class, HashCodeAndToStringCounter.class);
+ Constructor<?> constructor =
+ AutoValue_MemoizedTest_Value.class.getDeclaredConstructor(
+ String.class, String.class, HashCodeAndToStringCounter.class);
AnnotatedType paramType = constructor.getAnnotatedParameterTypes()[1];
- assertThat(paramType.isAnnotationPresent(
- org.checkerframework.checker.nullness.qual.Nullable.class))
+ assertThat(
+ paramType.isAnnotationPresent(
+ org.checkerframework.checker.nullness.qual.Nullable.class))
.isTrue();
}
@@ -448,8 +456,8 @@ public class MemoizedTest {
assertThat(memoizedHashCodeAndFinalEqualsMethod.equals(second)).isTrue();
assertThat(memoizedHashCodeAndFinalEqualsMethod.hashCodeCount).isEqualTo(0);
- memoizedHashCodeAndFinalEqualsMethod.hashCode();
- memoizedHashCodeAndFinalEqualsMethod.hashCode();
+ int unused1 = memoizedHashCodeAndFinalEqualsMethod.hashCode();
+ int unused2 = memoizedHashCodeAndFinalEqualsMethod.hashCode();
assertThat(memoizedHashCodeAndFinalEqualsMethod.hashCodeCount).isEqualTo(1);
}
@@ -465,8 +473,7 @@ public class MemoizedTest {
@AutoValue
abstract static class ResourceUriPath<InputT> extends AbstractTypePath<InputT, ResourceUri> {
- static <InputT> ResourceUriPath<InputT> create(
- TypeEdgeIterable<InputT, ResourceUri> edges) {
+ static <InputT> ResourceUriPath<InputT> create(TypeEdgeIterable<InputT, ResourceUri> edges) {
return new AutoValue_MemoizedTest_ResourceUriPath<>(edges);
}
@@ -482,4 +489,27 @@ public class MemoizedTest {
ResourceUriPath.create(new TypeEdgeIterable<String, ResourceUri>() {});
assertThat(path.edges()).isNotNull();
}
+
+ @Immutable
+ @AutoValue
+ abstract static class Unchanging<@ImmutableTypeParameter T> {
+ abstract T value();
+
+ @Override
+ @Memoized
+ public abstract int hashCode();
+
+ static <@ImmutableTypeParameter T> Unchanging<T> of(T value) {
+ return new AutoValue_MemoizedTest_Unchanging<T>(value);
+ }
+ }
+
+ @Test
+ public void copiedTypeAnnotations() {
+ for (Class<?> c = Unchanging.of("foo").getClass(); c != Object.class; c = c.getSuperclass()) {
+ assertThat(c.getTypeParameters()).hasLength(1);
+ assertThat(c.getTypeParameters()[0].isAnnotationPresent(ImmutableTypeParameter.class))
+ .isTrue();
+ }
+ }
}
diff --git a/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java b/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java
index 1879d1e5..064406da 100644
--- a/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java
+++ b/value/src/test/java/com/google/auto/value/extension/serializable/processor/SerializableAutoValueExtensionTest.java
@@ -415,4 +415,38 @@ public final class SerializableAutoValueExtensionTest {
assertThat(actualAutoValue).isEqualTo(autoValue);
}
+
+ /**
+ * Type that may result in nested lambdas in the generated code. Including this allows us to
+ * verify that we handle those correctly, in particular not reusing a lambda parameter name in
+ * another lambda nested inside the first one.
+ */
+ @SerializableAutoValue
+ @AutoValue
+ abstract static class ComplexType implements Serializable {
+ abstract ImmutableMap<String, ImmutableMap<String, Optional<String>>> a();
+
+ static ComplexType.Builder builder() {
+ return new AutoValue_SerializableAutoValueExtensionTest_ComplexType.Builder();
+ }
+
+ @AutoValue.Builder
+ abstract static class Builder {
+ abstract ComplexType.Builder setA(
+ ImmutableMap<String, ImmutableMap<String, Optional<String>>> a);
+
+ abstract ComplexType build();
+ }
+ }
+
+ @Test
+ public void complexType() {
+ ImmutableMap<String, ImmutableMap<String, Optional<String>>> map =
+ ImmutableMap.of("foo", ImmutableMap.of("bar", Optional.of("baz")));
+ ComplexType complexType = ComplexType.builder().setA(map).build();
+
+ ComplexType reserialized = SerializableTester.reserialize(complexType);
+
+ assertThat(reserialized).isEqualTo(complexType);
+ }
}
diff --git a/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java b/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java
index 388977fb..162d7cf6 100644
--- a/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java
+++ b/value/src/test/java/com/google/auto/value/extension/serializable/serializer/utils/FakeSerializerFactory.java
@@ -42,6 +42,15 @@ public final class FakeSerializerFactory implements SerializerFactory {
return new FakeIdentitySerializer(type, isIdentity);
}
+ // This doesn't follow the contract, and always returns the same string for a given prefix.
+ // That means it will be wrong if two identifiers with the same prefix are in the same scope in
+ // the generated code, but for our purposes in this fake it is OK, and means we don't have to
+ // hardwire knowledge of the uniqueness algorithm into golden text in tests.
+ @Override
+ public CodeBlock newIdentifier(String prefix) {
+ return CodeBlock.of("$L$$", prefix);
+ }
+
private static class FakeIdentitySerializer implements Serializer {
private final TypeMirror typeMirror;
diff --git a/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java
new file mode 100644
index 00000000..f5197472
--- /dev/null
+++ b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringTest.java
@@ -0,0 +1,961 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.toprettystring.ToPrettyStringTest.CollectionSubtypesWithFixedTypeParameters.StringList;
+import com.google.auto.value.extension.toprettystring.ToPrettyStringTest.CollectionSubtypesWithFixedTypeParameters.StringMap;
+import com.google.auto.value.extension.toprettystring.ToPrettyStringTest.PropertyHasToPrettyString.HasToPrettyString;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.primitives.ImmutableIntArray;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@SuppressWarnings("AutoValueImmutableFields")
+@RunWith(JUnit4.class)
+public class ToPrettyStringTest {
+ @AutoValue
+ abstract static class Primitives {
+ abstract int i();
+
+ abstract long l();
+
+ abstract byte b();
+
+ abstract short s();
+
+ abstract char c();
+
+ abstract float f();
+
+ abstract double d();
+
+ abstract boolean bool();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void primitives() {
+ Primitives valueType =
+ new AutoValue_ToPrettyStringTest_Primitives(
+ 1, 2L, (byte) 3, (short) 4, 'C', 6.6f, 7.7, false);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "Primitives {"
+ + "\n i = 1,"
+ + "\n l = 2,"
+ + "\n b = 3,"
+ + "\n s = 4,"
+ + "\n c = C,"
+ + "\n f = 6.6,"
+ + "\n d = 7.7,"
+ + "\n bool = false,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class PrimitiveArray {
+ @Nullable
+ @SuppressWarnings("mutable")
+ abstract long[] longs();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void primitiveArray() {
+ PrimitiveArray valueType =
+ new AutoValue_ToPrettyStringTest_PrimitiveArray(new long[] {1L, 2L, 10L, 200L});
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrimitiveArray {"
+ + "\n longs = ["
+ + "\n 1,"
+ + "\n 2,"
+ + "\n 10,"
+ + "\n 200,"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void primitiveArray_empty() {
+ PrimitiveArray valueType = new AutoValue_ToPrettyStringTest_PrimitiveArray(new long[0]);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrimitiveArray {" // force newline
+ + "\n longs = [],"
+ + "\n}");
+ }
+
+ @Test
+ public void primitiveArray_null() {
+ PrimitiveArray valueType = new AutoValue_ToPrettyStringTest_PrimitiveArray(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrimitiveArray {" // force newline
+ + "\n longs = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class PrettyCollection {
+ @Nullable
+ abstract Collection<Object> collection();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void prettyCollection() {
+ PrettyCollection valueType =
+ new AutoValue_ToPrettyStringTest_PrettyCollection(ImmutableList.of("hello", "world"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyCollection {"
+ + "\n collection = ["
+ + "\n hello,"
+ + "\n world,"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyCollection_elementsWithNewlines() {
+ PrettyCollection valueType =
+ new AutoValue_ToPrettyStringTest_PrettyCollection(
+ ImmutableList.of("hello\nworld\nnewline"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyCollection {"
+ + "\n collection = ["
+ + "\n hello"
+ + "\n world"
+ + "\n newline,"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyCollection_empty() {
+ PrettyCollection valueType =
+ new AutoValue_ToPrettyStringTest_PrettyCollection(ImmutableList.of());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyCollection {" // force newline
+ + "\n collection = [],"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyCollection_null() {
+ PrettyCollection valueType = new AutoValue_ToPrettyStringTest_PrettyCollection(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyCollection {" // force newline
+ + "\n collection = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class NestedCollection {
+ @Nullable
+ abstract Collection<Collection<Object>> nestedCollection();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void nestedCollection() {
+ NestedCollection valueType =
+ new AutoValue_ToPrettyStringTest_NestedCollection(
+ Arrays.asList(
+ ImmutableList.of("hello", "world"),
+ ImmutableList.of("hello2", "world2"),
+ null,
+ Arrays.asList("not null", null)));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "NestedCollection {"
+ + "\n nestedCollection = ["
+ + "\n ["
+ + "\n hello,"
+ + "\n world,"
+ + "\n ],"
+ + "\n ["
+ + "\n hello2,"
+ + "\n world2,"
+ + "\n ],"
+ + "\n null,"
+ + "\n ["
+ + "\n not null,"
+ + "\n null,"
+ + "\n ],"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void nestedCollection_elementsWithNewlines() {
+ NestedCollection valueType =
+ new AutoValue_ToPrettyStringTest_NestedCollection(
+ ImmutableList.of(
+ ImmutableList.of((Object) "hello\nworld\nnewline", "hello2\nworld2\nnewline2")));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "NestedCollection {"
+ + "\n nestedCollection = ["
+ + "\n ["
+ + "\n hello"
+ + "\n world"
+ + "\n newline,"
+ + "\n hello2"
+ + "\n world2"
+ + "\n newline2,"
+ + "\n ],"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void nestedCollection_empty() {
+ NestedCollection valueType =
+ new AutoValue_ToPrettyStringTest_NestedCollection(ImmutableList.of());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "NestedCollection {" // force newline
+ + "\n nestedCollection = [],"
+ + "\n}");
+ }
+
+ @Test
+ public void nestedCollection_nestedEmpty() {
+ NestedCollection valueType =
+ new AutoValue_ToPrettyStringTest_NestedCollection(
+ ImmutableList.of(ImmutableList.of(), ImmutableList.of()));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "NestedCollection {"
+ + "\n nestedCollection = ["
+ + "\n [],"
+ + "\n [],"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void nestedCollection_null() {
+ NestedCollection valueType = new AutoValue_ToPrettyStringTest_NestedCollection(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "NestedCollection {" // force newline
+ + "\n nestedCollection = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class ImmutablePrimitiveArray {
+ @Nullable
+ abstract ImmutableIntArray immutableIntArray();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void immutablePrimitiveArray() {
+ ImmutablePrimitiveArray valueType =
+ new AutoValue_ToPrettyStringTest_ImmutablePrimitiveArray(ImmutableIntArray.of(1, 2));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "ImmutablePrimitiveArray {"
+ + "\n immutableIntArray = ["
+ + "\n 1,"
+ + "\n 2,"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @Test
+ public void immutablePrimitiveArray_empty() {
+ ImmutablePrimitiveArray valueType =
+ new AutoValue_ToPrettyStringTest_ImmutablePrimitiveArray(ImmutableIntArray.of());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "ImmutablePrimitiveArray {" // force newline
+ + "\n immutableIntArray = [],"
+ + "\n}");
+ }
+
+ @Test
+ public void immutablePrimitiveArray_null() {
+ ImmutablePrimitiveArray valueType =
+ new AutoValue_ToPrettyStringTest_ImmutablePrimitiveArray(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "ImmutablePrimitiveArray {" // force newline
+ + "\n immutableIntArray = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class PrettyMap {
+ @Nullable
+ abstract Map<Object, Object> map();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void prettyMap() {
+ PrettyMap valueType = new AutoValue_ToPrettyStringTest_PrettyMap(ImmutableMap.of(1, 2));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMap {" // force newline
+ + "\n map = {"
+ + "\n 1: 2,"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyMap_keysAndValuesWithNewlines() {
+ PrettyMap valueType =
+ new AutoValue_ToPrettyStringTest_PrettyMap(
+ ImmutableMap.of(
+ "key1\nnewline", "value1\nnewline", "key2\nnewline", "value2\nnewline"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMap {"
+ + "\n map = {"
+ + "\n key1"
+ + "\n newline: value1"
+ + "\n newline,"
+ + "\n key2"
+ + "\n newline: value2"
+ + "\n newline,"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyMap_empty() {
+ PrettyMap valueType = new AutoValue_ToPrettyStringTest_PrettyMap(ImmutableMap.of());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMap {" // force newline
+ + "\n map = {},"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyMap_null() {
+ PrettyMap valueType = new AutoValue_ToPrettyStringTest_PrettyMap(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMap {" // force newline
+ + "\n map = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class MapOfMaps {
+ @Nullable
+ abstract Map<Map<Object, Object>, Map<Object, Object>> mapOfMaps();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ private static <K, V> Map<K, V> mapWithNulls(K k, V v) {
+ Map<K, V> map = new LinkedHashMap<>();
+ map.put(k, v);
+ return map;
+ }
+
+ @Test
+ public void mapOfMaps() {
+ Map<Map<Object, Object>, Map<Object, Object>> mapOfMaps = new LinkedHashMap<>();
+ mapOfMaps.put(ImmutableMap.of("k1_k", "k1_v"), ImmutableMap.of("v1_k", "v1_v"));
+ mapOfMaps.put(ImmutableMap.of("k2_k", "k2_v"), ImmutableMap.of("v2_k", "v2_v"));
+ mapOfMaps.put(mapWithNulls("keyForNullValue", null), mapWithNulls(null, "valueForNullKey"));
+ mapOfMaps.put(null, ImmutableMap.of("nullKeyKey", "nullKeyValue"));
+ mapOfMaps.put(ImmutableMap.of("nullValueKey", "nullValueValue"), null);
+ mapOfMaps.put(
+ ImmutableMap.of("keyForMapOfNullsKey", "keyForMapOfNullsValue"), mapWithNulls(null, null));
+ MapOfMaps valueType = new AutoValue_ToPrettyStringTest_MapOfMaps(mapOfMaps);
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "MapOfMaps {"
+ + "\n mapOfMaps = {"
+ + "\n {"
+ + "\n k1_k: k1_v,"
+ + "\n }: {"
+ + "\n v1_k: v1_v,"
+ + "\n },"
+ + "\n {"
+ + "\n k2_k: k2_v,"
+ + "\n }: {"
+ + "\n v2_k: v2_v,"
+ + "\n },"
+ + "\n {"
+ + "\n keyForNullValue: null,"
+ + "\n }: {"
+ + "\n null: valueForNullKey,"
+ + "\n },"
+ + "\n null: {"
+ + "\n nullKeyKey: nullKeyValue,"
+ + "\n },"
+ + "\n {"
+ + "\n nullValueKey: nullValueValue,"
+ + "\n }: null,"
+ + "\n {"
+ + "\n keyForMapOfNullsKey: keyForMapOfNullsValue,"
+ + "\n }: {"
+ + "\n null: null,"
+ + "\n },"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void mapOfMaps_elementsWithNewlines() {
+ MapOfMaps valueType =
+ new AutoValue_ToPrettyStringTest_MapOfMaps(
+ ImmutableMap.of(
+ ImmutableMap.of((Object) "k_k\nnewline", (Object) "k_v\nnewline"),
+ ImmutableMap.of((Object) "v_k\nnewline", (Object) "v_v\nnewline")));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "MapOfMaps {"
+ + "\n mapOfMaps = {"
+ + "\n {"
+ + "\n k_k"
+ + "\n newline: k_v"
+ + "\n newline,"
+ + "\n }: {"
+ + "\n v_k"
+ + "\n newline: v_v"
+ + "\n newline,"
+ + "\n },"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void mapOfMaps_empty() {
+ MapOfMaps valueType = new AutoValue_ToPrettyStringTest_MapOfMaps(ImmutableMap.of());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "MapOfMaps {" // force newline
+ + "\n mapOfMaps = {},"
+ + "\n}");
+ }
+
+ @Test
+ public void mapOfMaps_nestedEmpty() {
+ MapOfMaps valueType =
+ new AutoValue_ToPrettyStringTest_MapOfMaps(
+ ImmutableMap.of(ImmutableMap.of(), ImmutableMap.of()));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "MapOfMaps {" // force newline
+ + "\n mapOfMaps = {"
+ + "\n {}: {},"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void mapOfMaps_null() {
+ MapOfMaps valueType = new AutoValue_ToPrettyStringTest_MapOfMaps(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "MapOfMaps {" // force newline
+ + "\n mapOfMaps = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class PrettyMultimap {
+ @Nullable
+ abstract Multimap<Object, Object> multimap();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void prettyMultimap() {
+ PrettyMultimap valueType =
+ new AutoValue_ToPrettyStringTest_PrettyMultimap(
+ ImmutableMultimap.builder().putAll("k", "v1", "v2").build());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMultimap {" // force newline
+ + "\n multimap = {"
+ + "\n k: ["
+ + "\n v1,"
+ + "\n v2,"
+ + "\n ],"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyMultimap_keysAndValuesWithNewlines() {
+ PrettyMultimap valueType =
+ new AutoValue_ToPrettyStringTest_PrettyMultimap(
+ ImmutableMultimap.builder()
+ .putAll("key\nnewline", "value1\nnewline", "value2\nnewline")
+ .build());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMultimap {"
+ + "\n multimap = {"
+ + "\n key"
+ + "\n newline: ["
+ + "\n value1"
+ + "\n newline,"
+ + "\n value2"
+ + "\n newline,"
+ + "\n ],"
+ + "\n },"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyMultimap_empty() {
+ PrettyMultimap valueType =
+ new AutoValue_ToPrettyStringTest_PrettyMultimap(ImmutableMultimap.of());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMultimap {" // force newline
+ + "\n multimap = {},"
+ + "\n}");
+ }
+
+ @Test
+ public void prettyMultimap_null() {
+ PrettyMultimap valueType = new AutoValue_ToPrettyStringTest_PrettyMultimap(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PrettyMultimap {" // force newline
+ + "\n multimap = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class JavaOptional {
+ @Nullable
+ abstract java.util.Optional<Object> optional();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void javaOptional_present() {
+ JavaOptional valueType =
+ new AutoValue_ToPrettyStringTest_JavaOptional(java.util.Optional.of("hello, world"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "JavaOptional {" // force newline
+ + "\n optional = hello, world,"
+ + "\n}");
+ }
+
+ @Test
+ public void javaOptional_empty() {
+ JavaOptional valueType =
+ new AutoValue_ToPrettyStringTest_JavaOptional(java.util.Optional.empty());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "JavaOptional {" // force newline
+ + "\n optional = <empty>,"
+ + "\n}");
+ }
+
+ @Test
+ public void javaOptional_valueWithNewlines() {
+ JavaOptional valueType =
+ new AutoValue_ToPrettyStringTest_JavaOptional(
+ java.util.Optional.of("optional\nwith\nnewline"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "JavaOptional {" // force newline
+ + "\n optional = optional"
+ + "\n with"
+ + "\n newline,"
+ + "\n}");
+ }
+
+ @Test
+ public void javaOptional_null() {
+ @SuppressWarnings("NullOptional")
+ JavaOptional valueType = new AutoValue_ToPrettyStringTest_JavaOptional(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "JavaOptional {" // force newline
+ + "\n optional = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class GuavaOptional {
+ @Nullable
+ abstract com.google.common.base.Optional<Object> optional();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void guavaOptional_present() {
+ GuavaOptional valueType =
+ new AutoValue_ToPrettyStringTest_GuavaOptional(
+ com.google.common.base.Optional.of("hello, world"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "GuavaOptional {" // force newline
+ + "\n optional = hello, world,"
+ + "\n}");
+ }
+
+ @Test
+ public void guavaOptional_absent() {
+ GuavaOptional valueType =
+ new AutoValue_ToPrettyStringTest_GuavaOptional(com.google.common.base.Optional.absent());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "GuavaOptional {" // force newline
+ + "\n optional = <absent>,"
+ + "\n}");
+ }
+
+ @Test
+ public void guavaOptional_valueWithNewlines() {
+ GuavaOptional valueType =
+ new AutoValue_ToPrettyStringTest_GuavaOptional(
+ com.google.common.base.Optional.of("optional\nwith\nnewline"));
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "GuavaOptional {" // force newline
+ + "\n optional = optional"
+ + "\n with"
+ + "\n newline,"
+ + "\n}");
+ }
+
+ @Test
+ public void guavaOptional_null() {
+ @SuppressWarnings("NullOptional")
+ GuavaOptional valueType = new AutoValue_ToPrettyStringTest_GuavaOptional(null);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "GuavaOptional {" // force newline
+ + "\n optional = null,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class NestAllTheThings {
+ @Nullable
+ abstract com.google.common.base.Optional<
+ java.util.Optional<
+ List< // open list
+ Map<ImmutableIntArray, Multimap<int[][], Object>>
+ // close list
+ >>>
+ value();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void nestAllTheThings() {
+ NestAllTheThings valueType =
+ new AutoValue_ToPrettyStringTest_NestAllTheThings(
+ com.google.common.base.Optional.of(
+ java.util.Optional.of(
+ ImmutableList.of(
+ ImmutableMap.of(
+ ImmutableIntArray.of(-1, -2, -3),
+ ImmutableMultimap.of(
+ new int[][] {{1, 2}, {3, 4, 5}, {}}, "value\nwith\nnewline"))))));
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "NestAllTheThings {"
+ + "\n value = ["
+ + "\n {"
+ + "\n ["
+ + "\n -1,"
+ + "\n -2,"
+ + "\n -3,"
+ + "\n ]: {"
+ + "\n ["
+ + "\n ["
+ + "\n 1,"
+ + "\n 2,"
+ + "\n ],"
+ + "\n ["
+ + "\n 3,"
+ + "\n 4,"
+ + "\n 5,"
+ + "\n ],"
+ + "\n [],"
+ + "\n ]: ["
+ + "\n value"
+ + "\n with"
+ + "\n newline,"
+ + "\n ],"
+ + "\n },"
+ + "\n },"
+ + "\n ],"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class WithCustomName {
+ abstract int i();
+
+ @ToPrettyString
+ abstract String customName();
+ }
+
+ @Test
+ public void withCustomName() {
+ WithCustomName valueType = new AutoValue_ToPrettyStringTest_WithCustomName(1);
+
+ assertThat(valueType.customName())
+ .isEqualTo(
+ "WithCustomName {" // force newline
+ + "\n i = 1,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class OverridesToString {
+ abstract int i();
+
+ @ToPrettyString
+ @Override
+ public abstract String toString();
+ }
+
+ @Test
+ public void overridesToString() {
+ OverridesToString valueType = new AutoValue_ToPrettyStringTest_OverridesToString(1);
+
+ assertThat(valueType.toString())
+ .isEqualTo(
+ "OverridesToString {" // force newline
+ + "\n i = 1,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class PropertyHasToPrettyString {
+ static class HasToPrettyString<A> {
+ @Override
+ public String toString() {
+ throw new AssertionError();
+ }
+
+ @ToPrettyString
+ String toPrettyString() {
+ return "custom\n@ToPrettyString\nmethod";
+ }
+ }
+
+ static class HasInheritedToPrettyString extends HasToPrettyString<String> {}
+
+ interface HasToPrettyStringInInterface {
+ @ToPrettyString
+ default String toPrettyString() {
+ return "custom\n@ToPrettyString\nmethod\ninterface";
+ }
+ }
+
+ static class HasToPrettyStringFromSuperInterface implements HasToPrettyStringInInterface {}
+
+ abstract HasToPrettyString<String> parameterizedWithString();
+
+ abstract HasToPrettyString<Void> parameterizedWithVoid();
+
+ abstract HasInheritedToPrettyString superclass();
+
+ abstract HasToPrettyStringFromSuperInterface superinterface();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void propertyHasToPrettyString() {
+ PropertyHasToPrettyString valueType =
+ new AutoValue_ToPrettyStringTest_PropertyHasToPrettyString(
+ new PropertyHasToPrettyString.HasToPrettyString<>(),
+ new PropertyHasToPrettyString.HasToPrettyString<>(),
+ new PropertyHasToPrettyString.HasInheritedToPrettyString(),
+ new PropertyHasToPrettyString.HasToPrettyStringFromSuperInterface());
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "PropertyHasToPrettyString {"
+ + "\n parameterizedWithString = custom"
+ + "\n @ToPrettyString"
+ + "\n method,"
+ + "\n parameterizedWithVoid = custom"
+ + "\n @ToPrettyString"
+ + "\n method,"
+ + "\n superclass = custom"
+ + "\n @ToPrettyString"
+ + "\n method,"
+ + "\n superinterface = custom"
+ + "\n @ToPrettyString"
+ + "\n method"
+ + "\n interface,"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class CollectionSubtypesWithFixedTypeParameters {
+ static class StringList extends ArrayList<String> {}
+
+ static class StringMap extends LinkedHashMap<String, String> {}
+
+ abstract StringList list();
+
+ abstract StringMap map();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void fixedTypeParameters() {
+ StringList stringList = new StringList();
+ stringList.addAll(ImmutableList.of("a", "b", "c"));
+ StringMap stringMap = new StringMap();
+ stringMap.putAll(ImmutableMap.of("A", "a", "B", "b"));
+ CollectionSubtypesWithFixedTypeParameters valueType =
+ new AutoValue_ToPrettyStringTest_CollectionSubtypesWithFixedTypeParameters(
+ stringList, stringMap);
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "CollectionSubtypesWithFixedTypeParameters {"
+ + "\n list = ["
+ + "\n a,"
+ + "\n b,"
+ + "\n c,"
+ + "\n ],"
+ + "\n map = {"
+ + "\n A: a,"
+ + "\n B: b,"
+ + "\n },"
+ + "\n}");
+ }
+
+ @AutoValue
+ abstract static class JavaBeans {
+ abstract int getInt();
+
+ abstract boolean isBoolean();
+
+ abstract String getNotAJavaIdentifier();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+ }
+
+ @Test
+ public void javaBeans() {
+ JavaBeans valueType = new AutoValue_ToPrettyStringTest_JavaBeans(4, false, "not");
+
+ assertThat(valueType.toPrettyString())
+ .isEqualTo(
+ "JavaBeans {"
+ + "\n int = 4,"
+ + "\n boolean = false,"
+ + "\n notAJavaIdentifier = not,"
+ + "\n}");
+
+ // Check to make sure that we use the same property names that AutoValue does. This is mostly
+ // defensive, since in some scenarios AutoValue considers the property names of a java bean as
+ // having the prefix removed.
+ assertThat(valueType.toString())
+ .isEqualTo("JavaBeans{int=4, boolean=false, notAJavaIdentifier=not}");
+ }
+}
diff --git a/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java
new file mode 100644
index 00000000..6c51be1d
--- /dev/null
+++ b/value/src/test/java/com/google/auto/value/extension/toprettystring/ToPrettyStringValidatorTest.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.extension.toprettystring;
+
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.auto.value.extension.toprettystring.processor.ToPrettyStringValidator;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ToPrettyStringValidatorTest {
+ @Test
+ public void cannotBeStatic() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.Test",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Test {",
+ " @ToPrettyString",
+ " static String toPretty() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ Compilation compilation = compile(file);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("must be instance methods")
+ .inFile(file)
+ .onLineContaining("static String toPretty()");
+ }
+
+ @Test
+ public void mustReturnString() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.Test",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Test {",
+ " @ToPrettyString",
+ " CharSequence toPretty() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ Compilation compilation = compile(file);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("must return String")
+ .inFile(file)
+ .onLineContaining("CharSequence toPretty()");
+ }
+
+ @Test
+ public void noParameters() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.Test",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Test {",
+ " @ToPrettyString",
+ " String toPretty(String value) {",
+ " return value;",
+ " }",
+ "}",
+ "");
+ Compilation compilation = compile(file);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining("cannot have parameters")
+ .inFile(file)
+ .onLineContaining("String toPretty(String value)");
+ }
+
+ @Test
+ public void onlyOneToPrettyStringMethod_sameClass() {
+ JavaFileObject file =
+ JavaFileObjects.forSourceLines(
+ "test.Test",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Test {",
+ " @ToPrettyString",
+ " String toPretty1() {",
+ " return new String();",
+ " }",
+ "",
+ " @ToPrettyString",
+ " String toPretty2() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ Compilation compilation = compile(file);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ error(
+ "test.Test has multiple @ToPrettyString methods:",
+ " - test.Test.toPretty1()",
+ " - test.Test.toPretty2()"))
+ .inFile(file)
+ .onLineContaining("class Test");
+ }
+
+ @Test
+ public void onlyOneToPrettyStringMethod_superclass() {
+ JavaFileObject superclass =
+ JavaFileObjects.forSourceLines(
+ "test.Superclass",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Superclass {",
+ " @ToPrettyString",
+ " String toPretty1() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ JavaFileObject subclass =
+ JavaFileObjects.forSourceLines(
+ "test.Subclass",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Subclass extends Superclass {",
+ " @ToPrettyString",
+ " String toPretty2() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ Compilation compilation = compile(superclass, subclass);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ error(
+ "test.Subclass has multiple @ToPrettyString methods:",
+ " - test.Superclass.toPretty1()",
+ " - test.Subclass.toPretty2()"))
+ .inFile(subclass)
+ .onLineContaining("class Subclass");
+ }
+
+ @Test
+ public void onlyOneToPrettyStringMethod_superinterface() {
+ JavaFileObject superinterface =
+ JavaFileObjects.forSourceLines(
+ "test.Superinterface",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "interface Superinterface {",
+ " @ToPrettyString",
+ " default String toPretty1() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ JavaFileObject subclass =
+ JavaFileObjects.forSourceLines(
+ "test.Subclass",
+ "package test;",
+ "",
+ "import com.google.auto.value.extension.toprettystring.ToPrettyString;",
+ "",
+ "class Subclass implements Superinterface {",
+ " @ToPrettyString",
+ " String toPretty2() {",
+ " return new String();",
+ " }",
+ "}",
+ "");
+ Compilation compilation = compile(superinterface, subclass);
+
+ assertThat(compilation).failed();
+ assertThat(compilation).hadErrorCount(1);
+ assertThat(compilation)
+ .hadErrorContaining(
+ error(
+ "test.Subclass has multiple @ToPrettyString methods:",
+ " - test.Superinterface.toPretty1()",
+ " - test.Subclass.toPretty2()"))
+ .inFile(subclass)
+ .onLineContaining("class Subclass");
+ }
+
+ private static Compilation compile(JavaFileObject... javaFileObjects) {
+ return javac().withProcessors(new ToPrettyStringValidator()).compile(javaFileObjects);
+ }
+
+ private static String error(String... lines) {
+ return String.join("\n ", lines);
+ }
+}
diff --git a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java
index b8a36eac..1f79a074 100644
--- a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationCompilationTest.java
@@ -74,11 +74,13 @@ public class AutoAnnotationCompilationTest {
"",
"import com.example.annotations.MyAnnotation;",
"import com.example.enums.MyEnum;",
+ "import java.io.Serializable;",
GeneratedImport.importGeneratedAnnotationType(),
"",
"@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")",
"final class AutoAnnotation_AnnotationFactory_newMyAnnotation",
- " implements MyAnnotation {",
+ " implements MyAnnotation, Serializable {",
+ " private static final long serialVersionUID = -7473814294717163169L;",
" private final MyEnum value;",
" private static final int defaultedValue = 23;",
"",
@@ -129,6 +131,7 @@ public class AutoAnnotationCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoAnnotationProcessor())
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
.compile(annotationFactoryJavaFile, myAnnotationJavaFile, myEnumJavaFile);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -157,11 +160,13 @@ public class AutoAnnotationCompilationTest {
JavaFileObject expectedOutput =
JavaFileObjects.forSourceLines(
"AutoAnnotation_AnnotationFactory_newMyAnnotation",
+ "import java.io.Serializable;",
GeneratedImport.importGeneratedAnnotationType(),
"",
"@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")",
"final class AutoAnnotation_AnnotationFactory_newMyAnnotation",
- " implements MyAnnotation {",
+ " implements MyAnnotation, Serializable {",
+ " private static final long serialVersionUID = 0L;",
" AutoAnnotation_AnnotationFactory_newMyAnnotation() {",
" }",
"",
@@ -191,6 +196,7 @@ public class AutoAnnotationCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoAnnotationProcessor())
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
.compile(annotationFactoryJavaFile, myAnnotationJavaFile);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -237,12 +243,14 @@ public class AutoAnnotationCompilationTest {
"package com.example.factories;",
"",
"import com.example.annotations.MyAnnotation;",
+ "import java.io.Serializable",
"import java.util.Arrays;",
GeneratedImport.importGeneratedAnnotationType(),
"",
"@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")",
- "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation"
- + " {",
+ "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation,"
+ + " Serializable {",
+ " private static final long serialVersionUID = -8116050813861599066L;",
" private final int[] value;",
"",
" AutoAnnotation_AnnotationFactory_newMyAnnotation(int[] value) {",
@@ -288,6 +296,7 @@ public class AutoAnnotationCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoAnnotationProcessor())
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
.compile(annotationFactoryJavaFile, myAnnotationJavaFile, gwtCompatibleJavaFile);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -343,6 +352,7 @@ public class AutoAnnotationCompilationTest {
"",
"import com.example.annotations.MyAnnotation;",
"import com.example.enums.MyEnum;",
+ "import java.io.Serializable;",
"import java.util.Arrays;",
"import java.util.Collection;",
"import java.util.List;",
@@ -350,8 +360,9 @@ public class AutoAnnotationCompilationTest {
GeneratedImport.importGeneratedAnnotationType(),
"",
"@Generated(\"" + AutoAnnotationProcessor.class.getName() + "\")",
- "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation"
- + " {",
+ "final class AutoAnnotation_AnnotationFactory_newMyAnnotation implements MyAnnotation,"
+ + " Serializable {",
+ " private static final long serialVersionUID = -2102364343628921304L;",
" private final int[] value;",
" private final MyEnum[] enums;",
"",
@@ -426,6 +437,7 @@ public class AutoAnnotationCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoAnnotationProcessor())
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
.compile(annotationFactoryJavaFile, myEnumJavaFile, myAnnotationJavaFile);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -457,9 +469,7 @@ public class AutoAnnotationCompilationTest {
" @NotAutoAnnotation Empty notNewEmpty() {}",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(erroneousJavaFileObject);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(erroneousJavaFileObject);
assertThat(compilation)
.hadErrorContaining("NotAutoAnnotation")
.inFile(erroneousJavaFileObject)
diff --git a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java
index 55a4c5db..094b570d 100644
--- a/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/AutoAnnotationErrorsTest.java
@@ -57,9 +57,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation).succeededWithoutWarnings();
}
@@ -79,9 +77,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining("must be static")
.inFile(testSource)
@@ -103,9 +99,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining("must be an annotation type, not java.lang.String")
.inFile(testSource)
@@ -132,9 +126,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining("@AutoAnnotation methods cannot be overloaded")
.inFile(testSource)
@@ -196,9 +188,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining("method parameter 'fred' must have the same name")
.inFile(testSource)
@@ -221,9 +211,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining(
"method parameter 'value' has type java.lang.String "
@@ -267,9 +255,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(testSource, testAnnotation);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(testSource, testAnnotation);
assertThat(compilation)
.hadErrorContaining(
"method parameter 'value' has type "
@@ -296,9 +282,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining(
"method parameter 'other' must have the same name as a member of "
@@ -323,9 +307,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(TEST_ANNOTATION, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(TEST_ANNOTATION, testSource);
assertThat(compilation)
.hadErrorContaining("method needs a parameter with name 'value' and type int")
.inFile(testSource)
@@ -357,9 +339,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(annotationSource, testSource);
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(annotationSource, testSource);
assertThat(compilation)
.hadErrorContaining(
"@AutoAnnotation cannot yet supply a default value for annotation-valued member "
@@ -399,10 +379,7 @@ public class AutoAnnotationErrorsTest {
" }",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoAnnotationProcessor())
- .compile(annotationSource, testSource);
- assertThat(compilation)
- .hadErrorContaining("variable value$ is already defined in constructor");
+ javac().withProcessors(new AutoAnnotationProcessor()).compile(annotationSource, testSource);
+ assertThat(compilation).hadErrorContaining("variable value$ is already defined in constructor");
}
}
diff --git a/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java
new file mode 100644
index 00000000..50b6b271
--- /dev/null
+++ b/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java
@@ -0,0 +1,875 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
+import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
+
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.JavaFileObjects;
+import javax.tools.JavaFileObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public final class AutoBuilderCompilationTest {
+ private static final JavaFileObject EXPECTED_SIMPLE_OUTPUT =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.AutoBuilder_Baz_Builder",
+ "package foo.bar;",
+ "",
+ GeneratedImport.importGeneratedAnnotationType(),
+ "",
+ "@Generated(\"" + AutoBuilderProcessor.class.getName() + "\")",
+ "class AutoBuilder_Baz_Builder implements Baz.Builder {",
+ " private Integer anInt;",
+ " private String aString;",
+ "",
+ " AutoBuilder_Baz_Builder() {}",
+ "",
+ " @Override public Baz.Builder setAnInt(int anInt) {",
+ " this.anInt = anInt;",
+ " return this;",
+ " }",
+ "",
+ " @Override public Baz.Builder setAString(String aString) {",
+ " if (aString == null) {",
+ " throw new NullPointerException(\"Null aString\");",
+ " }",
+ " this.aString = aString;",
+ " return this;",
+ " }",
+ "",
+ " @Override",
+ " public Baz build() {",
+ " if (this.anInt == null",
+ " || this.aString == null) {",
+ " StringBuilder missing = new StringBuilder();",
+ " if (this.anInt == null) {",
+ " missing.append(\" anInt\");",
+ " }",
+ " if (this.aString == null) {",
+ " missing.append(\" aString\");",
+ " }",
+ " throw new IllegalStateException(\"Missing required properties:\" + missing);",
+ " }",
+ " return new Baz(",
+ " this.anInt,",
+ " this.aString);",
+ " }",
+ "}");
+
+ @Test
+ public void simpleSuccess() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " private final int anInt;",
+ " private final String aString;",
+ "",
+ " public Baz(int anInt, String aString) {",
+ " this.anInt = anInt;",
+ " this.aString = aString;",
+ " }",
+ "",
+ " public int anInt() {",
+ " return anInt;",
+ " }",
+ "",
+ " public String aString() {",
+ " return aString;",
+ " }",
+ "",
+ " public static Builder builder() {",
+ " return new AutoBuilder_Baz_Builder();",
+ " }",
+ "",
+ " @AutoBuilder",
+ " public interface Builder {",
+ " Builder setAnInt(int x);",
+ " Builder setAString(String x);",
+ " Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation)
+ .generatedSourceFile("foo.bar.AutoBuilder_Baz_Builder")
+ .hasSourceEquivalentTo(EXPECTED_SIMPLE_OUTPUT);
+ }
+
+ @Test
+ public void simpleRecord() {
+ double version = Double.parseDouble(JAVA_SPECIFICATION_VERSION.value());
+ assume().that(version).isAtLeast(16.0);
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public record Baz(int anInt, String aString) {",
+ " public static Builder builder() {",
+ " return new AutoBuilder_Baz_Builder();",
+ " }",
+ "",
+ " @AutoBuilder",
+ " public interface Builder {",
+ " Builder setAnInt(int x);",
+ " Builder setAString(String x);",
+ " Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation)
+ .generatedSourceFile("foo.bar.AutoBuilder_Baz_Builder")
+ .hasSourceEquivalentTo(EXPECTED_SIMPLE_OUTPUT);
+ }
+
+ @Test
+ public void buildOtherPackage() {
+ JavaFileObject built =
+ JavaFileObjects.forSourceLines(
+ "com.example.Built",
+ "package com.example;",
+ "",
+ "public class Built {",
+ " private final int anInt;",
+ " private final String aString;",
+ "",
+ " public Built(int anInt, String aString) {",
+ " this.anInt = anInt;",
+ " this.aString = aString;",
+ " }",
+ "}");
+ JavaFileObject builder =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Builder",
+ "package foo.bar;",
+ "",
+ "import com.example.Built;",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "@AutoBuilder(ofClass = Built.class)",
+ "public interface Builder {",
+ " public static Builder builder() {",
+ " return new AutoBuilder_Builder();",
+ " }",
+ "",
+ " Builder setAnInt(int x);",
+ " Builder setAString(String x);",
+ " Built build();",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(built, builder);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation).generatedSourceFile("foo.bar.AutoBuilder_Builder");
+ }
+
+ @Test
+ public void autoBuilderOnEnum() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "@AutoBuilder",
+ "public enum Baz {",
+ " ZIG, ZAG, DUSTIN,",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderWrongType] @AutoBuilder only applies to classes and interfaces")
+ .inFile(javaFileObject)
+ .onLineContaining("enum Baz");
+ }
+
+ @Test
+ public void autoBuilderPrivate() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " @AutoBuilder",
+ " private interface Builder {",
+ " Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[AutoBuilderPrivate] @AutoBuilder class must not be private")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void autoBuilderNestedInPrivate() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " private static class Private {",
+ " @AutoBuilder",
+ " public interface Builder {",
+ " Baz build();",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderInPrivate] @AutoBuilder class must not be nested in a private class")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void autoBuilderInner() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " @AutoBuilder",
+ " abstract class Builder {",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[AutoBuilderInner] Nested @AutoBuilder class must be static")
+ .inFile(javaFileObject)
+ .onLineContaining("class Builder");
+ }
+
+ @Test
+ public void innerConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " class Inner {}",
+ "",
+ " @AutoBuilder(ofClass = Inner.class)",
+ " interface Builder {",
+ " abstract Inner build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[AutoBuilderInner] Nested @AutoBuilder ofClass class must be static")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void noVisibleConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " @AutoBuilder(ofClass = System.class)",
+ " interface Builder {",
+ " abstract System build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[AutoBuilderNoVisible] No visible constructor for java.lang.System")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void noVisibleMethod() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " private static Baz of() {",
+ " return new Baz();",
+ " }",
+ "",
+ " @AutoBuilder(callMethod = \"of\")",
+ " interface Builder {",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderNoVisible] No visible static method named \"of\" for foo.bar.Baz")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void methodNotStatic() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " Baz of() {",
+ " return this;",
+ " }",
+ "",
+ " @AutoBuilder(callMethod = \"of\")",
+ " interface Builder {",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderNoVisible] No visible static method named \"of\" for foo.bar.Baz")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void noMatchingConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " public Baz(int notMe) {}",
+ "",
+ " public Baz(String notMeEither) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " Builder setBuh(String x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderNoMatch] Property names do not correspond to the parameter names of any"
+ + " constructor:\n"
+ + " Baz(int notMe)\n"
+ + " Baz(java.lang.String notMeEither)")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void twoMatchingConstructors() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " public Baz() {}",
+ "",
+ " public Baz(int buh) {}",
+ "",
+ " public Baz(String buh) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " Builder setBuh(String x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderAmbiguous] Property names correspond to more than one constructor:\n"
+ + " Baz(int buh)\n"
+ + " Baz(java.lang.String buh)")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void constructInterface() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public interface Baz {",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderEnclosing] @AutoBuilder must specify ofClass=Something.class or it must"
+ + " be nested inside the class to be built; actually nested inside interface"
+ + " foo.bar.Baz")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void inconsistentSetPrefix() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int one, int two) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder one(int x);",
+ " abstract Builder setTwo(int x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderSetNotSet] If any setter methods use the setFoo convention then all must")
+ .inFile(javaFileObject)
+ .onLineContaining("Builder one(int x)");
+ }
+
+ @Test
+ public void missingSetter() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int one, int two) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder one(int x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderBuilderMissingMethod] Expected a method with this signature:"
+ + " foo.bar.Baz.Builder two(int), or a twoBuilder() method")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder");
+ }
+
+ @Test
+ public void tooManyArgs() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int one, int two) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder one(int x);",
+ " abstract Builder two(int x);",
+ " abstract Builder many(int x, int y);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining("[AutoBuilderBuilderArgs] Builder methods must have 0 or 1 parameters")
+ .inFile(javaFileObject)
+ .onLineContaining("many(int x, int y)");
+ }
+
+ @Test
+ public void alienNoArgMethod() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int one, int two) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder one(int x);",
+ " abstract Builder two(int x);",
+ " abstract String alien();",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderBuilderNoArg] Method without arguments should be a build method returning"
+ + " foo.bar.Baz, or a getter method with the same name and type as a parameter of"
+ + " Baz(int one, int two), or fooBuilder() where foo is a parameter of Baz(int"
+ + " one, int two)")
+ .inFile(javaFileObject)
+ .onLineContaining("String alien()");
+ }
+
+ @Test
+ public void alienOneArgMethod() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int one, int two) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder one(int x);",
+ " abstract Builder two(int x);",
+ " abstract Builder three(int x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderBuilderWhatProp] Method three does not correspond to "
+ + "a parameter of Baz(int one, int two)")
+ .inFile(javaFileObject)
+ .onLineContaining("three(int x)");
+ }
+
+ @Test
+ public void setterReturnType() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int one, int two) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder one(int x);",
+ " abstract void two(int x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderBuilderRet] Setter methods must return foo.bar.Baz.Builder")
+ .inFile(javaFileObject)
+ .onLineContaining("two(int x)");
+ }
+
+ @Test
+ public void nullableSetterForNonNullableParameter() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "import org.checkerframework.checker.nullness.qual.Nullable;",
+ "",
+ "class Baz {",
+ " Baz(String thing) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder thing(@Nullable String x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ JavaFileObject nullableFileObject =
+ JavaFileObjects.forSourceLines(
+ "org.checkerframework.checker.nullness.qual.Nullable",
+ "package org.jspecify.nullness;",
+ "",
+ "import java.lang.annotation.ElementType;",
+ "import java.lang.annotation.Target;",
+ "",
+ "@Target(ElementType.TYPE_USE)",
+ "public @interface Nullable {}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject, nullableFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderNullNotNull] Parameter of setter method is @Nullable but parameter"
+ + " \"thing\" of Baz(java.lang.String thing) is not")
+ .inFile(javaFileObject)
+ .onLineContaining("thing(@Nullable String x)");
+ }
+
+ @Test
+ public void setterWrongType() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "class Baz {",
+ " Baz(int up, int down) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder up(int x);",
+ " abstract Builder down(String x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderGetVsSet] Parameter type java.lang.String of setter method should be int"
+ + " to match parameter \"down\" of Baz(int up, int down)")
+ .inFile(javaFileObject)
+ .onLineContaining("down(String x)");
+ }
+
+ @Test
+ public void setterWrongTypeEvenWithConversion() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "import java.util.Optional;",
+ "",
+ "class Baz {",
+ " Baz(Optional<String> maybe) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder {",
+ " abstract Builder maybe(int x);",
+ " abstract Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderGetVsSetOrConvert] Parameter type int of setter method should be"
+ + " java.util.Optional<java.lang.String> to match parameter \"maybe\" of"
+ + " Baz(java.util.Optional<java.lang.String> maybe), or it should be a type that"
+ + " can be passed to Optional.of to produce java.util.Optional<java.lang.String>")
+ .inFile(javaFileObject)
+ .onLineContaining("maybe(int x)");
+ }
+
+ @Test
+ public void typeParamMismatch() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "import java.util.Optional;",
+ "",
+ "class Baz<T> {",
+ " Baz(T param) {}",
+ "",
+ " @AutoBuilder",
+ " interface Builder<E> {",
+ " abstract Builder<E> param(E param);",
+ " abstract Baz<E> build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderTypeParams] Builder type parameters <E> must match type parameters <T> of"
+ + " Baz(T param)")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Builder<E>");
+ }
+}
diff --git a/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java
index 63e84199..788b543a 100644
--- a/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java
@@ -43,7 +43,9 @@ public class AutoOneOfCompilationTest {
"import java.io.Serializable;",
"",
"@AutoOneOf(TaskResult.Kind.class)",
- "public abstract class TaskResult<V, T extends Throwable> {",
+ "public abstract class TaskResult<V, T extends Throwable> implements Serializable {",
+ " private static final long serialVersionUID = 1234L;",
+ "",
" public enum Kind {VALUE, EXCEPTION, EMPTY}",
" public abstract Kind getKind();",
"",
@@ -92,6 +94,8 @@ public class AutoOneOfCompilationTest {
" // Parent class that each implementation will inherit from.",
" private abstract static class Parent_<V, T extends Throwable> "
+ "extends TaskResult<V, T> {",
+ " private static final long serialVersionUID = 1234L;",
+ "",
" @Override",
" public V value() {",
" throw new UnsupportedOperationException(getKind().toString());",
@@ -111,6 +115,8 @@ public class AutoOneOfCompilationTest {
" // Implementation when the contained property is \"value\".",
" private static final class Impl_value<V, T extends Throwable> "
+ "extends Parent_<V, T> {",
+ " private static final long serialVersionUID = 1234L;",
+ "",
" private final V value;",
"",
" Impl_value(V value) {",
@@ -152,6 +158,8 @@ public class AutoOneOfCompilationTest {
" // Implementation when the contained property is \"exception\".",
" private static final class Impl_exception<V, T extends Throwable> "
+ "extends Parent_<V, T> {",
+ " private static final long serialVersionUID = 1234L;",
+ "",
" private final Throwable exception;",
"",
" Impl_exception(Throwable exception) {",
@@ -193,6 +201,8 @@ public class AutoOneOfCompilationTest {
" // Implementation when the contained property is \"empty\".",
" private static final class Impl_empty<V, T extends Throwable> "
+ "extends Parent_<V, T> {",
+ " private static final long serialVersionUID = 1234L;",
+ "",
" static final Impl_empty<?, ?> INSTANCE = new Impl_empty<>();",
"",
" private Impl_empty() {}",
@@ -200,6 +210,10 @@ public class AutoOneOfCompilationTest {
" @Override",
" public void empty() {}",
"",
+ " private Object readResolve() {",
+ " return INSTANCE;",
+ " }",
+ "",
" @Override",
" public String toString() {",
" return \"TaskResult{empty}\";",
@@ -223,7 +237,8 @@ public class AutoOneOfCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoOneOfProcessor())
- .withOptions("-Xlint:-processing", "-implicit:none")
+ .withOptions(
+ "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=")
.compile(javaFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -307,7 +322,8 @@ public class AutoOneOfCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoOneOfProcessor())
- .withOptions("-Xlint:-processing", "-implicit:none")
+ .withOptions(
+ "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=")
.compile(javaFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
diff --git a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java
index e46c9f51..ab6690fd 100644
--- a/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java
@@ -57,8 +57,8 @@ public class AutoValueCompilationTest {
public void simpleSuccess() {
// Positive test case that ensures we generate the expected code for at least one case.
// Most AutoValue code-generation tests are functional, meaning that we check that the generated
- // code does the right thing rather than checking what it looks like, but this test is a sanity
- // check that we are not generating correct but weird code.
+ // code does the right thing rather than checking what it looks like, but this test checks that
+ // we are not generating correct but weird code.
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(
"foo.bar.Baz",
@@ -118,7 +118,10 @@ public class AutoValueCompilationTest {
" }",
"}");
Compilation compilation =
- javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
+ javac()
+ .withProcessors(new AutoValueProcessor())
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
+ .compile(javaFileObject);
assertThat(compilation)
.generatedSourceFile("foo.bar.AutoValue_Baz")
.hasSourceEquivalentTo(expectedOutput);
@@ -216,7 +219,10 @@ public class AutoValueCompilationTest {
" }",
"}");
Compilation compilation =
- javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
+ javac()
+ .withProcessors(new AutoValueProcessor())
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
+ .compile(javaFileObject);
assertThat(compilation)
.generatedSourceFile("foo.bar.AutoValue_Baz")
.hasSourceEquivalentTo(expectedOutput);
@@ -335,7 +341,6 @@ public class AutoValueCompilationTest {
" return false;",
" }",
"",
-
" @Override",
" public int hashCode() {",
" int h$ = 1;",
@@ -348,7 +353,8 @@ public class AutoValueCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoValueProcessor())
- .withOptions("-Xlint:-processing", "-implicit:none")
+ .withOptions(
+ "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=")
.compile(annotFileObject, outerFileObject, nestyFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -1129,10 +1135,10 @@ public class AutoValueCompilationTest {
" return this.anInt == that.anInt()",
" && Arrays.equals(this.aByteArray, "
+ "(that instanceof AutoValue_Baz) "
- + "? ((AutoValue_Baz) that).aByteArray : that.aByteArray())",
+ + "? ((AutoValue_Baz<?>) that).aByteArray : that.aByteArray())",
" && Arrays.equals(this.aNullableIntArray, "
+ "(that instanceof AutoValue_Baz) "
- + "? ((AutoValue_Baz) that).aNullableIntArray : that.aNullableIntArray())",
+ + "? ((AutoValue_Baz<?>) that).aNullableIntArray : that.aNullableIntArray())",
" && this.aList.equals(that.aList())",
" && this.anImmutableList.equals(that.anImmutableList())",
" && this.anOptionalString.equals(that.anOptionalString())",
@@ -1312,17 +1318,19 @@ public class AutoValueCompilationTest {
+ "NestedAutoValue.builder();",
" this.aNestedAutoValue = aNestedAutoValue$builder.build();",
" }",
- " String missing = \"\";",
- " if (this.anInt == null) {",
- " missing += \" anInt\";",
- " }",
- " if (this.aByteArray == null) {",
- " missing += \" aByteArray\";",
- " }",
- " if (this.aList == null) {",
- " missing += \" aList\";",
- " }",
- " if (!missing.isEmpty()) {",
+ " if (this.anInt == null",
+ " || this.aByteArray == null",
+ " || this.aList == null) {",
+ " StringBuilder missing = new StringBuilder();",
+ " if (this.anInt == null) {",
+ " missing.append(\" anInt\");",
+ " }",
+ " if (this.aByteArray == null) {",
+ " missing.append(\" aByteArray\");",
+ " }",
+ " if (this.aList == null) {",
+ " missing.append(\" aList\");",
+ " }",
" throw new IllegalStateException(\"Missing required properties:\" + missing);",
" }",
" return new AutoValue_Baz<T>(",
@@ -1339,7 +1347,8 @@ public class AutoValueCompilationTest {
Compilation compilation =
javac()
.withProcessors(new AutoValueProcessor())
- .withOptions("-Xlint:-processing", "-implicit:none")
+ .withOptions(
+ "-Xlint:-processing", "-implicit:none", "-A" + Nullables.NULLABLE_OPTION + "=")
.compile(javaFileObject, nestedJavaFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -1587,7 +1596,7 @@ public class AutoValueCompilationTest {
assertThat(compilation)
.hadErrorContaining(
"Parameter type java.lang.String of setter method should be int "
- + "to match getter foo.bar.Baz.blim")
+ + "to match property method foo.bar.Baz.blim()")
.inFile(javaFileObject)
.onLineContaining("Builder blim(String x)");
}
@@ -1620,10 +1629,10 @@ public class AutoValueCompilationTest {
.compile(javaFileObject);
assertThat(compilation)
.hadErrorContaining(
- "Parameter type java.lang.String of setter method should be "
- + "com.google.common.collect.ImmutableList<java.lang.String> to match getter "
- + "foo.bar.Baz.blam, or it should be a type that can be passed to "
- + "ImmutableList.copyOf")
+ "Parameter type java.lang.String of setter method should be"
+ + " com.google.common.collect.ImmutableList<java.lang.String> to match property"
+ + " method foo.bar.Baz.blam(), or it should be a type that can be passed to"
+ + " ImmutableList.copyOf")
.inFile(javaFileObject)
.onLineContaining("Builder blam(String x)");
}
@@ -1657,11 +1666,11 @@ public class AutoValueCompilationTest {
.compile(javaFileObject);
assertThat(compilation)
.hadErrorContaining(
- "Parameter type java.util.Collection<java.lang.Integer> of setter method should be "
- + "com.google.common.collect.ImmutableList<java.lang.String> to match getter "
- + "foo.bar.Baz.blam, or it should be a type that can be passed to "
- + "ImmutableList.copyOf to produce "
- + "com.google.common.collect.ImmutableList<java.lang.String>")
+ "Parameter type java.util.Collection<java.lang.Integer> of setter method should be"
+ + " com.google.common.collect.ImmutableList<java.lang.String> to match property"
+ + " method foo.bar.Baz.blam(), or it should be a type that can be passed to"
+ + " ImmutableList.copyOf to produce"
+ + " com.google.common.collect.ImmutableList<java.lang.String>")
.inFile(javaFileObject)
.onLineContaining("Builder blam(Collection<Integer> x)");
}
@@ -1694,7 +1703,7 @@ public class AutoValueCompilationTest {
assertThat(compilation)
.hadErrorContaining(
"Parameter type java.lang.String of setter method should be int "
- + "to match getter foo.bar.Baz.getBlim")
+ + "to match property method foo.bar.Baz.getBlim()")
.inFile(javaFileObject)
.onLineContaining("Builder blim(String x)");
}
@@ -1768,7 +1777,8 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining("Method does not correspond to a property of foo.bar.Item")
+ .hadErrorContaining(
+ "Method setTitle does not correspond to a property method of foo.bar.Item")
.inFile(javaFileObject)
.onLineContaining("Builder setTitle(String title)");
assertThat(compilation)
@@ -1802,7 +1812,7 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining("Method does not correspond to a property of foo.bar.Baz")
+ .hadErrorContaining("Method blim does not correspond to a property method of foo.bar.Baz")
.inFile(javaFileObject)
.onLineContaining("Builder blim(int x)");
}
@@ -1839,6 +1849,35 @@ public class AutoValueCompilationTest {
}
@Test
+ public void autoValueBuilderSetterReturnType() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "public abstract class Baz {",
+ " abstract int blim();",
+ "",
+ " @AutoValue.Builder",
+ " public interface Builder {",
+ " void blim(int x);",
+ " Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
+ .compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("Setter methods must return foo.bar.Baz.Builder")
+ .inFile(javaFileObject)
+ .onLineContaining("void blim(int x)");
+ }
+
+ @Test
public void autoValueBuilderWrongTypeGetter() {
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(
@@ -1866,10 +1905,15 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining(
- "Method matches a property of foo.bar.Baz but has return type T instead of U")
+ .hadErrorContainingMatch(
+ "Method matches a property of foo\\.bar\\.Baz<T, ?U> but has return type T instead of"
+ + " U")
.inFile(javaFileObject)
.onLineContaining("T blam()");
+ // The <T, ?U> is because we're depending on TypeMirror.toString(), and the JDK actually spells
+ // this as <T,U> with no space. While it's not completely sound to expect a given string from
+ // TypeMirror.toString(), in practice it's hard to imagine that it would be anything other
+ // than "foo.bar.Baz<T,U>" or "foo.bar.Baz<T, U>" given the specification.
}
@Test
@@ -1929,9 +1973,9 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining("Property strings has a property builder so it cannot be @Nullable")
+ .hadErrorContaining("Property strings is @Nullable so it cannot have a property builder")
.inFile(javaFileObject)
- .onLineContaining("@Nullable ImmutableList<String> strings()");
+ .onLineContaining("stringsBuilder()");
}
@Test
@@ -1963,9 +2007,9 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining("Property strings has a property builder so it cannot be @Nullable")
+ .hadErrorContaining("Property strings is @Nullable so it cannot have a property builder")
.inFile(javaFileObject)
- .onLineContaining("@Nullable ImmutableList<String> strings()");
+ .onLineContaining("stringsBuilder()");
}
@Test
@@ -2451,8 +2495,8 @@ public class AutoValueCompilationTest {
assertThat(compilation)
.hadErrorContaining(
"Method without arguments should be a build method returning foo.bar.Baz, or a getter"
- + " method with the same name and type as a getter method of foo.bar.Baz, or"
- + " fooBuilder() where foo() or getFoo() is a getter method of foo.bar.Baz")
+ + " method with the same name and type as a property method of foo.bar.Baz, or"
+ + " fooBuilder() where foo() or getFoo() is a property method of foo.bar.Baz")
.inFile(javaFileObject)
.onLineContaining("Builder whut()");
}
@@ -2481,7 +2525,7 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining("Method does not correspond to a property of foo.bar.Baz")
+ .hadErrorContaining("Method whut does not correspond to a property method of foo.bar.Baz")
.inFile(javaFileObject)
.onLineContaining("void whut(String x)");
}
@@ -2539,7 +2583,8 @@ public class AutoValueCompilationTest {
.compile(javaFileObject);
assertThat(compilation)
.hadErrorContaining(
- "Builder must have a single no-argument method returning foo.bar.Baz<T>")
+ "Builder must have a single no-argument method, typically called build(), that returns"
+ + " foo.bar.Baz<T>")
.inFile(javaFileObject)
.onLineContaining("public interface Builder<T>");
}
@@ -2569,11 +2614,15 @@ public class AutoValueCompilationTest {
.withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
.compile(javaFileObject);
assertThat(compilation)
- .hadErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz")
+ .hadErrorContaining(
+ "Builder must have a single no-argument method, typically called build(), that returns"
+ + " foo.bar.Baz")
.inFile(javaFileObject)
.onLineContaining("Baz build()");
assertThat(compilation)
- .hadErrorContaining("Builder must have a single no-argument method returning foo.bar.Baz")
+ .hadErrorContaining(
+ "Builder must have a single no-argument method, typically called build(), that returns"
+ + " foo.bar.Baz")
.inFile(javaFileObject)
.onLineContaining("Baz create()");
}
@@ -3286,7 +3335,7 @@ public class AutoValueCompilationTest {
"}");
private static final String GENERATED_PROPERTY_TYPE =
String.join(
- "\n",
+ "\n", //
"package foo.baz;",
"",
"public class GeneratedPropertyType {}");
@@ -3315,18 +3364,14 @@ public class AutoValueCompilationTest {
GENERATED_TYPES.forEach(
(typeName, source) -> {
try {
- JavaFileObject generated =
- processingEnv
- .getFiler()
- .createSourceFile(typeName);
+ JavaFileObject generated = processingEnv.getFiler().createSourceFile(typeName);
try (Writer writer = generated.openWriter()) {
writer.write(source);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
- }
- );
+ });
}
return false;
}
@@ -3337,7 +3382,41 @@ public class AutoValueCompilationTest {
}
}
+ // This is a regression test for the problem described in
+ // https://github.com/google/auto/issues/1087.
+ @Test
+ public void kotlinMetadataAnnotationsAreImplicitlyExcludedFromCopying() {
+ JavaFileObject metadata =
+ JavaFileObjects.forSourceLines(
+ "kotlin.Metadata", "package kotlin;", "", "public @interface Metadata {", "}");
+ JavaFileObject test =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Test",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "import kotlin.Metadata;",
+ "",
+ "@AutoValue.CopyAnnotations",
+ "@Metadata",
+ "@AutoValue",
+ "public abstract class Test {",
+ " public abstract String string();",
+ "}");
+ AutoValueProcessor autoValueProcessor = new AutoValueProcessor();
+ Compilation compilation =
+ javac()
+ .withProcessors(autoValueProcessor)
+ .withOptions("-Xlint:-processing", "-implicit:none")
+ .compile(test, metadata);
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(compilation)
+ .generatedSourceFile("foo.bar.AutoValue_Test")
+ .contentsAsUtf8String()
+ .doesNotContain("kotlin.Metadata");
+ }
+
private String sorted(String... imports) {
- return Arrays.stream(imports).sorted().collect(joining("\n"));
- }
+ return Arrays.stream(imports).sorted().collect(joining("\n"));
+ }
}
diff --git a/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java b/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java
index ce9eeed0..56eaad25 100644
--- a/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/ExtensionTest.java
@@ -172,6 +172,7 @@ public class ExtensionTest {
Compilation compilation =
javac()
.withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension())))
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
.compile(javaFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
@@ -250,9 +251,7 @@ public class ExtensionTest {
" abstract String dizzle();",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoValueProcessor(ImmutableList.of(ext1, ext2)))
- .compile(impl);
+ javac().withProcessors(new AutoValueProcessor(ImmutableList.of(ext1, ext2))).compile(impl);
assertThat(compilation)
.hadErrorContaining("wants to consume a method that was already consumed")
.inFile(impl)
@@ -596,10 +595,9 @@ public class ExtensionTest {
"}");
Compilation compilation =
javac()
- .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension())))
- .compile(javaFileObject);
- assertThat(compilation)
- .hadErrorContaining("writeToParcel");
+ .withProcessors(new AutoValueProcessor(ImmutableList.of(new FooExtension())))
+ .compile(javaFileObject);
+ assertThat(compilation).hadErrorContaining("writeToParcel");
assertThat(compilation)
.hadWarningContaining(
"Abstract method is neither a property getter nor a Builder converter, "
@@ -647,13 +645,12 @@ public class ExtensionTest {
"public abstract class Baz {",
"}");
Compilation compilation =
- javac()
- .withProcessors(new AutoValueProcessor(badJarLoader))
- .compile(javaFileObject);
+ javac().withProcessors(new AutoValueProcessor(badJarLoader)).compile(javaFileObject);
assertThat(compilation).succeeded();
- assertThat(compilation).hadWarningContaining(
- "This may be due to a corrupt jar file in the compiler's classpath.\n "
- + ServiceConfigurationError.class.getName());
+ assertThat(compilation)
+ .hadWarningContaining(
+ "This may be due to a corrupt jar file in the compiler's classpath.\n "
+ + ServiceConfigurationError.class.getName());
assertThat(compilation).generatedSourceFile("foo.bar.AutoValue_Baz");
}
@@ -857,8 +854,12 @@ public class ExtensionTest {
String sideClassName = "Side_" + context.autoValueClass().getSimpleName();
String sideClass =
"" //
- + "package " + context.packageName() + ";\n"
- + "class " + sideClassName + " {}\n";
+ + "package "
+ + context.packageName()
+ + ";\n"
+ + "class "
+ + sideClassName
+ + " {}\n";
Filer filer = context.processingEnvironment().getFiler();
try {
String sideClassFqName = context.packageName() + "." + sideClassName;
@@ -912,25 +913,27 @@ public class ExtensionTest {
@Test
public void propertyTypes() {
- JavaFileObject parent = JavaFileObjects.forSourceLines(
- "foo.bar.Parent",
- "package foo.bar;",
- "",
- "import java.util.List;",
- "",
- "interface Parent<T> {",
- " T thing();",
- " List<T> list();",
- "}");
- JavaFileObject autoValueClass = JavaFileObjects.forSourceLines(
- "foo.bar.Baz",
- "package foo.bar;",
- "",
- "import com.google.auto.value.AutoValue;",
- "",
- "@AutoValue",
- "abstract class Baz implements Parent<String> {",
- "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Parent",
+ "package foo.bar;",
+ "",
+ "import java.util.List;",
+ "",
+ "interface Parent<T> {",
+ " T thing();",
+ " List<T> list();",
+ "}");
+ JavaFileObject autoValueClass =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "abstract class Baz implements Parent<String> {",
+ "}");
ContextChecker checker =
context -> {
assertThat(context.builder()).isEmpty();
@@ -955,15 +958,16 @@ public class ExtensionTest {
@Test
public void finalAutoValueClassName() {
- JavaFileObject autoValueClass = JavaFileObjects.forSourceLines(
- "foo.bar.Baz",
- "package foo.bar;",
- "",
- "import com.google.auto.value.AutoValue;",
- "",
- "@AutoValue",
- "abstract class Baz {",
- "}");
+ JavaFileObject autoValueClass =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "abstract class Baz {",
+ "}");
ContextChecker checker =
context -> {
assertThat(context.finalAutoValueClassName()).isEqualTo("foo.bar.AutoValue_Baz");
@@ -982,43 +986,45 @@ public class ExtensionTest {
@Test
public void builderContext() {
- JavaFileObject parent = JavaFileObjects.forSourceLines(
- "foo.bar.Parent",
- "package foo.bar;",
- "",
- "import com.google.common.collect.ImmutableList;",
- "",
- "interface Parent<T> {",
- " T thing();",
- " ImmutableList<T> list();",
- "}");
- JavaFileObject autoValueClass = JavaFileObjects.forSourceLines(
- "foo.bar.Baz",
- "package foo.bar;",
- "",
- "import com.google.auto.value.AutoValue;",
- "import com.google.common.collect.ImmutableList;",
- "",
- "@AutoValue",
- "abstract class Baz implements Parent<String> {",
- " static Builder builder() {",
- " return new AutoValue_Baz.Builder();",
- " }",
- "",
- " abstract Builder toBuilder();",
- "",
- " @AutoValue.Builder",
- " abstract static class Builder {",
- " abstract Builder setThing(String x);",
- " abstract Builder setList(Iterable<String> x);",
- " abstract Builder setList(ImmutableList<String> x);",
- " abstract ImmutableList.Builder<String> listBuilder();",
- " abstract Baz autoBuild();",
- " Baz build() {",
- " return autoBuild();",
- " }",
- " }",
- "}");
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Parent",
+ "package foo.bar;",
+ "",
+ "import com.google.common.collect.ImmutableList;",
+ "",
+ "interface Parent<T> {",
+ " T thing();",
+ " ImmutableList<T> list();",
+ "}");
+ JavaFileObject autoValueClass =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "import com.google.common.collect.ImmutableList;",
+ "",
+ "@AutoValue",
+ "abstract class Baz implements Parent<String> {",
+ " static Builder builder() {",
+ " return new AutoValue_Baz.Builder();",
+ " }",
+ "",
+ " abstract Builder toBuilder();",
+ "",
+ " @AutoValue.Builder",
+ " abstract static class Builder {",
+ " abstract Builder setThing(String x);",
+ " abstract Builder setList(Iterable<String> x);",
+ " abstract Builder setList(ImmutableList<String> x);",
+ " abstract ImmutableList.Builder<String> listBuilder();",
+ " abstract Baz autoBuild();",
+ " Baz build() {",
+ " return autoBuild();",
+ " }",
+ " }",
+ "}");
ContextChecker checker =
context -> {
assertThat(context.builder()).isPresent();
@@ -1075,27 +1081,102 @@ public class ExtensionTest {
}
@Test
+ public void builderContextWithInheritance() {
+ JavaFileObject parent =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Parent",
+ "package foo.bar;",
+ "",
+ "interface Parent<BuilderT> {",
+ " BuilderT toBuilder();",
+ " interface Builder<T, BuilderT, BuiltT> {",
+ " BuilderT setThing(T x);",
+ " BuiltT build();",
+ " }",
+ "}");
+ JavaFileObject autoValueClass =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "abstract class Baz<T> implements Parent<Baz.Builder<T>> {",
+ " abstract T thing();",
+ " static <T> Builder<T> builder() {",
+ " return new AutoValue_Baz.Builder<>();",
+ " }",
+ "",
+ " @AutoValue.Builder",
+ " abstract static class Builder<T> implements Parent.Builder<T, Builder<T>, Baz<T>> {",
+ " }",
+ "}");
+ ContextChecker checker =
+ context -> {
+ assertThat(context.builder()).isPresent();
+ BuilderContext builderContext = context.builder().get();
+
+ assertThat(builderContext.builderType().getQualifiedName().toString())
+ .isEqualTo("foo.bar.Baz.Builder");
+
+ Set<ExecutableElement> builderMethods = builderContext.builderMethods();
+ assertThat(builderMethods).hasSize(1);
+ ExecutableElement builderMethod = Iterables.getOnlyElement(builderMethods);
+ assertThat(builderMethod.getSimpleName().toString()).isEqualTo("builder");
+
+ Set<ExecutableElement> toBuilderMethods = builderContext.toBuilderMethods();
+ assertThat(toBuilderMethods).hasSize(1);
+ ExecutableElement toBuilderMethod = Iterables.getOnlyElement(toBuilderMethods);
+ assertThat(toBuilderMethod.getSimpleName().toString()).isEqualTo("toBuilder");
+
+ Optional<ExecutableElement> buildMethod = builderContext.buildMethod();
+ assertThat(buildMethod).isPresent();
+ assertThat(buildMethod.get().getSimpleName().toString()).isEqualTo("build");
+ assertThat(buildMethod.get().getParameters()).isEmpty();
+ assertThat(buildMethod.get().getReturnType().toString()).isEqualTo("BuiltT");
+
+ ExecutableElement autoBuildMethod = builderContext.autoBuildMethod();
+ assertThat(autoBuildMethod).isEqualTo(buildMethod.get());
+
+ Map<String, Set<ExecutableElement>> setters = builderContext.setters();
+ assertThat(setters.keySet()).containsExactly("thing");
+ Set<ExecutableElement> thingSetters = setters.get("thing");
+ assertThat(thingSetters).hasSize(1);
+ ExecutableElement thingSetter = Iterables.getOnlyElement(thingSetters);
+ assertThat(thingSetter.getSimpleName().toString()).isEqualTo("setThing");
+ };
+ ContextCheckingExtension extension = new ContextCheckingExtension(checker);
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoValueProcessor(ImmutableList.of(extension)))
+ .compile(autoValueClass, parent);
+ assertThat(compilation).succeededWithoutWarnings();
+ }
+
+ @Test
public void oddBuilderContext() {
- JavaFileObject autoValueClass = JavaFileObjects.forSourceLines(
- "foo.bar.Baz",
- "package foo.bar;",
- "",
- "import com.google.auto.value.AutoValue;",
- "import com.google.common.collect.ImmutableList;",
- "",
- "@AutoValue",
- "abstract class Baz {",
- " abstract String string();",
- "",
- " @AutoValue.Builder",
- " abstract static class Builder {",
- " abstract Builder setString(String x);",
- " abstract Baz oddBuild();",
- " Baz build(int butNotReallyBecauseOfThisParameter) {",
- " return null;",
- " }",
- " }",
- "}");
+ JavaFileObject autoValueClass =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "import com.google.common.collect.ImmutableList;",
+ "",
+ "@AutoValue",
+ "abstract class Baz {",
+ " abstract String string();",
+ "",
+ " @AutoValue.Builder",
+ " abstract static class Builder {",
+ " abstract Builder setString(String x);",
+ " abstract Baz oddBuild();",
+ " Baz build(int butNotReallyBecauseOfThisParameter) {",
+ " return null;",
+ " }",
+ " }",
+ "}");
ContextChecker checker =
context -> {
assertThat(context.builder()).isPresent();
@@ -1126,25 +1207,26 @@ public class ExtensionTest {
// https://github.com/google/auto/issues/809
@Test
public void propertyErrorShouldNotCrash() {
- JavaFileObject autoValueClass = JavaFileObjects.forSourceLines(
- "test.Test",
- "package test;",
- "import com.google.auto.value.AutoValue;",
- "import java.util.List;",
- "",
- "@AutoValue",
- "public abstract class Test {",
- " abstract Integer property();",
- " abstract List<String> listProperty();",
- "",
- " @AutoValue.Builder",
- " public interface Builder {",
- " Builder property(Integer property);",
- " Builder listProperty(List<String> listProperty);",
- " Builder listProperty(Integer listPropertyValues);",
- " Test build();",
- " }",
- "}");
+ JavaFileObject autoValueClass =
+ JavaFileObjects.forSourceLines(
+ "test.Test",
+ "package test;",
+ "import com.google.auto.value.AutoValue;",
+ "import java.util.List;",
+ "",
+ "@AutoValue",
+ "public abstract class Test {",
+ " abstract Integer property();",
+ " abstract List<String> listProperty();",
+ "",
+ " @AutoValue.Builder",
+ " public interface Builder {",
+ " Builder property(Integer property);",
+ " Builder listProperty(List<String> listProperty);",
+ " Builder listProperty(Integer listPropertyValues);",
+ " Test build();",
+ " }",
+ "}");
// We don't actually expect the extension to be invoked. Previously it was, and that led to a
// NullPointerException when calling .setters() in the checker.
ContextChecker checker =
diff --git a/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java b/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java
index f3d3d611..18cca5e4 100644
--- a/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/GeneratedDoesNotExistTest.java
@@ -48,6 +48,8 @@ import org.junit.runners.Parameterized.Parameters;
*/
@RunWith(Parameterized.class)
public class GeneratedDoesNotExistTest {
+ private static final ImmutableList<String> STANDARD_OPTIONS =
+ ImmutableList.of("-A" + Nullables.NULLABLE_OPTION + "=");
@Parameters(name = "{0}")
public static Collection<Object[]> data() {
@@ -57,12 +59,16 @@ public class GeneratedDoesNotExistTest {
// TODO(b/72513371): use --release 8 once compile-testing supports that
params.add(
new Object[] {
- ImmutableList.of(), "javax.annotation.processing.Generated",
+ STANDARD_OPTIONS, "javax.annotation.processing.Generated",
});
}
params.add(
new Object[] {
- ImmutableList.of("-source", "8", "-target", "8"), "javax.annotation.Generated",
+ ImmutableList.<String>builder()
+ .addAll(STANDARD_OPTIONS)
+ .add("-source", "8", "-target", "8")
+ .build(),
+ "javax.annotation.Generated",
});
return params.build();
}
@@ -223,9 +229,9 @@ public class GeneratedDoesNotExistTest {
Processor noGeneratedProcessor = partialProxy(Processor.class, handler);
Compilation compilation =
javac()
- .withOptions(javacOptions)
- .withProcessors(noGeneratedProcessor)
- .compile(javaFileObject);
+ .withOptions(javacOptions)
+ .withProcessors(noGeneratedProcessor)
+ .compile(javaFileObject);
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("foo.bar.AutoValue_Baz")
diff --git a/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java b/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java
index 27cb0936..472f62db 100644
--- a/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/IncrementalExtensionTest.java
@@ -22,6 +22,7 @@ import com.google.auto.value.extension.AutoValueExtension;
import com.google.auto.value.extension.AutoValueExtension.IncrementalExtensionType;
import com.google.auto.value.extension.memoized.processor.MemoizeExtension;
import com.google.auto.value.extension.serializable.processor.SerializableAutoValueExtension;
+import com.google.auto.value.extension.toprettystring.processor.ToPrettyStringExtension;
import com.google.common.collect.ImmutableList;
import javax.annotation.processing.ProcessingEnvironment;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
@@ -46,7 +47,10 @@ public class IncrementalExtensionTest {
// different <?>.
assertThat(builtInExtensions)
.comparingElementsUsing(transforming(e -> (Object) e.getClass(), "is class"))
- .containsExactly(MemoizeExtension.class, SerializableAutoValueExtension.class);
+ .containsExactly(
+ MemoizeExtension.class,
+ SerializableAutoValueExtension.class,
+ ToPrettyStringExtension.class);
AutoValueProcessor processor = new AutoValueProcessor(builtInExtensions);
assertThat(processor.getSupportedOptions())
@@ -58,10 +62,12 @@ public class IncrementalExtensionTest {
AutoValueExtension nonIsolatingExtension = new NonIsolatingExtension();
assertThat(nonIsolatingExtension.incrementalType((ProcessingEnvironment) null))
.isEqualTo(IncrementalExtensionType.UNKNOWN);
- ImmutableList<AutoValueExtension> extensions = ImmutableList.<AutoValueExtension>builder()
- .addAll(AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader()))
- .add(nonIsolatingExtension)
- .build();
+ ImmutableList<AutoValueExtension> extensions =
+ ImmutableList.<AutoValueExtension>builder()
+ .addAll(
+ AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader()))
+ .add(nonIsolatingExtension)
+ .build();
AutoValueProcessor processor = new AutoValueProcessor(extensions);
assertThat(processor.getSupportedOptions())
@@ -73,10 +79,12 @@ public class IncrementalExtensionTest {
AutoValueExtension isolatingExtension = new IsolatingExtension();
assertThat(isolatingExtension.incrementalType((ProcessingEnvironment) null))
.isEqualTo(IncrementalExtensionType.ISOLATING);
- ImmutableList<AutoValueExtension> extensions = ImmutableList.<AutoValueExtension>builder()
- .addAll(AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader()))
- .add(isolatingExtension)
- .build();
+ ImmutableList<AutoValueExtension> extensions =
+ ImmutableList.<AutoValueExtension>builder()
+ .addAll(
+ AutoValueProcessor.extensionsFromLoader(AutoValueProcessor.class.getClassLoader()))
+ .add(isolatingExtension)
+ .build();
AutoValueProcessor processor = new AutoValueProcessor(extensions);
assertThat(processor.getSupportedOptions())
diff --git a/value/src/test/java/com/google/auto/value/processor/NullablesTest.java b/value/src/test/java/com/google/auto/value/processor/NullablesTest.java
new file mode 100644
index 00000000..9e345f53
--- /dev/null
+++ b/value/src/test/java/com/google/auto/value/processor/NullablesTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * 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 com.google.auto.value.processor;
+
+import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
+import static com.google.common.truth.OptionalSubject.optionals;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.TruthJUnit.assume;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static java.util.stream.Collectors.partitioningBy;
+
+import com.google.auto.common.MoreTypes;
+import com.google.common.collect.ImmutableList;
+import com.google.common.truth.Expect;
+import com.google.testing.compile.Compilation;
+import com.google.testing.compile.Compiler;
+import com.google.testing.compile.JavaFileObjects;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.util.ElementFilter;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class NullablesTest {
+ @Rule public Expect expect = Expect.create();
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Nullable {}
+
+ @Target(ElementType.TYPE_USE)
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Irrelevant {}
+
+ // The class here has various methods that we will examine with
+ // Nullables.nullableMentionedInMethods to ensure that we do indeed detect @Nullable annotations
+ // in various contexts.
+ // This test is a lot more complicated than it should be. Ideally we would just have this Methods
+ // class be an actual class nested inside this test, and we would use CompilationRule to get
+ // the corresponding TypeElement so we could check the various methods. Unfortunately, if we
+ // do that then we get a TypeElement where all type annotations have disappeared because of
+ // https://bugs.openjdk.java.net/browse/JDK-8225377. So instead we have to use Compiler to compile
+ // the code here with a special annotation processor that will check the annotations of the
+ // just-compiled class. Since the processor is running as part of the same compilation, we don't
+ // lose the type annotations.
+
+ private static final ImmutableList<String> METHOD_LINES =
+ ImmutableList.of(
+ // Methods in this class whose names begin with "no" do not mention @Nullable anywhere.",
+ // All other methods do.",
+ "package foo.bar;",
+ "",
+ "import " + Irrelevant.class.getCanonicalName() + ";",
+ "import " + Nullable.class.getCanonicalName() + ";",
+ "import java.util.List;",
+ "",
+ "abstract class Methods {",
+ " void noAnnotations() {}",
+ " abstract int noAnnotationsEither(int x);",
+ " abstract @Irrelevant String noRelevantAnnotations(@Irrelevant int x);",
+ " abstract @Nullable String nullableString();",
+ " abstract String @Nullable [] nullableArrayOfString();",
+ " abstract @Nullable String[] arrayOfNullableString();",
+ " abstract @Nullable String @Nullable [] nullableArrayOfNullableString();",
+ " abstract List<@Nullable String> listOfNullableString();",
+ " abstract List<? extends @Nullable Object> listOfExtendsNullable();",
+ " abstract List<? super @Nullable Number> listOfSuperNullable();",
+ " abstract <T extends @Nullable Object> T nullableTypeParamBound();",
+ " abstract <T> @Nullable T nullableTypeParamRef();",
+ " void nullableParam(@Nullable String x) {}",
+ " void nullableParamBound(List<? extends @Nullable String> x) {}",
+ "}");
+
+ @Test
+ public void nullableMentionedInMethods() {
+ // Sadly we can't rely on JDK 8 to handle type annotations correctly.
+ // Some versions do, some don't. So skip the test unless we are on at least JDK 9.
+ double javaVersion = Double.parseDouble(JAVA_SPECIFICATION_VERSION.value());
+ assume().that(javaVersion).isAtLeast(9.0);
+ NullableProcessor processor = new NullableProcessor(expect);
+ Compilation compilation =
+ Compiler.javac()
+ .withProcessors(processor)
+ .compile(JavaFileObjects.forSourceLines("foo.bar.Methods", METHOD_LINES));
+ assertThat(compilation).succeededWithoutWarnings();
+ assertThat(processor.ran).isTrue();
+ // If any `expect` calls failed then the test will fail now because of the Expect rule.
+ }
+
+ @SupportedAnnotationTypes("*")
+ private static class NullableProcessor extends AbstractProcessor {
+
+ private final Expect expect;
+ boolean ran;
+
+ NullableProcessor(Expect expect) {
+ this.expect = expect;
+ }
+
+ @Override
+ public SourceVersion getSupportedSourceVersion() {
+ return SourceVersion.latestSupported();
+ }
+
+ @Override
+ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+ if (roundEnv.processingOver()) {
+ TypeElement methodsElement =
+ processingEnv.getElementUtils().getTypeElement("foo.bar.Methods");
+ expect.that(methodsElement).isNotNull();
+
+ List<ExecutableElement> methods =
+ ElementFilter.methodsIn(methodsElement.getEnclosedElements());
+ Map<Boolean, List<ExecutableElement>> partitionedMethods =
+ methods.stream()
+ .collect(partitioningBy(p -> !p.getSimpleName().toString().startsWith("no")));
+ List<ExecutableElement> nullableMethods = partitionedMethods.get(true);
+ List<ExecutableElement> notNullableMethods = partitionedMethods.get(false);
+
+ expect
+ .about(optionals())
+ .that(Nullables.nullableMentionedInMethods(notNullableMethods))
+ .isEmpty();
+
+ TypeElement nullableElement =
+ processingEnv.getElementUtils().getTypeElement(Nullable.class.getCanonicalName());
+ expect.that(nullableElement).isNotNull();
+ DeclaredType nullableType = MoreTypes.asDeclared(nullableElement.asType());
+
+ for (ExecutableElement nullableMethod : nullableMethods) {
+ // Make a list with all the methods that don't have @Nullable plus one method that does.
+ ImmutableList<ExecutableElement> notNullablePlusNullable =
+ ImmutableList.<ExecutableElement>builder()
+ .addAll(notNullableMethods)
+ .add(nullableMethod)
+ .build();
+ expect
+ .withMessage("method %s should have @Nullable", nullableMethod)
+ .about(optionals())
+ .that(
+ Nullables.nullableMentionedInMethods(notNullablePlusNullable)
+ .map(AnnotationMirror::getAnnotationType))
+ .hasValue(nullableType);
+ }
+ ran = true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java b/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java
index 2c3bea0d..48d8cd6e 100644
--- a/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/PropertyAnnotationsTest.java
@@ -15,12 +15,13 @@
*/
package com.google.auto.value.processor;
-import static com.google.common.truth.Truth.assertAbout;
-import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
+import static com.google.testing.compile.CompilationSubject.assertThat;
+import static com.google.testing.compile.Compiler.javac;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
+import com.google.testing.compile.Compilation;
import com.google.testing.compile.JavaFileObjects;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
@@ -40,8 +41,7 @@ import org.junit.runners.JUnit4;
*/
@RunWith(JUnit4.class)
public class PropertyAnnotationsTest {
- private static final String TEST_ANNOTATION =
- "@PropertyAnnotationsTest.TestAnnotation";
+ private static final String TEST_ANNOTATION = "@PropertyAnnotationsTest.TestAnnotation";
private static final String TEST_ARRAY_ANNOTATION =
"@PropertyAnnotationsTest.TestArrayAnnotation";
@@ -272,12 +272,15 @@ public class PropertyAnnotationsTest {
.addMethodAnnotations(expectedMethodAnnotations)
.build();
- assertAbout(javaSource())
- .that(javaFileObject)
- .processedWith(new AutoValueProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(expectedOutput);
+ Compilation compilation =
+ javac()
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
+ .withProcessors(new AutoValueProcessor())
+ .compile(javaFileObject);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("foo.bar.AutoValue_Baz")
+ .hasSourceEquivalentTo(expectedOutput);
}
@Test
@@ -445,10 +448,12 @@ public class PropertyAnnotationsTest {
assertGeneratedMatches(
getImports(PropertyAnnotationsTest.class),
ImmutableList.of(
- TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A,"
+ TEST_ARRAY_ANNOTATION
+ + "(testEnums = {PropertyAnnotationsTest.TestEnum.A,"
+ " PropertyAnnotationsTest.TestEnum.B})"),
ImmutableList.of(
- TEST_ARRAY_ANNOTATION + "(testEnums = {PropertyAnnotationsTest.TestEnum.A,"
+ TEST_ARRAY_ANNOTATION
+ + "(testEnums = {PropertyAnnotationsTest.TestEnum.A,"
+ " PropertyAnnotationsTest.TestEnum.B})"));
}
@@ -512,12 +517,15 @@ public class PropertyAnnotationsTest {
.addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation")
.build();
- assertAbout(javaSource())
- .that(inputFile)
- .processedWith(new AutoValueProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(outputFile);
+ Compilation compilation =
+ javac()
+ .withOptions("-A" + Nullables.NULLABLE_OPTION)
+ .withProcessors(new AutoValueProcessor())
+ .compile(inputFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("foo.bar.AutoValue_Baz")
+ .hasSourceEquivalentTo(outputFile);
}
/**
@@ -545,16 +553,17 @@ public class PropertyAnnotationsTest {
.setImports(getImports(PropertyAnnotationsTest.class))
.addFieldAnnotations("@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation")
.addMethodAnnotations(
- "@Deprecated",
- "@PropertyAnnotationsTest.InheritedAnnotation",
- "@Baz.MethodsOnly")
+ "@Deprecated", "@PropertyAnnotationsTest.InheritedAnnotation", "@Baz.MethodsOnly")
.build();
- assertAbout(javaSource())
- .that(inputFile)
- .processedWith(new AutoValueProcessor())
- .compilesWithoutError()
- .and()
- .generatesSources(outputFile);
+ Compilation compilation =
+ javac()
+ .withOptions("-A" + Nullables.NULLABLE_OPTION + "=")
+ .withProcessors(new AutoValueProcessor())
+ .compile(inputFile);
+ assertThat(compilation).succeeded();
+ assertThat(compilation)
+ .generatedSourceFile("foo.bar.AutoValue_Baz")
+ .hasSourceEquivalentTo(outputFile);
}
}
diff --git a/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java b/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java
index 7af240c6..d488e599 100644
--- a/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/PropertyNamesTest.java
@@ -34,14 +34,12 @@ public class PropertyNamesTest {
.put("x", "x")
.put("", "")
.build();
-
+
@Test
public void decapitalizeLikeJavaBeans() {
- NORMAL_CASES
- .forEach(
- (input, output) -> {
- expect.that(PropertyNames.decapitalizeLikeJavaBeans(input)).isEqualTo(output);
- });
+ NORMAL_CASES.forEach(
+ (input, output) ->
+ expect.that(PropertyNames.decapitalizeLikeJavaBeans(input)).isEqualTo(output));
expect.that(PropertyNames.decapitalizeLikeJavaBeans(null)).isNull();
expect.that(PropertyNames.decapitalizeLikeJavaBeans("HTMLPage")).isEqualTo("HTMLPage");
expect.that(PropertyNames.decapitalizeLikeJavaBeans("OAuth")).isEqualTo("OAuth");
@@ -49,11 +47,9 @@ public class PropertyNamesTest {
@Test
public void decapitalizeNormally() {
- NORMAL_CASES
- .forEach(
- (input, output) -> {
- expect.that(PropertyNames.decapitalizeNormally(input)).isEqualTo(output);
- });
+ NORMAL_CASES.forEach(
+ (input, output) ->
+ expect.that(PropertyNames.decapitalizeNormally(input)).isEqualTo(output));
expect.that(PropertyNames.decapitalizeNormally(null)).isNull();
expect.that(PropertyNames.decapitalizeNormally("HTMLPage")).isEqualTo("hTMLPage");
expect.that(PropertyNames.decapitalizeNormally("OAuth")).isEqualTo("oAuth");
diff --git a/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java b/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java
index 98d31b48..48ecf5be 100644
--- a/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/ReformatterTest.java
@@ -15,7 +15,7 @@
*/
package com.google.auto.value.processor;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -29,7 +29,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ReformatterTest {
@Test
- public void testSimple() {
+ public void simple() {
String input =
"\n"
+ "package com.latin.declension; \n"
@@ -50,7 +50,7 @@ public class ReformatterTest {
+ "\n"
+ " Eodem ( Eadem eodem ) { }\n";
String output =
- "package com.latin.declension;\n"
+ "package com.latin.declension;\n"
+ "\n"
+ "public class Idem {\n"
+ "\n"
@@ -62,11 +62,11 @@ public class ReformatterTest {
+ " }\n"
+ "\n"
+ " Eodem (Eadem eodem) { }\n";
- assertEquals(output, Reformatter.fixup(input));
+ assertThat(Reformatter.fixup(input)).isEqualTo(output);
}
@Test
- public void testSpecialSpaces() {
+ public void specialSpaces() {
String input =
"\n"
+ "package com.example.whatever;\n"
@@ -79,7 +79,7 @@ public class ReformatterTest {
+ " static final char QUOTE2 = '\\\"' ;\n"
+ "}\n";
String output =
- "package com.example.whatever;\n"
+ "package com.example.whatever;\n"
+ "\n"
+ "public class SomeClass {\n"
+ " static final String STRING = \" hello world \\n\";\n"
@@ -88,13 +88,66 @@ public class ReformatterTest {
+ " static final char QUOTE = '\"';\n"
+ " static final char QUOTE2 = '\\\"';\n"
+ "}\n";
- assertEquals(output, Reformatter.fixup(input));
+ assertThat(Reformatter.fixup(input)).isEqualTo(output);
}
@Test
public void noTrailingNewline() {
String input = "package com.example.whatever;\n\npublic class SomeClass {}";
String output = input + "\n";
- assertEquals(output, Reformatter.fixup(input));
+ assertThat(Reformatter.fixup(input)).isEqualTo(output);
+ }
+
+ @Test
+ public void indent() {
+ String input =
+ " class Test {\n"
+ + "private final int field;\n"
+ + "\n"
+ + "Test(\n"
+ + "@Interesting Integer field,\n"
+ + "boolean ignored) {\n"
+ + "this.field = field;\n"
+ + "}\n"
+ + "\n"
+ + "@Override\n"
+ + "public boolean equals(Object x) {\n"
+ + "return x instanceof Test\n"
+ + "&& ((Test) x).field == field;\n"
+ + "// interesting\n"
+ + "}\n"
+ + "\n"
+ + "@Override\n"
+ + "public String toString() {\n"
+ + "return \"Test{\"\n"
+ + "+ \"field=\" + field\n"
+ + "+ \"}\";\n"
+ + "}\n"
+ + "}\n";
+ String output =
+ "class Test {\n"
+ + " private final int field;\n"
+ + "\n"
+ + " Test(\n"
+ + " @Interesting Integer field,\n"
+ + " boolean ignored) {\n"
+ + " this.field = field;\n"
+ + " }\n"
+ + "\n"
+ + " @Override\n"
+ + " public boolean equals(Object x) {\n"
+ + " return x instanceof Test\n"
+ + " && ((Test) x).field == field;\n"
+ + " // interesting\n"
+ + " }\n"
+ + "\n"
+ + " @Override\n"
+ + " public String toString() {\n"
+ + " return \"Test{\"\n"
+ + " + \"field=\" + field\n"
+ + " + \"}\";\n"
+ + " }\n"
+ + "}\n";
+ assertThat(Reformatter.fixup(input)).isEqualTo(output);
}
}
diff --git a/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java b/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java
index fab18056..5e2d230d 100644
--- a/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/SimpleServiceLoaderTest.java
@@ -28,6 +28,8 @@ import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.ServiceConfigurationError;
@@ -54,6 +56,35 @@ public final class SimpleServiceLoaderTest {
assertThat(classes).containsExactly(String.class, StringBuilder.class).inOrder();
}
+ // Sometimes you can have the same jar appear more than once in the classpath, perhaps in
+ // different versions. In that case we don't want to instantiate the same class more than once.
+ // This test checks that we don't.
+ @Test
+ public void loadWithDuplicates() throws Exception {
+ ClassLoader loader1 =
+ loaderForJarWithEntries(
+ CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName());
+ ClassLoader loader2 =
+ loaderForJarWithEntries(
+ CharSequence.class.getName(), String.class.getName(), StringBuilder.class.getName());
+ ClassLoader combinedLoader =
+ new ClassLoader() {
+ @Override
+ public Enumeration<URL> getResources(String name) throws IOException {
+ List<URL> urls = new ArrayList<>(Collections.list(loader1.getResources(name)));
+ urls.addAll(Collections.list(loader2.getResources(name)));
+ return Collections.enumeration(urls);
+ }
+ };
+
+ ImmutableList<CharSequence> providers =
+ SimpleServiceLoader.load(CharSequence.class, combinedLoader);
+
+ assertThat(providers).contains("");
+ List<Class<?>> classes = providers.stream().map(Object::getClass).collect(toList());
+ assertThat(classes).containsExactly(String.class, StringBuilder.class).inOrder();
+ }
+
@Test
public void blankLinesAndComments() throws Exception {
ClassLoader loader =
diff --git a/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java b/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java
index 39e2dc0e..7bc67790 100644
--- a/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/SimplifyWithAnnotationsTest.java
@@ -157,8 +157,7 @@ public class SimplifyWithAnnotationsTest {
void testTypeSpellings(TypeElement testClass) {
ExecutableElement witness =
- ElementFilter.methodsIn(testClass.getEnclosedElements())
- .stream()
+ ElementFilter.methodsIn(testClass.getEnclosedElements()).stream()
.filter(m -> m.getSimpleName().contentEquals("witness"))
.collect(onlyElement());
if (witness.getReturnType().getAnnotationMirrors().isEmpty()) {
diff --git a/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java b/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java
index c31f711e..83951e0a 100644
--- a/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/TypeEncoderTest.java
@@ -292,7 +292,7 @@ public class TypeEncoderTest {
TypeMirror multipleBoundsMirror = multipleBoundsElement.asType();
String text = "`import`\n";
text += "{" + TypeEncoder.encode(multipleBoundsMirror) + "}";
- text += "{" + TypeEncoder.formalTypeParametersString(multipleBoundsElement) + "}";
+ text += "{" + TypeEncoder.typeParametersString(multipleBoundsElement.getTypeParameters()) + "}";
String myPackage = getClass().getPackage().getName();
String decoded =
TypeEncoder.decode(text, elementUtils, typeUtils, myPackage, baseWithoutContainedTypes());
@@ -306,6 +306,7 @@ public class TypeEncoderTest {
@SuppressWarnings("ClassCanBeStatic")
static class Outer<T extends Number> {
class InnerWithoutTypeParam {}
+
class Middle<U> {
class InnerWithTypeParam<V> {}
}
diff --git a/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java b/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java
index 078ef513..895bed25 100644
--- a/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/TypeVariablesTest.java
@@ -115,10 +115,12 @@ public class TypeVariablesTest {
abstract static class Outer<T, U extends T> {
abstract Map<T, U> getFoo();
+
abstract List<? extends T> getBar();
abstract static class Inner<T, U extends T> {
abstract void setFoo(Map<T, U> foo);
+
abstract void setBar(List<? extends T> bar);
}
}
@@ -165,13 +167,15 @@ public class TypeVariablesTest {
List<ExecutableElement> immutableMapMethods =
ElementFilter.methodsIn(immutableMap.getEnclosedElements());
ExecutableElement copyOf = methodNamed(immutableMapMethods, "copyOf", erasedMap);
- expect.that(
- TypeVariables.canAssignStaticMethodResult(
- copyOf, immutableMapStringInteger, immutableMapStringNumber, typeUtils))
+ expect
+ .that(
+ TypeVariables.canAssignStaticMethodResult(
+ copyOf, immutableMapStringInteger, immutableMapStringNumber, typeUtils))
.isTrue();
- expect.that(
- TypeVariables.canAssignStaticMethodResult(
- copyOf, immutableMapStringNumber, immutableMapStringInteger, typeUtils))
+ expect
+ .that(
+ TypeVariables.canAssignStaticMethodResult(
+ copyOf, immutableMapStringNumber, immutableMapStringInteger, typeUtils))
.isFalse();
}
@@ -184,7 +188,9 @@ public class TypeVariablesTest {
return methods.stream()
.filter(m -> m.getSimpleName().contentEquals(name))
.filter(m -> m.getParameters().size() == 1)
- .filter(m -> typeUtils.isSameType(
+ .filter(
+ m ->
+ typeUtils.isSameType(
erasedParameterType, typeUtils.erasure(m.getParameters().get(0).asType())))
.findFirst()
.get();
diff --git a/value/userguide/autobuilder.md b/value/userguide/autobuilder.md
new file mode 100644
index 00000000..ccd191ca
--- /dev/null
+++ b/value/userguide/autobuilder.md
@@ -0,0 +1,491 @@
+# AutoBuilder
+
+
+AutoBuilder makes it easy to create a generalized builder, with setter methods
+that accumulate values, and a build method that calls a constructor or static
+method with those values as parameters. Callers don't need to know the order of
+those parameters. Parameters can also have default values. There can be
+validation before the constructor or method call.
+
+If you are familiar with [AutoValue builders](builders.md) then AutoBuilder
+should also be familiar. Where an `@AutoValue.Builder` has setter methods
+corresponding to the getter methods in the `@AutoValue` class, an `@AutoBuilder`
+has setter methods corresponding to the parameters of a constructor or static
+method. Apart from that, the two are very similar.
+
+AutoBuilder is **unstable** and it is possible that its API
+may change. We do not recommend depending on it for production code yet.
+
+## Example: calling a constructor
+
+Here is a simple example:
+
+```
+@AutoBuilder(ofClass = Person.class)
+abstract class PersonBuilder {
+ static PersonBuilder personBuilder() {
+ return new AutoBuilder_PersonBuilder();
+ }
+
+ abstract PersonBuilder setName(String name);
+ abstract PersonBuilder setId(int id);
+ abstract Person build();
+}
+```
+
+It might be used like this:
+
+```
+Person p = PersonBuilder.personBuilder().setName("Priz").setId(6).build();
+```
+
+That would have the same effect as this:
+
+```
+Person p = new Person("Priz", 6);
+```
+
+But it doesn't require you to know what order the constructor parameters are in.
+
+Here, `setName` and `setId` are _setter methods_. Calling
+`builder.setName("Priz")` records the value `"Priz"` for the parameter `name`,
+and likewise with `setId`.
+
+There is also a `build()` method. Calling that method invokes the `Person`
+constructor with the parameters that were previously set.
+
+## Example: calling a Kotlin constructor
+
+Kotlin has named arguments and default arguments for constructors and functions,
+which means there is not much need for anything like AutoBuilder there. But if
+you are constructing an instance of a Kotlin data class from Java code,
+AutoBuilder can help.
+
+Given this trivial Kotlin data class:
+
+```
+class KotlinData(val int: Int, val string: String?)
+```
+
+You might make a builder for it like this:
+
+```
+@AutoBuilder(ofClass = KotlinData.class)
+public abstract class KotlinDataBuilder {
+ public static KotlinDataBuilder kotlinDataBuilder() {
+ return new AutoBuilder_KotlinDataBuilder();
+ }
+
+ public abstract setInt(int x);
+ public abstract setString(@Nullable String x);
+ public abstract KotlinData build();
+}
+```
+
+The Kotlin type `String?` corresponds to `@Nullable String` in the AutoBuilder
+class, where `@Nullable` is any annotation with that name, such as
+`org.jetbrains.annotations.Nullable`.
+
+## The generated subclass
+
+Like `@AutoValue.Builder`, compiling an `@AutoBuilder` class will generate a
+concrete subclass. In the example above, this will be `class
+AutoBuilder_PersonBuilder extends PersonBuilder`. It is common to have a static
+`builder()` method, as in the example, which calls `new AutoBuilder_...()`. That
+will typically be the only reference to the generated class.
+
+If the `@AutoBuilder` type is nested then the name of the generated class
+reflects that nesting. For example:
+
+```
+class Outer {
+ static class Inner {
+ @AutoBuilder
+ abstract static class Builder {...}
+ }
+ static Inner.Builder builder() {
+ return new AutoBuilder_Outer_Inner_Builder();
+ }
+}
+```
+
+## `@AutoBuilder` annotation parameters
+
+`@AutoBuilder` has two annotation parameters, `ofClass` and `callMethod`.
+
+If `ofClass` is specified, then `build()` will call a constructor or static
+method of that class. Otherwise it will call a constructor or static method of
+the class _containing_ the `@AutoBuilder` class.
+
+If `callMethod` is specified, then `build()` will call a static method with that
+name. Otherwise `build()` will call a constructor.
+
+The following examples illustrate the various possibilities. These examples use
+an interface for the `@AutoBuilder` type. You can also use an abstract class; if
+it is nested then it must be static.
+
+### Both `callMethod` and `ofClass`
+
+```
+@AutoBuilder(callMethod = "of", ofClass = LocalTime.class)
+interface LocalTimeBuilder {
+ ...
+ LocalTime build(); // calls: LocalTime.of(...)
+}
+```
+
+### Only `ofClass`
+
+```
+@AutoBuilder(ofClass = Thread.class)
+interface ThreadBuilder {
+ ...
+ Thread build(); // calls: new Thread(...)
+}
+```
+
+### Only `callMethod`
+
+```
+class Foo {
+ static String concat(String first, String middle, String last) {...}
+
+ @AutoBuilder(callMethod = "concat")
+ interface ConcatBuilder {
+ ...
+ String build(); // calls: Foo.concat(first, middle, last)
+ }
+}
+```
+
+Notice in this example that the static method returns `String`. The implicit
+`ofClass` is `Foo`, but the static method can return any type.
+
+### Neither `callMethod` nor `ofClass`
+
+```
+class Person {
+ Person(String name, int id) {...}
+
+ @AutoBuilder
+ interface Builder {
+ ...
+ Person build(); // calls: new Person(name, id)
+ }
+}
+```
+
+## The build method
+
+The build method must have a certain return type. If it calls a constructor then
+its return type must be the type of the constructed class. If it calls a static
+method then its return type must be the return type of the static method.
+
+The build method is often called `build()` but it does not have to be. The only
+requirement is that there must be exactly one no-arg abstract method that has
+the return type just described and that does not correspond to a parameter name.
+
+The following example uses the name `call()` since that more accurately reflects
+what it does:
+
+```
+public class LogUtil {
+ public static void log(Level severity, String message, Object... params) {...}
+
+ @AutoBuilder(callMethod = "log")
+ public interface Caller {
+ Caller setSeverity(Level level);
+ Caller setMessage(String message);
+ Caller setParams(Object... params);
+ void call(); // calls: LogUtil.log(severity, message, params)
+ }
+```
+
+## Overloaded constructors or methods
+
+There might be more than one constructor or static method that matches the
+`callMethod` and `ofClass`. AutoBuilder will ignore any that are not visible to
+the generated class, meaning private, or package-private and in a different
+package. Of the others, it will pick the one whose parameter names match the
+`@AutoBuilder` setter methods. It is a compilation error if there is not exactly
+one such method or constructor.
+
+## Generics
+
+If the builder calls the constructor of a generic type, then it must have the
+same type parameters as that type, as in this example:
+
+```
+class NumberPair<T extends Number> {
+ NumberPair(T first, T second) {...}
+
+ @AutoBuilder
+ interface Builder<T extends Number> {
+ Builder<T> setFirst(T x);
+ Builder<T> setSecond(T x);
+ NumberPair<T> build();
+ }
+}
+```
+
+If the builder calls a static method with type parameters, then it must have the
+same type parameters, as in this example:
+
+```
+class Utils {
+ static <K extends Number, V> Map<K, V> singletonNumberMap(K key, V value) {...}
+
+ @AutoBuilder(callMethod = "singletonNumberMap")
+ interface Builder<K extends Number, V> {
+ Builder<K, V> setKey(K x);
+ Builder<K, V> setValue(V x);
+ Map<K, V> build();
+ }
+}
+```
+
+Although it's unusual, a Java constructor can have its own type parameters,
+separately from any that its containing class might have. A builder that calls a
+constructor like that must have the type parameters of the class followed by the
+type parameters of the constructor:
+
+```
+class CheckedSet<E> implements Set<E> {
+ <T extends E> CheckedSet(Class<T> type) {...}
+
+ @AutoBuilder
+ interface Builder<E, T extends E> {
+ Builder<E, T> setType(Class<T> type);
+ CheckedSet<E> build();
+ }
+}
+```
+
+## Required, optional, and nullable parameters
+
+Parameters that are annotated `@Nullable` are null by default. Parameters of
+type `Optional`, `OptionalInt`, `OptionalLong`, and `OptionalDouble` are empty
+by default. Every other parameter is _required_, meaning that the build method
+will throw `IllegalStateException` if any are omitted.
+
+To establish default values for parameters, set them in the `builder()` method
+before returning the builder.
+
+```
+class Foo {
+ Foo(String bar, @Nullable String baz, String buh) {...}
+
+ static Builder builder() {
+ return new AutoBuilder_Foo_Builder()
+ .setBar(DEFAULT_BAR);
+ }
+
+ @AutoBuilder
+ interface Builder {
+ Builder setBar(String x);
+ Builder setBaz(String x);
+ Builder setBuh(String x);
+ Foo build();
+ }
+
+ {
+ builder().build(); // IllegalStateException, buh is not set
+ builder().setBuh("buh").build(); // OK, bar=DEFAULT_BAR and baz=null
+ builder().setBaz(null).setBuh("buh").build(); // OK
+ builder().setBar(null); // NullPointerException, bar is not @Nullable
+ }
+}
+```
+
+Trying to set a parameter that is _not_ annotated `@Nullable` to `null` will
+produce a `NullPointerException`.
+
+`@Nullable` here is any annotation with that name, such as
+`javax.annotation.Nullable` or
+`org.checkerframework.checker.nullness.qual.Nullable`.
+
+## Getters
+
+The `@AutoBuilder` class or interface can also have _getter_ methods. A getter
+method returns the value that has been set for a certain parameter. Its return
+type can be either the same as the parameter type, or an `Optional` wrapping
+that type. Calling the getter before any value has been set will throw an
+exception in the first case or return an empty `Optional` in the second.
+
+In this example, the `nickname` parameter defaults to the same value as the
+`name` parameter but can also be set to a different value:
+
+```
+public class Named {
+ Named(String name, String nickname) {...}
+
+ @AutoBuilder
+ public abstract static class Builder {
+ public abstract Builder setName(String x);
+ public abstract Builder setNickname(String x);
+ abstract String getName();
+ abstract Optional<String> getNickname();
+ abstract Named autoBuild();
+
+ public Named build() {
+ if (!getNickname().isPresent()) {
+ setNickname(getName());
+ }
+ return autoBuild();
+ }
+ }
+}
+```
+
+The example illustrates having a package-private `autoBuild()` method that
+AutoBuilder implements. The public `build()` method calls it after adjusting the
+nickname if necessary.
+
+The builder in the example is an abstract class rather than an interface. An
+abstract class allows us to distinguish between public methods for users of the
+builder to call, and package-private methods that the builder's own logic uses.
+
+## Naming conventions
+
+A setter method for the parameter `foo` can be called either `setFoo` or `foo`.
+A getter method can be called either `getFoo` or `foo`, and for a `boolean`
+parameter it can also be called `isFoo`. The choice for getters and setters is
+independent. For example your getter might be `foo()` while your setter is
+`setFoo(T)`.
+
+By convention, the parameter name of a setter method either echoes the parameter
+being set:<br>
+`Builder setName(String name);`<br>
+or it is just `x`:<br>
+`Builder setName(String x);`<br>
+
+If class `Foo` has a nested `@AutoBuilder` that builds instances of `Foo`, then
+conventionally that type is called `Builder`, and instances of it are obtained
+by calling a static `Foo.builder()` method:
+
+```
+Foo foo1 = Foo.builder().setBar(bar).setBaz(baz).build();
+Foo.Builder fooBuilder = Foo.builder();
+```
+
+If an `@AutoBuilder` for `Foo` is its own top-level class then that class will
+typically be called `FooBuilder` and it will have a static `fooBuilder()` method
+that returns an instance of `FooBuilder`. That way callers can statically import
+`FooBuilder.fooBuilder` and just write `fooBuilder()` in their code.
+
+```
+@AutoBuilder(ofClass = Foo.class)
+public abstract class FooBuilder {
+ public static FooBuilder fooBuilder() {
+ return new AutoBuilder_FooBuilder();
+ }
+ ...
+ public abstract Foo build();
+}
+```
+
+If an `@AutoBuilder` is designed to call a static method that is not a factory
+method, the word "call" is better than "build" in the name of the type
+(`FooCaller`), the static method (`fooCaller()`), and the "build" method (`call()`).
+
+```
+@AutoBuilder(callMethod = "log", ofClass = MyLogger.class)
+public abstract class LogCaller {
+ public static LogCaller logCaller() {
+ return new AutoBuilder_LogCaller();
+ }
+ ...
+ public abstract void call();
+}
+
+// used as:
+logCaller().setLevel(Level.INFO).setMessage("oops").call();
+```
+
+## Other builder features
+
+There are a number of other builder features that have not been detailed here
+because they are the same as for `@AutoValue.Builder`. They include:
+
+* [Special treatment of collections](builders-howto.md#collection)
+* [Handling of nested builders](builders-howto.md#nested_builders)
+
+There is currently no equivalent of AutoValue's
+[`toBuilder()`](builders-howto.md#to_builder). Unlike AutoValue, there is not
+generally a mapping back from the result of the constructor or method to its
+parameters.
+
+## When parameter names are unavailable
+
+AutoBuilder depends on knowing the names of parameters. But parameter names are
+not always available in Java. They _are_ available in these cases, at least:
+
+* In code that is being compiled at the same time as the `@AutoBuilder` class
+ or interface.
+* In _records_ (from Java 16 onwards).
+* In the constructors of Kotlin data classes.
+* In code that was compiled with the [`-parameters`] option.
+
+A Java compiler bug means that parameter names are not available to AutoBuilder
+when compiling with JDK versions before 11, in any of these cases except the
+first. We recommend building with a recent JDK, using the `--release` option if
+necessary to produce code that can run on earlier versions.
+
+If parameter names are unavailable, you always have the option of introducing a
+static method in the same class as the `@AutoBuilder` type, and having it call
+the method you want. Since it is compiled at the same time, its parameter names
+are available.
+
+Here's an example of fixing a problem this way. The code here typically will not
+compile, since parameter names of JDK methods are not available:
+
+```
+import java.time.LocalTime;
+
+public class TimeUtils {
+ // Does not work, since parameter names from LocalTime.of are unavailable.
+ @AutoBuilder(callMethod = "of", ofClass = LocalTime.class)
+ public interface TimeBuilder {
+ TimeBuilder setHour(int x);
+ TimeBuilder setMinute(int x);
+ TimeBuilder setSecond(int x);
+ LocalTime build();
+ }
+}
+```
+
+It will produce an error message like this:
+
+```
+error: [AutoBuilderNoMatch] Property names do not correspond to the parameter names of any static method named "of":
+ public interface TimeBuilder {
+ ^
+ of(int arg0, int arg1)
+ of(int arg0, int arg1, int arg2)
+ of(int arg0, int arg1, int arg2, int arg3)
+```
+
+The names `arg0`, `arg1`, etc are concocted by the compiler because it doesn't
+have the real names.
+
+Introducing a static method fixes the problem:
+
+```
+import java.time.LocalTime;
+
+public class TimeUtils {
+ static LocalTime localTimeOf(int hour, int second, int second) {
+ return LocalTime.of(hour, minute, second);
+ }
+
+ @AutoBuilder(callMethod = "localTimeOf")
+ public interface TimeBuilder {
+ TimeBuilder setHour(int x);
+ TimeBuilder setMinute(int x);
+ TimeBuilder setSecond(int x);
+ LocalTime build();
+ }
+}
+```
+
+[`-parameters`]: https://docs.oracle.com/en/java/javase/16/docs/specs/man/javac.html#option-parameters
diff --git a/value/userguide/builders-howto.md b/value/userguide/builders-howto.md
index 00038e70..e7cf5373 100644
--- a/value/userguide/builders-howto.md
+++ b/value/userguide/builders-howto.md
@@ -32,6 +32,8 @@ How do I...
* ... [offer **both** accumulation and set-at-once methods for the same
collection-valued property?](#collection_both)
* ... [access nested builders while building?](#nested_builders)
+* ... [create a "step builder"?](#step)
+* ... [create a builder for something other than an `@AutoValue`?](#autobuilder)
## <a name="beans"></a>... use (or not use) `set` prefixes?
@@ -79,10 +81,13 @@ it will simply default to `null` as you would expect. And if it is
[Optional](#optional) it will default to an empty `Optional` as you might also
expect. But if it isn't either of those things (including if it is a
primitive-valued property, which *can't* be null), then `build()` will throw an
-unchecked exception.
+unchecked exception. This includes collection properties, which must be given a
+value. They don't default to empty unless there is a
+[collection builder](#accumulate).
-But this presents a problem, since one of the main *advantages* of a builder in
-the first place is that callers can specify only the properties they care about!
+But this requirement to supply a value presents a problem, since one of the main
+*advantages* of a builder in the first place is that callers can specify only
+the properties they care about!
The solution is to provide a default value for such properties. Fortunately this
is easy: just set it on the newly-constructed builder instance before returning
@@ -306,7 +311,8 @@ property of type `Optional<String>`, say, then it will default to an empty
`Optional` without needing to [specify](#default) a default explicitly. And,
instead of or as well as the normal `setFoo(Optional<String>)` method, you can
have `setFoo(String)`. Then `setFoo(s)` is equivalent to
-`setFoo(Optional.of(s))`.
+`setFoo(Optional.of(s))`. (If it is `setFoo(@Nullable String)`, then `setFoo(s)`
+is equivalent to `setFoo(Optional.ofNullable(s))`.)
Here, `Optional` means either [`java.util.Optional`] from Java (Java 8 or
later), or [`com.google.common.base.Optional`] from Guava. Java 8 also
@@ -419,7 +425,22 @@ followed by the string `Builder`. Even if the properties follow the
`getCountries()` convention, the builder method must be `countriesBuilder()`
and not `getCountriesBuilder()`.
-You may notice a small problem with this example: the caller can no longer
+It's also possible to have a method like `countriesBuilder` with a single
+argument, provided that the `Builder` class has a public constructor or a
+static `builder` method, with one parameter that the argument can be assigned
+to. For example, if `countries()` were an `ImmutableSortedSet<String>` and you
+wanted to supply a `Comparator` to `ImmutableSortedSet.Builder`, you could
+write:
+
+```java
+ public abstract ImmutableSortedSet.Builder<String>
+ countriesBuilder(Comparator<String> comparator);
+```
+
+That works because `ImmutableSortedSet.Builder` has a constructor that
+accepts a `Comparator` parameter.
+
+You may notice a small problem with these examples: the caller can no longer
create their instance in a single chained statement:
```java
@@ -598,5 +619,21 @@ If `speciesBuilder()` is never called then the final `species()` property will
be set as if by `speciesBuilder().build()`. In the example, that would result
in an exception because the required properties of `Species` have not been set.
+## <a name="step"></a>... create a "step builder"?
+
+A [_step builder_](http://rdafbn.blogspot.com/2012/07/step-builder-pattern_28.html)
+is a collection of builder interfaces that take you step by step through the
+setting of each of a list of required properties. We think that these are a nice
+idea in principle but not necessarily in practice. Regardless, if you want to
+use AutoValue to implement a step builder,
+[this example](https://github.com/google/auto/issues/1000#issuecomment-792875738)
+shows you how.
+
+## <a name="autobuilder"></a> ... create a builder for something other than an `@AutoValue`?
+
+Sometimes you want to make a builder like the kind described here, but have it
+build something other than an `@AutoValue` class, or even call a static method.
+In that case you can use `@AutoBuilder`. See
+[its documentation](autobuilder.md).
[protobuf]: https://developers.google.com/protocol-buffers/docs/reference/java-generated#builders
diff --git a/value/userguide/builders.md b/value/userguide/builders.md
index 11c68c68..1de6bfa4 100644
--- a/value/userguide/builders.md
+++ b/value/userguide/builders.md
@@ -10,15 +10,14 @@ Fortunately, AutoValue can generate builder classes too! This page explains how.
Note that we recommend reading and understanding the basic usage shown in the
[introduction](index.md) first.
-
-## How to use AutoValue with Builders<a name="howto"></a>
+## How to use AutoValue with Builders <a name="howto"></a>
As explained in the introduction, the AutoValue concept is that **you write an
abstract value class, and AutoValue implements it**. Builder generation works in
the exact same way: you also create an abstract builder class, nesting it inside
your abstract value class, and AutoValue generates implementations for both.
-### In `Animal.java`<a name="example_java"></a>
+### In `Animal.java` <a name="example_java"></a>
```java
import com.google.auto.value.AutoValue;
@@ -45,7 +44,7 @@ Note that in real life, some classes and methods would presumably be public and
have **Javadoc**. We're leaving these off in the User Guide only to keep the
examples clean and short.
-### Usage<a name="usage"></a>
+### Usage <a name="usage"></a>
```java
public void testAnimal() {
@@ -65,12 +64,12 @@ public void testAnimal() {
}
```
-### What does AutoValue generate?<a name="generated"></a>
+### What does AutoValue generate? <a name="generated"></a>
For the `Animal` example shown above, here is [typical code AutoValue might
generate](generated-builder-example.md).
-## Warnings<a name="warnings"></a>
+## Warnings <a name="warnings"></a>
Be sure to put the static `builder()` method directly in your value class (e.g.,
`Animal`) and not the nested abstract `Builder` class. That ensures that the
@@ -92,10 +91,16 @@ exposing yourself to initialization-order problems.
time?](builders-howto.md#normalize)
* ... [expose **both** a builder and a factory
method?](builders-howto.md#both)
+* ... [handle `Optional` properties?](builders-howto.md#optional)
* ... [use a **collection**-valued property?](builders-howto.md#collection)
* ... [let my builder **accumulate** values for a collection-valued
property (not require them all at once)?](builders-howto.md#accumulate)
* ... [accumulate values for a collection-valued property, without
- **breaking the chain**?](builders-howto.md#add)
+ **"breaking the chain"**?](builders-howto.md#add)
* ... [offer **both** accumulation and set-at-once methods for the same
collection-valued property?](builders-howto.md#collection_both)
+* ... [access nested builders while
+ building?](builders-howto.md#nested_builders)
+* ... [create a "step builder"?](builders-howto.md#step)
+* ... [create a builder for something other than an
+ `@AutoValue`?](builders-howto.md#autobuilder)
diff --git a/value/userguide/extensions.md b/value/userguide/extensions.md
index ec6c4675..6acb3ca9 100644
--- a/value/userguide/extensions.md
+++ b/value/userguide/extensions.md
@@ -9,7 +9,6 @@ AutoValue can be extended to implement new features for classes annotated with
Each extension is a class. If that class is on the `processorpath` when you
compile your `@AutoValue` class, the extension can run.
-
Some extensions are triggered by their own annotations, which you add to your
class; others may be triggered in other ways. Consult the extension's
documentation for usage instructions.
@@ -40,4 +39,3 @@ behavior by overriding or implementing new methods.
[AutoService]: https://github.com/google/auto/tree/master/service
[`AutoValueExtension`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/AutoValueExtension.java
[`ServiceLoader`]: http://docs.oracle.com/javase/7/docs/api/java/util/ServiceLoader.html
-
diff --git a/value/userguide/howto.md b/value/userguide/howto.md
index 6f9142c1..c4511854 100644
--- a/value/userguide/howto.md
+++ b/value/userguide/howto.md
@@ -42,6 +42,7 @@ How do I...
* ... [make a class where only one of its properties is ever set?](#oneof)
* ... [copy annotations from a class/method to the implemented
class/method/field?](#copy_annotations)
+* ... [create a **pretty string** representation?](#toprettystring)
## <a name="builder"></a>... also generate a builder for my value class?
@@ -239,16 +240,16 @@ If you're sure, here is how to do it:
abstract class IgnoreExample {
static IgnoreExample create(String normalProperty, String ignoredProperty) {
IgnoreExample ie = new AutoValue_IgnoreExample(normalProperty);
- ie.ignoredProperty = ignoredProperty;
+ ie.ignoredProperty.set(ignoredProperty);
return ie;
}
abstract String normalProperty();
- private String ignoredProperty; // sadly, it can't be `final`
+ private final AtomicReference<String> ignoredProperty = new AtomicReference<>();
final String ignoredProperty() {
- return ignoredProperty;
+ return ignoredProperty.get();
}
}
```
@@ -256,6 +257,11 @@ abstract class IgnoreExample {
Note that this means the field is also ignored by `toString`; to AutoValue
it simply doesn't exist.
+Note that we use `AtomicReference<String>` to ensure that other threads will
+correctly see the value that was written. You could also make the field
+`volatile`, or use `synchronized` (`synchronized (ie)` around the assignment and
+`synchronized` on the `ignoredProperty()` method).
+
## <a name="supertypes"></a>... have AutoValue also implement abstract methods from my supertypes?
AutoValue will recognize every abstract accessor method whether it is defined
@@ -347,7 +353,6 @@ This is not allowed. Object arrays are very badly-behaved and unlike primitive
arrays, they can be replaced with a proper `List` implementation for very little
added cost.
-
If it's important to accept an object array at construction time, refer to the
*first* example shown [here](#mutable_property).
@@ -676,3 +681,44 @@ final class AutoValue_Example extends Example {
[`@AutoValue.CopyAnnotations`]: http://static.javadoc.io/com.google.auto.value/auto-value/1.6/com/google/auto/value/AutoValue.CopyAnnotations.html
+## <a name="toprettystring"></a>... create a pretty string representation?
+
+If you have a value class with a long `toString()` representation, annotate a
+method with [`@ToPrettyString`] and AutoValue will generate an implementation that
+returns a pretty String rendering of the instance. For example:
+
+```java
+@AutoValue
+abstract class Song {
+ abstract String lyrics();
+ abstract List<Artist> artists();
+
+ @ToPrettyString
+ abstract String toPrettyString();
+}
+```
+
+Below is a sample rendering of the result of calling `toPrettyString()`.
+
+```
+Song {
+ lyrics = I'm off the deep end, watch as I dive in
+ I'll never meet the ground
+ Crash through the surface, where they can't hurt us
+ We're far from the shallow now.,
+ artists = [
+ Artist {
+ name = Lady Gaga,
+ },
+ Artist {
+ name = Bradley Cooper,
+ }
+ ],
+}
+```
+
+`@ToPrettyString` can be used on the default `toString()` to override the
+default AutoValue-generated `toString()` implementation, or on another
+user-defined method.
+
+[`@ToPrettyString`]: https://github.com/google/auto/blob/master/value/src/main/java/com/google/auto/value/extension/toprettystring/ToPrettyString.java
diff --git a/value/userguide/index.md b/value/userguide/index.md
index 2e05d54b..d6ef95c7 100644
--- a/value/userguide/index.md
+++ b/value/userguide/index.md
@@ -13,7 +13,6 @@
>
> -- *Joshua Bloch, author, Effective Java*
-
## <a name="background"></a>Background
**Value classes** are extremely common in Java projects. These are classes for
@@ -150,18 +149,18 @@ Gradle users can declare the dependencies in their `build.gradle` script:
```groovy
dependencies {
- // Use 'api' rather than 'compile' for Android or java-library projects.
- compile "com.google.auto.value:auto-value-annotations:${autoValueVersion}"
+ compileOnlyApi "com.google.auto.value:auto-value-annotations:${autoValueVersion}"
annotationProcessor "com.google.auto.value:auto-value:${autoValueVersion}"
}
```
-Note: If you are using a version of Gradle prior to 4.6, you must apply an
-annotation processing plugin [as described in these instructions][tbroyer-apt].
+Note: If you are using a version of Gradle prior to 6.7, use `compile` or (for
+Android or java-library projects) `api` instead of `compileOnlyApi`. If you are
+using a version prior to 4.6, you must apply an annotation processing plugin
+[as described in these instructions][tbroyer-apt].
[tbroyer-apt]: https://plugins.gradle.org/plugin/net.ltgt.apt
-
### <a name="usage"></a>Usage
Your choice to use AutoValue is essentially *API-invisible*. This means that, to
@@ -276,13 +275,7 @@ How do I...
set?](howto.md#oneof)
* ... [copy annotations from a class/method to the implemented
class/method/field?](howto.md#copy_annotations)
+* ... [create a **pretty string** representation?](howto.md#toprettystring)
-<!-- TODO(kevinb): should the above be only a selected subset? -->
-
-## <a name="more"></a>More information
-
-See the links in the sidebar at the top left.
-
-<!-- TODO(kevinb): there are some tidbits of information that don't seem to
- belong anywhere yet; such as how it implements floating-point equality -->
+<!-- TODO(kevinb): should the above be only a selected subset? -->
diff --git a/value/userguide/practices.md b/value/userguide/practices.md
index 0121bd83..e78c6e5a 100644
--- a/value/userguide/practices.md
+++ b/value/userguide/practices.md
@@ -1,7 +1,6 @@
# Best practices
-
## <a name="interchangeable"></a>"Equals means interchangeable"
Don't use AutoValue to implement value semantics unless you really want value
diff --git a/value/userguide/why.md b/value/userguide/why.md
index 603abb83..40c994f6 100644
--- a/value/userguide/why.md
+++ b/value/userguide/why.md
@@ -15,5 +15,4 @@ This
[slide presentation] compares AutoValue to numerous alternatives and explains
why we think it is better.
-
[slide presentation]: https://docs.google.com/presentation/d/14u_h-lMn7f1rXE1nDiLX0azS3IkgjGl5uxp5jGJ75RE/edit