Updating javac behavior w.r.t. preview API. Based on darcy's patch. JDK-8226585-branch
authorjlahoda
Thu, 12 Sep 2019 17:47:24 +0200
branchJDK-8226585-branch
changeset 58109 ee07de0d2c16
parent 58018 a3c63a9dfb2c
child 58290 d885633d9de4
Updating javac behavior w.r.t. preview API. Based on darcy's patch.
make/CompileInterimLangtools.gmk
src/java.base/share/classes/java/lang/String.java
src/java.base/share/classes/jdk/internal/PreviewFeature.java
src/java.base/share/classes/module-info.java
src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java
src/jdk.compiler/share/classes/com/sun/source/tree/SwitchExpressionTree.java
src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java
src/jdk.compiler/share/classes/com/sun/source/tree/YieldTree.java
src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java
src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java
src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java
src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java
src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java
src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java
src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java
src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java
src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java
src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java
src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java
test/langtools/tools/javac/preview/PreviewErrors.java
--- a/make/CompileInterimLangtools.gmk	Thu Sep 05 12:39:48 2019 +0200
+++ b/make/CompileInterimLangtools.gmk	Thu Sep 12 17:47:24 2019 +0200
@@ -49,6 +49,14 @@
 TARGETS += $(patsubst %, $(BUILDTOOLS_OUTPUTDIR)/gensrc/%/module-info.java, \
     $(INTERIM_LANGTOOLS_MODULES))
 
+$(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/PreviewFeature.java: \
+    $(TOPDIR)/src/java.base/share/classes/jdk/internal/PreviewFeature.java
+	$(call LogInfo, Copying PreviewFeature.java)
+	$(call MakeDir, $(@D))
+	$(CP) $< $@
+
+TARGETS += $(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim/jdk/internal/PreviewFeature.java
+
 ################################################################################
 # Setup the rules to build interim langtools, which is compiled by the boot
 # javac and can be run on the boot jdk. This will be used to compile the rest of
@@ -72,6 +80,8 @@
       BIN := $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules/$1.interim, \
       ADD_JAVAC_FLAGS := --module-path $(BUILDTOOLS_OUTPUTDIR)/interim_langtools_modules \
           $$(INTERIM_LANGTOOLS_ADD_EXPORTS) \
+          --patch-module java.base=$(BUILDTOOLS_OUTPUTDIR)/gensrc/java.base.interim \
+          --add-exports java.base/jdk.internal=jdk.compiler.interim \
           -Xlint:-module, \
   ))
 
--- a/src/java.base/share/classes/java/lang/String.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/java.base/share/classes/java/lang/String.java	Thu Sep 12 17:47:24 2019 +0200
@@ -2963,10 +2963,11 @@
      *
      * @since 13
      *
-     * @deprecated  This method is associated with text blocks, a preview language feature.
-     *              Text blocks and/or this method may be changed or removed in a future release.
+     * @preview This method is associated with text blocks, a preview language feature.
+     *          Text blocks and/or this method may be changed or removed in a future release.
      */
