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 } 158 159 /** 160 * Report usage of a preview feature. Usages reported through this method will affect the 161 * set of sourcefiles with dependencies on preview features. 162 * @param pos the position at which the preview feature was used. 163 * @param feature the preview feature used. 164 */ 165 public void warnPreview(int pos, Feature feature) { 166 warnPreview(new SimpleDiagnosticPosition(pos), feature); 167 } 168 169 /** 170 * Report usage of a preview feature. Usages reported through this method will affect the 171 * set of sourcefiles with dependencies on preview features. 172 * @param pos the position at which the preview feature was used. 173 * @param feature the preview feature used. 174 */ 175 public void warnPreview(DiagnosticPosition pos, Feature feature) { 176 Assert.check(isEnabled()); 177 Assert.check(isPreview(feature)); 178 markUsesPreview(pos); 179 previewHandler.report(pos, feature.isPlural() ? 180 LintWarnings.PreviewFeatureUsePlural(feature.nameFragment()) : 181 LintWarnings.PreviewFeatureUse(feature.nameFragment())); 182 } 183 184 /** 185 * Report usage of a preview feature in classfile. 186 * @param classfile the name of the classfile with preview features enabled 187 * @param majorVersion the major version found in the classfile. 188 */ 189 public void warnPreview(JavaFileObject classfile, int majorVersion) { 190 Assert.check(isEnabled()); 191 if (verbose) { 192 log.mandatoryWarning(null, 193 LintWarnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); 194 } 195 } 196 197 /** 198 * Mark the current source file as using a preview feature. The corresponding classfile 199 * will be generated with minor version {@link ClassFile#PREVIEW_MINOR_VERSION}. 200 * @param pos the position at which the preview feature was used. 201 */ 202 public void markUsesPreview(DiagnosticPosition pos) { 203 sourcesWithPreviewFeatures.add(log.currentSourceFile()); 204 } 205 206 public void reportPreviewWarning(DiagnosticPosition pos, LintWarning warnKey) { 207 previewHandler.report(pos, warnKey); 208 } 209 210 public boolean usesPreview(JavaFileObject file) { 211 return sourcesWithPreviewFeatures.contains(file); 212 } 213 214 /** 215 * Are preview features enabled? 216 * @return true, if preview features are enabled. 217 */ 218 public boolean isEnabled() { 219 return enabled; 220 } 221 222 /** 223 * Is given feature a preview feature? 224 * @param feature the feature to be tested. 225 * @return true, if given feature is a preview feature. 226 */ 227 public boolean isPreview(Feature feature) { 228 return switch (feature) { 229 case PRIMITIVE_PATTERNS -> true; 230 //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). 231 //When real preview features will be added, this method can be implemented to return 'true' 232 //for those selected features, and 'false' for all the others. 233 default -> forcePreview; 234 }; 235 } 236 237 /** 238 * Generate an error key which captures the fact that a given preview feature could not be used 239 * due to the preview feature support being disabled. 240 * @param feature the feature for which the diagnostic has to be generated. 241 * @return the diagnostic. 242 */ 243 public Error disabledError(Feature feature) { 244 Assert.check(!isEnabled()); 245 return feature.isPlural() ? 246 Errors.PreviewFeatureDisabledPlural(feature.nameFragment()) : 247 Errors.PreviewFeatureDisabled(feature.nameFragment()); 248 } 249 250 /** 251 * Generate an error key which captures the fact that a preview classfile cannot be loaded 252 * due to the preview feature support being disabled. 253 * @param classfile the name of the classfile with preview features enabled 254 * @param majorVersion the major version found in the classfile. 255 */ 256 public Error disabledError(JavaFileObject classfile, int majorVersion) { 257 Assert.check(!isEnabled()); 258 return Errors.PreviewFeatureDisabledClassfile(classfile, majorVersionToSource.get(majorVersion).name); 259 } 260 261 /** 262 * Check whether the given symbol has been declared using 263 * a preview language feature. 264 * 265 * @param sym Symbol to check 266 * @return true iff sym has been declared using a preview language feature 267 */ 268 public boolean declaredUsingPreviewFeature(Symbol sym) { 269 return false; 270 } 271 272 /** 273 * Report any deferred diagnostics. 274 */ 275 public void reportDeferredDiagnostics() { 276 previewHandler.reportDeferredDiagnostic(); 277 } 278 279 public void clear() { 280 previewHandler.clear(); 281 } 282 283 public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { 284 if (isPreview(feature) && !isEnabled()) { 285 //preview feature without --preview flag, error 286 log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); 287 } else { 288 if (!feature.allowedInSource(source)) { 289 log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, 290 feature.error(source.name)); 291 } 292 if (isEnabled() && isPreview(feature)) { 293 warnPreview(pos, feature); 294 } 295 } 296 } 297 298 }