diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiReleaseTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiReleaseTests.java
index bb9b96e1dd9..ade860f5f88 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiReleaseTests.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/MultiReleaseTests.java
@@ -35,6 +35,10 @@ public class MultiReleaseTests extends BuilderTests {
private static final String JAVA9_SRC_FOLDER = "src9";
private static final String DEFAULT_SRC_FOLDER = "src";
+// static {
+// TESTS_NAMES = new String[] { "testMultiReleaseModuleInfoPerRelease" };
+// }
+
public MultiReleaseTests(String name) {
super(name);
}
@@ -319,6 +323,92 @@ public String print() {
expectingNoProblems();
}
+ /**
+ * Test multi-release compilation with different module-info.java per release.
+ * This verifies that each source folder with a different release uses its own
+ * module-info.java for compilation, not a shared module from the project.
+ * See issue https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4268
+ */
+ public void testMultiReleaseModuleInfoPerRelease() throws JavaModelException, IOException {
+ // Create modular project with Java 11 as base
+ IPath projectPath = createMRProject(CompilerOptions.VERSION_11);
+ IPath defaultSrc = env.getPackageFragmentRootPath(projectPath, DEFAULT_SRC_FOLDER);
+
+ // Base module-info requires no extra modules
+ env.addClass(defaultSrc, "", "module-info",
+ """
+ module MRmodular {
+ }
+ """
+ );
+
+ // Base Test.java - should have errors for both java.desktop and java.xml types
+ IPath classDefault = env.addClass(defaultSrc, "p", "Test",
+ """
+ package p;
+ public class Test {
+ java.awt.Window w11;
+ org.w3c.dom.Element element11;
+ }
+ """
+ );
+
+ // Java 17 source with module-info requiring java.desktop
+ IClasspathAttribute[] attributes17 = new IClasspathAttribute[] {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "17") };
+ IPath src17 = env.addPackageFragmentRoot(projectPath, "src17", attributes17);
+ env.addClass(src17, "", "module-info",
+ """
+ module MRmodular {
+ requires java.desktop;
+ }
+ """
+ );
+ env.addClass(src17, "p", "Test",
+ """
+ package p;
+ public class Test {
+ java.awt.Window w17;
+ org.w3c.dom.Element element17;
+ }
+ """
+ );
+
+ // Java 21 source with module-info requiring java.xml
+ IClasspathAttribute[] attributes21 = new IClasspathAttribute[] {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "21") };
+ IPath src21 = env.addPackageFragmentRoot(projectPath, "src21", attributes21);
+ env.addClass(src21, "", "module-info",
+ """
+ module MRmodular {
+ requires java.xml;
+ }
+ """
+ );
+ IPath class21 = env.addClass(src21, "p", "Test",
+ """
+ package p;
+ public class Test {
+ java.awt.Window w21;
+ org.w3c.dom.Element element21;
+ }
+ """
+ );
+
+ fullBuild();
+ //As our default module descriptor does not import anything both should give an error
+ expectingSpecificProblemsFor(defaultSrc, new Problem[] { //
+ new Problem("", "The type java.awt.Window is not accessible", classDefault, 32, 47, 40, IMarker.SEVERITY_ERROR),
+ new Problem("", "The type org.w3c.dom.Element is not accessible", classDefault, 54, 73, 40, IMarker.SEVERITY_ERROR)
+ });
+ //java.desktop includes java.xml so no errors to expect here
+ expectingNoProblemsFor(src17);
+ //we only import java.xml so desktop should give an error!
+ expectingSpecificProblemsFor(src21, new Problem[] { //
+ new Problem("", "The type java.awt.Window is not accessible", class21, 32, 47, 40, IMarker.SEVERITY_ERROR),
+ });
+ }
+
private IPath whenSetupMRRpoject() throws JavaModelException {
return whenSetupMRRpoject(CompilerOptions.VERSION_1_8);
}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
index 102de7b4839..b5a24e8a043 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java
@@ -152,6 +152,8 @@ private static Class[] getAllTestClasses() {
ReconcilerTests21.class,
ReconcilerStatementsRecoveryTests.class,
ReconcilerMultiReleaseTests.class,
+ ReconcilerModuleMultiReleaseTests.class,
+ SelectionMultiReleaseTests.class,
// Copy and move operation tests
CopyMoveElementsTests.class,
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerModuleMultiReleaseTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerModuleMultiReleaseTests.java
new file mode 100644
index 00000000000..e96ae5cb0a0
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ReconcilerModuleMultiReleaseTests.java
@@ -0,0 +1,195 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.model;
+
+import junit.framework.Test;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IProblemRequestor;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.WorkingCopyOwner;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+
+/**
+ * Tests that reconciling (problem detection in working copies) of a multi-release
+ * modular project resolves modules and types as seen from the release specific source
+ * folder the reconciled unit lives in, honoring a per-release {@code module-info.java}.
+ *
+ * See https://github.com/eclipse-jdt/eclipse.jdt.core/issues/4268
+ */
+public class ReconcilerModuleMultiReleaseTests extends ModifyingResourceTests {
+
+ static {
+// TESTS_NAMES = new String[] { "testReconcileUsesReleaseModuleInfo21" };
+ }
+
+ public ReconcilerModuleMultiReleaseTests(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ return buildModelTestSuite(ReconcilerModuleMultiReleaseTests.class);
+ }
+
+ @Override
+ public void setUpSuite() throws Exception {
+ super.setUpSuite();
+ IJavaProject project = createJava9ProjectWithJREAttributes("ReconcilerModuleMR",
+ new String[] { "src", "src17", "src21" }, null, "21");
+ IClasspathEntry[] classpath = project.getRawClasspath();
+ for (int i = 0; i < classpath.length; i++) {
+ IClasspathEntry entry = classpath[i];
+ if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+ if (entry.getPath().toString().endsWith("src17")) {
+ classpath[i] = JavaCore.newSourceEntry(entry.getPath(), null, null, null,
+ new IClasspathAttribute[] {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "17") });
+ } else if (entry.getPath().toString().endsWith("src21")) {
+ classpath[i] = JavaCore.newSourceEntry(entry.getPath(), null, null, null,
+ new IClasspathAttribute[] {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "21") });
+ }
+ }
+ }
+ project.setRawClasspath(classpath, new NullProgressMonitor());
+ project.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED);
+
+ createFolder("/ReconcilerModuleMR/src/p");
+ createFolder("/ReconcilerModuleMR/src17/p");
+ createFolder("/ReconcilerModuleMR/src21/p");
+
+ // base module requires nothing
+ createFile("/ReconcilerModuleMR/src/module-info.java", """
+ module MRmodular {
+ }
+ """);
+ // release 17 requires java.desktop (which transitively reads java.xml)
+ createFile("/ReconcilerModuleMR/src17/module-info.java", """
+ module MRmodular {
+ requires java.desktop;
+ }
+ """);
+ // release 21 requires java.xml
+ createFile("/ReconcilerModuleMR/src21/module-info.java", """
+ module MRmodular {
+ requires java.xml;
+ }
+ """);
+ }
+
+ @Override
+ public void tearDownSuite() throws Exception {
+ deleteProject("ReconcilerModuleMR");
+ super.tearDownSuite();
+ }
+
+ private void assertReconcileProblems(String path, String source, String expectedProblems) throws Exception {
+ ProblemRequestor problemRequestor = new ProblemRequestor();
+ WorkingCopyOwner owner = new WorkingCopyOwner() {
+ @Override
+ public IProblemRequestor getProblemRequestor(ICompilationUnit unit) {
+ return problemRequestor;
+ }
+ };
+ ICompilationUnit wc = getWorkingCopy(path, source, owner, problemRequestor);
+ try {
+ problemRequestor.initialize(source.toCharArray());
+ wc.reconcile(ICompilationUnit.NO_AST, true/*force problem detection*/, owner, null);
+ assertProblems("Unexpected problems for " + path, expectedProblems, problemRequestor);
+ } finally {
+ wc.discardWorkingCopy();
+ }
+ }
+
+ // java.xml is required in src21, so org.w3c.dom.Element must be accessible (no problem).
+ public void testReconcileUsesReleaseModuleInfo21() throws Exception {
+ assertReconcileProblems("/ReconcilerModuleMR/src21/p/Use.java", """
+ package p;
+ public class Use {
+ org.w3c.dom.Element element;
+ }
+ """, "----------\n----------\n");
+ }
+
+ // java.desktop is required in src17 and reads java.xml transitively, so both are accessible.
+ public void testReconcileUsesReleaseModuleInfo17() throws Exception {
+ assertReconcileProblems("/ReconcilerModuleMR/src17/p/Use.java", """
+ package p;
+ public class Use {
+ java.awt.Window window;
+ org.w3c.dom.Element element;
+ }
+ """, "----------\n----------\n");
+ }
+
+ // the base module requires nothing, so org.w3c.dom.Element is not accessible here.
+ public void testReconcileUsesBaseModuleInfo() throws Exception {
+ assertReconcileProblems("/ReconcilerModuleMR/src/p/Use.java", """
+ package p;
+ public class Use {
+ org.w3c.dom.Element element;
+ }
+ """,
+ "----------\n" +
+ "1. ERROR in /ReconcilerModuleMR/src/p/Use.java (at line 3)\n" +
+ " org.w3c.dom.Element element;\n" +
+ " ^^^^^^^^^^^^^^^^^^^\n" +
+ "The type org.w3c.dom.Element is not accessible\n" +
+ "----------\n");
+ }
+
+ private ITypeBinding resolveFieldType(String path, String source) throws Exception {
+ ICompilationUnit wc = getWorkingCopy(path, source);
+ try {
+ ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
+ parser.setResolveBindings(true);
+ parser.setSource(wc);
+ org.eclipse.jdt.core.dom.CompilationUnit ast =
+ (org.eclipse.jdt.core.dom.CompilationUnit) parser.createAST(null);
+ TypeDeclaration type = (TypeDeclaration) ast.types().get(0);
+ FieldDeclaration field = type.getFields()[0];
+ return field.getType().resolveBinding();
+ } finally {
+ wc.discardWorkingCopy();
+ }
+ }
+
+ // ASTParser.createAST (resolved DOM AST) must resolve org.w3c.dom.Element as seen from src21.
+ public void testCreateASTUsesReleaseModuleInfo21() throws Exception {
+ ITypeBinding binding = resolveFieldType("/ReconcilerModuleMR/src21/p/Use.java", """
+ package p;
+ public class Use {
+ org.w3c.dom.Element element;
+ }
+ """);
+ assertNotNull("Type binding should be resolved", binding);
+ assertEquals("org.w3c.dom.Element", binding.getQualifiedName());
+ }
+
+ // java.desktop reads java.xml transitively, so the binding resolves in src17 as well.
+ public void testCreateASTUsesReleaseModuleInfo17() throws Exception {
+ ITypeBinding binding = resolveFieldType("/ReconcilerModuleMR/src17/p/Use.java", """
+ package p;
+ public class Use {
+ org.w3c.dom.Element element;
+ }
+ """);
+ assertNotNull("Type binding should be resolved", binding);
+ assertEquals("org.w3c.dom.Element", binding.getQualifiedName());
+ }
+}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SelectionMultiReleaseTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SelectionMultiReleaseTests.java
new file mode 100644
index 00000000000..c450dd8534a
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SelectionMultiReleaseTests.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2026 Christoph Läubrich and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.model;
+
+import junit.framework.Test;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaElement;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaCore;
+
+/**
+ * Tests that code selection (Hover / Open Declaration) in a multi-release modular
+ * project resolves types as seen from the release specific source folder the selected
+ * unit lives in.
+ *
+ * See https://github.com/eclipse-jdt/eclipse.jdt.core/pull/4534#issuecomment-4743290623
+ * where selecting a type that is only accessible through a release specific
+ * {@code module-info.java} produced multiple (duplicate) results because selection
+ * always resolved against the base {@code module-info.java}.
+ */
+public class SelectionMultiReleaseTests extends AbstractJavaModelTests {
+
+ static {
+// TESTS_NAMES = new String[] { "testSelectElementInRelease21" };
+ }
+
+ public SelectionMultiReleaseTests(String name) {
+ super(name);
+ }
+
+ public static Test suite() {
+ return buildModelTestSuite(SelectionMultiReleaseTests.class);
+ }
+
+ @Override
+ public void setUpSuite() throws Exception {
+ super.setUpSuite();
+ IJavaProject project = createJava9ProjectWithJREAttributes("SelectionMR",
+ new String[] { "src", "src17", "src21" }, null, "21");
+ IClasspathEntry[] classpath = project.getRawClasspath();
+ for (int i = 0; i < classpath.length; i++) {
+ IClasspathEntry entry = classpath[i];
+ if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+ if (entry.getPath().toString().endsWith("src17")) {
+ classpath[i] = JavaCore.newSourceEntry(entry.getPath(), null, null, null,
+ new IClasspathAttribute[] {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "17") });
+ } else if (entry.getPath().toString().endsWith("src21")) {
+ classpath[i] = JavaCore.newSourceEntry(entry.getPath(), null, null, null,
+ new IClasspathAttribute[] {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.RELEASE, "21") });
+ }
+ }
+ }
+ project.setRawClasspath(classpath, new NullProgressMonitor());
+ project.setOption(JavaCore.COMPILER_RELEASE, JavaCore.ENABLED);
+
+ createFolder("/SelectionMR/src/p");
+ createFolder("/SelectionMR/src17/p");
+ createFolder("/SelectionMR/src21/p");
+
+ // base module requires nothing
+ createFile("/SelectionMR/src/module-info.java", """
+ module MRmodular {
+ }
+ """);
+ createFile("/SelectionMR/src/p/Test.java", """
+ package p;
+ public class Test {
+ java.awt.Window w;
+ org.w3c.dom.Element element;
+ }
+ """);
+
+ // release 17 requires java.desktop (which transitively reads java.xml)
+ createFile("/SelectionMR/src17/module-info.java", """
+ module MRmodular {
+ requires java.desktop;
+ }
+ """);
+ createFile("/SelectionMR/src17/p/Test.java", """
+ package p;
+ public class Test {
+ java.awt.Window w;
+ org.w3c.dom.Element element;
+ }
+ """);
+
+ // release 21 requires java.xml
+ createFile("/SelectionMR/src21/module-info.java", """
+ module MRmodular {
+ requires java.xml;
+ }
+ """);
+ createFile("/SelectionMR/src21/p/Test.java", """
+ package p;
+ public class Test {
+ java.awt.Window w;
+ org.w3c.dom.Element element;
+ }
+ """);
+ }
+
+ @Override
+ public void tearDownSuite() throws Exception {
+ deleteProject("SelectionMR");
+ super.tearDownSuite();
+ }
+
+ private IType assertSingleType(String unitPath, String reference, String selection, String expectedFqn)
+ throws Exception {
+ ICompilationUnit unit = getCompilationUnit(unitPath);
+ String source = unit.getSource();
+ int referenceStart = source.indexOf(reference);
+ assertTrue("reference '" + reference + "' not found in " + unitPath, referenceStart >= 0);
+ int start = source.indexOf(selection, referenceStart);
+ IJavaElement[] elements = unit.codeSelect(start, selection.length());
+ StringBuilder details = new StringBuilder();
+ for (IJavaElement element : elements) {
+ details.append('\n').append(element);
+ }
+ assertEquals("Expected exactly one selection result but got: " + details, 1, elements.length);
+ assertTrue("Selection result is not a type: " + elements[0], elements[0] instanceof IType);
+ IType type = (IType) elements[0];
+ assertEquals("Unexpected resolved type", expectedFqn, type.getFullyQualifiedName());
+ return type;
+ }
+
+ // org.w3c.dom.Element is reachable in src21 via 'requires java.xml': selecting it must
+ // resolve to exactly that single type and not offer all the JDK types named 'Element'.
+ public void testSelectElementInRelease21() throws Exception {
+ assertSingleType("/SelectionMR/src21/p/Test.java", "org.w3c.dom.Element", "Element",
+ "org.w3c.dom.Element");
+ }
+
+ // java.awt.Window is reachable in src17 via 'requires java.desktop'.
+ public void testSelectWindowInRelease17() throws Exception {
+ assertSingleType("/SelectionMR/src17/p/Test.java", "java.awt.Window", "Window",
+ "java.awt.Window");
+ }
+
+ // java.desktop reads java.xml transitively, so org.w3c.dom.Element is reachable in src17 too.
+ public void testSelectElementInRelease17() throws Exception {
+ assertSingleType("/SelectionMR/src17/p/Test.java", "org.w3c.dom.Element", "Element",
+ "org.w3c.dom.Element");
+ }
+}
diff --git a/org.eclipse.jdt.core/META-INF/MANIFEST.MF b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
index 171a059f602..642bb31c198 100644
--- a/org.eclipse.jdt.core/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.core/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.core; singleton:=true
-Bundle-Version: 3.46.100.qualifier
+Bundle-Version: 3.47.0.qualifier
Bundle-Activator: org.eclipse.jdt.core.JavaCore
Bundle-Vendor: %providerName
Bundle-Localization: plugin
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java
index ce5dcbf7898..74ff1b9fc77 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java
@@ -716,7 +716,11 @@ public static void resolve(
try {
int amountOfWork = (compilationUnits.length + bindingKeys.length) * 2; // 1 for beginToCompile, 1 for resolve
SubMonitor subMonitor = SubMonitor.convert(monitor, amountOfWork);
- environment = new CancelableNameEnvironment(((JavaProject) javaProject), owner, subMonitor);
+ // resolve as seen from the source folder the units live in, honoring a release specific module-info.java
+ int release = compilationUnits.length > 0
+ ? JavaProject.getRelease(compilationUnits[0])
+ : JavaProject.NO_RELEASE;
+ environment = new CancelableNameEnvironment(((JavaProject) javaProject), owner, subMonitor, false, release);
problemFactory = new CancelableProblemFactory(subMonitor);
CompilerOptions compilerOptions = getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0);
compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0;
@@ -810,7 +814,12 @@ public static CompilationUnitDeclaration resolve(
classpaths.toArray(allEntries);
environment = new NameEnvironmentWithProgress(allEntries, null, monitor);
} else {
- environment = new CancelableNameEnvironment((JavaProject) javaProject, owner, monitor);
+ // resolve as seen from the source folder the unit lives in, honoring a release specific module-info.java
+ char[] fileName = sourceUnit.getFileName();
+ int release = fileName != null && fileName.length > 0
+ ? ((JavaProject) javaProject).getRelease(IPath.fromPortableString(new String(fileName)))
+ : JavaProject.NO_RELEASE;
+ environment = new CancelableNameEnvironment((JavaProject) javaProject, owner, monitor, false, release);
}
problemFactory = new CancelableProblemFactory(monitor);
CompilerOptions compilerOptions = getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0);
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleBinding.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleBinding.java
index c5c637ee198..7675bdd1cd0 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleBinding.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ModuleBinding.java
@@ -19,6 +19,7 @@
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.eclipse.jdt.internal.compiler.util.Util;
+import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.core.NameLookup.Answer;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
@@ -124,7 +125,7 @@ public IJavaElement getJavaElement() {
if (!(nameEnvironment instanceof SearchableEnvironment)) return null;
NameLookup nameLookup = ((SearchableEnvironment) nameEnvironment).nameLookup;
if (nameLookup == null) return null;
- Answer answer = nameLookup.findModule(this.getName().toCharArray());
+ Answer answer = nameLookup.findModule(this.getName().toCharArray(), JavaProject.NO_RELEASE);
if (answer == null) return null;
return answer.module;
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
index 7efc551c598..93ea3715a88 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IJavaProject.java
@@ -625,6 +625,32 @@ IPackageFragmentRoot findPackageFragmentRoot(IPath path)
*/
IModuleDescription getModuleDescription() throws JavaModelException;
+ /**
+ * Returns an {@link IModuleDescription} this project represents or null if the Java project doesn't represent any
+ * named module. A Java project is said to represent a module if any of its source package fragment roots (see
+ * {@link IPackageFragmentRoot#K_SOURCE}) contains a valid Java module descriptor, or if one of its classpath
+ * entries has a valid {@link IClasspathAttribute#PATCH_MODULE} attribute affecting the current project. In the
+ * latter case the corresponding module description of the location referenced by that classpath entry is returned.
+ *
+ *
Furthermore, if the project is multi-release aware, then the specified release
+ * selects the most specific suitable module description. For this purpose the {@link IClasspathAttribute#RELEASE}
+ * attribute of each source folder is inspected, if present. Source folders are then search from the requested release down
+ * to the lowest release and finally to a source folder with no {@link IClasspathAttribute#RELEASE} attribute.
+ * The first valid module description found in a source folder visited during this search is then returned.
+ *
+ * A module description contributed via {@link IClasspathAttribute#PATCH_MODULE} is considered only if the
+ * regular search finds no module description
+ *
+ * @param release
+ * Specify the upper bound for version specific source folders to be searched.
+ * @return a {@link IModuleDescription} this project represents.
+ * @exception JavaModelException
+ * if this element does not exist or if an exception occurs while accessing its
+ * corresponding resource
+ * @since 3.47
+ */
+ IModuleDescription getModuleDescription(int release) throws JavaModelException;
+
/**
* Returns the IModuleDescription owned by this project or
* null if the Java project doesn't own a valid Java module descriptor.
@@ -640,6 +666,28 @@ IPackageFragmentRoot findPackageFragmentRoot(IPath path)
*/
IModuleDescription getOwnModuleDescription() throws JavaModelException;
+ /**
+ * Returns an {@link IModuleDescription} owned by this project or null if
+ * the Java project doesn't own a suitable Java module descriptor.
+ * This method considers only module descriptions contained in any of the project's source package fragment roots
+ * (see {@link IPackageFragmentRoot#K_SOURCE}). In
+ * particular any {@link IClasspathAttribute#PATCH_MODULE} attribute is not considered.
+ * Furthermore, if the project is multi-release aware, then the specified release
+ * selects the most specific suitable module description. For this purpose the {@link IClasspathAttribute#RELEASE}
+ * attribute of each source folder is inspected, if present. Source folders are then search from the requested release down
+ * to the lowest release and finally to a source folder with no {@link IClasspathAttribute#RELEASE} attribute.
+ * The first valid module description found in a source folder visited during this search is then returned.
+ *
+ * @param release
+ * Specify the upper bound for version specific source folders to be searched.
+ * @return a {@link IModuleDescription} this project owns.
+ * @exception JavaModelException
+ * if this element does not exist or if an exception occurs while accessing its
+ * corresponding resource
+ * @since 3.47
+ */
+ IModuleDescription getOwnModuleDescription(int release) throws JavaModelException;
+
/**
* Returns the raw classpath for the project, as a list of classpath
* entries. This corresponds to the exact set of entries which were assigned
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java
index 17a149aa1a3..1ce5b2db1c3 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java
@@ -116,7 +116,8 @@ public void codeComplete(
throw new IllegalArgumentException("Completion requestor cannot be null"); //$NON-NLS-1$
}
JavaProject project = getJavaProject();
- SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, requestor.isTestCodeExcluded());
+ int release = JavaProject.getRelease(this);
+ SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, requestor.isTestCodeExcluded(), release);
CompletionEngine engine = new CompletionEngine(environment, requestor, project.getOptions(true), project, owner, monitor);
String source = getClassFile().getSource();
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnitProblemFinder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnitProblemFinder.java
index 54f7167edd6..79ffdea645b 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnitProblemFinder.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/CompilationUnitProblemFinder.java
@@ -236,23 +236,10 @@ private static boolean isTestSource(IJavaProject project, ICompilationUnit cu) {
}
private static int getRelease(IJavaProject project, ICompilationUnit cu) {
- try {
- IClasspathEntry[] rawClasspath = project.getRawClasspath();
- final IPath resourcePath = cu.getResource().getFullPath();
- for (IClasspathEntry e : rawClasspath) {
- if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE && e.getPath().isPrefixOf(resourcePath)) {
- String value = ClasspathEntry.getExtraAttribute(e, IClasspathAttribute.RELEASE);
- if (value != null)
- return Integer.parseInt(value);
- }
- }
- } catch (JavaModelException | NumberFormatException e) {
- Util.log(e, "Exception while determining the release value for compilation unit \"" + cu.getElementName() //$NON-NLS-1$
- + "\"."); //$NON-NLS-1$
- }
- return JavaProject.NO_RELEASE;
+ return JavaProject.getRelease(cu);
}
+
/*
* Can return null if the process was aborted or canceled
*/
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
index a0176f8de54..3aa26114d4e 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
@@ -1749,7 +1749,7 @@ public IModuleDescription findModule(String moduleName, WorkingCopyOwner owner)
* Internal findModule with instantiated name lookup
*/
IModuleDescription findModule(String moduleName, NameLookup lookup) throws JavaModelException {
- NameLookup.Answer answer = lookup.findModule(moduleName.toCharArray());
+ NameLookup.Answer answer = lookup.findModule(moduleName.toCharArray(), NO_RELEASE);
if (answer != null)
return answer.module;
return null;
@@ -2842,7 +2842,14 @@ public SearchableEnvironment newSearchableNameEnvironment(ICompilationUnit[] wor
* Returns a new search name environment for this project. This name environment first looks in the given working copies.
*/
public SearchableEnvironment newSearchableNameEnvironment(ICompilationUnit[] workingCopies, boolean excludeTestCode) throws JavaModelException {
- return new SearchableEnvironment(this, workingCopies, excludeTestCode, NO_RELEASE);
+ return newSearchableNameEnvironment(workingCopies, excludeTestCode, NO_RELEASE);
+ }
+ /*
+ * Returns a new search name environment for this project that resolves types and modules as seen from a source
+ * folder targeting the given {@code release} (see {@link IClasspathAttribute#RELEASE}).
+ */
+ public SearchableEnvironment newSearchableNameEnvironment(ICompilationUnit[] workingCopies, boolean excludeTestCode, int release) throws JavaModelException {
+ return new SearchableEnvironment(this, workingCopies, excludeTestCode, release);
}
/*
@@ -2853,7 +2860,75 @@ public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner
return newSearchableNameEnvironment(owner, false);
}
public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner, boolean excludeTestCode) throws JavaModelException {
- return new SearchableEnvironment(this, owner, excludeTestCode, NO_RELEASE);
+ return newSearchableNameEnvironment(owner, excludeTestCode, NO_RELEASE);
+ }
+ /*
+ * Returns a new search name environment for this project that resolves types and modules as seen from a source
+ * folder targeting the given {@code release} (see {@link IClasspathAttribute#RELEASE}). This is needed for
+ * multi-release projects where a release specific source folder may declare its own {@code module-info.java}.
+ */
+ public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner, boolean excludeTestCode, int release) throws JavaModelException {
+ return new SearchableEnvironment(this, owner, excludeTestCode, release);
+ }
+
+ /*
+ * Returns the release a source folder represented by the given classpath entry targets by inspecting its
+ * {@link IClasspathAttribute#RELEASE} attribute, or {@link #NO_RELEASE} if the entry has no (valid) release
+ * attribute.
+ */
+ public static int getRelease(IClasspathEntry entry) {
+ if (entry != null) {
+ String attribute = ClasspathEntry.getExtraAttribute(entry, IClasspathAttribute.RELEASE);
+ if (attribute != null) {
+ try {
+ return Integer.parseInt(attribute);
+ } catch (NumberFormatException e) {
+ // can't determine the release from the classpath so assume default release,
+ // this would already be reported at other places.
+ }
+ }
+ }
+ return NO_RELEASE;
+ }
+
+ /*
+ * Returns the release the source folder containing the given element targets (see
+ * {@link IClasspathAttribute#RELEASE}), or {@link #NO_RELEASE} if the element is not contained in a release
+ * specific source folder (e.g. it lives in a library or in a folder without a release attribute).
+ */
+ public static int getRelease(IJavaElement element) {
+ if (element == null) {
+ return NO_RELEASE;
+ }
+ IPackageFragmentRoot root = (IPackageFragmentRoot) element.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+ if (root == null) {
+ return NO_RELEASE;
+ }
+ try {
+ return getRelease(root.getResolvedClasspathEntry());
+ } catch (JavaModelException e) {
+ return NO_RELEASE;
+ }
+ }
+
+ /*
+ * Returns the release the source folder of this project containing the given resource path targets (see
+ * {@link IClasspathAttribute#RELEASE}), or {@link #NO_RELEASE} if the path is not located in a release specific
+ * source folder.
+ */
+ public int getRelease(IPath sourcePath) {
+ if (sourcePath != null) {
+ try {
+ for (IClasspathEntry entry : getRawClasspath()) {
+ if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE && entry.getPath().isPrefixOf(sourcePath)) {
+ return getRelease(entry);
+ }
+ }
+ } catch (JavaModelException e) {
+ // can't determine the release, assume default release
+ }
+ }
+ return NO_RELEASE;
}
/*
@@ -3759,10 +3834,15 @@ protected IStatus validateExistence(IResource underlyingResource) {
@Override
public IModuleDescription getModuleDescription() throws JavaModelException {
- JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
- IModuleDescription module = info.getModule();
- if (module != null)
+ return getModuleDescription(NO_RELEASE);
+ }
+
+ @Override
+ public IModuleDescription getModuleDescription(int release) throws JavaModelException {
+ IModuleDescription module = getOwnModuleDescription(release);
+ if (module != null) {
return module;
+ }
for(IClasspathEntry entry : getRawClasspath()) {
List patchedModules = getPatchedModules(entry);
if (patchedModules.size() == 1) { // > 1 is malformed, 0 means not affecting this project
@@ -3770,7 +3850,7 @@ public IModuleDescription getModuleDescription() throws JavaModelException {
switch (entry.getEntryKind()) {
case IClasspathEntry.CPE_PROJECT:
IJavaProject referencedProject = getJavaModel().getJavaProject(entry.getPath().toString());
- module = referencedProject.getModuleDescription();
+ module = referencedProject.getModuleDescription(release);
if (module != null && module.getElementName().equals(mainModule))
return module;
break;
@@ -3789,6 +3869,40 @@ public IModuleDescription getModuleDescription() throws JavaModelException {
@Override
public IModuleDescription getOwnModuleDescription() throws JavaModelException {
+ return getOwnModuleDescription(NO_RELEASE);
+ }
+
+ @Override
+ public IModuleDescription getOwnModuleDescription(int release) throws JavaModelException {
+ if (release >= FIRST_MULTI_RELEASE) {
+ IModuleDescription releaseSpecificDescriptor = Arrays.stream(getRawClasspath())
+ .map(e -> {
+ String attribute = ClasspathEntry.getExtraAttribute(e, IClasspathAttribute.RELEASE);
+ if (attribute != null) {
+ try {
+ return new ReleaseClasspathEntry(e, Integer.parseInt(attribute));
+ } catch (NumberFormatException nfe) {
+ // can't use then
+ }
+ }
+ return null;
+ })
+ .filter(Objects::nonNull).filter(entry -> entry.release() <= release)
+ .sorted(Comparator.comparingInt(ReleaseClasspathEntry::release).reversed())
+ .map(entry -> {
+ for (IPackageFragmentRoot root : findPackageFragmentRoots(entry.entry())) {
+ IModuleDescription module = root.getModuleDescription();
+ if (module != null) {
+ return module;
+ }
+ }
+ return null;
+ })
+ .filter(Objects::nonNull).findFirst().orElse(null);
+ if (releaseSpecificDescriptor != null) {
+ return releaseSpecificDescriptor;
+ }
+ }
JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
return info.getModule();
}
@@ -3830,11 +3944,16 @@ public IModuleDescription getAutomaticModuleDescription() throws JavaModelExcept
}
public void setModuleDescription(IModuleDescription module) throws JavaModelException {
+ IPackageFragmentRoot newRoot = (IPackageFragmentRoot) module.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
+ IClasspathEntry classpathEntry = newRoot.getRawClasspathEntry();
+ if (ClasspathEntry.getExtraAttribute(classpathEntry, IClasspathAttribute.RELEASE) != null) {
+ // Do not update the projects module descriptor with something from a release folder!
+ return;
+ }
JavaProjectElementInfo info = (JavaProjectElementInfo) getElementInfo();
IModuleDescription current = info.getModule();
if (current != null) {
IPackageFragmentRoot root = (IPackageFragmentRoot) current.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
- IPackageFragmentRoot newRoot = (IPackageFragmentRoot) module.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
if (!root.equals(newRoot))
throw new JavaModelException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID,
Messages.bind(Messages.classpath_duplicateEntryPath, TypeConstants.MODULE_INFO_FILE_NAME_STRING, getElementName())));
@@ -3870,4 +3989,8 @@ public Manifest getManifest() {
public Set determineModulesOfProjectsWithNonEmptyClasspath() throws JavaModelException {
return ModuleUpdater.determineModulesOfProjectsWithNonEmptyClasspath(this, getExpandedClasspath());
}
+
+ private static final record ReleaseClasspathEntry(IClasspathEntry entry, int release) {
+
+ }
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java
index 7870536f0d7..4b33cdd5b92 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java
@@ -854,7 +854,7 @@ public Answer findType(
}
}
Answer answer = new Answer(type, accessRestriction, entry,
- getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get));
+ getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get, release));
if (!answer.ignoreIfBetter()) {
if (answer.isBetter(suggestedAnswer))
return answer;
@@ -914,19 +914,7 @@ else if (suggestedAnswer == null && considerSecondaryTypes) {
}
private int getRelease(IPackageFragmentRoot root) {
- IClasspathEntry entry = this.rootToResolvedEntries.get(root);
- if (entry != null) {
- String extraAttributes = ClasspathEntry.getExtraAttribute(entry, IClasspathAttribute.RELEASE);
- if (extraAttributes != null) {
- try {
- return Integer.parseInt(extraAttributes);
- } catch (NumberFormatException e) {
- // we can't determine the release from the classpath so assume default release,
- // this would already be reported at other places.
- }
- }
- }
- return JavaProject.NO_RELEASE;
+ return JavaProject.getRelease(this.rootToResolvedEntries.get(root));
}
public static IModule getModuleDescriptionInfo(IModuleDescription moduleDesc) {
@@ -947,7 +935,7 @@ public static IModule getModuleDescriptionInfo(IModuleDescription moduleDesc) {
}
/** Internal utility, which is able to answer explicit and automatic modules. */
- static IModuleDescription getModuleDescription(JavaProject project, IPackageFragmentRoot root, Map cache, Function rootToEntry) {
+ static IModuleDescription getModuleDescription(JavaProject project, IPackageFragmentRoot root, Map cache, Function rootToEntry, int release) {
IModuleDescription module = cache.get(root);
if (module != null)
return module != NO_MODULE ? module : null;
@@ -963,7 +951,7 @@ static IModuleDescription getModuleDescription(JavaProject project, IPackageFrag
}
try {
if (root.getKind() == IPackageFragmentRoot.K_SOURCE)
- module = root.getJavaProject().getModuleDescription(); // from any root in this project
+ module = root.getJavaProject().getModuleDescription(release); // from any root in this project
} catch (JavaModelException e) {
cache.put(root, NO_MODULE);
return null;
@@ -988,7 +976,7 @@ static IModuleDescription getModuleDescription(JavaProject project, IPackageFrag
}
public IModule getModuleDescriptionInfo(PackageFragmentRoot root) {
- IModuleDescription desc = getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get);
+ IModuleDescription desc = getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get, JavaProject.NO_RELEASE);
if (desc != null) {
return getModuleDescriptionInfo(desc);
}
@@ -1112,9 +1100,10 @@ public Answer findType(String name, boolean partialMatch, int acceptFlags, boole
}
return findType(className, packageName, partialMatch, acceptFlags, considerSecondaryTypes, waitForIndexes, checkRestrictions, monitor);
}
- public Answer findModule(char[] moduleName) {
+
+ public Answer findModule(char[] moduleName, int release) {
JavaElementRequestor requestor = new JavaElementRequestor();
- seekModule(moduleName, false, requestor);
+ seekModule(moduleName, false, requestor, release);
IModuleDescription[] modules = requestor.getModules();
if (modules.length == 0) {
try {
@@ -1396,9 +1385,9 @@ public void seekTypes(String name, IPackageFragment pkg, boolean partialMatch, i
}
public void seekModuleReferences(String name, IJavaElementRequestor requestor, IJavaProject javaProject) {
- seekModule(name.toCharArray(), true /* prefix */, requestor);
+ seekModule(name.toCharArray(), true /* prefix */, requestor, JavaProject.NO_RELEASE);
}
- public void seekModule(char[] name, boolean prefixMatch, IJavaElementRequestor requestor) {
+ public void seekModule(char[] name, boolean prefixMatch, IJavaElementRequestor requestor, int release) {
long start = -1;
if (VERBOSE)
start = System.currentTimeMillis();
@@ -1421,7 +1410,7 @@ public void seekModule(char[] name, boolean prefixMatch, IJavaElementRequestor r
continue;
}
}
- module = getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get);
+ module = getModuleDescription(this.rootProject, root, this.rootToModule, this.rootToResolvedEntries::get, release);
if (module != null && prefixMatcher.matches(name, module.getElementName().toCharArray(), false)) {
requestor.acceptModule(module);
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NamedMember.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NamedMember.java
index 86b47a1ef0d..08812e37ff2 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NamedMember.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NamedMember.java
@@ -266,7 +266,8 @@ public String[][] resolveType(String typeName) throws JavaModelException {
*/
public String[][] resolveType(String typeName, WorkingCopyOwner owner) throws JavaModelException {
JavaProject project = getJavaProject();
- SearchableEnvironment environment = project.newSearchableNameEnvironment(owner);
+ int release = JavaProject.getRelease(this);
+ SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, false, release);
class TypeResolveRequestor implements ISelectionRequestor {
String[][] answers = null;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/Openable.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/Openable.java
index 3129dacb26c..5421f952f88 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/Openable.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/Openable.java
@@ -128,7 +128,8 @@ protected void codeComplete(
throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
}
JavaProject project = getJavaProject();
- SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, requestor.isTestCodeExcluded());
+ int release = JavaProject.getRelease(this);
+ SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, requestor.isTestCodeExcluded(), release);
// set unit to skip
environment.unitToSkip = unitToSkip;
@@ -154,8 +155,10 @@ protected IJavaElement[] codeSelect(org.eclipse.jdt.internal.compiler.env.ICompi
}
JavaProject project = getJavaProject();
- SearchableEnvironment environment = project.newSearchableNameEnvironment(owner);
-
+ // resolve types and modules as seen from the source folder the unit lives in, so that a release specific
+ // module-info.java (multi-release project) is honoured (avoids duplicate selection results).
+ int release = JavaProject.getRelease(this);
+ SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, false, release);
SelectionRequestor requestor= new SelectionRequestor(environment.nameLookup, this);
IBuffer buffer = getBuffer();
if (buffer == null) {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java
index 0c71b37ceef..487b8a3510a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java
@@ -308,7 +308,7 @@ private NameEnvironmentAnswer createAnswer(Answer lookupAnswer, String packageNa
* ISearchRequestor.acceptModule(char[][] moduleName)
*/
public void findModules(char[] prefix, ISearchRequestor requestor, IJavaProject javaProject) {
- this.nameLookup.seekModule(prefix, true, new SearchableEnvironmentRequestor(requestor));
+ this.nameLookup.seekModule(prefix, true, new SearchableEnvironmentRequestor(requestor), this.release);
}
@Override
@@ -1137,7 +1137,7 @@ private IModuleDescription getModuleDescription(IPackageFragmentRoot[] roots) {
this.rootToModule = new HashMap<>();
}
for (IPackageFragmentRoot root : roots) {
- IModuleDescription moduleDescription = NameLookup.getModuleDescription(this.project, root, this.rootToModule, this.nameLookup.rootToResolvedEntries::get);
+ IModuleDescription moduleDescription = NameLookup.getModuleDescription(this.project, root, this.rootToModule, this.nameLookup.rootToResolvedEntries::get, this.release);
if (moduleDescription != null)
return moduleDescription;
}
@@ -1149,7 +1149,7 @@ private IPackageFragmentRoot[] findModuleContext(char[] moduleName) {
if (this.knownModuleLocations != null && moduleName != null && moduleName.length > 0) {
moduleContext = this.knownModuleLocations.get(String.valueOf(moduleName));
if (moduleContext == null) {
- Answer moduleAnswer = this.nameLookup.findModule(moduleName);
+ Answer moduleAnswer = this.nameLookup.findModule(moduleName, this.release);
if (moduleAnswer != null) {
IProject currentProject = moduleAnswer.module.getJavaProject().getProject();
IJavaElement current = moduleAnswer.module.getParent();
@@ -1223,7 +1223,7 @@ public void cleanup() {
@Override
public org.eclipse.jdt.internal.compiler.env.IModule getModule(char[] name) {
- NameLookup.Answer answer = this.nameLookup.findModule(name);
+ NameLookup.Answer answer = this.nameLookup.findModule(name, this.release);
IModule module = null;
if (answer != null) {
module = NameLookup.getModuleDescriptionInfo(answer.module);
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SelectionRequestor.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SelectionRequestor.java
index 94da6a35e51..eaac735f1cc 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SelectionRequestor.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SelectionRequestor.java
@@ -886,7 +886,7 @@ public IJavaElement[] getElements() {
return this.elements;
}
protected IModuleDescription resolveModule(char[] moduleName) {
- Answer answer = this.nameLookup.findModule(moduleName);
+ Answer answer = this.nameLookup.findModule(moduleName, JavaProject.NO_RELEASE);
if (answer != null) {
return answer.module;
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java
index 99cd8127d58..d96ea03fec2 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SourceType.java
@@ -124,7 +124,8 @@ public void codeComplete(
}
JavaProject project = getJavaProject();
- SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, requestor.isTestCodeExcluded());
+ int release = JavaProject.getRelease(this);
+ SearchableEnvironment environment = project.newSearchableNameEnvironment(owner, requestor.isTestCodeExcluded(), release);
CompletionEngine engine = new CompletionEngine(environment, requestor, project.getOptions(true), project, owner, monitor);
String source = getCompilationUnit().getSource();
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
index 84a7015323f..5fb504e8e6a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java
@@ -154,7 +154,7 @@ private void computeClasspathLocations(
this.moduleUpdater.addReadUnnamedForNonEmptyClasspath(javaProject, classpathEntries);
}
}
- IModuleDescription projectModule = javaProject.getModuleDescription();
+ IModuleDescription projectModule = javaProject.getModuleDescription(releaseTarget);
String patchedModuleName = ModuleEntryProcessor.pushPatchToFront(classpathEntries, javaProject);
IModule patchedModule = null;
@@ -402,6 +402,7 @@ private void computeClasspathLocations(
try {
AbstractModule sourceModule = (AbstractModule)projectModule;
IModule info = (IModule) sourceModule.getElementInfo();
+ // Add all source locations to the module path entry
final ClasspathLocation[] sourceLocations2;
if(sLocationsForTest.size() == 0) {
sourceLocations2 = this.sourceLocations;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java
index 8c2d4d4d280..53fed18e89b 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java
@@ -94,7 +94,8 @@ public HierarchyBuilder(TypeHierarchy hierarchy) throws JavaModelException {
unitsToLookInside = workingCopies;
}
if (project != null) {
- SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside);
+ int release = JavaProject.getRelease(focusType);
+ SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside, false, release);
this.nameLookup = searchableEnvironment.nameLookup;
this.hierarchyResolver =
new HierarchyResolver(
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java
index 90bfc0ac407..5cc5cad7628 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java
@@ -214,7 +214,8 @@ public int compare(Object a, Object b) {
}
}
- SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside);
+ int release = inProjectOfFocusType ? JavaProject.getRelease(focusType) : JavaProject.NO_RELEASE;
+ SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(unitsToLookInside, false, release);
this.nameLookup = searchableEnvironment.nameLookup;
Map options = project.getOptions(true);
// disable task tags to speed up parsing
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/RegionBasedHierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/RegionBasedHierarchyBuilder.java
index 00cec01afe1..659ad0468dc 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/RegionBasedHierarchyBuilder.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/RegionBasedHierarchyBuilder.java
@@ -80,7 +80,11 @@ private void createTypeHierarchyBasedOnRegion(HashMap allOpenablesInRegion, IPro
try {
// resolve
- SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(this.hierarchy.workingCopies);
+ IType focusType = this.hierarchy.focusType;
+ int release = focusType != null && project.equals(focusType.getJavaProject())
+ ? JavaProject.getRelease(focusType)
+ : JavaProject.NO_RELEASE;
+ SearchableEnvironment searchableEnvironment = project.newSearchableNameEnvironment(this.hierarchy.workingCopies, false, release);
this.nameLookup = searchableEnvironment.nameLookup;
this.hierarchyResolver.resolve(openables, null, monitor);
} catch (JavaModelException e) {
diff --git a/org.eclipse.jdt.core/pom.xml b/org.eclipse.jdt.core/pom.xml
index 05d45844bff..de774adf31e 100644
--- a/org.eclipse.jdt.core/pom.xml
+++ b/org.eclipse.jdt.core/pom.xml
@@ -17,7 +17,7 @@
4.41.0-SNAPSHOT
org.eclipse.jdt.core
- 3.46.100-SNAPSHOT
+ 3.47.0-SNAPSHOT
eclipse-plugin