Summary
m2e resolves maven.compiler.source, maven.compiler.target, and maven.compiler.release incorrectly when these properties use ${...} property indirection through a parent POM. m2e falls back to the parent's default value instead of the child's override, causing incorrect Java compliance (e.g., Java 1.8 instead of Java 25, in the example below).
Maven CLI (mvn help:effective-pom) resolves the same properties correctly.
Environment
- m2e version:
org.eclipse.m2e.core_2.7.600.20251121-1832 (bundled with jdtls 1.57.0)
- Eclipse JDT LS:
org.eclipse.jdt.ls.core_1.57.0.202602261110
- Java: OpenJDK 25.0.2 (Eclipse Temurin)
- Maven: 3.9.12
- OS: Linux (Ubuntu)
Minimal Reproducer
Parent POM (simplified)
<project>
<groupId>com.example</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0.0</version>
<packaging>pom</packaging>
<properties>
<project.build.java.target>1.8</project.build.java.target>
<maven.compiler.source>${project.build.java.target}</maven.compiler.source>
<maven.compiler.target>${project.build.java.target}</maven.compiler.target>
</properties>
</project>
Child POM
<project>
<parent>
<groupId>com.example</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>my-app</artifactId>
<properties>
<!-- Override the parent's default to Java 25 -->
<project.build.java.target>25</project.build.java.target>
<!-- These use the same indirection as the parent -->
<maven.compiler.release>${project.build.java.target}</maven.compiler.release>
<maven.compiler.source>${project.build.java.target}</maven.compiler.source>
<maven.compiler.target>${project.build.java.target}</maven.compiler.target>
</properties>
</project>
Java source (src/main/java/com/example/App.java)
package com.example;
import java.util.List;
public class App {
// List.of() requires Java 9+
private static final List<String> ITEMS = List.of("a", "b", "c");
}
Expected Behavior
m2e should resolve the effective value of maven.compiler.source, maven.compiler.target, and maven.compiler.release to 25 (the child's override of project.build.java.target), matching Maven CLI behavior:
$ mvn help:effective-pom | grep maven.compiler
<maven.compiler.release>25</maven.compiler.release>
<maven.compiler.source>25</maven.compiler.source>
<maven.compiler.target>25</maven.compiler.target>
The project should compile without errors, and Eclipse/jdtls should report compliance=25.
Actual Behavior
m2e generates .settings/org.eclipse.jdt.core.prefs and workspace metadata with:
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.release=disabled
Additionally, m2e generates .classpath files with the wrong JRE container:
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
This means even if you manually correct the .settings compliance to 25, the class library on the build path is still Java 1.8, so APIs like List.of() remain unresolved.
This causes Eclipse/jdtls to report:
The method of(String, String, String) is undefined for the type List
The error appears on List.of("a", "b", "c") because m2e thinks the project targets Java 1.8, where List.of() does not exist.
Root Cause Analysis
The parent POM defines:
<project.build.java.target>1.8</project.build.java.target>
<maven.compiler.source>${project.build.java.target}</maven.compiler.source>
<maven.compiler.target>${project.build.java.target}</maven.compiler.target>
The child POM overrides:
<project.build.java.target>25</project.build.java.target>
<maven.compiler.release>${project.build.java.target}</maven.compiler.release>
Maven CLI correctly resolves the ${project.build.java.target} reference against the merged (child-wins) property model, yielding 25.
m2e appears to resolve the ${project.build.java.target} reference against the parent's property value (1.8) instead of the child's overridden value (25). This suggests that m2e resolves property references before (or independently of) merging the child's property overrides.
Additional Notes
- This was observed using jdtls (Eclipse JDT Language Server) via Emacs/lsp-java, but the bug seem to be in m2e's property resolution, not in jdtls or the editor.
- The issue also occurs on project rebuild (
java/buildWorkspace): m2e re-imports the project and overwrites the .settings files with the wrong compliance on every rebuild cycle.
- Cleaning the workspace and reimporting does not fix it -- m2e consistently resolves to the parent's default.
Summary
m2e resolves
maven.compiler.source,maven.compiler.target, andmaven.compiler.releaseincorrectly when these properties use${...}property indirection through a parent POM. m2e falls back to the parent's default value instead of the child's override, causing incorrect Java compliance (e.g., Java 1.8 instead of Java 25, in the example below).Maven CLI (
mvn help:effective-pom) resolves the same properties correctly.Environment
org.eclipse.m2e.core_2.7.600.20251121-1832(bundled with jdtls 1.57.0)org.eclipse.jdt.ls.core_1.57.0.202602261110Minimal Reproducer
Parent POM (simplified)
Child POM
Java source (src/main/java/com/example/App.java)
Expected Behavior
m2e should resolve the effective value of
maven.compiler.source,maven.compiler.target, andmaven.compiler.releaseto25(the child's override ofproject.build.java.target), matching Maven CLI behavior:The project should compile without errors, and Eclipse/jdtls should report
compliance=25.Actual Behavior
m2e generates
.settings/org.eclipse.jdt.core.prefsand workspace metadata with:Additionally, m2e generates
.classpathfiles with the wrong JRE container:This means even if you manually correct the
.settingscompliance to 25, the class library on the build path is still Java 1.8, so APIs likeList.of()remain unresolved.This causes Eclipse/jdtls to report:
The error appears on
List.of("a", "b", "c")because m2e thinks the project targets Java 1.8, whereList.of()does not exist.Root Cause Analysis
The parent POM defines:
The child POM overrides:
Maven CLI correctly resolves the
${project.build.java.target}reference against the merged (child-wins) property model, yielding25.m2e appears to resolve the
${project.build.java.target}reference against the parent's property value (1.8) instead of the child's overridden value (25). This suggests that m2e resolves property references before (or independently of) merging the child's property overrides.Additional Notes
java/buildWorkspace): m2e re-imports the project and overwrites the.settingsfiles with the wrong compliance on every rebuild cycle.