1 /* 2 * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.tools.javac.code; 27 28 import com.sun.tools.javac.code.Lint.LintCategory; 29 import com.sun.tools.javac.code.Source.Feature; 30 import com.sun.tools.javac.code.Symbol.ModuleSymbol; 31 import com.sun.tools.javac.jvm.Target; 32 import com.sun.tools.javac.resources.CompilerProperties.Errors; 33 import com.sun.tools.javac.resources.CompilerProperties.LintWarnings; 34 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 35 import com.sun.tools.javac.util.Assert; 36 import com.sun.tools.javac.util.Context; 37 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 38 import com.sun.tools.javac.util.JCDiagnostic.Error; 39 import com.sun.tools.javac.util.JCDiagnostic.LintWarning; 40 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; 41 import com.sun.tools.javac.util.JCDiagnostic.Warning; 42 import com.sun.tools.javac.util.Log; 43 import com.sun.tools.javac.util.MandatoryWarningHandler; 44 import com.sun.tools.javac.util.Names; 45 import com.sun.tools.javac.util.Options; 46 47 import javax.tools.JavaFileObject; 48 import java.util.HashMap; 49 import java.util.HashSet; 50 import java.util.Map; 51 import java.util.Set; 52 53 import static com.sun.tools.javac.main.Option.PREVIEW; 54 import com.sun.tools.javac.util.JCDiagnostic; 55 56 /** 57 * Helper class to handle preview language features. This class maps certain language features 58 * (see {@link Feature} into 'preview' features; the mapping is completely ad-hoc, so as to allow 59 * for maximum flexibility, which allows to migrate preview feature into supported features with ease. 60 * 61 * This class acts as a centralized point against which usages of preview features are reported by 62 * clients (e.g. other javac classes). Internally, this class collects all such usages and generates 63 * diagnostics to inform the user of such usages. Such diagnostics can be enabled using the 64 * {@link LintCategory#PREVIEW} lint category, and are suppressible by usual means. 65 */ 66 public class Preview { 67 68 /** flag: are preview features enabled */ 69 private final boolean enabled; 70 71 /** flag: is the "preview" lint category enabled? */ 72 private final boolean verbose; 73 74 /** the diag handler to manage preview feature usage diagnostics */ 75 private final MandatoryWarningHandler previewHandler; 76 77 /** test flag: should all features be considered as preview features? */ 78 private final boolean forcePreview; 79 80 /** a mapping from classfile numbers to Java SE versions */ 81 private final Map<Integer, Source> majorVersionToSource; 82 83 private final Set<JavaFileObject> sourcesWithPreviewFeatures = new HashSet<>(); 84 85 private final Names names; 86 private final Log log; 87 private final Source source; 88 89 protected static final Context.Key<Preview> previewKey = new Context.Key<>(); 90 91 public static Preview instance(Context context) { 92 Preview instance = context.get(previewKey); 93 if (instance == null) { 94 instance = new Preview(context); 95 } 96 return instance; 97 } 98 99 @SuppressWarnings("this-escape") 100 protected Preview(Context context) { 101 context.put(previewKey, this); 102 Options options = Options.instance(context); 103 names = Names.instance(context); 104 enabled = options.isSet(PREVIEW); 105 log = Log.instance(context); 106 source = Source.instance(context); 107 verbose = Lint.instance(context).isEnabled(LintCategory.PREVIEW); 108 previewHandler = new MandatoryWarningHandler(log, source, verbose, true, LintCategory.PREVIEW); 109 forcePreview = options.isSet("forcePreview"); 110 majorVersionToSource = initMajorVersionToSourceMap(); 111 } 112 113 private Map<Integer, Source> initMajorVersionToSourceMap() { 114 Map<Integer, Source> majorVersionToSource = new HashMap<>(); 115 for (Target t : Target.values()) { 116 int major = t.majorVersion; 117 Source source = Source.lookup(t.name); 118 if (source != null) { 119 majorVersionToSource.put(major, source); 120 } 121 } 122 return majorVersionToSource; 123 } 124 125 /** 126 * Returns true if {@code s} is deemed to participate in the preview of {@code previewSymbol}, and 127 * therefore no warnings or errors will be produced. 128 * 129 * @param syms the symbol table 130 * @param s the symbol depending on the preview symbol 131 * @param previewSymbol the preview symbol marked with @Preview 132 * @return true if {@code s} is participating in the preview of {@code previewSymbol} 133 */ 134 public boolean participatesInPreview(Symtab syms, Symbol s, Symbol previewSymbol) { 135 // All symbols in the same module as the preview symbol participate in the preview API 136 if (previewSymbol.packge().modle == s.packge().modle) { 137 return true; 138 } 139 140 return participatesInPreview(syms, s.packge().modle); 141 } 142 143 /** 144 * Returns true if module {@code m} is deemed to participate in the preview, and 145 * therefore no warnings or errors will be produced. 146 * 147 * @param syms the symbol table 148 * @param m the module to check 149 * @return true if {@code m} is participating in the preview of {@code previewSymbol} 150 */ 151 public boolean participatesInPreview(Symtab syms, ModuleSymbol m) { 152 // If java.base's jdk.internal.javac package is exported to s's module then 153 // s participates in the preview API 154 return syms.java_base.exports.stream() 155 .filter(ed -> ed.packge.fullname == names.jdk_internal_javac) 156 .anyMatch(ed -> ed.modules.contains(m)) || 157 //the specification lists the java.se module as participating in preview: 158 m.name == names.java_se; 159 } 160 161 /** 162 * Report usage of a preview feature. Usages reported through this method will affect the 163 * set of sourcefiles with dependencies on preview features. 164 * @param pos the position at which the preview feature was used. 165 * @param feature the preview feature used. 166 */ 167 public void warnPreview(int pos, Feature feature) { 168 warnPreview(new SimpleDiagnosticPosition(pos), feature); 169 } 170 171 /** 172 * Report usage of a preview feature. Usages reported through this method will affect the 173 * set of sourcefiles with dependencies on preview features. 174 * @param pos the position at which the preview feature was used. 175 * @param feature the preview feature used. 176 */ 177 public void warnPreview(DiagnosticPosition pos, Feature feature) { 178 Assert.check(isEnabled()); 179 Assert.check(isPreview(feature)); 180 markUsesPreview(pos); 181 previewHandler.report(pos, feature.isPlural() ? 182 LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : 183 LintWarnings.PreviewFeatureUse(feature.nameFragment())); 184 } 185 186 /** 187 * Report usage of a preview feature in classfile. 188 * @param classfile the name of the classfile with preview features enabled 189 * @param majorVersion the major version found in the classfile. 190 */ 191 public void warnPreview(JavaFileObject classfile, int majorVersion) { 192 Assert.check(isEnabled()); 193 if (verbose) { 194 log.mandatoryWarning(null, 195 LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); 196 } 197 } 198 199 /** 200 * Mark the current source file as using a preview feature. The corresponding classfile 201 * will be generated with minor version {@link ClassFile#PREVIEW_MINOR_VERSION}. 202 * @param pos the position at which the preview feature was used. 203 */ 204 public void markUsesPreview(DiagnosticPosition pos) { 205 sourcesWithPreviewFeatures.add(log.currentSourceFile()); 206 } 207 208 public void reportPreviewWarning(DiagnosticPosition pos, LintWarning warnKey) { 209 previewHandler.report(pos, warnKey); 210 } 211 212 public boolean usesPreview(JavaFileObject file) { 213 return sourcesWithPreviewFeatures.contains(file); 214 } 215 216 /** 217 * Are preview features enabled? 218 * @return true, if preview features are enabled. 219 */ 220 public boolean isEnabled() { 221 return enabled; 222 } 223 224 /** 225 * Is given feature a preview feature? 226 * @param feature the feature to be tested. 227 * @return true, if given feature is a preview feature. 228 */ 229 public boolean isPreview(Feature feature) { 230 return switch (feature) { 231 case IMPLICIT_CLASSES -> true; 232 case FLEXIBLE_CONSTRUCTORS -> true; 233 case PRIMITIVE_PATTERNS -> true; 234 case MODULE_IMPORTS -> true; 235 case JAVA_BASE_TRANSITIVE -> true; 236 //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). 237 //When real preview features will be added, this method can be implemented to return 'true' 238 //for those selected features, and 'false' for all the others. 239 default -> forcePreview; 240 }; 241 } 242 243 /** 244 * Generate an error key which captures the fact that a given preview feature could not be used 245 * due to the preview feature support being disabled. 246 * @param feature the feature for which the diagnostic has to be generated. 247 * @return the diagnostic. 248 */ 249 public Error disabledError(Feature feature) { 250 Assert.check(!isEnabled()); 251 return feature.isPlural() ? 252 Errors.PreviewFeatureDisabledPlural(feature.nameFragment()) : 253 Errors.PreviewFeatureDisabled(feature.nameFragment()); 254 } 255 256 /** 257 * Generate an error key which captures the fact that a preview classfile cannot be loaded 258 * due to the preview feature support being disabled. 259 * @param classfile the name of the classfile with preview features enabled 260 * @param majorVersion the major version found in the classfile. 261 */ 262 public Error disabledError(JavaFileObject classfile, int majorVersion) { 263 Assert.check(!isEnabled()); 264 return Errors.PreviewFeatureDisabledClassfile(classfile, majorVersionToSource.get(majorVersion).name); 265 } 266 267 /** 268 * Check whether the given symbol has been declared using 269 * a preview language feature. 270 * 271 * @param sym Symbol to check 272 * @return true iff sym has been declared using a preview language feature 273 */ 274 public boolean declaredUsingPreviewFeature(Symbol sym) { 275 return false; 276 } 277 278 /** 279 * Report any deferred diagnostics. 280 */ 281 public void reportDeferredDiagnostics() { 282 previewHandler.reportDeferredDiagnostic(); 283 } 284 285 public void clear() { 286 previewHandler.clear(); 287 } 288 289 public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { 290 if (isPreview(feature) && !isEnabled()) { 291 //preview feature without --preview flag, error 292 log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); 293 } else { 294 if (!feature.allowedInSource(source)) { 295 log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, 296 feature.error(source.name)); 297 } 298 if (isEnabled() && isPreview(feature)) { 299 warnPreview(pos, feature); 300 } 301 } 302 } 303 304 }