1 /* 2 * Copyright (c) 2018, 2024, 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.jvm.Target; 31 import com.sun.tools.javac.resources.CompilerProperties.Errors; 32 import com.sun.tools.javac.resources.CompilerProperties.Warnings; 33 import com.sun.tools.javac.util.Assert; 34 import com.sun.tools.javac.util.Context; 35 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition; 36 import com.sun.tools.javac.util.JCDiagnostic.Error; 37 import com.sun.tools.javac.util.JCDiagnostic.SimpleDiagnosticPosition; 38 import com.sun.tools.javac.util.JCDiagnostic.Warning; 39 import com.sun.tools.javac.util.Log; 40 import com.sun.tools.javac.util.MandatoryWarningHandler; 41 import com.sun.tools.javac.util.Names; 42 import com.sun.tools.javac.util.Options; 43 44 import javax.tools.JavaFileObject; 45 import java.util.HashMap; 46 import java.util.HashSet; 47 import java.util.Map; 48 import java.util.Set; 49 50 import static com.sun.tools.javac.main.Option.PREVIEW; 51 import com.sun.tools.javac.util.JCDiagnostic; 52 53 /** 54 * Helper class to handle preview language features. This class maps certain language features 55 * (see {@link Feature} into 'preview' features; the mapping is completely ad-hoc, so as to allow 56 * for maximum flexibility, which allows to migrate preview feature into supported features with ease. 57 * 58 * This class acts as a centralized point against which usages of preview features are reported by 59 * clients (e.g. other javac classes). Internally, this class collects all such usages and generates 60 * diagnostics to inform the user of such usages. Such diagnostics can be enabled using the 61 * {@link LintCategory#PREVIEW} lint category, and are suppressible by usual means. 62 */ 63 public class Preview { 64 65 /** flag: are preview features enabled */ 66 private final boolean enabled; 67 68 /** the diag handler to manage preview feature usage diagnostics */ 69 private final MandatoryWarningHandler previewHandler; 70 71 /** test flag: should all features be considered as preview features? */ 72 private final boolean forcePreview; 73 74 /** a mapping from classfile numbers to Java SE versions */ 75 private final Map<Integer, Source> majorVersionToSource; 76 77 private final Set<JavaFileObject> sourcesWithPreviewFeatures = new HashSet<>(); 78 79 private final Names names; 80 private final Lint lint; 81 private final Log log; 82 private final Source source; 83 84 protected static final Context.Key<Preview> previewKey = new Context.Key<>(); 85 86 public static Preview instance(Context context) { 87 Preview instance = context.get(previewKey); 88 if (instance == null) { 89 instance = new Preview(context); 90 } 91 return instance; 92 } 93 94 @SuppressWarnings("this-escape") 95 protected Preview(Context context) { 96 context.put(previewKey, this); 97 Options options = Options.instance(context); 98 names = Names.instance(context); 99 enabled = options.isSet(PREVIEW); 100 log = Log.instance(context); 101 lint = Lint.instance(context); 102 source = Source.instance(context); 103 this.previewHandler = 104 new MandatoryWarningHandler(log, source, lint.isEnabled(LintCategory.PREVIEW), true, "preview", LintCategory.PREVIEW); 105 forcePreview = options.isSet("forcePreview"); 106 majorVersionToSource = initMajorVersionToSourceMap(); 107 } 108 109 private Map<Integer, Source> initMajorVersionToSourceMap() { 110 Map<Integer, Source> majorVersionToSource = new HashMap<>(); 111 for (Target t : Target.values()) { 112 int major = t.majorVersion; 113 Source source = Source.lookup(t.name); 114 if (source != null) { 115 majorVersionToSource.put(major, source); 116 } 117 } 118 return majorVersionToSource; 119 } 120 121 /** 122 * Returns true if {@code s} is deemed to participate in the preview of {@code previewSymbol}, and 123 * therefore no warnings or errors will be produced. 124 * 125 * @param syms the symbol table 126 * @param s the symbol depending on the preview symbol 127 * @param previewSymbol the preview symbol marked with @Preview 128 * @return true if {@code s} is participating in the preview of {@code previewSymbol} 129 */ 130 public boolean participatesInPreview(Symtab syms, Symbol s, Symbol previewSymbol) { 131 // All symbols in the same module as the preview symbol participate in the preview API 132 if (previewSymbol.packge().modle == s.packge().modle) { 133 return true; 134 } 135 136 // If java.base's jdk.internal.javac package is exported to s's module then 137 // s participates in the preview API 138 return syms.java_base.exports.stream() 139 .filter(ed -> ed.packge.fullname == names.jdk_internal_javac) 140 .anyMatch(ed -> ed.modules.contains(s.packge().modle)); 141 } 142 143 /** 144 * Report usage of a preview feature. Usages reported through this method will affect the 145 * set of sourcefiles with dependencies on preview features. 146 * @param pos the position at which the preview feature was used. 147 * @param feature the preview feature used. 148 */ 149 public void warnPreview(int pos, Feature feature) { 150 warnPreview(new SimpleDiagnosticPosition(pos), feature); 151 } 152 153 /** 154 * Report usage of a preview feature. Usages reported through this method will affect the 155 * set of sourcefiles with dependencies on preview features. 156 * @param pos the position at which the preview feature was used. 157 * @param feature the preview feature used. 158 */ 159 public void warnPreview(DiagnosticPosition pos, Feature feature) { 160 Assert.check(isEnabled()); 161 Assert.check(isPreview(feature)); 162 if (!lint.isSuppressed(LintCategory.PREVIEW)) { 163 sourcesWithPreviewFeatures.add(log.currentSourceFile()); 164 previewHandler.report(pos, feature.isPlural() ? 165 Warnings.PreviewFeatureUsePlural(feature.nameFragment()) : 166 Warnings.PreviewFeatureUse(feature.nameFragment())); 167 } 168 } 169 170 /** 171 * Report usage of a preview feature in classfile. 172 * @param classfile the name of the classfile with preview features enabled 173 * @param majorVersion the major version found in the classfile. 174 */ 175 public void warnPreview(JavaFileObject classfile, int majorVersion) { 176 Assert.check(isEnabled()); 177 if (lint.isEnabled(LintCategory.PREVIEW)) { 178 log.mandatoryWarning(LintCategory.PREVIEW, null, 179 Warnings.PreviewFeatureUseClassfile(classfile, majorVersionToSource.get(majorVersion).name)); 180 } 181 } 182 183 public void markUsesPreview(DiagnosticPosition pos) { 184 sourcesWithPreviewFeatures.add(log.currentSourceFile()); 185 } 186 187 public void reportPreviewWarning(DiagnosticPosition pos, Warning warnKey) { 188 previewHandler.report(pos, warnKey); 189 } 190 191 public boolean usesPreview(JavaFileObject file) { 192 return sourcesWithPreviewFeatures.contains(file); 193 } 194 195 /** 196 * Are preview features enabled? 197 * @return true, if preview features are enabled. 198 */ 199 public boolean isEnabled() { 200 return enabled; 201 } 202 203 /** 204 * Is given feature a preview feature? 205 * @param feature the feature to be tested. 206 * @return true, if given feature is a preview feature. 207 */ 208 public boolean isPreview(Feature feature) { 209 return switch (feature) { 210 case IMPLICIT_CLASSES -> true; 211 case FLEXIBLE_CONSTRUCTORS -> true; 212 case PRIMITIVE_PATTERNS -> true; 213 case MODULE_IMPORTS -> true; 214 //Note: this is a backdoor which allows to optionally treat all features as 'preview' (for testing). 215 //When real preview features will be added, this method can be implemented to return 'true' 216 //for those selected features, and 'false' for all the others. 217 default -> forcePreview; 218 }; 219 } 220 221 /** 222 * Generate an error key which captures the fact that a given preview feature could not be used 223 * due to the preview feature support being disabled. 224 * @param feature the feature for which the diagnostic has to be generated. 225 * @return the diagnostic. 226 */ 227 public Error disabledError(Feature feature) { 228 Assert.check(!isEnabled()); 229 return feature.isPlural() ? 230 Errors.PreviewFeatureDisabledPlural(feature.nameFragment()) : 231 Errors.PreviewFeatureDisabled(feature.nameFragment()); 232 } 233 234 /** 235 * Generate an error key which captures the fact that a preview classfile cannot be loaded 236 * due to the preview feature support being disabled. 237 * @param classfile the name of the classfile with preview features enabled 238 * @param majorVersion the major version found in the classfile. 239 */ 240 public Error disabledError(JavaFileObject classfile, int majorVersion) { 241 Assert.check(!isEnabled()); 242 return Errors.PreviewFeatureDisabledClassfile(classfile, majorVersionToSource.get(majorVersion).name); 243 } 244 245 /** 246 * Check whether the given symbol has been declared using 247 * a preview language feature. 248 * 249 * @param sym Symbol to check 250 * @return true iff sym has been declared using a preview language feature 251 */ 252 public boolean declaredUsingPreviewFeature(Symbol sym) { 253 return false; 254 } 255 256 /** 257 * Report any deferred diagnostics. 258 */ 259 public void reportDeferredDiagnostics() { 260 previewHandler.reportDeferredDiagnostic(); 261 } 262 263 public void clear() { 264 previewHandler.clear(); 265 } 266 267 public void checkSourceLevel(DiagnosticPosition pos, Feature feature) { 268 if (isPreview(feature) && !isEnabled()) { 269 //preview feature without --preview flag, error 270 log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, disabledError(feature)); 271 } else { 272 if (!feature.allowedInSource(source)) { 273 log.error(JCDiagnostic.DiagnosticFlag.SOURCE_LEVEL, pos, 274 feature.error(source.name)); 275 } 276 if (isEnabled() && isPreview(feature)) { 277 warnPreview(pos, feature); 278 } 279 } 280 } 281 282 }