mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-03-22 15:19:59 +07:00
[java] IDEA-345517 Feature-based support of unsupported preview language levels
GitOrigin-RevId: 3037d36588c239d6236ba64a7df2bf2a3b51cba4
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d0cf7b359a
commit
2aa52eea2d
@@ -80,7 +80,7 @@ public final class LanguageLevelUtil {
|
||||
|
||||
@Nullable
|
||||
public static String getShortMessage(@NotNull LanguageLevel languageLevel) {
|
||||
return ourPresentableShortMessage.get(languageLevel.getSupportedLevel());
|
||||
return ourPresentableShortMessage.get(languageLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,10 @@ package com.intellij.pom.java;
|
||||
import com.intellij.core.JavaPsiBundle;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents Java language, JVM, or standard library features and provides information
|
||||
* whether a particular features is available in a given context
|
||||
@@ -59,15 +63,20 @@ public enum JavaFeature {
|
||||
ALWAYS_STRICTFP(LanguageLevel.JDK_17, "feature.strictfp"),
|
||||
INNER_NOT_CAPTURE_THIS(LanguageLevel.JDK_18, "feature.no.this.capture"),
|
||||
JAVADOC_SNIPPETS(LanguageLevel.JDK_18, "feature.javadoc.snippets"),
|
||||
PATTERNS_IN_SWITCH(LanguageLevel.JDK_21, "feature.patterns.in.switch"),
|
||||
PATTERN_GUARDS_AND_RECORD_PATTERNS(LanguageLevel.JDK_21, "feature.pattern.guard.and.record.patterns"),
|
||||
PATTERNS_IN_SWITCH(LanguageLevel.JDK_21, "feature.patterns.in.switch",
|
||||
LanguageLevel.JDK_17_PREVIEW, LanguageLevel.JDK_18_PREVIEW, LanguageLevel.JDK_19_PREVIEW, LanguageLevel.JDK_20_PREVIEW),
|
||||
PATTERN_GUARDS_AND_RECORD_PATTERNS(LanguageLevel.JDK_21, "feature.pattern.guard.and.record.patterns",
|
||||
LanguageLevel.JDK_19_PREVIEW, LanguageLevel.JDK_20_PREVIEW),
|
||||
/**
|
||||
* Was a preview feature in Java 20 Preview.
|
||||
* Keep the implementation, as it could reappear in the future.
|
||||
*/
|
||||
RECORD_PATTERNS_IN_FOR_EACH(LanguageLevel.JDK_X, "feature.record.patterns.in.for.each"),
|
||||
VIRTUAL_THREADS(LanguageLevel.JDK_21, "feature.virtual.threads"),
|
||||
FOREIGN_FUNCTIONS(LanguageLevel.JDK_21, "feature.foreign.functions"),
|
||||
RECORD_PATTERNS_IN_FOR_EACH(LanguageLevel.JDK_X, "feature.record.patterns.in.for.each",
|
||||
LanguageLevel.JDK_20_PREVIEW),
|
||||
VIRTUAL_THREADS(LanguageLevel.JDK_21, "feature.virtual.threads",
|
||||
LanguageLevel.JDK_19_PREVIEW, LanguageLevel.JDK_20_PREVIEW),
|
||||
FOREIGN_FUNCTIONS(LanguageLevel.JDK_21, "feature.foreign.functions",
|
||||
LanguageLevel.JDK_19_PREVIEW, LanguageLevel.JDK_20_PREVIEW),
|
||||
ENUM_QUALIFIED_NAME_IN_SWITCH(LanguageLevel.JDK_21, "feature.enum.qualified.name.in.switch"),
|
||||
STRING_TEMPLATES(LanguageLevel.JDK_21_PREVIEW, "feature.string.templates"),
|
||||
UNNAMED_PATTERNS_AND_VARIABLES(LanguageLevel.JDK_22, "feature.unnamed.vars") {
|
||||
@@ -90,16 +99,30 @@ public enum JavaFeature {
|
||||
@PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE)
|
||||
private final @NotNull String myKey;
|
||||
private final boolean myCanBeCustomized;
|
||||
private final Set<LanguageLevel> myObsoletePreviewLevels;
|
||||
|
||||
JavaFeature(@NotNull LanguageLevel level, @NotNull @PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key) {
|
||||
this(level, key, false);
|
||||
}
|
||||
|
||||
JavaFeature(@NotNull LanguageLevel level, @NotNull @PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key,
|
||||
@NotNull LanguageLevel @NotNull ... obsoletePreviewLevels) {
|
||||
myLevel = level;
|
||||
myKey = key;
|
||||
myCanBeCustomized = false;
|
||||
myObsoletePreviewLevels = EnumSet.noneOf(LanguageLevel.class);
|
||||
for (LanguageLevel obsoletePreviewLevel : obsoletePreviewLevels) {
|
||||
if (!obsoletePreviewLevel.isUnsupported()) throw new IllegalArgumentException(obsoletePreviewLevel.toString());
|
||||
myObsoletePreviewLevels.add(obsoletePreviewLevel);
|
||||
}
|
||||
}
|
||||
|
||||
JavaFeature(@NotNull LanguageLevel level, @NotNull @PropertyKey(resourceBundle = JavaPsiBundle.BUNDLE) String key,
|
||||
boolean canBeCustomized) {
|
||||
myLevel = level;
|
||||
myKey = key;
|
||||
myCanBeCustomized = canBeCustomized;
|
||||
myObsoletePreviewLevels = Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,8 +149,8 @@ public enum JavaFeature {
|
||||
}
|
||||
|
||||
public boolean isSufficient(@NotNull LanguageLevel useSiteLevel) {
|
||||
useSiteLevel = useSiteLevel.getSupportedLevel();
|
||||
return useSiteLevel.isAtLeast(myLevel) &&
|
||||
return (useSiteLevel.isAtLeast(myLevel) ||
|
||||
useSiteLevel.isUnsupported() && myObsoletePreviewLevels.contains(useSiteLevel)) &&
|
||||
(!myLevel.isPreview() || useSiteLevel.isPreview());
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,14 @@ import java.util.stream.Stream;
|
||||
/**
|
||||
* Represents a language level (i.e. features available) of a Java code.
|
||||
* The {@link org.jetbrains.jps.model.java.LanguageLevel} class is a compiler-side counterpart of this enum.
|
||||
* <p>
|
||||
* Unsupported language levels are marked as {@link ApiStatus.Obsolete} to draw attention. They should not be normally used,
|
||||
* except probably in rare tests and inside {@link JavaFeature}.
|
||||
*
|
||||
* @see com.intellij.openapi.roots.LanguageLevelModuleExtension
|
||||
* @see com.intellij.openapi.roots.LanguageLevelProjectExtension
|
||||
* @see JavaSdkVersion
|
||||
* @see JavaFeature
|
||||
*/
|
||||
public enum LanguageLevel {
|
||||
JDK_1_3(JavaPsiBundle.messagePointer("jdk.1.3.language.level.description"), 3),
|
||||
@@ -41,27 +45,23 @@ public enum LanguageLevel {
|
||||
JDK_15(JavaPsiBundle.messagePointer("jdk.15.language.level.description"), 15),
|
||||
JDK_16(JavaPsiBundle.messagePointer("jdk.16.language.level.description"), 16),
|
||||
JDK_17(JavaPsiBundle.messagePointer("jdk.17.language.level.description"), 17),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_17_PREVIEW(17),
|
||||
JDK_18(JavaPsiBundle.messagePointer("jdk.18.language.level.description"), 18),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_18_PREVIEW(18),
|
||||
JDK_19(JavaPsiBundle.messagePointer("jdk.19.language.level.description"), 19),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_19_PREVIEW(19),
|
||||
JDK_20(JavaPsiBundle.messagePointer("jdk.20.language.level.description"), 20),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_20_PREVIEW(20),
|
||||
JDK_21(JavaPsiBundle.messagePointer("jdk.21.language.level.description"), 21),
|
||||
JDK_21_PREVIEW(JavaPsiBundle.messagePointer("jdk.21.preview.language.level.description"), 21),
|
||||
JDK_22(JavaPsiBundle.messagePointer("jdk.22.language.level.description"), 22),
|
||||
JDK_22_PREVIEW(JavaPsiBundle.messagePointer("jdk.22.preview.language.level.description"), 22),
|
||||
JDK_X(JavaPsiBundle.messagePointer("jdk.X.language.level.description"), 23),
|
||||
|
||||
// Unsupported
|
||||
// Marked as obsolete to draw attention, as they should not be normally used in code or in tests,
|
||||
// except the tests that explicitly test the obsolete levels
|
||||
|
||||
@ApiStatus.Obsolete
|
||||
JDK_17_PREVIEW(17, JDK_21),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_18_PREVIEW(18, JDK_21),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_19_PREVIEW(19, JDK_21),
|
||||
@ApiStatus.Obsolete
|
||||
JDK_20_PREVIEW(20, JDK_21),
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -72,27 +72,27 @@ public enum LanguageLevel {
|
||||
private final Supplier<@Nls String> myPresentableText;
|
||||
private final JavaVersion myVersion;
|
||||
private final boolean myPreview;
|
||||
private final @Nullable LanguageLevel myAlias;
|
||||
private final boolean myUnsupported;
|
||||
private static final Map<Integer, LanguageLevel> ourStandardVersions =
|
||||
Stream.of(values()).filter(ver -> !ver.isPreview())
|
||||
.collect(Collectors.toMap(ver -> ver.myVersion.feature, Function.identity()));
|
||||
|
||||
LanguageLevel(Supplier<@Nls String> presentableTextSupplier, int major) {
|
||||
this(presentableTextSupplier, major, null);
|
||||
this(presentableTextSupplier, major, false);
|
||||
}
|
||||
|
||||
LanguageLevel(int major, @NotNull LanguageLevel alias) {
|
||||
this(JavaPsiBundle.messagePointer("jdk.unsupported.preview.language.level.description", major), major, alias);
|
||||
LanguageLevel(int major) {
|
||||
this(JavaPsiBundle.messagePointer("jdk.unsupported.preview.language.level.description", major), major, true);
|
||||
}
|
||||
|
||||
LanguageLevel(Supplier<@Nls String> presentableTextSupplier, int major, @Nullable LanguageLevel alias) {
|
||||
LanguageLevel(Supplier<@Nls String> presentableTextSupplier, int major, boolean unsupported) {
|
||||
myPresentableText = presentableTextSupplier;
|
||||
myVersion = JavaVersion.compose(major);
|
||||
if (alias != null && alias.isUnsupported()) {
|
||||
throw new IllegalArgumentException("Cannot alias to unsupported version");
|
||||
}
|
||||
myAlias = alias;
|
||||
myUnsupported = unsupported;
|
||||
myPreview = name().endsWith("_PREVIEW") || name().endsWith("_X");
|
||||
if (myUnsupported && !myPreview) {
|
||||
throw new IllegalArgumentException("Only preview versions could be unsupported: " + name());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPreview() {
|
||||
@@ -102,19 +102,9 @@ public enum LanguageLevel {
|
||||
/**
|
||||
* @return true if this language level is not supported anymore. It's still possible to invoke compiler or launch the program
|
||||
* using this language level. However, it's not guaranteed that the code insight features will work correctly.
|
||||
* All the code insight features will use the {@linkplain #getSupportedLevel() alias level} instead
|
||||
*/
|
||||
public boolean isUnsupported() {
|
||||
return myAlias != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the closest supported language level for the unsupported level;
|
||||
* returns this for supported level
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public @NotNull LanguageLevel getSupportedLevel() {
|
||||
return myAlias == null ? this : myAlias;
|
||||
return myUnsupported;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,13 +71,9 @@ public final class AcceptedLanguageLevelsSettings implements PersistentStateComp
|
||||
|
||||
int previewFeature = languageLevel.toJavaVersion().feature;
|
||||
if (languageLevel.isUnsupported()) {
|
||||
LanguageLevel supportedLevel = languageLevel.getSupportedLevel();
|
||||
String supportedLevelPresentation = supportedLevel.isPreview()
|
||||
? JavaBundle.message("java.preview.level", supportedLevel.toJavaVersion().feature)
|
||||
: String.valueOf(supportedLevel.toJavaVersion().feature);
|
||||
PREVIEW_NOTIFICATION_GROUP.createNotification(
|
||||
JavaBundle.message("java.preview.features.unsupported.title"),
|
||||
JavaBundle.message("java.preview.features.unsupported", previewFeature, supportedLevelPresentation),
|
||||
JavaBundle.message("java.preview.features.unsupported", previewFeature),
|
||||
NotificationType.ERROR)
|
||||
.notify(project);
|
||||
}
|
||||
|
||||
@@ -36,7 +36,10 @@ import com.intellij.util.TimeoutUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.JBIterable;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
import org.jetbrains.annotations.*;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
@@ -1078,8 +1081,7 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element language level. If {@linkplain LanguageLevel#isUnsupported() unsupported} language level
|
||||
* is selected for project or module, this method returns the supported alias.
|
||||
* Returns the element language level.
|
||||
* <p>
|
||||
* Note that it's a rare case when one may need a language level. Usually, it's interesting to check
|
||||
* whether a particular language feature is available at a given context.
|
||||
@@ -1090,16 +1092,6 @@ public final class PsiUtil extends PsiUtilCore {
|
||||
*/
|
||||
@NotNull
|
||||
public static LanguageLevel getLanguageLevel(@NotNull PsiElement element) {
|
||||
return getDeclaredLanguageLevel(element).getSupportedLevel();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param element element to get Java language level for
|
||||
* @return the language level. May return {@linkplain LanguageLevel#isUnsupported() unsupported} level.
|
||||
*/
|
||||
@NotNull
|
||||
@ApiStatus.Experimental
|
||||
public static LanguageLevel getDeclaredLanguageLevel(@NotNull PsiElement element) {
|
||||
if (element instanceof PsiDirectory) {
|
||||
return JavaDirectoryService.getInstance().getLanguageLevel((PsiDirectory)element);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java;
|
||||
|
||||
import com.intellij.pom.java.JavaFeature;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public final class LanguageLevelTest {
|
||||
@Test
|
||||
@@ -13,7 +13,9 @@ public final class LanguageLevelTest {
|
||||
@SuppressWarnings("UsagesOfObsoleteApi")
|
||||
LanguageLevel jdk17Preview = LanguageLevel.JDK_17_PREVIEW;
|
||||
assertTrue(jdk17Preview.isUnsupported());
|
||||
assertEquals(LanguageLevel.JDK_21, jdk17Preview.getSupportedLevel());
|
||||
assertTrue(JavaFeature.PATTERNS_IN_SWITCH.isSufficient(jdk17Preview));
|
||||
assertFalse(JavaFeature.PATTERN_GUARDS_AND_RECORD_PATTERNS.isSufficient(jdk17Preview));
|
||||
assertEquals(LanguageLevel.JDK_17, jdk17Preview.getNonPreviewLevel());
|
||||
assertFalse(JavaFeature.PATTERNS_IN_SWITCH.isSufficient(jdk17Preview.getNonPreviewLevel()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class JavaElementFactoryTest extends LightJavaCodeInsightFixtureTestCase
|
||||
|
||||
public void testArrayClassLanguageLevel() {
|
||||
for (LanguageLevel level : LanguageLevel.values()) {
|
||||
assertEquals(level, PsiUtil.getDeclaredLanguageLevel(myFactory.getArrayClass(level)));
|
||||
assertEquals(level, PsiUtil.getLanguageLevel(myFactory.getArrayClass(level)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -952,7 +952,7 @@ java.preview.features.notification.title=Java preview features
|
||||
java.preview.features.warning=Newer IDE versions may discontinue support for Java preview features. When Java {0} is released, support for the {1} (Preview) language level may be dropped.
|
||||
java.preview.features.unsupported.title=Unsupported Java preview features
|
||||
java.preview.features.unsupported=Java language level <b>{0} (Preview)</b> used in this project is not supported anymore. \
|
||||
The code insight features will assume language level {1} instead.<br>\
|
||||
The code insight features may work incorrectly.<br>\
|
||||
It''s strongly encouraged to migrate to newer Java version or stop using preview features.
|
||||
java.terms.exception=exception
|
||||
java.terms.region=region
|
||||
|
||||
@@ -22,18 +22,14 @@ public enum LanguageLevel {
|
||||
JDK_14(14),
|
||||
JDK_15(15),
|
||||
JDK_16(16),
|
||||
JDK_17(17),
|
||||
JDK_18(18),
|
||||
JDK_19(19),
|
||||
JDK_17(17), JDK_17_PREVIEW(17),
|
||||
JDK_18(18), JDK_18_PREVIEW(18),
|
||||
JDK_19(19), JDK_19_PREVIEW(19),
|
||||
JDK_20(20), JDK_20_PREVIEW(20),
|
||||
JDK_21(21), JDK_21_PREVIEW(21),
|
||||
JDK_22(22), JDK_22_PREVIEW(22),
|
||||
JDK_X(23),
|
||||
|
||||
// Unsupported in IDE
|
||||
JDK_17_PREVIEW(17),
|
||||
JDK_18_PREVIEW(18),
|
||||
JDK_19_PREVIEW(19),
|
||||
;
|
||||
|
||||
public static final LanguageLevel HIGHEST = JDK_21;
|
||||
|
||||
Reference in New Issue
Block a user