aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java12
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoOneOfProcessor.java9
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueProcessor.java31
-rw-r--r--value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java88
-rw-r--r--value/src/main/java/com/google/auto/value/processor/BuilderSpec.java32
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java60
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoOneOfCompilationTest.java85
-rw-r--r--value/src/test/java/com/google/auto/value/processor/AutoValueCompilationTest.java103
8 files changed, 356 insertions, 64 deletions
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
index 79bccafd..fc0d8b3e 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoBuilderProcessor.java
@@ -80,7 +80,7 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
private static final String ALLOW_OPTION = "com.google.auto.value.AutoBuilderIsUnstable";
public AutoBuilderProcessor() {
- super(AUTO_BUILDER_NAME);
+ super(AUTO_BUILDER_NAME, /* appliesToInterfaces= */ true);
}
@Override
@@ -101,14 +101,6 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
if (processingEnv.getOptions().containsKey(ALLOW_OPTION)) {
errorReporter().reportWarning(autoBuilderType, "The -A%s option is obsolete", 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();
@@ -125,7 +117,7 @@ public class AutoBuilderProcessor extends AutoValueishProcessor {
Optional<BuilderMethodClassifier<VariableElement>> maybeClassifier =
BuilderMethodClassifierForAutoBuilder.classify(
methods, errorReporter(), processingEnv, executable, builtType, autoBuilderType);
- if (!maybeClassifier.isPresent()) {
+ if (!maybeClassifier.isPresent() || errorReporter().errorCount() > 0) {
// We've already output one or more error messages.
return;
}
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 711b138c..4d19d216 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
@@ -60,7 +60,7 @@ import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType;
@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.ISOLATING)
public class AutoOneOfProcessor extends AutoValueishProcessor {
public AutoOneOfProcessor() {
- super(AUTO_ONE_OF_NAME);
+ super(AUTO_ONE_OF_NAME, /* appliesToInterfaces= */ false);
}
@Override
@@ -75,13 +75,6 @@ public class AutoOneOfProcessor extends AutoValueishProcessor {
@Override
void processType(TypeElement autoOneOfType) {
- if (autoOneOfType.getKind() != ElementKind.CLASS) {
- errorReporter()
- .abortWithError(
- autoOneOfType,
- "[AutoOneOfNotClass] @" + AUTO_ONE_OF_NAME + " only applies to classes");
- }
- checkModifiersIfNested(autoOneOfType);
DeclaredType kindMirror = mirrorForKindType(autoOneOfType);
// We are going to classify the methods of the @AutoOneOf class into several categories.
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 ab7da924..4479a056 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
@@ -18,6 +18,7 @@ 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.base.Preconditions.checkState;
import static com.google.common.collect.Sets.difference;
import static com.google.common.collect.Sets.intersection;
import static java.util.Comparator.naturalOrder;
@@ -45,7 +46,6 @@ 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.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
@@ -79,21 +79,24 @@ public class AutoValueProcessor extends AutoValueishProcessor {
@VisibleForTesting
AutoValueProcessor(ClassLoader loaderForExtensions) {
- super(AUTO_VALUE_NAME);
- this.extensions = null;
- this.loaderForExtensions = loaderForExtensions;
+ this(ImmutableList.of(), loaderForExtensions);
}
@VisibleForTesting
- public AutoValueProcessor(Iterable<? extends AutoValueExtension> extensions) {
- super(AUTO_VALUE_NAME);
- this.extensions = ImmutableList.copyOf(extensions);
- this.loaderForExtensions = null;
+ public AutoValueProcessor(Iterable<? extends AutoValueExtension> testExtensions) {
+ this(testExtensions, null);
+ }
+
+ private AutoValueProcessor(
+ Iterable<? extends AutoValueExtension> testExtensions, ClassLoader loaderForExtensions) {
+ super(AUTO_VALUE_NAME, /* appliesToInterfaces= */ false);
+ this.extensions = ImmutableList.copyOf(testExtensions);
+ this.loaderForExtensions = loaderForExtensions;
}
// Depending on how this AutoValueProcessor was constructed, we might already have a list of
- // extensions when init() is run, or, if `extensions` is null, we have a ClassLoader that will be
- // used to get the list using the ServiceLoader API.
+ // extensions when init() is run, or, if `loaderForExtensions` is not null, it is a ClassLoader
+ // that will be used to get the list using the ServiceLoader API.
private ImmutableList<AutoValueExtension> extensions;
private final ClassLoader loaderForExtensions;
@@ -108,7 +111,8 @@ public class AutoValueProcessor extends AutoValueishProcessor {
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
- if (extensions == null) {
+ if (loaderForExtensions != null) {
+ checkState(extensions.isEmpty());
try {
extensions = extensionsFromLoader(loaderForExtensions);
} catch (RuntimeException | Error e) {
@@ -165,10 +169,6 @@ public class AutoValueProcessor extends AutoValueishProcessor {
@Override
void processType(TypeElement type) {
- if (type.getKind() != ElementKind.CLASS) {
- errorReporter()
- .abortWithError(type, "[AutoValueNotClass] @AutoValue only applies to classes");
- }
if (ancestorIsAutoValue(type)) {
errorReporter()
.abortWithError(type, "[AutoValueExtend] One @AutoValue class may not extend another");
@@ -180,7 +180,6 @@ public class AutoValueProcessor extends AutoValueishProcessor {
"[AutoValueImplAnnotation] @AutoValue may not be used to implement an annotation"
+ " interface; try using @AutoAnnotation instead");
}
- checkModifiersIfNested(type);
// We are going to classify the methods of the @AutoValue class into several categories.
// This covers the methods in the class itself and the ones it inherits from supertypes.
diff --git a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
index 55a94a71..56e8a98a 100644
--- a/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
+++ b/value/src/main/java/com/google/auto/value/processor/AutoValueishProcessor.java
@@ -29,6 +29,7 @@ import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
+import static javax.lang.model.util.ElementFilter.constructorsIn;
import com.google.auto.common.MoreElements;
import com.google.auto.common.MoreTypes;
@@ -92,19 +93,22 @@ import javax.tools.JavaFileObject;
*/
abstract class AutoValueishProcessor extends AbstractProcessor {
private final String annotationClassName;
+ private final boolean appliesToInterfaces;
/**
- * Qualified names of {@code @AutoValue} or {@code AutoOneOf} classes that we attempted to process
- * but had to abandon because we needed other types that they referenced and those other types
- * were missing.
+ * Qualified names of {@code @AutoValue} (etc) classes that we attempted to process but had to
+ * abandon because we needed other types that they referenced and those other types were missing.
*/
private final List<String> deferredTypeNames = new ArrayList<>();
- AutoValueishProcessor(String annotationClassName) {
+ AutoValueishProcessor(String annotationClassName, boolean appliesToInterfaces) {
this.annotationClassName = annotationClassName;
+ this.appliesToInterfaces = appliesToInterfaces;
}
- /** The annotation we are processing, {@code AutoValue} or {@code AutoOneOf}. */
+ /**
+ * The annotation we are processing, for example {@code AutoValue} or {@code AutoBuilder}.
+ */
private TypeElement annotationType;
/** The simple name of {@link #annotationType}. */
private String simpleAnnotationName;
@@ -117,6 +121,10 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
super.init(processingEnv);
errorReporter = new ErrorReporter(processingEnv);
nullables = new Nullables(processingEnv);
+ annotationType = elementUtils().getTypeElement(annotationClassName);
+ if (annotationType != null) {
+ simpleAnnotationName = annotationType.getSimpleName().toString();
+ }
}
final ErrorReporter errorReporter() {
@@ -132,9 +140,9 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
}
/**
- * Qualified names of {@code @AutoValue} or {@code AutoOneOf} classes that we attempted to process
- * but had to abandon because we needed other types that they referenced and those other types
- * were missing. This is used by tests.
+ * Qualified names of {@code @AutoValue} (etc) classes that we attempted to process but had to
+ * abandon because we needed other types that they referenced and those other types were missing.
+ * This is used by tests.
*/
final ImmutableList<String> deferredTypeNames() {
return ImmutableList.copyOf(deferredTypeNames);
@@ -339,7 +347,6 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
@Override
public final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
- annotationType = elementUtils().getTypeElement(annotationClassName);
if (annotationType == null) {
// This should not happen. If the annotation type is not found, how did the processor get
// triggered?
@@ -352,7 +359,6 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
+ " because the annotation class was not found");
return false;
}
- simpleAnnotationName = annotationType.getSimpleName().toString();
List<TypeElement> deferredTypes =
deferredTypeNames.stream()
.map(name -> elementUtils().getTypeElement(name))
@@ -364,9 +370,10 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
for (TypeElement type : deferredTypes) {
errorReporter.reportError(
type,
- "[AutoValueUndefined] Did not generate @%s class for %s because it references"
+ "[%sUndefined] Did not generate @%s class for %s because it references"
+ " undefined types",
simpleAnnotationName,
+ simpleAnnotationName,
type.getQualifiedName());
}
return false;
@@ -381,6 +388,7 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
deferredTypeNames.clear();
for (TypeElement type : types) {
try {
+ validateType(type);
processType(type);
} catch (AbortProcessingException e) {
// We abandoned this type; continue with the next.
@@ -396,7 +404,8 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
String trace = Throwables.getStackTraceAsString(e);
errorReporter.reportError(
type,
- "[AutoValueException] @%s processor threw an exception: %s",
+ "[%sException] @%s processor threw an exception: %s",
+ simpleAnnotationName,
simpleAnnotationName,
trace);
throw e;
@@ -406,8 +415,37 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
}
/**
- * Analyzes a single {@code @AutoValue} or {@code @AutoOneOf} class, and outputs the corresponding
- * implementation class or classes.
+ * Validations common to all the subclasses. An {@code @AutoFoo} type must be a class, or possibly
+ * an interface for {@code @AutoBuilder}. If it is a class then it must have a non-private no-arg
+ * constructor.
+ */
+ private void validateType(TypeElement type) {
+ ElementKind kind = type.getKind();
+ boolean kindOk =
+ kind.equals(ElementKind.CLASS)
+ || (appliesToInterfaces && kind.equals(ElementKind.INTERFACE));
+ if (!kindOk) {
+ String appliesTo = appliesToInterfaces ? "classes and interfaces" : "classes";
+ errorReporter.abortWithError(
+ type,
+ "[%sWrongType] @%s only applies to %s",
+ simpleAnnotationName,
+ simpleAnnotationName,
+ appliesTo);
+ }
+ checkModifiersIfNested(type);
+ if (!hasVisibleNoArgConstructor(type)) {
+ errorReporter.reportError(
+ type,
+ "[%sConstructor] @%s class must have a non-private no-arg constructor",
+ simpleAnnotationName,
+ simpleAnnotationName);
+ }
+ }
+
+ /**
+ * Analyzes a single {@code @AutoValue} (etc) class, and outputs the corresponding implementation
+ * class or classes.
*
* @param type the class with the {@code @AutoValue} or {@code @AutoOneOf} annotation.
*/
@@ -469,7 +507,9 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
if (p.isNullable() && returnType.getKind().isPrimitive()) {
errorReporter()
.reportError(
- propertyMethod, "[AutoValueNullPrimitive] Primitive types cannot be @Nullable");
+ propertyMethod,
+ "[%sNullPrimitive] Primitive types cannot be @Nullable",
+ simpleAnnotationName);
}
});
return props.build();
@@ -508,11 +548,10 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
}
/**
- * Returns the name of the generated {@code @AutoValue} or {@code @AutoOneOf} class, for example
- * {@code AutoOneOf_TaskResult} or {@code $$AutoValue_SimpleMethod}.
+ * Returns the name of the generated {@code @AutoValue} (etc) class, for example {@code
+ * AutoOneOf_TaskResult} or {@code $$AutoValue_SimpleMethod}.
*
- * @param type the name of the type bearing the {@code @AutoValue} or {@code @AutoOneOf}
- * annotation.
+ * @param type the name of the type bearing the {@code @AutoValue} (etc) annotation.
* @param prefix the prefix to use in the generated class. This may start with one or more dollar
* signs, for an {@code @AutoValue} implementation where there are AutoValue extensions.
*/
@@ -589,7 +628,8 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
for (ExecutableElement context : contexts) {
errorReporter.reportError(
context,
- "[AutoValueDupProperty] More than one @%s property called %s",
+ "[%sDupProperty] More than one @%s property called %s",
+ simpleAnnotationName,
simpleAnnotationName,
name);
}
@@ -1187,6 +1227,14 @@ abstract class AutoValueishProcessor extends AbstractProcessor {
return getAnnotationMirror(element, annotationName).isPresent();
}
+ /** True if the type is a class with a non-private no-arg constructor, or is an interface. */
+ static boolean hasVisibleNoArgConstructor(TypeElement type) {
+ return type.getKind().isInterface()
+ || constructorsIn(type.getEnclosedElements()).stream()
+ .anyMatch(
+ c -> c.getParameters().isEmpty() && !c.getModifiers().contains(Modifier.PRIVATE));
+ }
+
final void writeSourceFile(String className, String text, TypeElement originatingType) {
try {
JavaFileObject sourceFile =
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 dfdbb8c7..b612c104 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
@@ -18,6 +18,7 @@ package com.google.auto.value.processor;
import static com.google.auto.common.MoreElements.getLocalAndInheritedMethods;
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.hasVisibleNoArgConstructor;
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;
@@ -86,16 +87,9 @@ class BuilderSpec {
Optional<TypeElement> builderTypeElement = Optional.empty();
for (TypeElement containedClass : typesIn(autoValueClass.getEnclosedElements())) {
if (hasAnnotationMirror(containedClass, AUTO_VALUE_BUILDER_NAME)) {
- if (!CLASS_OR_INTERFACE.contains(containedClass.getKind())) {
- errorReporter.reportError(
- containedClass,
- "[AutoValueBuilderClass] @AutoValue.Builder can only apply to a class or an"
- + " interface");
- } else if (!containedClass.getModifiers().contains(Modifier.STATIC)) {
- errorReporter.reportError(
- containedClass,
- "[AutoValueInnerBuilder] @AutoValue.Builder cannot be applied to a non-static class");
- } else if (builderTypeElement.isPresent()) {
+ findBuilderError(containedClass)
+ .ifPresent(error -> errorReporter.reportError(containedClass, "%s", error));
+ if (builderTypeElement.isPresent()) {
errorReporter.reportError(
containedClass,
"[AutoValueTwoBuilders] %s already has a Builder: %s",
@@ -114,6 +108,24 @@ class BuilderSpec {
}
}
+ /** Finds why this {@code @AutoValue.Builder} class is bad, if it is bad. */
+ private Optional<String> findBuilderError(TypeElement builderTypeElement) {
+ if (!CLASS_OR_INTERFACE.contains(builderTypeElement.getKind())) {
+ return Optional.of(
+ "[AutoValueBuilderClass] @AutoValue.Builder can only apply to a class or an"
+ + " interface");
+ } else if (!builderTypeElement.getModifiers().contains(Modifier.STATIC)) {
+ return Optional.of(
+ "[AutoValueInnerBuilder] @AutoValue.Builder cannot be applied to a non-static class");
+ } else if (builderTypeElement.getKind().equals(ElementKind.CLASS)
+ && !hasVisibleNoArgConstructor(builderTypeElement)) {
+ return Optional.of(
+ "[AutoValueBuilderConstructor] @AutoValue.Builder class must have a non-private no-arg"
+ + " constructor");
+ }
+ return Optional.empty();
+ }
+
/** Representation of an {@code AutoValue.Builder} class or interface. */
class Builder implements AutoValueExtension.BuilderContext {
private final TypeElement builderTypeElement;
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
index 400250f0..96649bd1 100644
--- a/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java
+++ b/value/src/test/java/com/google/auto/value/processor/AutoBuilderCompilationTest.java
@@ -247,6 +247,66 @@ public final class AutoBuilderCompilationTest {
}
@Test
+ public void autoBuilderClassMustHaveNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " @AutoBuilder",
+ " abstract static class Builder {",
+ " Builder(int bogus) {}",
+ " Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderConstructor] @AutoBuilder class must have a non-private no-arg"
+ + " constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Builder");
+ }
+
+ @Test
+ public void autoBuilderClassMustHaveVisibleNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoBuilder;",
+ "",
+ "public class Baz {",
+ " @AutoBuilder",
+ " abstract static class Builder {",
+ " private Builder() {}",
+ " Baz build();",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoBuilderProcessor())
+ .withOptions("-Acom.google.auto.value.AutoBuilderIsUnstable")
+ .compile(javaFileObject);
+ assertThat(compilation).failed();
+ assertThat(compilation)
+ .hadErrorContaining(
+ "[AutoBuilderConstructor] @AutoBuilder class must have a non-private no-arg"
+ + " constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Builder");
+ }
+
+ @Test
public void autoBuilderNestedInPrivate() {
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(
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 788b543a..a55b74d0 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
@@ -463,6 +463,33 @@ public class AutoOneOfCompilationTest {
}
@Test
+ public void mustBeClass() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Pet",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoOneOf;",
+ "",
+ "@AutoOneOf(Pet.Kind.class)",
+ "public interface Pet {",
+ " public enum Kind {",
+ " DOG,",
+ " CAT,",
+ " }",
+ " Kind getKind();",
+ " String dog();",
+ " String cat();",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoOneOf only applies to classes")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Pet");
+ }
+
+ @Test
public void cantBeNullable() {
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(
@@ -490,4 +517,62 @@ public class AutoOneOfCompilationTest {
.inFile(javaFileObject)
.onLineContaining("@Nullable String dog()");
}
+
+ @Test
+ public void mustHaveNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Pet",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoOneOf;",
+ "",
+ "@AutoOneOf(Pet.Kind.class)",
+ "public abstract class Pet {",
+ " Pet(boolean cuddly) {}",
+ "",
+ " public enum Kind {",
+ " DOG,",
+ " CAT,",
+ " }",
+ " public abstract Kind getKind();",
+ " public abstract String dog();",
+ " public abstract String cat();",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoOneOf class must have a non-private no-arg constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Pet");
+ }
+
+ @Test
+ public void mustHaveVisibleNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Pet",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoOneOf;",
+ "",
+ "@AutoOneOf(Pet.Kind.class)",
+ "public abstract class Pet {",
+ " private Pet() {}",
+ "",
+ " public enum Kind {",
+ " DOG,",
+ " CAT,",
+ " }",
+ " public abstract Kind getKind();",
+ " public abstract String dog();",
+ " public abstract String cat();",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new AutoOneOfProcessor()).compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoOneOf class must have a non-private no-arg constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Pet");
+ }
}
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 1bb84f75..cd21ef37 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
@@ -556,6 +556,27 @@ public class AutoValueCompilationTest {
}
@Test
+ public void autoValueMustBeClass() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "public interface Baz {",
+ " String buh();",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoValue only applies to classes")
+ .inFile(javaFileObject)
+ .onLineContaining("interface Baz");
+ }
+
+ @Test
public void autoValueMustBeStatic() {
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(
@@ -636,6 +657,52 @@ public class AutoValueCompilationTest {
}
@Test
+ public void autoValueMustHaveNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "public abstract class Baz {",
+ " Baz(int buh) {}",
+ "",
+ " public abstract int buh();",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoValue class must have a non-private no-arg constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Baz");
+ }
+
+ @Test
+ public void autoValueMustHaveVisibleNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Baz",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "@AutoValue",
+ "public abstract class Baz {",
+ " private Baz() {}",
+ "",
+ " public abstract int buh();",
+ "}");
+ Compilation compilation =
+ javac().withProcessors(new AutoValueProcessor()).compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoValue class must have a non-private no-arg constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Baz");
+ }
+
+ @Test
public void noMultidimensionalPrimitiveArrays() {
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(
@@ -1449,6 +1516,42 @@ public class AutoValueCompilationTest {
}
@Test
+ public void autoValueBuilderMustHaveNoArgConstructor() {
+ JavaFileObject javaFileObject =
+ JavaFileObjects.forSourceLines(
+ "foo.bar.Example",
+ "package foo.bar;",
+ "",
+ "import com.google.auto.value.AutoValue;",
+ "",
+ "class Example {",
+ " @AutoValue",
+ " abstract static class Baz {",
+ " abstract int foo();",
+ "",
+ " static Builder builder() {",
+ " return new AutoValue_Example_Baz.Builder();",
+ " }",
+ "",
+ " @AutoValue.Builder",
+ " abstract static class Builder {",
+ " Builder(int defaultFoo) {}",
+ " abstract Builder foo(int x);",
+ " abstract Baz build();",
+ " }",
+ " }",
+ "}");
+ Compilation compilation =
+ javac()
+ .withProcessors(new AutoValueProcessor(), new AutoValueBuilderProcessor())
+ .compile(javaFileObject);
+ assertThat(compilation)
+ .hadErrorContaining("@AutoValue.Builder class must have a non-private no-arg constructor")
+ .inFile(javaFileObject)
+ .onLineContaining("class Builder");
+ }
+
+ @Test
public void autoValueBuilderOnEnum() {
JavaFileObject javaFileObject =
JavaFileObjects.forSourceLines(