From 56d315da480dcd2198e2000ead301c3be8b27d84 Mon Sep 17 00:00:00 2001 From: Jan Lahoda Date: Wed, 4 Dec 2024 09:47:40 +0000 Subject: [PATCH] 8343540: Report preview error for inherited effectively-preview methods Reviewed-by: vromero --- .../com/sun/tools/javac/comp/Attr.java | 2 +- .../com/sun/tools/javac/comp/Check.java | 31 +- .../tools/javac/preview/PreviewTest.java | 435 +++++++++++++++++- 3 files changed, 449 insertions(+), 19 deletions(-) diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java index 62c12334629..62f7c15a95f 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4728,7 +4728,7 @@ public class Attr extends JCTree.Visitor { chk.checkDeprecated(tree.pos(), env.info.scope.owner, sym); chk.checkSunAPI(tree.pos(), sym); chk.checkProfile(tree.pos(), sym); - chk.checkPreview(tree.pos(), env.info.scope.owner, sym); + chk.checkPreview(tree.pos(), env.info.scope.owner, site, sym); } if (pt.isErroneous()) { 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 5a442bac302..08084b5abed 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 @@ -1845,7 +1845,8 @@ public class Check { } if (shouldCheckPreview(m, other, origin)) { - checkPreview(tree.pos(), m, other); + checkPreview(TreeInfo.diagnosticPositionFor(m, tree), + m, origin.type, other); } Type mt = types.memberType(origin.type, m); @@ -1925,7 +1926,8 @@ public class Check { private boolean shouldCheckPreview(MethodSymbol m, MethodSymbol other, ClassSymbol origin) { if (m.owner != origin || //performance - only do the expensive checks when the overridden method is a Preview API: - (other.flags() & PREVIEW_API) == 0) { + ((other.flags() & PREVIEW_API) == 0 && + (other.owner.flags() & PREVIEW_API) == 0)) { return false; } @@ -3828,8 +3830,29 @@ public class Check { } void checkPreview(DiagnosticPosition pos, Symbol other, Symbol s) { - if ((s.flags() & PREVIEW_API) != 0 && !preview.participatesInPreview(syms, other, s) && !disablePreviewCheck) { - if ((s.flags() & PREVIEW_REFLECTIVE) == 0) { + checkPreview(pos, other, Type.noType, s); + } + + void checkPreview(DiagnosticPosition pos, Symbol other, Type site, Symbol s) { + boolean sIsPreview; + Symbol previewSymbol; + if ((s.flags() & PREVIEW_API) != 0) { + sIsPreview = true; + previewSymbol= s; + } else if ((s.kind == Kind.MTH || s.kind == Kind.VAR) && + site.tsym != null && + (site.tsym.flags() & PREVIEW_API) == 0 && + (s.owner.flags() & PREVIEW_API) != 0) { + //calling a method, or using a field, whose owner is a preview, but + //using a site that is not a preview. Also produce an error or warning: + sIsPreview = true; + previewSymbol = s.owner; + } else { + sIsPreview = false; + previewSymbol = null; + } + if (sIsPreview && !preview.participatesInPreview(syms, other, s) && !disablePreviewCheck) { + if ((previewSymbol.flags() & PREVIEW_REFLECTIVE) == 0) { if (!preview.isEnabled()) { log.error(pos, Errors.IsPreview(s)); } else { diff --git a/test/langtools/tools/javac/preview/PreviewTest.java b/test/langtools/tools/javac/preview/PreviewTest.java index 24cb3d57dde..ad67ef8ac87 100644 --- a/test/langtools/tools/javac/preview/PreviewTest.java +++ b/test/langtools/tools/javac/preview/PreviewTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,7 +23,7 @@ /* * @test - * @bug 8282823 + * @bug 8282823 8343540 * @library /tools/lib * @enablePreview * @modules @@ -229,13 +229,13 @@ public class PreviewTest extends TestRunner { .getOutputLines(Task.OutputKind.DIRECT); List expected = - List.of("IUseIntf2P.java:3:8: compiler.err.is.preview: test()", - "IUseIntfDef2P.java:3:8: compiler.err.is.preview: test()", + List.of("IUseIntf2P.java:4:25: compiler.err.is.preview: test()", + "IUseIntfDef2P.java:4:25: compiler.err.is.preview: test()", "UseClass2P.java:4:17: compiler.err.is.preview: test()", - "UseIntf2P.java:3:8: compiler.err.is.preview: test()", - "UseIntfDef2P.java:3:8: compiler.err.is.preview: test()", + "UseIntf2P.java:4:17: compiler.err.is.preview: test()", + "UseIntfDef2P.java:4:17: compiler.err.is.preview: test()", "UseSubClass12P.java:3:17: compiler.err.is.preview: test()", - "UseSubIntfDef12P.java:2:8: compiler.err.is.preview: test()", + "UseSubIntfDef12P.java:3:17: compiler.err.is.preview: test()", "7 errors"); if (!log.equals(expected)) @@ -257,13 +257,13 @@ public class PreviewTest extends TestRunner { .getOutputLines(Task.OutputKind.DIRECT); expected = - List.of("IUseIntf2P.java:3:8: compiler.warn.is.preview: test()", - "IUseIntfDef2P.java:3:8: compiler.warn.is.preview: test()", + List.of("IUseIntf2P.java:4:25: compiler.warn.is.preview: test()", + "IUseIntfDef2P.java:4:25: compiler.warn.is.preview: test()", "UseClass2P.java:4:17: compiler.warn.is.preview: test()", - "UseIntf2P.java:3:8: compiler.warn.is.preview: test()", - "UseIntfDef2P.java:3:8: compiler.warn.is.preview: test()", + "UseIntf2P.java:4:17: compiler.warn.is.preview: test()", + "UseIntfDef2P.java:4:17: compiler.warn.is.preview: test()", "UseSubClass12P.java:3:17: compiler.warn.is.preview: test()", - "UseSubIntfDef12P.java:2:8: compiler.warn.is.preview: test()", + "UseSubIntfDef12P.java:3:17: compiler.warn.is.preview: test()", "7 warnings"); if (!log.equals(expected)) @@ -392,7 +392,7 @@ public class PreviewTest extends TestRunner { .getOutputLines(Task.OutputKind.DIRECT); List expected = - List.of("AbstractP.java:3:17: compiler.err.is.preview: test()", + List.of("AbstractP.java:4:26: compiler.err.is.preview: test()", "ReabstractP.java:4:26: compiler.err.is.preview: test()", "2 errors"); @@ -415,7 +415,7 @@ public class PreviewTest extends TestRunner { .getOutputLines(Task.OutputKind.DIRECT); expected = - List.of("AbstractP.java:3:17: compiler.warn.is.preview: test()", + List.of("AbstractP.java:4:26: compiler.warn.is.preview: test()", "ReabstractP.java:4:26: compiler.warn.is.preview: test()", "2 warnings"); @@ -463,6 +463,413 @@ public class PreviewTest extends TestRunner { } } + @Test //JDK-8343540: + public void nonPreviewImplementsPreview(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public interface Preview { + public static final int FIELD = 0; + public default void test() {} + } + """, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST, + reflective=true) + public interface ReflectivePreview { + public default void test() {} + } + """, + """ + package preview.api; + public interface NonPreviewIntf extends Preview { + } + """, + """ + package preview.api; + public class NonPreview implements Preview { + } + """, + """ + package preview.api; + public class ReflectiveNonPreview implements ReflectivePreview { + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.NonPreviewIntf; + import preview.api.Preview; + import preview.api.ReflectiveNonPreview; + public class Test { + public void test(NonPreview np, + Produce prod) { + np.test(); + acceptRunnable(np::test); + accept(NonPreview::test); + prod.produce().test(); + acceptRunnable(prod.produce()::test); + int i = np.FIELD; + } + public void test(T1 t1, T2 t2, T3 t3) { + t1.test(); + t2.test(); + t3.test(); + } + public void test(ReflectiveNonPreview np) { + np.test(); + } + public void test(Preview p) { + p.test(); + acceptRunnable(p::test); + accept(Preview::test); + } + private static class ExtendsNonPreview extends NonPreview { + public void test() {} //error/warning here: + } + private static class ImplementsPreview implements Preview { + //no error/warning (already was on Preview after implements) + public void test() {} + } + private static class ImplicitReceiver extends NonPreview { + public void g() { + test(); //implicit this - error/warning + int i = FIELD; //implicit this - error/warning + } + } + private void acceptRunnable(Runnable r) {} + private void accept(Accept accept) {} + interface Accept { + public void accept(T t); + } + interface Produce { + public T produce(); + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:4:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:26:22: compiler.err.is.preview: preview.api.Preview", + "Test.java:34:55: compiler.err.is.preview: preview.api.Preview", + "Test.java:9:11: compiler.err.is.preview: test()", + "Test.java:10:24: compiler.err.is.preview: test()", + "Test.java:11:16: compiler.err.is.preview: test()", + "Test.java:12:23: compiler.err.is.preview: test()", + "Test.java:13:24: compiler.err.is.preview: test()", + "Test.java:14:19: compiler.err.is.preview: FIELD", + "Test.java:19:11: compiler.err.is.preview: test()", + "Test.java:20:11: compiler.err.is.preview: test()", + "Test.java:21:11: compiler.err.is.preview: test()", + "Test.java:24:11: compiler.warn.is.preview.reflective: test()", + "Test.java:29:16: compiler.err.is.preview: preview.api.Preview", + "Test.java:32:21: compiler.err.is.preview: test()", + "Test.java:36:21: compiler.err.is.preview: test()", + "Test.java:40:13: compiler.err.is.preview: test()", + "Test.java:41:21: compiler.err.is.preview: FIELD", + "17 errors", + "1 warning"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview2(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public interface Preview { + public default void test() {} + } + """, + """ + package preview.api; + public interface NonPreviewIntf extends Preview { + public default void test() {} + } + """, + """ + package preview.api; + public class NonPreview implements Preview { + public void test() {} + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.NonPreviewIntf; + public class Test { + public void test(NonPreview np1, + NonPreviewIntf np2) { + np1.test(); + np2.test(); + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.SUCCESS) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview3(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public int field; + public static void test() {} + } + """, + """ + package preview.api; + public class NonPreview extends Preview { + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.Preview; + public class Test { + public void test(NonPreview np, Preview p) { + NonPreview.test(); + Preview.test(); + int i1 = np.field; + int i2 = p.field; + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:3:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:5:37: compiler.err.is.preview: preview.api.Preview", + "Test.java:6:19: compiler.err.is.preview: test()", + "Test.java:7:9: compiler.err.is.preview: preview.api.Preview", + "Test.java:8:20: compiler.err.is.preview: field", + "5 errors"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview4(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public class Preview { + public int field; + public static void test() {} + } + """, + """ + package preview.api; + public class NonPreview extends Preview { + public int field; + public static void test() {} + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.Preview; + public class Test { + public void test(NonPreview np, Preview p) { + NonPreview.test(); + Preview.test(); + int i1 = np.field; + int i2 = p.field; + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:3:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:5:37: compiler.err.is.preview: preview.api.Preview", + "Test.java:7:9: compiler.err.is.preview: preview.api.Preview", + "3 errors"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + + @Test //JDK-8343540: + public void nonPreviewImplementsPreview5(Path base) throws Exception { + Path apiSrc = base.resolve("api-src"); + tb.writeJavaFiles(apiSrc, + """ + package preview.api; + @jdk.internal.javac.PreviewFeature(feature=jdk.internal.javac.PreviewFeature.Feature.TEST) + public interface Preview { + public static final int CONST1 = 0; + public static final int CONST2 = 0; + } + """, + """ + package preview.api; + public interface NonPreviewIntf extends Preview { + public static final int CONST2 = 0; + } + """, + """ + package preview.api; + public class NonPreview implements Preview { + public static final int CONST2 = 0; + } + """); + Path apiClasses = base.resolve("api-classes"); + + new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(apiClasses) + .options("--patch-module", "java.base=" + apiSrc.toString(), + "-Werror") + .files(tb.findJavaFiles(apiSrc)) + .run() + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + Path testSrc = base.resolve("test-src"); + tb.writeJavaFiles(testSrc, + """ + package test; + import preview.api.NonPreview; + import preview.api.NonPreviewIntf; + import preview.api.Preview; + public class Test { + public void test() { + int i1 = NonPreview.CONST1; + int i2 = NonPreviewIntf.CONST1; + int i3 = Preview.CONST1; + int i4 = NonPreview.CONST2; + int i5 = NonPreviewIntf.CONST2; + int i6 = Preview.CONST2; + } + } + """); + Path testClasses = base.resolve("test-classes"); + List log = new JavacTask(tb, Task.Mode.CMDLINE) + .outdir(testClasses) + .options("--patch-module", "java.base=" + apiClasses.toString(), + "--add-exports", "java.base/preview.api=ALL-UNNAMED", + "-XDrawDiagnostics") + .files(tb.findJavaFiles(testSrc)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = + List.of("Test.java:4:19: compiler.err.is.preview: preview.api.Preview", + "Test.java:7:28: compiler.err.is.preview: CONST1", + "Test.java:8:32: compiler.err.is.preview: CONST1", + "Test.java:9:18: compiler.err.is.preview: preview.api.Preview", + "Test.java:12:18: compiler.err.is.preview: preview.api.Preview", + "5 errors"); + + if (!log.equals(expected)) + throw new Exception("expected output not found" + log); + } + private int verifyPreviewClassfiles(Path directory) throws Exception { Path[] classfiles = tb.findFiles("class", directory);