-    @Deprecated(forRemoval=true, since="13")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.TEXT_BLOCKS,
+                                 essentialAPI=true)
     public String stripIndent() {
         int length = length();
         if (length == 0) {
@@ -3080,10 +3081,11 @@
      *
      * @since 13
      *
-     * @deprecated  This method is associated with text blocks, a preview language feature.
-     *              Text blocks and/or this method may be changed or removed in a future release.
+     * @preview  This method is associated with text blocks, a preview language feature.
+     *           Text blocks and/or this method may be changed or removed in a future release.
      */
-    @Deprecated(forRemoval=true, since="13")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.TEXT_BLOCKS,
+                                 essentialAPI=true)
     public String translateEscapes() {
         if (isEmpty()) {
             return "";
@@ -3324,10 +3326,11 @@
      *
      * @since 13
      *
-     * @deprecated  This method is associated with text blocks, a preview language feature.
-     *              Text blocks and/or this method may be changed or removed in a future release.
+     * @preview  This method is associated with text blocks, a preview language feature.
+     *           Text blocks and/or this method may be changed or removed in a future release.
      */
-    @Deprecated(forRemoval=true, since="13")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.TEXT_BLOCKS,
+                                 essentialAPI=true)
     public String formatted(Object... args) {
         return new Formatter().format(this, args).toString();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/PreviewFeature.java	Thu Sep 12 17:47:24 2019 +0200
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal;
+
+import java.lang.annotation.*;
+
+/**
+ * Indicates the API declaration in question is associated with a
+ * <em>preview feature</em>. See JEP 12: "Preview Language and VM
+ * Features" (http://openjdk.java.net/jeps/12).
+ * @since 13
+ */
+// Match the meaningful targets of java.lang.Deprecated, omit local
+// variables and parameter declarations
+@Target({ElementType.METHOD,
+         ElementType.CONSTRUCTOR,
+         ElementType.FIELD,
+         ElementType.PACKAGE,
+         ElementType.MODULE,
+         ElementType.TYPE})
+ // CLASS retention will hopefully be sufficient for the purposes at hand
+@Retention(RetentionPolicy.CLASS)
+// *Not* @Documented
+public @interface PreviewFeature {
+    /**
+     * Name of the preview feature the annotated API is associated
+     * with.
+     */
+    public Feature feature();
+
+    public boolean essentialAPI() default false;
+
+    public enum Feature {
+        SWITCH_EXPRESSIONS,
+        TEXT_BLOCKS;
+    }
+}
--- a/src/java.base/share/classes/module-info.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/java.base/share/classes/module-info.java	Thu Sep 12 17:47:24 2019 +0200
@@ -135,7 +135,8 @@
     exports com.sun.security.ntlm to
         java.security.sasl;
     exports jdk.internal to
-        jdk.jfr;
+        jdk.jfr,
+        jdk.compiler;
     exports jdk.internal.access to
         java.desktop,
         java.logging,
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/CaseTree.java	Thu Sep 12 17:47:24 2019 +0200
@@ -61,11 +61,11 @@
      * @return labels for this case
      * @since 12
      *
-     * @deprecated This method is modeling a case with multiple labels,
+     * @preview This method is modeling a case with multiple labels,
      * which is part of a preview feature and may be removed
      * if the preview feature is removed.
      */
-    @Deprecated(forRemoval=true, since="12")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
     List<? extends ExpressionTree> getExpressions();
 
     /**
@@ -86,11 +86,11 @@
      * @return case value or null
      * @since 12
      *
-     * @deprecated This method is modeling a rule case,
+     * @preview This method is modeling a rule case,
      * which is part of a preview feature and may be removed
      * if the preview feature is removed.
      */
-    @Deprecated(forRemoval=true, since="12")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
     public default Tree getBody() {
         return null;
     }
@@ -101,11 +101,12 @@
      * @return the kind of this case
      * @since 12
      *
-     * @deprecated This method is used to model a rule case,
+     * @preview This method is used to model a rule case,
      * which is part of a preview feature and may be removed
      * if the preview feature is removed.
      */
-    @Deprecated(forRemoval=true, since="12")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     public default CaseKind getCaseKind() {
         return CaseKind.STATEMENT;
     }
@@ -119,11 +120,12 @@
      *
      * @since 12
      *
-     * @deprecated This enum is used to model a rule case,
+     * @preview This enum is used to model a rule case,
      * which is part of a preview feature and may be removed
      * if the preview feature is removed.
      */
-    @Deprecated(forRemoval=true, since="12")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     public enum CaseKind {
         /**
          * Case is in the form: {@code case <expression>: <statements>}.
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/SwitchExpressionTree.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/SwitchExpressionTree.java	Thu Sep 12 17:47:24 2019 +0200
@@ -41,11 +41,11 @@
  *
  * @since 12
  *
- * @deprecated This method is modeling switch expressions,
+ * @preview This method is modeling switch expressions,
  * which are part of a preview feature and may be removed
  * if the preview feature is removed.
  */
-@Deprecated(forRemoval=true, since="12")
+@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
 public interface SwitchExpressionTree extends ExpressionTree {
     /**
      * Returns the expression for the {@code switch} expression.
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java	Thu Sep 12 17:47:24 2019 +0200
@@ -244,13 +244,13 @@
          *
          * @since 12
          *
-         * @deprecated
+         * @preview
          * This enum constant is modeling switch expressions,
          * which are part of a preview feature and may be removed
          * if the preview feature is removed.
          */
-        @Deprecated(forRemoval=true, since="12")
-        @SuppressWarnings("removal")
+        @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+        @SuppressWarnings("preview")
         SWITCH_EXPRESSION(SwitchExpressionTree.class),
 
         /**
@@ -663,13 +663,13 @@
          *
          * @since 13
          *
-         * @deprecated
+         * @preview
          * This enum constant is modeling yield statement,
          * which are part of a preview feature and may be removed
          * if the preview feature is removed.
          */
-        @Deprecated(forRemoval=true, since="13")
-        @SuppressWarnings("removal")
+        @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+        @SuppressWarnings("preview")
         YIELD(YieldTree.class);
 
 
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/TreeVisitor.java	Thu Sep 12 17:47:24 2019 +0200
@@ -361,13 +361,13 @@
      * @return a result value
      * @since 12
      *
-     * @deprecated
+     * @preview
      * This method is modeling switch expressions,
      * which are part of a preview feature and may be removed
      * if the preview feature is removed.
      */
-    @Deprecated(forRemoval=true, since="12")
-    @SuppressWarnings("removal")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     R visitSwitchExpression(SwitchExpressionTree node, P p);
 
     /**
@@ -563,12 +563,12 @@
      * @return a result value
      * @since 13
      *
-     * @deprecated
+     * @preview
      * This method is modeling yield statement,
      * which are part of a preview feature and may be removed
      * if the preview feature is removed.
      */
-    @Deprecated(forRemoval=true, since="13")
-    @SuppressWarnings("removal")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     R visitYield(YieldTree node, P p);
 }
--- a/src/jdk.compiler/share/classes/com/sun/source/tree/YieldTree.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/tree/YieldTree.java	Thu Sep 12 17:47:24 2019 +0200
@@ -37,11 +37,11 @@
  *
  * @since 13
  *
- * @deprecated This class is modeling yield from switch expressions,
+ * @preview This class is modeling yield from switch expressions,
  * which are part of a preview feature and may be removed if
  * the preview feature is removed.
  */
-@Deprecated(forRemoval=true, since="13")
+@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
 public interface YieldTree extends StatementTree {
 
     /**
--- a/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/SimpleTreeVisitor.java	Thu Sep 12 17:47:24 2019 +0200
@@ -270,14 +270,14 @@
      * @param p {@inheritDoc}
      * @return  the result of {@code defaultAction}
      *
-     * @deprecated
+     * @preview
      * This method is modeling switch expressions,
      * which are part of a preview feature and may be removed
      * if the preview feature is removed.
      */
     @Override
-    @Deprecated(forRemoval=true, since="12")
-    @SuppressWarnings("removal")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     public R visitSwitchExpression(SwitchExpressionTree node, P p) {
         return defaultAction(node, p);
     }
@@ -791,8 +791,8 @@
      * @return  the result of {@code defaultAction}
      */
     @Override
-    @Deprecated(forRemoval=true, since="13")
-    @SuppressWarnings("removal")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     public R visitYield(YieldTree node, P p) {
         return defaultAction(node, p);
     }
--- a/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/source/util/TreeScanner.java	Thu Sep 12 17:47:24 2019 +0200
@@ -341,14 +341,14 @@
      * @param p  {@inheritDoc}
      * @return the result of scanning
      *
-     * @deprecated
+     * @preview
      * This method is modeling switch expressions,
      * which are part of a preview feature and may be removed
      * if the preview feature is removed.
      */
     @Override
-    @Deprecated(forRemoval=true, since="12")
-    @SuppressWarnings("removal")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     public R visitSwitchExpression(SwitchExpressionTree node, P p) {
         R r = scan(node.getExpression(), p);
         r = scanAndReduce(node.getCases(), p, r);
@@ -363,7 +363,7 @@
      * @return the result of scanning
      */
     @Override
-    @SuppressWarnings("removal")
+    @SuppressWarnings("preview")
     public R visitCase(CaseTree node, P p) {
         R r = scan(node.getExpressions(), p);
         if (node.getCaseKind() == CaseKind.RULE)
@@ -942,14 +942,14 @@
      * @param p  {@inheritDoc}
      * @return the result of scanning
      *
-     * @deprecated
+     * @preview
      * This method is modeling switch expressions,
      * which are part of a preview feature and may be removed
      * if the preview feature is removed.
      */
     @Override
-    @Deprecated(forRemoval=true, since="13")
-    @SuppressWarnings("removal")
+    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.SWITCH_EXPRESSIONS)
+    @SuppressWarnings("preview")
     public R visitYield(YieldTree node, P p) {
         return scan(node.getValue(), p);
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Flags.java	Thu Sep 12 17:47:24 2019 +0200
@@ -286,7 +286,17 @@
     /**
      * Flag to indicate the given ModuleSymbol is an automatic module.
      */
-    public static final long AUTOMATIC_MODULE = 1L<<52;
+    public static final long AUTOMATIC_MODULE = 1L<<52; //ModuleSymbols only
+
+    /**
+     * Flag to indicate the given PackageSymbol contains any non-.java and non-.class resources.
+     */
+    public static final long HAS_RESOURCE = 1L<<52; //PackageSymbols only
+
+    /**
+     * Flag to indicate the given ParamSymbol has a user-friendly name filled.
+     */
+    public static final long NAME_FILLED = 1L<<52; //ParamSymbols only
 
     /**
      * Flag to indicate the given ModuleSymbol is a system module.
@@ -304,9 +314,9 @@
     public static final long DEPRECATED_REMOVAL = 1L<<55;
 
     /**
-     * Flag to indicate the given PackageSymbol contains any non-.java and non-.class resources.
+     * Flag to indicate the API element in question is for a preview API.
      */
-    public static final long HAS_RESOURCE = 1L<<56;
+    public static final long PREVIEW_API = 1L<<56; //any Symbol kind
 
     /**
      * Flag for synthesized default constructors of anonymous classes that have an enclosing expression.
@@ -320,9 +330,9 @@
     public static final long BODY_ONLY_FINALIZE = 1L<<17; //blocks only
 
     /**
-     * Flag to indicate the given ParamSymbol has a user-friendly name filled.
+     * Flag to indicate the API element in question is for a preview API.
      */
-    public static final long NAME_FILLED = 1L<<58; //ParamSymbols only
+    public static final long PREVIEW_ESSENTIAL_API = 1L<<58; //any Symbol kind
 
     /** Modifier masks.
      */
@@ -441,7 +451,8 @@
         HAS_RESOURCE(Flags.HAS_RESOURCE),
         POTENTIALLY_AMBIGUOUS(Flags.POTENTIALLY_AMBIGUOUS),
         ANONCONSTR_BASED(Flags.ANONCONSTR_BASED),
-        NAME_FILLED(Flags.NAME_FILLED);
+        NAME_FILLED(Flags.NAME_FILLED),
+        PREVIEW_API(Flags.PREVIEW_API);
 
         Flag(long flag) {
             this.value = flag;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java	Thu Sep 12 17:47:24 2019 +0200
@@ -122,6 +122,10 @@
             values.add(LintCategory.OPENS);
             values.add(LintCategory.MODULE);
             values.add(LintCategory.REMOVAL);
+            Preview preview = Preview.instance(context);
+            if (!preview.isEnabled()) {
+                values.add(LintCategory.PREVIEW);
+            }
         }
 
         // Look for specific overrides
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Preview.java	Thu Sep 12 17:47:24 2019 +0200
@@ -27,7 +27,7 @@
 
 import com.sun.tools.javac.code.Lint.LintCategory;
 import com.sun.tools.javac.code.Source.Feature;
-import com.sun.tools.javac.comp.Infer;
+import com.sun.tools.javac.comp.Check;
 import com.sun.tools.javac.jvm.Target;
 import com.sun.tools.javac.resources.CompilerProperties.Errors;
 import com.sun.tools.javac.resources.CompilerProperties.Warnings;
@@ -36,17 +36,11 @@
 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
 import com.sun.tools.javac.util.JCDiagnostic.Error;
 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition;
-import com.sun.tools.javac.util.Log;
-import com.sun.tools.javac.util.MandatoryWarningHandler;
-import com.sun.tools.javac.util.Name;
 import com.sun.tools.javac.util.Options;
 
 import javax.tools.JavaFileObject;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
 
 import static com.sun.tools.javac.main.Option.PREVIEW;
 
@@ -62,12 +56,9 @@
  */
 public class Preview {
 
-    /** flag: are preview featutres enabled */
+    /** flag: are preview features enabled */
     private final boolean enabled;
 
-    /** the diag handler to manage preview feature usage diagnostics */
-    private final MandatoryWarningHandler previewHandler;
-
     /** test flag: should all features be considered as preview features? */
     private final boolean forcePreview;
 
@@ -75,8 +66,7 @@
     private final Map<Integer, Source> majorVersionToSource;
 
 
-    private final Lint lint;
-    private final Log log;
+    private final Check check;
 
     private static final Context.Key<Preview> previewKey = new Context.Key<>();
 
@@ -92,10 +82,7 @@
         context.put(previewKey, this);
         Options options = Options.instance(context);
         enabled = options.isSet(PREVIEW);
-        log = Log.instance(context);
-        lint = Lint.instance(context);
-        this.previewHandler =
-                new MandatoryWarningHandler(log, lint.isEnabled(LintCategory.PREVIEW), true, "preview", LintCategory.PREVIEW);
+        check = Check.instance(context);
         forcePreview = options.isSet("forcePreview");
         majorVersionToSource = initMajorVersionToSourceMap();
     }
@@ -131,11 +118,9 @@
     public void warnPreview(DiagnosticPosition pos, Feature feature) {
         Assert.check(isEnabled());
         Assert.check(isPreview(feature));
-        if (!lint.isSuppressed(LintCategory.PREVIEW)) {
-            previewHandler.report(pos, feature.isPlural() ?
+        check.warnPreview(pos, feature.isPlural() ?
                     Warnings.PreviewFeatureUsePlural(feature.nameFragment()) :
                     Warnings.PreviewFeatureUse(feature.nameFragment()));
-        }
     }
 
     /**
@@ -145,10 +130,8 @@
      */
     public void warnPreview(JavaFileObject classfile, int majorVersion) {
         Assert.check(isEnabled());
-        if (!lint.isSuppressed(LintCategory.PREVIEW)) {
-            previewHandler.report(null,
+        check.warnPreview(null,
                     Warnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name));
-        }
     }
 
     /**
@@ -200,10 +183,4 @@
         return Errors.PreviewFeatureDisabledClassfile(classfile, majorVersionToSource.get(majorVersion).name);
     }
 
-    /**
-     * Report any deferred diagnostics.
-     */
-    public void reportDeferredDiagnostics() {
-        previewHandler.reportDeferredDiagnostic();
-    }
 }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symbol.java	Thu Sep 12 17:47:24 2019 +0200
@@ -378,6 +378,10 @@
         return (flags_field & DEPRECATED_REMOVAL) != 0;
     }
 
+    public boolean isPreviewApi() {
+        return (flags_field & PREVIEW_API) != 0;
+    }
+
     public boolean isDeprecatableViaAnnotation() {
         switch (getKind()) {
             case LOCAL_VARIABLE:
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Symtab.java	Thu Sep 12 17:47:24 2019 +0200
@@ -214,6 +214,7 @@
     public final Type documentedType;
     public final Type elementTypeType;
     public final Type functionalInterfaceType;
+    public final Type previewFeatureType;
 
     /** The symbol representing the length field of an array.
      */
@@ -570,6 +571,7 @@
         lambdaMetafactory = enterClass("java.lang.invoke.LambdaMetafactory");
         stringConcatFactory = enterClass("java.lang.invoke.StringConcatFactory");
         functionalInterfaceType = enterClass("java.lang.FunctionalInterface");
+        previewFeatureType = enterClass("jdk.internal.PreviewFeature");
 
         synthesizeEmptyInterfaceIfMissing(autoCloseableType);
         synthesizeEmptyInterfaceIfMissing(cloneableType);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java	Thu Sep 12 17:47:24 2019 +0200
@@ -1486,7 +1486,7 @@
             // check that there are no duplicate case labels or default clauses.
             Set<Object> labels = new HashSet<>(); // The set of case labels.
             boolean hasDefault = false;      // Is there a default label?
-            @SuppressWarnings("removal")
+            @SuppressWarnings("preview")
             CaseKind caseKind = null;
             boolean wasError = false;
             for (List<JCCase> l = cases; l.nonEmpty(); l = l.tail) {
@@ -4092,6 +4092,7 @@
                 chk.checkDeprecated(tree.pos(), env.info.scope.owner, sym);
                 chk.checkSunAPI(tree.pos(), sym);
                 chk.checkProfile(tree.pos(), sym);
+                chk.checkPreview(tree.pos(), sym);
             }
 
             // If symbol is a variable, check that its type and
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java	Thu Sep 12 17:47:24 2019 +0200
@@ -93,6 +93,7 @@
     private final Source source;
     private final Target target;
     private final Profile profile;
+    private final Preview preview;
     private final boolean warnOnAnyAccessToMembers;
 
     // The set of lint options currently in effect. It is initialized
@@ -139,10 +140,12 @@
         syntheticNameChar = target.syntheticNameChar();
 
         profile = Profile.instance(context);
+        preview = Preview.instance(context);
 
         boolean verboseDeprecated = lint.isEnabled(LintCategory.DEPRECATION);
         boolean verboseRemoval = lint.isEnabled(LintCategory.REMOVAL);
         boolean verboseUnchecked = lint.isEnabled(LintCategory.UNCHECKED);
+        boolean verbosePreview = lint.isEnabled(LintCategory.PREVIEW);
         boolean enforceMandatoryWarnings = true;
 
         deprecationHandler = new MandatoryWarningHandler(log, verboseDeprecated,
@@ -153,6 +156,8 @@
                 enforceMandatoryWarnings, "unchecked", LintCategory.UNCHECKED);
         sunApiHandler = new MandatoryWarningHandler(log, false,
                 enforceMandatoryWarnings, "sunapi", null);
+        previewHandler = new MandatoryWarningHandler(log, verbosePreview,
+                enforceMandatoryWarnings, "preview", null);
 
         deferredLintHandler = DeferredLintHandler.instance(context);
     }
@@ -182,6 +187,10 @@
      */
     private MandatoryWarningHandler sunApiHandler;
 
+    /** A handler for messages about preview features.
+     */
+    private MandatoryWarningHandler previewHandler;
+
     /** A handler for deferred lint warnings.
      */
     private DeferredLintHandler deferredLintHandler;
@@ -224,6 +233,27 @@
         }
     }
 
+    /** Warn about deprecated symbol.
+     *  @param pos        Position to be used for error reporting.
+     *  @param sym        The deprecated symbol.
+     */
+    void warnPreview(DiagnosticPosition pos, Symbol sym) {
+        if ((sym.flags() & PREVIEW_ESSENTIAL_API) != 0) {
+            previewHandler.report(pos, Warnings.IsPreview(sym));
+        } else {
+            warnPreview(pos, Warnings.IsPreview(sym));
+        }
+    }
+
+    /** Log a preview warning.
+     *  @param pos        Position to be used for error reporting.
+     *  @param msg        A Warning describing the problem.
+     */
+    public void warnPreview(DiagnosticPosition pos, Warning warnKey) {
+        if (!lint.isSuppressed(LintCategory.PREVIEW))
+            previewHandler.report(pos, warnKey);
+    }
+
     /** Warn about unchecked operation.
      *  @param pos        Position to be used for error reporting.
      *  @param msg        A string describing the problem.
@@ -262,6 +292,7 @@
         removalHandler.reportDeferredDiagnostic();
         uncheckedHandler.reportDeferredDiagnostic();
         sunApiHandler.reportDeferredDiagnostic();
+        previewHandler.reportDeferredDiagnostic();
     }
 
 
@@ -3290,6 +3321,16 @@
         }
     }
 
+    void checkPreview(DiagnosticPosition pos, Symbol s) {
+        if ((s.flags() & PREVIEW_API) != 0) {
+            if ((s.flags() & PREVIEW_ESSENTIAL_API) != 0 && !preview.isEnabled()) {
+                log.error(pos, Errors.IsPreview(s));
+            } else {
+                deferredLintHandler.report(() -> warnPreview(pos, s));
+            }
+        }
+    }
+
 /* *************************************************************************
  * Check for recursive annotation elements.
  **************************************************************************/
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Resolve.java	Thu Sep 12 17:47:24 2019 +0200
@@ -2819,6 +2819,7 @@
                                     typeargtypes, allowBoxing,
                                     useVarargs);
         chk.checkDeprecated(pos, env.info.scope.owner, sym);
+        chk.checkPreview(pos, sym);
         return sym;
     }
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/TypeEnter.java	Thu Sep 12 17:47:24 2019 +0200
@@ -433,7 +433,7 @@
         Type attribImportType(JCTree tree, Env<AttrContext> env) {
             Assert.check(completionEnabled);
             Lint prevLint = chk.setLint(allowDeprecationOnImport ?
-                    lint : lint.suppress(LintCategory.DEPRECATION, LintCategory.REMOVAL));
+                    lint : lint.suppress(LintCategory.DEPRECATION, LintCategory.REMOVAL, LintCategory.PREVIEW));
             try {
                 // To prevent deep recursion, suppress completion of some
                 // types.
@@ -1168,6 +1168,8 @@
                                 sym.flags_field |= DEPRECATED_REMOVAL;
                             }
                         });
+            } else if (a.annotationType.type == syms.previewFeatureType) {
+                sym.flags_field |= Flags.PREVIEW_API;
             }
         }
     }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassReader.java	Thu Sep 12 17:47:24 2019 +0200
