diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java index f9e297fa8e1..9523d7dc433 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/TypeAnnotations.java @@ -32,6 +32,7 @@ import com.sun.tools.javac.code.Attribute.TypeCompound; import com.sun.tools.javac.code.Symbol.ClassSymbol; +import com.sun.tools.javac.code.Symbol.RecordComponent; import com.sun.tools.javac.code.Symbol.TypeSymbol; import com.sun.tools.javac.code.Type.ArrayType; import com.sun.tools.javac.code.Type.CapturedType; @@ -84,6 +85,7 @@ import com.sun.tools.javac.util.Log; import com.sun.tools.javac.util.Names; +import static com.sun.tools.javac.code.Flags.RECORD; import static com.sun.tools.javac.code.Kinds.Kind.*; /** @@ -1302,6 +1304,16 @@ public void visitVarDef(final JCVariableDecl tree) { if (!sigOnly) { scan(tree.init); } + + // Now that type and declaration annotations have been segregated into their own buckets ... + if (sigOnly) { + if (tree.sym != null && tree.sym.getKind() == ElementKind.FIELD && (tree.sym.flags_field & RECORD) != 0) { + RecordComponent rc = ((ClassSymbol)tree.sym.owner).getRecordComponent(tree.sym); + rc.setTypeAttributes(tree.sym.getRawTypeAttributes()); + // to get all the type annotations applied to the type + rc.type = tree.sym.type; + } + } } @Override diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java index 497163de8bf..9f92c2e5d86 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -2998,9 +2998,15 @@ private void validateAnnotation(JCAnnotation a, JCTree declarationTree, Symbol s rc.appendAttributes(s.getRawAttributes().stream().filter(anno -> Arrays.stream(getTargetNames(anno.type.tsym)).anyMatch(name -> name == names.RECORD_COMPONENT) ).collect(List.collector())); - rc.setTypeAttributes(s.getRawTypeAttributes()); - // to get all the type annotations applied to the type - rc.type = s.type; + + /* At this point, we used to carry over any type annotations from the VARDEF to the record component, but + * that is problematic, since we get here only when *some* annotation is applied to the SE5 (declaration) + * annotation location, inadvertently failing to carry over the type annotations when the VarDef has no + * annotations in the SE5 annotation location. + * + * Now type annotations are assigned to record components in a method that would execute irrespective of + * whether there are SE5 annotations on a VarDef viz com.sun.tools.javac.code.TypeAnnotations.TypeAnnotationPositions.visitVarDef + */ } } } diff --git a/test/langtools/tools/javac/records/RecordCompilationTests.java b/test/langtools/tools/javac/records/RecordCompilationTests.java index ca53ec63e39..713a910abb3 100644 --- a/test/langtools/tools/javac/records/RecordCompilationTests.java +++ b/test/langtools/tools/javac/records/RecordCompilationTests.java @@ -1479,6 +1479,57 @@ record R(@Anno String s) {} setCompileOptions(previousOptions); } + // JDK-8292159: TYPE_USE annotations on generic type arguments + // of record components discarded + public void testOnlyTypeAnnotationsOnComponentField() throws Exception { + String code = + """ + import java.lang.annotation.*; + import java.util.List; + @Target({ElementType.TYPE_USE}) + @Retention(RetentionPolicy.RUNTIME) + @interface Anno { } + record R(List<@Anno String> s) {} + """; + + File dir = assertOK(true, code); + + ClassFile classFile = ClassFile.read(findClassFileOrFail(dir, "R.class")); + + // field first + Assert.check(classFile.fields.length == 1); + Field field = classFile.fields[0]; + checkTypeAnno( + classFile, + (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(field.attributes, RuntimeVisibleTypeAnnotations_attribute.class), + "FIELD", + "Anno"); + + // checking for the annotation on the corresponding parameter of the canonical constructor + Method init = findMethodOrFail(classFile, ""); + checkTypeAnno( + classFile, + (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(init.attributes, RuntimeVisibleTypeAnnotations_attribute.class), + "METHOD_FORMAL_PARAMETER", "Anno"); + + // checking for the annotation in the accessor + Method accessor = findMethodOrFail(classFile, "s"); + checkTypeAnno( + classFile, + (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail(accessor.attributes, RuntimeVisibleTypeAnnotations_attribute.class), + "METHOD_RETURN", "Anno"); + + // checking for the annotation in the Record attribute + Record_attribute record = (Record_attribute) findAttributeOrFail(classFile.attributes, Record_attribute.class); + Assert.check(record.component_count == 1); + checkTypeAnno( + classFile, + (RuntimeVisibleTypeAnnotations_attribute) findAttributeOrFail( + record.component_info_arr[0].attributes, + RuntimeVisibleTypeAnnotations_attribute.class), + "FIELD", "Anno"); + } + private void checkTypeAnno(ClassFile classFile, RuntimeTypeAnnotations_attribute rtAnnos, String positionType,