Skip to content

Commit 56bf699

Browse files
author
Leland Takamine
committed
Update SuperficialValidation to validate supertypes and superinterfaces recursively.
1 parent 9f36c57 commit 56bf699

File tree

2 files changed

+191
-19
lines changed

2 files changed

+191
-19
lines changed

common/src/main/java/com/google/auto/common/SuperficialValidation.java

+42-19
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
*/
1616
package com.google.auto.common;
1717

18+
import java.util.HashSet;
1819
import java.util.List;
1920
import java.util.Map;
21+
import java.util.Set;
22+
import java.util.stream.Collectors;
2023
import javax.lang.model.element.AnnotationMirror;
2124
import javax.lang.model.element.AnnotationValue;
2225
import javax.lang.model.element.AnnotationValueVisitor;
@@ -46,27 +49,43 @@
4649
* @author Gregory Kick
4750
*/
4851
public final class SuperficialValidation {
52+
53+
private final Set<Element> visited = new HashSet<>();
54+
55+
private SuperficialValidation() {}
56+
4957
public static boolean validateElements(Iterable<? extends Element> elements) {
58+
return new SuperficialValidation().validateElementsInternal(elements);
59+
}
60+
61+
public static boolean validateElement(Element element) {
62+
return new SuperficialValidation().validateElementInternal(element);
63+
}
64+
65+
private boolean validateElementsInternal(Iterable<? extends Element> elements) {
5066
for (Element element : elements) {
51-
if (!validateElement(element)) {
67+
if (!validateElementInternal(element)) {
5268
return false;
5369
}
5470
}
5571
return true;
5672
}
5773

58-
private static final ElementVisitor<Boolean, Void> ELEMENT_VALIDATING_VISITOR =
74+
private final ElementVisitor<Boolean, Void> ELEMENT_VALIDATING_VISITOR =
5975
new AbstractElementVisitor6<Boolean, Void>() {
6076
@Override public Boolean visitPackage(PackageElement e, Void p) {
6177
// don't validate enclosed elements because it will return types in the package
6278
return validateAnnotations(e.getAnnotationMirrors());
6379
}
6480

6581
@Override public Boolean visitType(TypeElement e, Void p) {
82+
TypeMirror superclass = e.getSuperclass();
6683
return isValidBaseElement(e)
67-
&& validateElements(e.getTypeParameters())
84+
&& validateElementsInternal(e.getTypeParameters())
6885
&& validateTypes(e.getInterfaces())
69-
&& validateType(e.getSuperclass());
86+
&& validateType(superclass)
87+
&& e.getInterfaces().stream().map(MoreTypes::asElement).allMatch(i -> validateElementInternal(i))
88+
&& (superclass.getKind() == TypeKind.NONE || validateElementInternal(MoreTypes.asElement(superclass)));
7089
}
7190

7291
@Override public Boolean visitVariable(VariableElement e, Void p) {
@@ -79,8 +98,8 @@ && validateTypes(e.getInterfaces())
7998
&& (defaultValue == null || validateAnnotationValue(defaultValue, e.getReturnType()))
8099
&& validateType(e.getReturnType())
81100
&& validateTypes(e.getThrownTypes())
82-
&& validateElements(e.getTypeParameters())
83-
&& validateElements(e.getParameters());
101+
&& validateElementsInternal(e.getTypeParameters())
102+
&& validateElementsInternal(e.getParameters());
84103
}
85104

86105
@Override public Boolean visitTypeParameter(TypeParameterElement e, Void p) {
@@ -94,17 +113,21 @@ && validateElements(e.getTypeParameters())
94113
}
95114
};
96115

97-
public static boolean validateElement(Element element) {
98-
return element.accept(ELEMENT_VALIDATING_VISITOR, null);
116+
private boolean validateElementInternal(Element element) {
117+
if (visited.add(element)) {
118+
return element.accept(ELEMENT_VALIDATING_VISITOR, null);
119+
} else {
120+
return true;
121+
}
99122
}
100123

101-
private static boolean isValidBaseElement(Element e) {
124+
private boolean isValidBaseElement(Element e) {
102125
return validateType(e.asType())
103126
&& validateAnnotations(e.getAnnotationMirrors())
104-
&& validateElements(e.getEnclosedElements());
127+
&& validateElementsInternal(e.getEnclosedElements());
105128
}
106129

107-
private static boolean validateTypes(Iterable<? extends TypeMirror> types) {
130+
private boolean validateTypes(Iterable<? extends TypeMirror> types) {
108131
for (TypeMirror type : types) {
109132
if (!validateType(type)) {
110133
return false;
@@ -118,7 +141,7 @@ private static boolean validateTypes(Iterable<? extends TypeMirror> types) {
118141
* an issue. Javac turns the whole type parameter into an error type if it can't figure out the
119142
* bounds.
120143
*/
121-
private static final TypeVisitor<Boolean, Void> TYPE_VALIDATING_VISITOR =
144+
private final TypeVisitor<Boolean, Void> TYPE_VALIDATING_VISITOR =
122145
new SimpleTypeVisitor6<Boolean, Void>() {
123146
@Override
124147
protected Boolean defaultAction(TypeMirror t, Void p) {
@@ -163,11 +186,11 @@ && validateTypes(t.getThrownTypes())
163186
}
164187
};
165188

166-
private static boolean validateType(TypeMirror type) {
189+
private boolean validateType(TypeMirror type) {
167190
return type.accept(TYPE_VALIDATING_VISITOR, null);
168191
}
169192

170-
private static boolean validateAnnotations(
193+
private boolean validateAnnotations(
171194
Iterable<? extends AnnotationMirror> annotationMirrors) {
172195
for (AnnotationMirror annotationMirror : annotationMirrors) {
173196
if (!validateAnnotation(annotationMirror)) {
@@ -177,13 +200,13 @@ private static boolean validateAnnotations(
177200
return true;
178201
}
179202

180-
private static boolean validateAnnotation(AnnotationMirror annotationMirror) {
203+
private boolean validateAnnotation(AnnotationMirror annotationMirror) {
181204
return validateType(annotationMirror.getAnnotationType())
182205
&& validateAnnotationValues(annotationMirror.getElementValues());
183206
}
184207

185208
@SuppressWarnings("unused")
186-
private static boolean validateAnnotationValues(
209+
private boolean validateAnnotationValues(
187210
Map<? extends ExecutableElement, ? extends AnnotationValue> valueMap) {
188211
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> valueEntry :
189212
valueMap.entrySet()) {
@@ -195,7 +218,7 @@ private static boolean validateAnnotationValues(
195218
return true;
196219
}
197220

198-
private static final AnnotationValueVisitor<Boolean, TypeMirror> VALUE_VALIDATING_VISITOR =
221+
private final AnnotationValueVisitor<Boolean, TypeMirror> VALUE_VALIDATING_VISITOR =
199222
new SimpleAnnotationValueVisitor6<Boolean, TypeMirror>() {
200223
@Override protected Boolean defaultAction(Object o, TypeMirror expectedType) {
201224
return MoreTypes.isTypeOf(o.getClass(), expectedType);
@@ -232,7 +255,7 @@ public Boolean visitArray(List<? extends AnnotationValue> values, TypeMirror exp
232255
@Override
233256
public Boolean visitEnumConstant(VariableElement enumConstant, TypeMirror expectedType) {
234257
return MoreTypes.equivalence().equivalent(enumConstant.asType(), expectedType)
235-
&& validateElement(enumConstant);
258+
&& validateElementInternal(enumConstant);
236259
}
237260

238261
@Override public Boolean visitType(TypeMirror type, TypeMirror ignored) {
@@ -276,7 +299,7 @@ public Boolean visitEnumConstant(VariableElement enumConstant, TypeMirror expect
276299
}
277300
};
278301

279-
private static boolean validateAnnotationValue(
302+
private boolean validateAnnotationValue(
280303
AnnotationValue annotationValue, TypeMirror expectedType) {
281304
return annotationValue.accept(VALUE_VALIDATING_VISITOR, expectedType);
282305
}

common/src/test/java/com/google/auto/common/SuperficialValidationTest.java

+149
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
import static com.google.common.truth.Truth.assertThat;
2020
import static com.google.common.truth.Truth.assertWithMessage;
2121
import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
22+
import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
2223

24+
import com.google.common.collect.ImmutableList;
2325
import com.google.common.collect.ImmutableSet;
2426
import com.google.testing.compile.JavaFileObjects;
2527
import java.util.Set;
@@ -199,6 +201,63 @@ public void handlesRecursiveType() {
199201
.compilesWithoutError();
200202
}
201203

204+
@Test
205+
public void handlesInnerSubclass() {
206+
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
207+
"test.TestClass",
208+
"package test;",
209+
"",
210+
"abstract class TestClass {",
211+
" class InnerTestClass extends TestClass {}",
212+
"}");
213+
assertAbout(javaSource())
214+
.that(javaFileObject)
215+
.processedWith(new AssertingProcessor() {
216+
@Override void runAssertions() {
217+
TypeElement testClassElement =
218+
processingEnv.getElementUtils().getTypeElement("test.TestClass");
219+
assertThat(SuperficialValidation.validateElement(testClassElement)).isTrue();
220+
}
221+
})
222+
.compilesWithoutError();
223+
}
224+
225+
@Test
226+
public void handlesRecursiveSuperinterface() {
227+
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
228+
"test.TestClass",
229+
"package test;",
230+
"",
231+
"interface TestClass implements TestClass {}");
232+
assertAbout(javaSource())
233+
.that(javaFileObject)
234+
.processedWith(new AssertingProcessor() {
235+
@Override
236+
void runAssertions() {
237+
assertWithMessage("Should not reach annotation processing.").fail();
238+
}
239+
})
240+
.failsToCompile();
241+
}
242+
243+
@Test
244+
public void handlesRecursiveSuperclass() {
245+
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
246+
"test.TestClass",
247+
"package test;",
248+
"",
249+
"class TestClass extends TestClass {}");
250+
assertAbout(javaSource())
251+
.that(javaFileObject)
252+
.processedWith(new AssertingProcessor() {
253+
@Override
254+
void runAssertions() {
255+
assertWithMessage("Should not reach annotation processing.").fail();
256+
}
257+
})
258+
.failsToCompile();
259+
}
260+
202261
@Test
203262
public void missingWildcardBound() {
204263
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
@@ -276,6 +335,96 @@ void runAssertions() {
276335
.failsToCompile();
277336
}
278337

338+
@Test
339+
public void missingSuperclass() {
340+
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
341+
"test.TestClass",
342+
"package test;",
343+
"",
344+
"class TestClass extends Missing {}");
345+
assertAbout(javaSource())
346+
.that(javaFileObject)
347+
.processedWith(new AssertingProcessor() {
348+
@Override
349+
void runAssertions() {
350+
TypeElement testClassElement =
351+
processingEnv.getElementUtils().getTypeElement("test.TestClass");
352+
assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
353+
}
354+
})
355+
.failsToCompile();
356+
}
357+
358+
@Test
359+
public void missingSuperinterface() {
360+
JavaFileObject javaFileObject = JavaFileObjects.forSourceLines(
361+
"test.TestClass",
362+
"package test;",
363+
"",
364+
"class TestClass implements Missing {}");
365+
assertAbout(javaSource())
366+
.that(javaFileObject)
367+
.processedWith(new AssertingProcessor() {
368+
@Override
369+
void runAssertions() {
370+
TypeElement testClassElement =
371+
processingEnv.getElementUtils().getTypeElement("test.TestClass");
372+
assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
373+
}
374+
})
375+
.failsToCompile();
376+
}
377+
378+
@Test
379+
public void missingGrandparentSuperclass() {
380+
JavaFileObject parentJavaFileObject = JavaFileObjects.forSourceLines(
381+
"test.Parent",
382+
"package test;",
383+
"",
384+
"class Parent extends Missing {}");
385+
JavaFileObject testClassJavaFileObject = JavaFileObjects.forSourceLines(
386+
"test.TestClass",
387+
"package test;",
388+
"",
389+
"class TestClass extends Parent {}");
390+
assertAbout(javaSources())
391+
.that(ImmutableList.of(parentJavaFileObject, testClassJavaFileObject))
392+
.processedWith(new AssertingProcessor() {
393+
@Override
394+
void runAssertions() {
395+
TypeElement testClassElement =
396+
processingEnv.getElementUtils().getTypeElement("test.TestClass");
397+
assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
398+
}
399+
})
400+
.failsToCompile();
401+
}
402+
403+
@Test
404+
public void missingGrandparentSuperinterface() {
405+
JavaFileObject parentJavaFileObject = JavaFileObjects.forSourceLines(
406+
"test.Parent",
407+
"package test;",
408+
"",
409+
"interface Parent extends Missing {}");
410+
JavaFileObject testClassJavaFileObject = JavaFileObjects.forSourceLines(
411+
"test.TestClass",
412+
"package test;",
413+
"",
414+
"class TestClass implements Parent {}");
415+
assertAbout(javaSources())
416+
.that(ImmutableList.of(parentJavaFileObject, testClassJavaFileObject))
417+
.processedWith(new AssertingProcessor() {
418+
@Override
419+
void runAssertions() {
420+
TypeElement testClassElement =
421+
processingEnv.getElementUtils().getTypeElement("test.TestClass");
422+
assertThat(SuperficialValidation.validateElement(testClassElement)).isFalse();
423+
}
424+
})
425+
.failsToCompile();
426+
}
427+
279428
private abstract static class AssertingProcessor extends AbstractProcessor {
280429
@Override
281430
public Set<String> getSupportedAnnotationTypes() {

0 commit comments

Comments
 (0)