@@ -1403,6 +1403,16 @@
                             }
                         }
                     }
+                }  else if (proxy.type.tsym == syms.previewFeatureType.tsym) {
+                    for (Pair<Name, Attribute> v : proxy.values) {
+                        if (v.fst == names.essentialAPI && v.snd instanceof Attribute.Constant) {
+                            Attribute.Constant c = (Attribute.Constant)v.snd;
+                            if (c.type == syms.booleanType && ((Integer)c.value) != 0) {
+                                sym.flags_field |= PREVIEW_ESSENTIAL_API;
+                            }
+                        }
+                    }
+                    sym.flags_field |= PREVIEW_API;
                 }
                 proxies.append(proxy);
             }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/main/JavaCompiler.java	Thu Sep 12 17:47:24 2019 +0200
@@ -1735,7 +1735,6 @@
                 log.warning(Warnings.ProcUseProcOrImplicit);
         }
         chk.reportDeferredDiagnostics();
-        preview.reportDeferredDiagnostics();
         if (log.compressedOutput) {
             log.mandatoryNote(null, Notes.CompressedDiags);
         }
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/parser/JavacParser.java	Thu Sep 12 17:47:24 2019 +0200
@@ -1432,7 +1432,7 @@
         }
         List<JCStatement> stats = null;
         JCTree body = null;
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         CaseKind kind;
         switch (token.kind) {
             case ARROW:
@@ -2897,7 +2897,7 @@
                 nextToken();
                 checkSourceLevel(Feature.SWITCH_MULTIPLE_CASE_LABELS);
             };
