diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java index 89422ac4671..c9c2375fab9 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/DeferredAttr.java @@ -25,6 +25,7 @@ package com.sun.tools.javac.comp; +import com.sun.source.tree.AnnotatedTypeTree; import com.sun.source.tree.LambdaExpressionTree.BodyKind; import com.sun.source.tree.NewClassTree; import com.sun.tools.javac.code.*; @@ -62,6 +63,7 @@ import java.util.function.Predicate; import java.util.function.Supplier; import com.sun.source.tree.MemberReferenceTree; +import com.sun.source.tree.ModifiersTree; import com.sun.tools.javac.code.Type; import com.sun.tools.javac.tree.JCTree.JCMemberReference.OverloadKind; @@ -191,6 +193,19 @@ public class DeferredAttr extends JCTree.Visitor { result.pos = t.pos; return result; } + + @Override + public JCTree visitAnnotatedType(AnnotatedTypeTree node, Void p) { + return copy(((JCAnnotatedType) node).underlyingType, p); + } + + @Override + public JCTree visitModifiers(ModifiersTree node, Void p) { + JCModifiers mods = (JCModifiers) super.visitModifiers(node, p); + + mods.annotations = List.nil(); + return mods; + } }; deferredCopier = new TypeMapping () { @Override diff --git a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java index f89e3ff7398..9c61c3f6d4a 100644 --- a/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java +++ b/test/langtools/tools/javac/annotations/typeAnnotations/TypeAnnotationsOnVariables.java @@ -23,7 +23,7 @@ /* * @test - * @bug 8371155 8379550 8384843 + * @bug 8371155 8379550 8381965 8384843 * @summary Verify type annotations on local-like variables are propagated to * their types at an appropriate time. * @library /tools/lib @@ -559,6 +559,192 @@ public class TypeAnnotationsOnVariables { " Test$TypeAnno"); } + @Test + void explicitLambdaHeader3() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.function.Consumer; + import java.util.List; + + class Test { + @Target(ElementType.TYPE_USE) + @interface TypeAnno { } + + static final Consumer> TEST = + id((List<@TypeAnno String> arg1) -> {}); + + private static Consumer id(Consumer t) { return t;} + private void test() { + Object test = + id((List<@TypeAnno String> arg2) -> {}); + } + } + """); + Files.createDirectories(classes); + List actual = new ArrayList<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + Trees trees = Trees.instance(task); + new TreePathScanner() { + @Override + public Void visitVariable(VariableTree node, Void p) { + actual.add(node.getName() + ": " + typeToString(trees.getTypeMirror(getCurrentPath()))); + return super.visitVariable(node, p); + } + @Override + public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { + actual.add(treeToString(node)+ ": " + typeToString(trees.getTypeMirror(getCurrentPath()))); + return super.visitLambdaExpression(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + }); + }) + .run() + .writeAll(); + + List expected = List.of( + "TEST: java.util.function.Consumer>", + "(List<@TypeAnno String> arg1)->{ }: java.util.function.Consumer>", + "arg1: java.util.List", + "t: java.util.function.Consumer", + "test: java.lang.Object", + "(List<@TypeAnno String> arg2)->{ }: java.util.function.Consumer>", + "arg2: java.util.List" + ); + + actual.forEach(System.out::println); + if (!expected.equals(actual)) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + + Path testClass = classes.resolve("Test.class"); + TestClassDesc testClassDesc = TestClassDesc.create(testClass); + MethodModel clInit = singletonValue(testClassDesc.name2Method().get("")); + assertEmpty(getAnnotationsFromHeader(clInit)); + assertEmpty(getAnnotationsFromCode(clInit)); + MethodModel test = singletonValue(testClassDesc.name2Method().get("test")); + assertEmpty(getAnnotationsFromHeader(test)); + assertEmpty(getAnnotationsFromCode(test)); + + checkTypeAnnotations(testClassDesc, + "lambda$static$0", + this::getAnnotationsFromHeader, + " 0: LTest$TypeAnno;(): METHOD_FORMAL_PARAMETER, param_index=0, location=[TYPE_ARGUMENT(0)]", + " Test$TypeAnno"); + + checkTypeAnnotations(testClassDesc, + "lambda$test$0", + this::getAnnotationsFromHeader, + " 0: LTest$TypeAnno;(): METHOD_FORMAL_PARAMETER, param_index=0, location=[TYPE_ARGUMENT(0)]", + " Test$TypeAnno"); + } + + @Test + void explicitLambdaHeader4() throws Exception { + Path src = base.resolve("src"); + Path classes = base.resolve("classes"); + tb.writeJavaFiles(src, + """ + import java.lang.annotation.ElementType; + import java.lang.annotation.Target; + import java.util.function.Consumer; + import java.util.List; + + class Test { + @Target(ElementType.TYPE_USE) + @interface TypeAnno { } + + static final Consumer> TEST = + id((@TypeAnno List arg) -> {}); + + private static Consumer id(Consumer t) { return t;} + private void test() { + Object test = + id((@TypeAnno List arg) -> {}); + } + } + """); + Files.createDirectories(classes); + List actual = new ArrayList<>(); + new JavacTask(tb) + .options("-d", classes.toString()) + .files(tb.findJavaFiles(src)) + .callback(task -> { + task.addTaskListener(new TaskListener() { + @Override + public void finished(TaskEvent e) { + if (e.getKind() != TaskEvent.Kind.ANALYZE) { + return ; + } + Trees trees = Trees.instance(task); + new TreePathScanner() { + @Override + public Void visitVariable(VariableTree node, Void p) { + actual.add(node.getName() + ": " + typeToString(trees.getTypeMirror(getCurrentPath()))); + return super.visitVariable(node, p); + } + @Override + public Void visitLambdaExpression(LambdaExpressionTree node, Void p) { + actual.add(treeToString(node)+ ": " + typeToString(trees.getTypeMirror(getCurrentPath()))); + return super.visitLambdaExpression(node, p); + } + }.scan(e.getCompilationUnit(), null); + } + }); + }) + .run() + .writeAll(); + + List expected = List.of( + "TEST: java.util.function.Consumer>", + "(@TypeAnno List arg)->{ }: java.util.function.Consumer>", + "arg: java.util.@Test.TypeAnno List", + "t: java.util.function.Consumer", + "test: java.lang.Object", + "(@TypeAnno List arg)->{ }: java.util.function.Consumer>", + "arg: java.util.@Test.TypeAnno List" + ); + + actual.forEach(System.out::println); + if (!expected.equals(actual)) { + throw new AssertionError("Expected: " + expected + ", but got: " + actual); + } + + Path testClass = classes.resolve("Test.class"); + TestClassDesc testClassDesc = TestClassDesc.create(testClass); + MethodModel clInit = singletonValue(testClassDesc.name2Method().get("")); + assertEmpty(getAnnotationsFromHeader(clInit)); + assertEmpty(getAnnotationsFromCode(clInit)); + MethodModel test = singletonValue(testClassDesc.name2Method().get("test")); + assertEmpty(getAnnotationsFromHeader(test)); + assertEmpty(getAnnotationsFromCode(test)); + + checkTypeAnnotations(testClassDesc, + "lambda$static$0", + this::getAnnotationsFromHeader, + " 0: LTest$TypeAnno;(): METHOD_FORMAL_PARAMETER, param_index=0", + " Test$TypeAnno"); + + checkTypeAnnotations(testClassDesc, + "lambda$test$0", + this::getAnnotationsFromHeader, + " 0: LTest$TypeAnno;(): METHOD_FORMAL_PARAMETER, param_index=0", + " Test$TypeAnno"); + } + private void checkTypeAnnotations(TestClassDesc testClassDesc, String lambdaMethodName, String... expectedEntries) throws IOException {