diff --git a/java/idea-ui/resources/messages/JavaUiBundle.properties b/java/idea-ui/resources/messages/JavaUiBundle.properties index 10e4bfe6a2f8..86eb52729675 100644 --- a/java/idea-ui/resources/messages/JavaUiBundle.properties +++ b/java/idea-ui/resources/messages/JavaUiBundle.properties @@ -664,6 +664,7 @@ label.select.project.type.to.configure=Select project type to configure language.level.combo.lts.versions=LTS Versions language.level.combo.other.versions=Other Versions language.level.combo.experimental.versions=Experimental Versions +language.level.combo.unsupported.versions=Unsupported Versions project.or.module.jdk.misconfigured={0, choice, 0#Project|1#Module} JDK is misconfigured notification.group.build.script.found=Build script found diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.kt b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.kt index 5249e5913d87..42b5527fb25a 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.kt +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/LanguageLevelCombo.kt @@ -11,12 +11,16 @@ import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.ui.popup.ListSeparator import com.intellij.pom.java.AcceptedLanguageLevelsSettings import com.intellij.pom.java.LanguageLevel +import com.intellij.ui.ColoredText import com.intellij.ui.GroupedComboBoxRenderer +import com.intellij.ui.SimpleColoredComponent +import com.intellij.ui.SimpleTextAttributes import com.intellij.util.ArrayUtil import org.jetbrains.annotations.Nls import org.jetbrains.annotations.NonNls import javax.swing.DefaultComboBoxModel +@OptIn(ExperimentalStdlibApi::class) abstract class LanguageLevelCombo @JvmOverloads constructor( defaultItem: @Nls String?, // It could be an open method but since we use it in constructor we have a leaking this problem @@ -37,13 +41,22 @@ abstract class LanguageLevelCombo @JvmOverloads constructor( val otherItems = items.filterAndAdd(buildList { LanguageLevel.entries .sortedBy { it.toJavaVersion().feature } - .filter { level: LanguageLevel -> level <= highestWithPreview && (level.isPreview || !ArrayUtil.contains(level, *LTS)) } + .filter { level: LanguageLevel -> level <= highestWithPreview && !level.isUnsupported && + (level.isPreview || !ArrayUtil.contains(level, *LTS)) } .forEach { level: LanguageLevel -> add(level) } }) val experimentalItems = items.filterAndAdd(buildList { for (level in LanguageLevel.entries) { - if (level > highestWithPreview) { + if (level > highestWithPreview && !level.isUnsupported) { + add(level) + } + } + }) + + val unsupportedItems = items.filterAndAdd(buildList { + for (level in LanguageLevel.entries) { + if (level.isUnsupported) { add(level) } } @@ -52,7 +65,8 @@ abstract class LanguageLevelCombo @JvmOverloads constructor( val separatorsMap: Map = listOf( ltsItems.firstOrNull() to ListSeparator(JavaUiBundle.message("language.level.combo.lts.versions")), otherItems.firstOrNull() to ListSeparator(JavaUiBundle.message("language.level.combo.other.versions")), - experimentalItems.firstOrNull() to ListSeparator(JavaUiBundle.message("language.level.combo.experimental.versions")) + experimentalItems.firstOrNull() to ListSeparator(JavaUiBundle.message("language.level.combo.experimental.versions")), + unsupportedItems.firstOrNull() to ListSeparator(JavaUiBundle.message("language.level.combo.unsupported.versions")), ).filter { it.first != null }.toMap() isSwingPopup = false @@ -64,6 +78,14 @@ abstract class LanguageLevelCombo @JvmOverloads constructor( else -> "" } + override fun customize(item: SimpleColoredComponent, value: Any, index: Int, isSelected: Boolean, cellHasFocus: Boolean) { + if (value is LanguageLevel && value.isUnsupported) { + item.append(ColoredText.singleFragment(value.presentableText, SimpleTextAttributes.ERROR_ATTRIBUTES)) + } else { + super.customize(item, value, index, isSelected, cellHasFocus) + } + } + override fun separatorFor(value: Any): ListSeparator? = separatorsMap[value] } } diff --git a/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java b/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java index c103e9fcf82c..059c2af4ce7c 100644 --- a/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java +++ b/java/java-analysis-api/src/com/intellij/openapi/module/LanguageLevelUtil.java @@ -25,13 +25,20 @@ import static com.intellij.reference.SoftReference.dereference; public final class LanguageLevelUtil { /** - * Returns explicitly specified custom language level for {@code module}, or {@code null} if the module uses 'Project default' language level + * Returns explicitly specified custom language level for {@code module}, or {@code null} if the module uses 'Project default' language level. + * May return {@linkplain LanguageLevel#isUnsupported() unsupported} language level. + * @param module to get the language level for. */ public static @Nullable LanguageLevel getCustomLanguageLevel(@NotNull Module module) { LanguageLevelModuleExtension moduleExtension = ModuleRootManager.getInstance(module).getModuleExtension(LanguageLevelModuleExtension.class); return moduleExtension != null ? moduleExtension.getLanguageLevel() : null; } + /** + * Returns effective language level for the module (either custom module level, or project level if module level is not specified). + * May return {@linkplain LanguageLevel#isUnsupported() unsupported} language level. + * @param module to get the language level for. + */ @NotNull public static LanguageLevel getEffectiveLanguageLevel(@NotNull final Module module) { ApplicationManager.getApplication().assertReadAccessAllowed(); @@ -73,7 +80,7 @@ public final class LanguageLevelUtil { @Nullable public static String getShortMessage(@NotNull LanguageLevel languageLevel) { - return ourPresentableShortMessage.get(languageLevel); + return ourPresentableShortMessage.get(languageLevel.getSupportedLevel()); } /** @@ -136,7 +143,9 @@ public final class LanguageLevelUtil { if (forbiddenApi == null) return null; if (forbiddenApi.contains(signature)) return languageLevel; if (languageLevel.compareTo(LanguageLevel.HIGHEST) == 0) return null; - LanguageLevel nextLanguageLevel = LanguageLevel.values()[languageLevel.ordinal() + 1]; + LanguageLevel[] values = LanguageLevel.values(); + if (languageLevel.ordinal() == values.length - 1) return null; + LanguageLevel nextLanguageLevel = values[languageLevel.ordinal() + 1]; return getLastIncompatibleLanguageLevelForSignature(signature, nextLanguageLevel); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java index 22007828278f..4bc1131eba11 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightingFeature.java @@ -121,6 +121,7 @@ public enum HighlightingFeature { } boolean isSufficient(@NotNull LanguageLevel useSiteLevel) { + useSiteLevel = useSiteLevel.getSupportedLevel(); return useSiteLevel.isAtLeast(level) && (!level.isPreview() || useSiteLevel.isPreview()); } @@ -128,6 +129,7 @@ public enum HighlightingFeature { boolean isLimited() { return false; } + /** * Override if feature was preview and then accepted as standard */ diff --git a/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties b/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties index 699ec73bc57a..0b32aaf32dea 100644 --- a/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties +++ b/java/java-frontback-psi-api/resources/messages/JavaPsiBundle.properties @@ -138,6 +138,7 @@ jdk.21.preview.language.level.description=21 (Preview) - String templates, unnam jdk.22.language.level.description=22 - Unnamed variables and patterns jdk.22.preview.language.level.description=22 (Preview) - Statements before super(), string templates (2nd preview) etc. jdk.X.language.level.description=X - Experimental features +jdk.unsupported.preview.language.level.description={0} (Preview) - Unsupported local.class.preposition=local method.context.display={0}() in {1} diff --git a/java/java-frontback-psi-api/src/com/intellij/openapi/roots/LanguageLevelProjectExtension.java b/java/java-frontback-psi-api/src/com/intellij/openapi/roots/LanguageLevelProjectExtension.java index 16eada22c4eb..4f6642bbc2a5 100644 --- a/java/java-frontback-psi-api/src/com/intellij/openapi/roots/LanguageLevelProjectExtension.java +++ b/java/java-frontback-psi-api/src/com/intellij/openapi/roots/LanguageLevelProjectExtension.java @@ -18,6 +18,10 @@ public abstract class LanguageLevelProjectExtension { return project.getService(LanguageLevelProjectExtension.class); } + /** + * @return configured project language level. + * May return {@linkplain LanguageLevel#isUnsupported() unsupported} language level. + */ @NotNull public abstract LanguageLevel getLanguageLevel(); diff --git a/java/java-frontback-psi-api/src/com/intellij/pom/java/LanguageLevel.java b/java/java-frontback-psi-api/src/com/intellij/pom/java/LanguageLevel.java index 71e684e9e38b..fb7ac8cd42db 100644 --- a/java/java-frontback-psi-api/src/com/intellij/pom/java/LanguageLevel.java +++ b/java/java-frontback-psi-api/src/com/intellij/pom/java/LanguageLevel.java @@ -4,7 +4,9 @@ package com.intellij.pom.java; import com.intellij.core.JavaPsiBundle; import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.lang.JavaVersion; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -43,7 +45,19 @@ public enum LanguageLevel { 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),; + 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_20_PREVIEW), + @ApiStatus.Obsolete + JDK_18_PREVIEW(18, JDK_20_PREVIEW), + @ApiStatus.Obsolete + JDK_19_PREVIEW(19, JDK_20_PREVIEW), + ; /** * Should point to the latest released JDK. @@ -53,10 +67,23 @@ public enum LanguageLevel { private final Supplier<@Nls String> myPresentableText; private final JavaVersion myVersion; private final boolean myPreview; + private final @Nullable LanguageLevel myAlias; LanguageLevel(Supplier<@Nls String> presentableTextSupplier, int major) { + this(presentableTextSupplier, major, null); + } + + LanguageLevel(int major, @NotNull LanguageLevel alias) { + this(JavaPsiBundle.messagePointer("jdk.unsupported.preview.language.level.description", major), major, alias); + } + + LanguageLevel(Supplier<@Nls String> presentableTextSupplier, int major, @Nullable LanguageLevel alias) { myPresentableText = presentableTextSupplier; myVersion = JavaVersion.compose(major); + if (alias != null && alias.isUnsupported()) { + throw new IllegalArgumentException("Cannot alias to unsupported version"); + } + myAlias = alias; myPreview = name().endsWith("_PREVIEW") || name().endsWith("_X"); } @@ -64,6 +91,24 @@ public enum LanguageLevel { return myPreview; } + /** + * @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 corresponding preview level, or {@code null} if level has no paired preview level */ @@ -77,6 +122,14 @@ public enum LanguageLevel { } } + /** + * @return corresponding non-preview level; this if this level is non-preview already + */ + public @NotNull LanguageLevel getNonPreviewLevel() { + if (!myPreview) return this; + return valueOf(StringUtil.substringBefore(name(), "_PREVIEW")); + } + @NotNull @Nls public String getPresentableText() { diff --git a/java/java-impl-inspections/src/com/intellij/codeInspection/CapturingCleanerInspection.java b/java/java-impl-inspections/src/com/intellij/codeInspection/CapturingCleanerInspection.java index 6a50db97a0f6..b664fe3eff30 100644 --- a/java/java-impl-inspections/src/com/intellij/codeInspection/CapturingCleanerInspection.java +++ b/java/java-impl-inspections/src/com/intellij/codeInspection/CapturingCleanerInspection.java @@ -2,6 +2,7 @@ package com.intellij.codeInspection; import com.intellij.java.JavaBundle; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -94,7 +95,7 @@ public class CapturingCleanerInspection extends AbstractBaseJavaLocalInspectionT } if (runnableExpr instanceof PsiNewExpression newExpression) { if (newExpression.getAnonymousClass() != null) { - if (PsiUtil.isLanguageLevel18OrHigher(trackedClass)) { + if (PsiUtil.getDeclaredLanguageLevel(trackedClass).isAtLeast(LanguageLevel.JDK_18)) { PsiElement elementCapturingThis = getLambdaOrInnerClassElementCapturingThis(newExpression, trackedClass); if (elementCapturingThis != null) { return elementCapturingThis; @@ -109,7 +110,8 @@ public class CapturingCleanerInspection extends AbstractBaseJavaLocalInspectionT if (aClass == null) return null; if (aClass.getContainingClass() != trackedClass) return null; if (aClass.hasModifierProperty(PsiModifier.STATIC)) return null; - if (PsiUtil.isLanguageLevel18OrHigher(trackedClass) && getLambdaOrInnerClassElementCapturingThis(newExpression, trackedClass) == null) return null; + if (PsiUtil.getDeclaredLanguageLevel(trackedClass).isAtLeast(LanguageLevel.JDK_18) && + getLambdaOrInnerClassElementCapturingThis(newExpression, trackedClass) == null) return null; return classReference; } return null; diff --git a/java/java-impl/src/com/intellij/pom/java/AcceptedLanguageLevelsSettings.java b/java/java-impl/src/com/intellij/pom/java/AcceptedLanguageLevelsSettings.java index b6937ba143bc..f2a41b3558e0 100644 --- a/java/java-impl/src/com/intellij/pom/java/AcceptedLanguageLevelsSettings.java +++ b/java/java-impl/src/com/intellij/pom/java/AcceptedLanguageLevelsSettings.java @@ -63,25 +63,38 @@ public final class AcceptedLanguageLevelsSettings implements PersistentStateComp showNotificationToAccept(project, unacceptedLevels, level); } } - if (!previewLevels.isEmpty() && !PropertiesComponent.getInstance(project).getBoolean(IGNORE_USED_PREVIEW_FEATURES, false)) { + if (!previewLevels.isEmpty()) { LanguageLevel languageLevel = previewLevels.first(); if (languageLevel == LanguageLevel.JDK_X) { return; } int previewFeature = languageLevel.toJavaVersion().feature; - PREVIEW_NOTIFICATION_GROUP.createNotification( - JavaBundle.message("java.preview.features.notification.title"), - JavaBundle.message("java.preview.features.warning", previewFeature + 1, previewFeature), - NotificationType.WARNING) - .addAction(new NotificationAction(IdeBundle.message("action.Anonymous.text.do.not.show.again")) { - @Override - public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { - PropertiesComponent.getInstance(project).setValue(IGNORE_USED_PREVIEW_FEATURES, true); - notification.expire(); - } - }) - .notify(project); + 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), + NotificationType.ERROR) + .notify(project); + } + else if (!PropertiesComponent.getInstance(project).getBoolean(IGNORE_USED_PREVIEW_FEATURES, false)) { + PREVIEW_NOTIFICATION_GROUP.createNotification( + JavaBundle.message("java.preview.features.notification.title"), + JavaBundle.message("java.preview.features.warning", previewFeature + 1, previewFeature), + NotificationType.WARNING) + .addAction(new NotificationAction(IdeBundle.message("action.Anonymous.text.do.not.show.again")) { + @Override + public void actionPerformed(@NotNull AnActionEvent e, @NotNull Notification notification) { + PropertiesComponent.getInstance(project).setValue(IGNORE_USED_PREVIEW_FEATURES, true); + notification.expire(); + } + }) + .notify(project); + } } } diff --git a/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java b/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java index 7642958ae1d6..61b1f770bd10 100644 --- a/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java +++ b/java/java-impl/src/com/siyeh/ig/memory/AnonymousInnerClassMayBeStaticInspection.java @@ -16,6 +16,7 @@ package com.siyeh.ig.memory; import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; import com.intellij.psi.util.PsiTreeUtil; @@ -55,7 +56,7 @@ public class AnonymousInnerClassMayBeStaticInspection extends BaseInspection { if (anonymousClass instanceof PsiEnumConstantInitializer) { return; } - if (PsiUtil.isLanguageLevel18OrHigher(anonymousClass) && + if (PsiUtil.getDeclaredLanguageLevel(anonymousClass).isAtLeast(LanguageLevel.JDK_18) && !InheritanceUtil.isInheritor(anonymousClass, CommonClassNames.JAVA_IO_SERIALIZABLE)) { // Since Java 18, non-serializable anonymous classes don't capture 'this' reference return; diff --git a/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java b/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java index ea5d41ea8d09..2f2251941ac9 100644 --- a/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java +++ b/java/java-psi-api/src/com/intellij/openapi/roots/LanguageLevelModuleExtension.java @@ -20,6 +20,11 @@ import org.jetbrains.annotations.Nullable; public interface LanguageLevelModuleExtension { void setLanguageLevel(LanguageLevel languageLevel); + + /** + * Returns explicitly specified custom language level for {@code module}, or {@code null} if the module uses 'Project default' language level. + * May return {@linkplain LanguageLevel#isUnsupported() unsupported} language level. + */ @Nullable LanguageLevel getLanguageLevel(); } diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java index c8e8756b010e..09b4c520ef13 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiUtil.java @@ -32,10 +32,7 @@ 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.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.Unmodifiable; +import org.jetbrains.annotations.*; import java.util.*; import java.util.function.Predicate; @@ -1078,12 +1075,23 @@ public final class PsiUtil extends PsiUtilCore { return getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_17); } - public static boolean isLanguageLevel18OrHigher(@NotNull PsiElement element) { - return getLanguageLevel(element).isAtLeast(LanguageLevel.JDK_18); - } - + /** + * @param element element to get Java language level for + * @return the language level. If {@linkplain LanguageLevel#isUnsupported() unsupported} language level + * is selected for project or module, this method returns the supported alias. + */ @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); } diff --git a/java/java-tests/testSrc/com/intellij/java/LanguageLevelTest.java b/java/java-tests/testSrc/com/intellij/java/LanguageLevelTest.java new file mode 100644 index 000000000000..f0d97f4d22d7 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/LanguageLevelTest.java @@ -0,0 +1,19 @@ +// 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.LanguageLevel; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public final class LanguageLevelTest { + @Test + public void unsupported() { + @SuppressWarnings("UsagesOfObsoleteApi") + LanguageLevel jdk17Preview = LanguageLevel.JDK_17_PREVIEW; + assertTrue(jdk17Preview.isUnsupported()); + assertEquals(LanguageLevel.JDK_20_PREVIEW, jdk17Preview.getSupportedLevel()); + assertEquals(LanguageLevel.JDK_17, jdk17Preview.getNonPreviewLevel()); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/UnsupportedVersionHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/UnsupportedVersionHighlightingTest.java new file mode 100644 index 000000000000..70917628b00e --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/UnsupportedVersionHighlightingTest.java @@ -0,0 +1,49 @@ +// 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.codeInsight; + +import com.intellij.execution.CantRunException; +import com.intellij.execution.configurations.JavaParameters; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + +public final class UnsupportedVersionHighlightingTest extends LightJavaCodeInsightFixtureTestCase { + @Override + protected @NotNull LightProjectDescriptor getProjectDescriptor() { + return JAVA_17; + } + + public void testPatternInSwitch() { + //noinspection UsagesOfObsoleteApi + IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_17_PREVIEW, () -> { + doTestPatternInSwitch(); + }); + } + + private void doTestPatternInSwitch() { + myFixture.configureByText("Test.java", """ + class X { + void test(Object obj) { + switch(obj) { + // Simple patterns are supported due to aliasing + case String s -> System.out.println("hello"); + // guards with && are not supported anymore + case Integer i && i > 0 -> System.out.println("oops"); + default -> {} + } + } + } + """); + myFixture.checkHighlighting(); + JavaParameters javaParameters = new JavaParameters(); + try { + javaParameters.configureByModule(getModule(), JavaParameters.CLASSES_ONLY); + } + catch (CantRunException e) { + throw new RuntimeException(e); + } + assertEquals("--enable-preview", javaParameters.getVMParametersList().getParametersString()); + } +} diff --git a/java/java-tests/testSrc/com/intellij/java/psi/impl/JavaElementFactoryTest.java b/java/java-tests/testSrc/com/intellij/java/psi/impl/JavaElementFactoryTest.java index a65e74adef97..2830e95afe83 100644 --- a/java/java-tests/testSrc/com/intellij/java/psi/impl/JavaElementFactoryTest.java +++ b/java/java-tests/testSrc/com/intellij/java/psi/impl/JavaElementFactoryTest.java @@ -37,7 +37,7 @@ public class JavaElementFactoryTest extends LightJavaCodeInsightFixtureTestCase public void testArrayClassLanguageLevel() { for (LanguageLevel level : LanguageLevel.values()) { - assertEquals(level, PsiUtil.getLanguageLevel(myFactory.getArrayClass(level))); + assertEquals(level, PsiUtil.getDeclaredLanguageLevel(myFactory.getArrayClass(level))); } } } \ No newline at end of file diff --git a/java/openapi/resources/messages/JavaBundle.properties b/java/openapi/resources/messages/JavaBundle.properties index 93b91cfa433f..09de026381fc 100644 --- a/java/openapi/resources/messages/JavaBundle.properties +++ b/java/openapi/resources/messages/JavaBundle.properties @@ -941,8 +941,13 @@ java.preview.features.accept.notification.link=Accept java.preview.features.alert.title=Experimental Feature Alert java.preview.features.legal.notice=You must accept the terms of legal notice of the beta Java specification to enable support for "{0}".

\ The implementation of an early-draft specification developed under the Java Community Process (JCP) is made available for testing and evaluation purposes only and is not compatible with any specification of the JCP.{1} +java.preview.level={0} (Preview) 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 {0} (Preview) used in this project is not supported anymore. \ + The code insight features will assume language level {1} instead.
\ + It''s strongly encouraged to migrate to newer Java version or stop using preview features. java.terms.exception=exception java.terms.region=region java.terms.instance.initializer=instance initializer diff --git a/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java b/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java index 35aa1029d8cc..3797d3a5c140 100644 --- a/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java +++ b/jps/model-api/src/org/jetbrains/jps/model/java/LanguageLevel.java @@ -28,7 +28,13 @@ public enum LanguageLevel { JDK_20(20), JDK_20_PREVIEW(20), JDK_21(21), JDK_21_PREVIEW(21), JDK_22(22), JDK_22_PREVIEW(22), - JDK_X(23); + 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; diff --git a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt index d9959e554906..28084516d363 100644 --- a/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt +++ b/jvm/jvm-analysis-impl/src/com/intellij/codeInspection/JavaApiUsageInspection.kt @@ -167,7 +167,10 @@ class JavaApiUsageInspection : AbstractBaseUastLocalInspectionTool() { val sourcePsi = sourceNode.sourcePsi ?: return if (target !is PsiMember) return val module = ModuleUtilCore.findModuleForPsiElement(sourcePsi) ?: return - val languageLevel = getEffectiveLanguageLevel(module) + var languageLevel = getEffectiveLanguageLevel(module) + if (languageLevel.isUnsupported) { + languageLevel = languageLevel.nonPreviewLevel + } val lastIncompatibleLevel = LanguageLevelUtil.getLastIncompatibleLanguageLevel(target, languageLevel) if (lastIncompatibleLevel != null) { if (shouldReportSinceLevelForElement(lastIncompatibleLevel, sourcePsi) == true) return