From b4cba2d172ac21401be9fc59d9d511d6b2e515e3 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Wed, 1 Jul 2026 13:20:27 +0800 Subject: [PATCH] Reconcile stale auto-enabled preview features for invisible projects jdt.ls auto-enables preview features (and silences the report) for an invisible project when its Java version equals the latest supported version, then persists that. After a compiler bump the latest version moves forward, leaving preview enabled at a non-latest compliance, which fails the whole build. Reset those options to their defaults on import when they look auto-enabled (enabled + report severity ignore) and the compliance is below the latest supported version. --- .../jdt/ls/core/internal/JVMConfigurator.java | 41 +++++++++++++++ .../managers/InvisibleProjectImporter.java | 5 ++ .../InvisibleProjectImporterTest.java | 52 +++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JVMConfigurator.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JVMConfigurator.java index a0bdb7045f..34e34e106c 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JVMConfigurator.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JVMConfigurator.java @@ -348,4 +348,45 @@ public static void configureJVMSettings(IJavaProject javaProject, IVMInstall vmI } } + /** + * Reset preview feature options that jdt.ls auto-enabled for an invisible project but that became + * invalid after the bundled compiler advanced to a newer Java release (preview enabled at a + * non-latest compliance fails the whole build). Only options that look auto-enabled by jdt.ls + * (enabled, with the preview-report severity set to {@code ignore}) on a project whose compliance + * is below {@link JavaCore#latestSupportedJavaVersion()} are reset; deliberate user settings, + * managed projects, projects with a linked (user-owned) settings folder and projects on the + * latest JDK are left untouched. + * + * @param javaProject the unmanaged project to reconcile. + * @return {@code true} if the preview options were reset. + */ + public static boolean reconcilePreviewFeatureSettings(IJavaProject javaProject) { + if (javaProject == null || !ProjectUtils.isUnmanagedFolder(javaProject.getProject())) { + return false; + } + // A linked settings folder holds user-owned prefs on disk that jdt.ls never auto-enabled; + // don't reconcile (and don't write back into) those, mirroring configureJVMSettings. + if (ProjectUtils.isSettingsFolderLinked(javaProject.getProject())) { + return false; + } + if (!JavaCore.ENABLED.equals(javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true))) { + return false; + } + // Only reconcile settings that were auto-enabled (and silenced) by jdt.ls, not ones a user set on purpose. + if (!JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, true))) { + return false; + } + String compliance = javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true); + if (compliance == null || JavaCore.compareJavaVersions(compliance, JavaCore.latestSupportedJavaVersion()) >= 0) { + // The project is still on the latest supported version, enabling preview is valid. + return false; + } + Hashtable defaultOptions = JavaCore.getDefaultOptions(); + javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, defaultOptions.get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES)); + javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, defaultOptions.get(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES)); + JavaLanguageServerPlugin.logInfo("Reset stale preview feature settings for project " + javaProject.getProject().getName() + + " (compliance " + compliance + " is below the latest supported Java version " + JavaCore.latestSupportedJavaVersion() + ")."); + return true; + } + } diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporter.java index e07fe5fc7e..b08a6c25e8 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporter.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporter.java @@ -62,6 +62,7 @@ import org.eclipse.jdt.ls.core.internal.IConstants; import org.eclipse.jdt.ls.core.internal.JDTUtils; import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin; +import org.eclipse.jdt.ls.core.internal.JVMConfigurator; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.preferences.PreferenceManager; @@ -157,6 +158,10 @@ public static boolean loadInvisibleProject(IPath javaFile, IPath rootPath, boole IJavaProject javaProject = JavaCore.create(invisibleProject); + // Reset stale preview feature settings that would otherwise break the build after the bundled + // compiler advances to a newer Java release. See https://github.com/redhat-developer/vscode-java/issues/4420 + JVMConfigurator.reconcilePreviewFeatureSettings(javaProject); + IFolder workspaceLinkFolder = invisibleProject.getFolder(ProjectUtils.WORKSPACE_LINK); PreferenceManager preferencesManager = JavaLanguageServerPlugin.getPreferencesManager(); List sourcePathsFromPreferences = null; diff --git a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporterTest.java b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporterTest.java index f48391402e..661cba429e 100644 --- a/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporterTest.java +++ b/org.eclipse.jdt.ls.tests/src/org/eclipse/jdt/ls/core/internal/managers/InvisibleProjectImporterTest.java @@ -47,6 +47,7 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jdt.ls.core.internal.JavaProjectHelper; +import org.eclipse.jdt.ls.core.internal.JVMConfigurator; import org.eclipse.jdt.ls.core.internal.ProjectUtils; import org.eclipse.jdt.ls.core.internal.ResourceUtils; import org.eclipse.jdt.ls.core.internal.TestVMType; @@ -233,6 +234,57 @@ public void testPreviewFeaturesDisabledForNotLatestJDK() throws Exception { } } + @Test + public void testStalePreviewFeaturesAreReset() throws Exception { + // Use a fixture without a .settings folder so it isn't linked: jdt.ls only auto-enables preview + // (the state this reconciles) when the settings folder is not linked. + IProject invisibleProject = copyAndImportFolder("singlefile/java14", "foo/bar/Foo.java"); + assertTrue(invisibleProject.exists()); + IJavaProject javaProject = JavaCore.create(invisibleProject); + // Simulate the state left behind after the bundled compiler advanced to a newer "latest": + // preview was auto-enabled (and silenced) by jdt.ls at a compliance that is no longer the latest. + String olderThanLatest = JavaCore.getAllVersions().get(JavaCore.getAllVersions().size() - 2); + javaProject.setOption(JavaCore.COMPILER_COMPLIANCE, olderThanLatest); + javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE); + + assertTrue(JVMConfigurator.reconcilePreviewFeatureSettings(javaProject)); + + assertEquals(JavaCore.DISABLED, javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true)); + assertEquals(JavaCore.getDefaultOptions().get(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES), + javaProject.getOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, true)); + } + + @Test + public void testPreviewFeaturesKeptWhenOnLatestJDK() throws Exception { + IProject invisibleProject = copyAndImportFolder("singlefile/java14", "foo/bar/Foo.java"); + assertTrue(invisibleProject.exists()); + IJavaProject javaProject = JavaCore.create(invisibleProject); + javaProject.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.latestSupportedJavaVersion()); + javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE); + + assertFalse(JVMConfigurator.reconcilePreviewFeatureSettings(javaProject)); + + assertEquals(JavaCore.ENABLED, javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true)); + } + + @Test + public void testExplicitPreviewFeaturesNotReset() throws Exception { + IProject invisibleProject = copyAndImportFolder("singlefile/java14", "foo/bar/Foo.java"); + assertTrue(invisibleProject.exists()); + IJavaProject javaProject = JavaCore.create(invisibleProject); + String olderThanLatest = JavaCore.getAllVersions().get(JavaCore.getAllVersions().size() - 2); + javaProject.setOption(JavaCore.COMPILER_COMPLIANCE, olderThanLatest); + javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED); + // A deliberate user setting keeps the default report severity (warning), not ignore. + javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.WARNING); + + assertFalse(JVMConfigurator.reconcilePreviewFeatureSettings(javaProject)); + + assertEquals(JavaCore.ENABLED, javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true)); + } + @Test public void testSpecifyingOutputPath() throws Exception { Preferences preferences = preferenceManager.getPreferences();