Skip to content

Commit 59c4ecc

Browse files
lmcreanGoogle Java Core Libraries
authored andcommitted
Improve exception messages for annotation-related methods on synthetic TypeVariable instances.
Fixes #7974 RELNOTES=n/a PiperOrigin-RevId: 810604229
1 parent f5d8ecc commit 59c4ecc

File tree

4 files changed

+38
-8
lines changed

4 files changed

+38
-8
lines changed

android/guava/src/com/google/common/reflect/TypeResolver.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,13 @@ Type resolveInternal(TypeVariable<?> var, TypeTable forDependants) {
368368
* by us. And that equality is guaranteed to hold because it doesn't involve the JDK
369369
* TypeVariable implementation at all.
370370
*
371-
* TODO: b/147144588 - But what about when the TypeVariable has annotations? Our
372-
* implementation currently doesn't support annotations _at all_. It could at least be made
373-
* to respond to queries about annotations by returning null/empty, but are there situations
374-
* in which it should return something else?
371+
* NOTE: b/147144588 - Custom TypeVariables created by Guava do not preserve
372+
* annotations. This is intentional. The semantics of annotation handling during
373+
* type resolution are unclear and have changed across Java versions. Until there's
374+
* a clear specification for what annotations should mean on resolved TypeVariables
375+
* with modified bounds, annotation methods will throw
376+
* UnsupportedOperationException. Frameworks requiring annotation preservation
377+
* should use the original TypeVariable when bounds haven't changed.
375378
*/
376379
if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY
377380
&& Arrays.equals(bounds, resolvedBounds)) {

android/guava/src/com/google/common/reflect/Types.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,18 @@ private static final class TypeVariableInvocationHandler implements InvocationHa
382382
String methodName = method.getName();
383383
Method typeVariableMethod = typeVariableMethods.get(methodName);
384384
if (typeVariableMethod == null) {
385+
if (methodName.equals("getAnnotatedBounds")
386+
|| methodName.equals("isAnnotationPresent")
387+
// Each of these prefixes is shared by a family of methods:
388+
|| methodName.startsWith("getAnnotation")
389+
|| methodName.startsWith("getDeclaredAnnotation")) {
390+
throw new UnsupportedOperationException(
391+
"Annotation methods are not supported on synthetic TypeVariables created during type"
392+
+ " resolution. The semantics of annotations on resolved types with modified"
393+
+ " bounds are undefined. Use the original TypeVariable for annotation access."
394+
+ " See b/147144588.");
395+
}
396+
// If any other method appears or if we forgot one, include it in the exception message:
385397
throw new UnsupportedOperationException(methodName);
386398
} else {
387399
try {

guava/src/com/google/common/reflect/TypeResolver.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -368,10 +368,13 @@ Type resolveInternal(TypeVariable<?> var, TypeTable forDependants) {
368368
* by us. And that equality is guaranteed to hold because it doesn't involve the JDK
369369
* TypeVariable implementation at all.
370370
*
371-
* TODO: b/147144588 - But what about when the TypeVariable has annotations? Our
372-
* implementation currently doesn't support annotations _at all_. It could at least be made
373-
* to respond to queries about annotations by returning null/empty, but are there situations
374-
* in which it should return something else?
371+
* NOTE: b/147144588 - Custom TypeVariables created by Guava do not preserve
372+
* annotations. This is intentional. The semantics of annotation handling during
373+
* type resolution are unclear and have changed across Java versions. Until there's
374+
* a clear specification for what annotations should mean on resolved TypeVariables
375+
* with modified bounds, annotation methods will throw
376+
* UnsupportedOperationException. Frameworks requiring annotation preservation
377+
* should use the original TypeVariable when bounds haven't changed.
375378
*/
376379
if (Types.NativeTypeVariableEquals.NATIVE_TYPE_VARIABLE_ONLY
377380
&& Arrays.equals(bounds, resolvedBounds)) {

guava/src/com/google/common/reflect/Types.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,18 @@ private static final class TypeVariableInvocationHandler implements InvocationHa
382382
String methodName = method.getName();
383383
Method typeVariableMethod = typeVariableMethods.get(methodName);
384384
if (typeVariableMethod == null) {
385+
if (methodName.equals("getAnnotatedBounds")
386+
|| methodName.equals("isAnnotationPresent")
387+
// Each of these prefixes is shared by a family of methods:
388+
|| methodName.startsWith("getAnnotation")
389+
|| methodName.startsWith("getDeclaredAnnotation")) {
390+
throw new UnsupportedOperationException(
391+
"Annotation methods are not supported on synthetic TypeVariables created during type"
392+
+ " resolution. The semantics of annotations on resolved types with modified"
393+
+ " bounds are undefined. Use the original TypeVariable for annotation access."
394+
+ " See b/147144588.");
395+
}
396+
// If any other method appears or if we forgot one, include it in the exception message:
385397
throw new UnsupportedOperationException(methodName);
386398
} else {
387399
try {

0 commit comments

Comments
 (0)