Skip to content

Reconcile stale auto-enabled preview features for invisible projects - #3833

Open
wenytang-ms wants to merge 1 commit into
eclipse-jdtls:mainfrom
wenytang-ms:fix/reconcile-stale-preview-features
Open

Reconcile stale auto-enabled preview features for invisible projects#3833
wenytang-ms wants to merge 1 commit into
eclipse-jdtls:mainfrom
wenytang-ms:fix/reconcile-stale-preview-features

Conversation

@wenytang-ms

Copy link
Copy Markdown
Contributor

Fixes #3832

jdt.ls auto-enables preview features (and sets the preview-report severity to ignore) for an invisible project when its Java version equals latestSupportedJavaVersion(), and persists that. After a compiler bump the "latest" moves forward, so the project ends up with preview enabled at a non-latest compliance and the build fails with Preview features enabled at an invalid source release level. configureJVMSettings isn't re-run for an existing invisible project on reload, so it never self-heals — only renaming the folder (a fresh import) works around it.

This adds JVMConfigurator.reconcilePreviewFeatureSettings, called from InvisibleProjectImporter, which resets the preview options to their defaults only when:

  • the project is an unmanaged folder,
  • the options look auto-enabled by jdt.ls (enablePreview=enabled and report severity ignore),
  • and the compliance is below latestSupportedJavaVersion().

Managed projects (Maven/Gradle), deliberate user settings (which keep the default warning severity) and projects legitimately on the latest JDK are left untouched. This complements #3131/#3137, which only notifies the client but doesn't reconcile the value jdt.ls set itself.

Downstream report: redhat-developer/vscode-java#4420.

Tests: added coverage in InvisibleProjectImporterTest for reset when below latest, kept when on the latest JDK, and not reset when the user set preview explicitly.

@wenytang-ms
wenytang-ms force-pushed the fix/reconcile-stale-preview-features branch 3 times, most recently from a24272f to bcd2bc2 Compare July 1, 2026 06:09
@wenytang-ms

wenytang-ms commented Jul 1, 2026

Copy link
Copy Markdown
Contributor Author
image

error issue with test case

// InvisibleProjectImporterTest.java
@Test
	public void reproStalePreviewBreaksBuild() throws Exception {
		// 1) Importing an invisible project normally
		IProject invisibleProject = copyAndImportFolder("singlefile/lesson1", "src/org/samples/HelloWorld.java");
		IJavaProject javaProject = JavaCore.create(invisibleProject);
		assertNoErrors(invisibleProject); // still ok

		// 2) Simulate the stale state left over after a "compiler bump":
		//    Older versions of jdt.ls automatically enabled preview and muted reports on the then-latest version.
		//    Now that bundled ecj has moved up a version, this compliance is no longer the latest.
		String olderThanLatest = JavaCore.getAllVersions().get(JavaCore.getAllVersions().size() - 2);
		javaProject.setOption(JavaCore.COMPILER_COMPLIANCE, olderThanLatest);
		javaProject.setOption(JavaCore.COMPILER_SOURCE, olderThanLatest);
		javaProject.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, olderThanLatest);
		javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
		javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);

		// 3) Trigger Build Task
		invisibleProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
		waitForBackgroundJobs();

		// 4) Observing the crash site: A "preview at invalid source level" error occurred, and bin was empty.
		IMarker[] markers = invisibleProject.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
		System.out.println("=== markers ===");
		for (IMarker m : markers) {
			System.out.println(m.getAttribute(IMarker.SEVERITY) + " : " + m.getAttribute(IMarker.MESSAGE));
		}
		// You will see:
		//   Preview features enabled at an invalid source release level N,
		//   preview can be enabled only at source level M
	}

@wenytang-ms

Copy link
Copy Markdown
Contributor Author

How to reproduce & verify

The stale flag only needs a below-latest compliance with preview still enabled. This can be reproduced deterministically in a plug-in test:

@Test
public void reproStalePreviewBreaksBuild() throws Exception {
    IProject invisibleProject = copyAndImportFolder("singlefile/lesson1", "src/org/samples/HelloWorld.java");
    IJavaProject javaProject = JavaCore.create(invisibleProject);

    // State left behind after the bundled compiler advanced past the version
    // jdt.ls originally auto-enabled preview at:
    String olderThanLatest = JavaCore.getAllVersions().get(JavaCore.getAllVersions().size() - 2);
    javaProject.setOption(JavaCore.COMPILER_COMPLIANCE, olderThanLatest);
    javaProject.setOption(JavaCore.COMPILER_SOURCE, olderThanLatest);
    javaProject.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, olderThanLatest);
    javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
    javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);

    invisibleProject.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
    waitForBackgroundJobs();

    for (IMarker m : invisibleProject.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE)) {
        System.out.println(m.getAttribute(IMarker.SEVERITY) + " : " + m.getAttribute(IMarker.MESSAGE));
    }
}

Before the fix the build fails with an unsuppressible, error-severity problem (note REPORT_PREVIEW_FEATURES=IGNORE does not silence it), so no .class files are produced:

2 : Preview features enabled at an invalid source release level 25, preview can be enabled only at source level 26

That empty bin is what surfaces downstream as the ClassNotFoundException when a -javaagent jar is loaded.

With the fix, calling JVMConfigurator.reconcilePreviewFeatureSettings(javaProject) before the build (which is what InvisibleProjectImporter now does on import) resets the stale options to their defaults, the marker disappears, and the build succeeds.

@wenytang-ms

Copy link
Copy Markdown
Contributor Author

@jjohnstn please help review this PR, and the CI seems flaky, so can you rerun this pipeline?

@wenytang-ms
wenytang-ms force-pushed the fix/reconcile-stale-preview-features branch 2 times, most recently from 131cf7c to e8194ff Compare July 1, 2026 08:23
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.
@datho7561
datho7561 force-pushed the fix/reconcile-stale-preview-features branch from e8194ff to b4cba2d Compare July 9, 2026 20:40
@datho7561

datho7561 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

I'm testing with an Eclipse project (i.e. there's a .project, a .classpath, and a .settings in the folder containing the source code). In the .settings, I've configured preview to be enabled and set the compliance to 24.

When I open the project with this PR, I get a project wide warning that preview can't be enabled on 24. When I open a source file, this problem goes away. I'm guessing that's this PR in action? It looks like the setting isn't persisted (the same thing happens if I close and reopen the project, and when I check the .settings, preview is still enabled and the compliance is still 24). I think the setting needs to be persisted.

From my testing this seems unrelated to what you did.

@datho7561 datho7561 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, it took me a while to figure out how to test this, but I managed to make a Java 25 invisible project with preview features enabled. When I open this project without this PR, I don't seem to get notified anywhere that the build is failing due to preview features being enabled on an incompatible compiler version (!!!), but the Java > Run and Java Debug CodeLens commands fail stating that the build failed, and the output folder is empty. I think it would be helpful to figure out why a diagnostic or popup doesn't appear when the build fails due to enabling preview features on an older release; I'll make a separate issue for that.

When I open the Java 25 invisible project with preview features enabled with this PR instead, it automatically disables the preview features. The spot where I use https://openjdk.org/jeps/530 in the code is marked as an error as a result. It's kind of clunky to disable preview support, since if the user was using preview features, suddenly the project doesn't build. Maybe it's better to upgrade to the latest supported version and keep preview support on to address this? What do you think?

@datho7561

Copy link
Copy Markdown
Contributor

See #3846

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Auto-enabled preview features aren't reconciled after a compiler bump, breaking invisible project builds

2 participants