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 a7b8ebcfa9..8dacc2c0a4 100644 --- a/common/src/main/java/com/google/auto/common/SuperficialValidation.java +++ b/common/src/main/java/com/google/auto/common/SuperficialValidation.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.AnnotationValueVisitor; @@ -63,10 +64,13 @@ public static boolean validateElements(Iterable elements) { } @Override public Boolean visitType(TypeElement e, Void p) { + TypeMirror superclass = e.getSuperclass(); return isValidBaseElement(e) && validateElements(e.getTypeParameters()) && validateTypes(e.getInterfaces()) - && validateType(e.getSuperclass()); + && validateType(superclass) + && e.getInterfaces().stream().map(MoreTypes::asElement).allMatch(SuperficialValidation::validateElement) + && (superclass.getKind() == TypeKind.NONE || validateElement(MoreTypes.asElement(superclass))); } @Override public Boolean visitVariable(VariableElement e, Void p) { 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 15e54fff52..bbb41a8772 100644 --- a/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java +++ b/common/src/test/java/com/google/auto/common/SuperficialValidationTest.java @@ -19,7 +19,9 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; +import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.testing.compile.JavaFileObjects; import java.util.Set; @@ -199,6 +201,42 @@ public void handlesRecursiveType() { .compilesWithoutError(); } + @Test + public void handlesRecursiveSuperinterface() { + JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( + "test.TestClass", + "package test;", + "", + "interface TestClass implements TestClass {}"); + assertAbout(javaSource()) + .that(javaFileObject) + .processedWith(new AssertingProcessor() { + @Override + void runAssertions() { + assertWithMessage("Should not reach annotation processing.").fail(); + } + }) + .failsToCompile(); + } + + @Test + public void handlesRecursiveSuperclass() { + JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( + "test.TestClass", + "package test;", + "", + "class TestClass extends TestClass {}"); + assertAbout(javaSource()) + .that(javaFileObject) + .processedWith(new AssertingProcessor() { + @Override + void runAssertions() { + assertWithMessage("Should not reach annotation processing.").fail(); + } + }) + .failsToCompile(); + } + @Test public void missingWildcardBound() { JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( @@ -276,6 +314,96 @@ void runAssertions() { .failsToCompile(); } + @Test + public void missingSuperclass() { + JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( + "test.TestClass", + "package test;", + "", + "class TestClass extends Missing {}"); + assertAbout(javaSource()) + .that(javaFileObject) + .processedWith(new AssertingProcessor() { + @Override + void runAssertions() { + TypeElement testClassElement = + processingEnv.getElementUtils().getTypeElement("test.TestClass"); + assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); + } + }) + .failsToCompile(); + } + + @Test + public void missingSuperinterface() { + JavaFileObject javaFileObject = JavaFileObjects.forSourceLines( + "test.TestClass", + "package test;", + "", + "class TestClass implements Missing {}"); + assertAbout(javaSource()) + .that(javaFileObject) + .processedWith(new AssertingProcessor() { + @Override + void runAssertions() { + TypeElement testClassElement = + processingEnv.getElementUtils().getTypeElement("test.TestClass"); + assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); + } + }) + .failsToCompile(); + } + + @Test + public void missingGrandparentSuperclass() { + JavaFileObject parentJavaFileObject = JavaFileObjects.forSourceLines( + "test.Parent", + "package test;", + "", + "class Parent extends Missing {}"); + JavaFileObject testClassJavaFileObject = JavaFileObjects.forSourceLines( + "test.TestClass", + "package test;", + "", + "class TestClass extends Parent {}"); + assertAbout(javaSources()) + .that(ImmutableList.of(parentJavaFileObject, testClassJavaFileObject)) + .processedWith(new AssertingProcessor() { + @Override + void runAssertions() { + TypeElement testClassElement = + processingEnv.getElementUtils().getTypeElement("test.TestClass"); + assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); + } + }) + .failsToCompile(); + } + + @Test + public void missingGrandparentSuperinterface() { + JavaFileObject parentJavaFileObject = JavaFileObjects.forSourceLines( + "test.Parent", + "package test;", + "", + "interface Parent extends Missing {}"); + JavaFileObject testClassJavaFileObject = JavaFileObjects.forSourceLines( + "test.TestClass", + "package test;", + "", + "class TestClass implements Parent {}"); + assertAbout(javaSources()) + .that(ImmutableList.of(parentJavaFileObject, testClassJavaFileObject)) + .processedWith(new AssertingProcessor() { + @Override + void runAssertions() { + TypeElement testClassElement = + processingEnv.getElementUtils().getTypeElement("test.TestClass"); + assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse(); + } + }) + .failsToCompile(); + } + private abstract static class AssertingProcessor extends AbstractProcessor { @Override public Set getSupportedAnnotationTypes() {