-            @SuppressWarnings("removal")
+            @SuppressWarnings("preview")
             CaseKind caseKind;
             JCTree body = null;
             if (token.kind == ARROW) {
@@ -2922,7 +2922,7 @@
         }
         case DEFAULT: {
             nextToken();
-            @SuppressWarnings("removal")
+            @SuppressWarnings("preview")
             CaseKind caseKind;
             JCTree body = null;
             if (token.kind == ARROW) {
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Thu Sep 12 17:47:24 2019 +0200
@@ -1775,6 +1775,14 @@
     {0} in {1} has been deprecated and marked for removal
 
 # 0: symbol
+compiler.warn.is.preview=\
+    {0} is an API that is part of a preview feature
+
+# 0: symbol
+compiler.err.is.preview=\
+    {0} is an API that is part of a preview feature
+
+# 0: symbol
 compiler.warn.has.been.deprecated.module=\
     module {0} has been deprecated
 
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/JCTree.java	Thu Sep 12 17:47:24 2019 +0200
@@ -1250,17 +1250,17 @@
     public static class JCCase extends JCStatement implements CaseTree {
         //as CaseKind is deprecated for removal (as it is part of a preview feature),
         //using indirection through these fields to avoid unnecessary @SuppressWarnings:
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public static final CaseKind STATEMENT = CaseKind.STATEMENT;
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public static final CaseKind RULE = CaseKind.RULE;
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public final CaseKind caseKind;
         public List<JCExpression> pats;
         public List<JCStatement> stats;
         public JCTree body;
         public boolean completesNormally;
-        protected JCCase(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pats,
+        protected JCCase(@SuppressWarnings("preview") CaseKind caseKind, List<JCExpression> pats,
                          List<JCStatement> stats, JCTree body) {
             Assert.checkNonNull(pats);
             Assert.check(pats.isEmpty() || pats.head != null);
@@ -1277,18 +1277,18 @@
         @Override @DefinedBy(Api.COMPILER_TREE)
         public JCExpression getExpression() { return pats.head; }
         @Override @DefinedBy(Api.COMPILER_TREE)
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public List<JCExpression> getExpressions() { return pats; }
         @Override @DefinedBy(Api.COMPILER_TREE)
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public List<JCStatement> getStatements() {
             return caseKind == CaseKind.STATEMENT ? stats : null;
         }
         @Override @DefinedBy(Api.COMPILER_TREE)
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public JCTree getBody() { return body; }
         @Override @DefinedBy(Api.COMPILER_TREE)
-        @SuppressWarnings("removal")
+        @SuppressWarnings("preview")
         public CaseKind getCaseKind() {
             return caseKind;
         }
@@ -1305,7 +1305,7 @@
     /**
      * A "switch ( ) { }" construction.
      */
-    @SuppressWarnings("removal")
+    @SuppressWarnings("preview")
     public static class JCSwitchExpression extends JCPolyExpression implements SwitchExpressionTree {
         public JCExpression selector;
         public List<JCCase> cases;
@@ -1586,7 +1586,7 @@
     /**
      * A break-with from a switch expression.
      */
-    @SuppressWarnings("removal")
+    @SuppressWarnings("preview")
     public static class JCYield extends JCStatement implements YieldTree {
         public JCExpression value;
         public JCTree target;
@@ -3105,7 +3105,7 @@
         JCLabeledStatement Labelled(Name label, JCStatement body);
         JCSwitch Switch(JCExpression selector, List<JCCase> cases);
         JCSwitchExpression SwitchExpression(JCExpression selector, List<JCCase> cases);
-        JCCase Case(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pat,
+        JCCase Case(@SuppressWarnings("preview") CaseKind caseKind, List<JCExpression> pat,
                     List<JCStatement> stats, JCTree body);
         JCSynchronized Synchronized(JCExpression lock, JCBlock body);
         JCTry Try(JCBlock body, List<JCCatch> catchers, JCBlock finalizer);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeCopier.java	Thu Sep 12 17:47:24 2019 +0200
@@ -144,7 +144,7 @@
     }
 
     @DefinedBy(Api.COMPILER_TREE)
-    @SuppressWarnings("removal")
+    @SuppressWarnings("preview")
     public JCTree visitYield(YieldTree node, P p) {
         JCYield t = (JCYield) node;
         JCExpression value = copy(t.value, p);
@@ -380,7 +380,7 @@
     }
 
     @DefinedBy(Api.COMPILER_TREE)
-    @SuppressWarnings("removal")
+    @SuppressWarnings("preview")
     public JCTree visitSwitchExpression(SwitchExpressionTree node, P p) {
         JCSwitchExpression t = (JCSwitchExpression) node;
         JCExpression selector = copy(t.selector, p);
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/tree/TreeMaker.java	Thu Sep 12 17:47:24 2019 +0200
@@ -274,7 +274,7 @@
         return tree;
     }
 
-    public JCCase Case(@SuppressWarnings("removal") CaseKind caseKind, List<JCExpression> pats,
+    public JCCase Case(@SuppressWarnings("preview") CaseKind caseKind, List<JCExpression> pats,
                        List<JCStatement> stats, JCTree body) {
         JCCase tree = new JCCase(caseKind, pats, stats, body);
         tree.pos = pos;
--- a/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Thu Sep 05 12:39:48 2019 +0200
+++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Names.java	Thu Sep 12 17:47:24 2019 +0200
@@ -86,6 +86,7 @@
     public final Name error;
     public final Name finalize;
     public final Name forRemoval;
+    public final Name essentialAPI;
     public final Name getClass;
     public final Name hasNext;
     public final Name hashCode;
@@ -236,6 +237,7 @@
         error = fromString("<error>");
         finalize = fromString("finalize");
         forRemoval = fromString("forRemoval");
+        essentialAPI = fromString("essentialAPI");
         getClass = fromString("getClass");
         hasNext = fromString("hasNext");
         hashCode = fromString("hashCode");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/langtools/tools/javac/preview/PreviewErrors.java	Thu Sep 12 17:47:24 2019 +0200
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2019, 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8226585
+ * @summary Verify behavior w.r.t. preview feature API errors and warnings
+ * @library /tools/lib
+ * @modules
+ *      java.base/jdk.internal
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.ToolBox toolbox.JavacTask
+ * @compile --enable-preview -source ${jdk.version} PreviewErrors.java
+ * @run main/othervm --enable-preview PreviewErrors
+ */
+
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.TestRunner;
+import toolbox.ToolBox;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import jdk.internal.PreviewFeature;
+
+public class PreviewErrors extends TestRunner {
+
+    protected ToolBox tb;
+
+    PreviewErrors() {
+        super(System.err);
+        tb = new ToolBox();
+    }
+
+    public static void main(String... args) throws Exception {
+        PreviewErrors t = new PreviewErrors();
+        t.runTests();
+    }
+
+    /**
+     * Run all methods annotated with @Test, and throw an exception if any
+     * errors are reported..
+     *
+     * @throws Exception if any errors occurred
+     */
+    protected void runTests() throws Exception {
+        runTests(m -> new Object[] { Paths.get(m.getName()) });
+    }
+
+    Path[] findJavaFiles(Path... paths) throws IOException {
+        return tb.findJavaFiles(paths);
+    }
+
+    @Test
+    public void essentialApi(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Path srcJavaBase = src.resolve("java.base");
+        Path classes = base.resolve("classes");
+        Path classesJavaBase = classes.resolve("java.base");
+
+        Files.createDirectories(classesJavaBase);
+
+        Path srcTest = src.resolve("test");
+        Path classesTest = classes.resolve("test");
+
+        Files.createDirectories(classesTest);
+
+        for (EssentialAPI essential : EssentialAPI.values()) {
+            tb.writeJavaFiles(srcJavaBase,
+                              """
+                              package java.lang;
+                              public class Extra {
+                                  @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.${preview}
+                                                               ${essential})
+                                  public static void test() { }
+                              }
+                              """.replace("${preview}", PreviewFeature.Feature.values()[0].name())
+                                 .replace("${essential}", essential.code()));
+
+            new JavacTask(tb)
+                    .outdir(classesJavaBase)
+                    .options("--patch-module", "java.base=" + srcJavaBase.toString())
+                    .files(findJavaFiles(srcJavaBase))
+                    .run()
+                    .writeAll();
+
+            for (Preview preview : Preview.values()) {
+                for (Lint lint : Lint.values()) {
+                    for (Suppress suppress : Suppress.values()) {
+                        tb.writeJavaFiles(srcTest,
+                                          """
+                                          package test;
+                                          public class Test {
+                                              ${suppress}
+                                              public void test() {
+                                                  Extra.test();
+                                              }
+                                          }
+                                          """.replace("${suppress}", suppress.code()));
+
+                        List<String> options = new ArrayList<>();
+
+                        options.add("-XDrawDiagnostics");
+                        options.add("--patch-module");
+                        options.add("java.base=" + classesJavaBase.toString());
+                        options.add("-source");
+                        options.add(String.valueOf(Runtime.version().feature()));
+
+                        if (preview.opt() != null) {
+                            options.add(preview.opt());
+                        }
+
+                        if (lint.opt() != null) {
+                            options.add(lint.opt());
+                        }
+                        List<String> output;
+                        List<String> expected;
+                        Task.Expect expect;
+
+                        if (essential == EssentialAPI.YES) {
+                            if (preview == Preview.YES) {
+                                if (lint == Lint.ENABLE_PREVIEW) {
+                                    expected = List.of("Test.java:5:14: compiler.warn.is.preview: test()",
+                                                       "1 warning");
+                                } else {
+                                    expected = List.of("- compiler.note.preview.filename: Test.java",
+                                                       "- compiler.note.preview.recompile");
+                                }
+                                expect = Task.Expect.SUCCESS;
+                            } else {
+                                expected = List.of("Test.java:5:14: compiler.err.is.preview: test()",
+                                                   "1 error");
+                                expect = Task.Expect.FAIL;
+                            }
+                        } else {
+                            if (suppress == Suppress.YES) {
+                                expected = List.of("");
+                            } else if ((preview == Preview.YES && (lint == Lint.NONE || lint == Lint.DISABLE_PREVIEW)) ||
+                                       (preview == Preview.NO && lint == Lint.DISABLE_PREVIEW)) {
+                                expected = List.of("- compiler.note.preview.filename: Test.java",
+                                                   "- compiler.note.preview.recompile");
+                            } else {
+                                expected = List.of("Test.java:5:14: compiler.warn.is.preview: test()",
+                                                   "1 warning");
+                            }
+                            expect = Task.Expect.SUCCESS;
+                        }
+
+                        output = new JavacTask(tb)
+                                .outdir(classesTest)
+                                .options(options)
+                                .files(findJavaFiles(srcTest))
+                                .run(expect)
+                                .writeAll()
+                                .getOutputLines(Task.OutputKind.DIRECT);
+
+                        if (!expected.equals(output)) {
+                            throw new IllegalStateException("Unexpected output for " + essential + ", " + preview + ", " + lint + ", " + suppress + ": " + output);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public enum EssentialAPI {
+        YES(", essentialAPI=true"),
+        NO(", essentialAPI=false");
+
+        private final String code;
+
+        private EssentialAPI(String code) {
+            this.code = code;
+        }
+
+        public String code() {
+            return code;
+        }
+    }
+
+    public enum Preview {
+        YES("--enable-preview"),
+        NO(null);
+
+        private final String opt;
+
+        private Preview(String opt) {
+            this.opt = opt;
+        }
+
+        public String opt() {
+            return opt;
+        }
+    }
+
+    public enum Lint {
+        NONE(null),
+        ENABLE_PREVIEW("-Xlint:preview"),
+        DISABLE_PREVIEW("-Xlint:-preview");
+
+        private final String opt;
+
+        private Lint(String opt) {
+            this.opt = opt;
+        }
+
+        public String opt() {
+            return opt;
+        }
+    }
+
+    public enum Suppress {
+        YES("@SuppressWarnings(\"preview\")"),
+        NO("");
+
+        private final String code;
+
+        private Suppress(String code) {
+            this.code = code;
+        }
+
+        public String code() {
+            return code;
+        }
+    }
+}