Skip to content

Failure to resolve Maven property indirection correctly when overriding it on a child pom #2156

@saulotoledo

Description

@saulotoledo

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions