From a2826b91d916cd83303afc6f8b5f0145451f81a6 Mon Sep 17 00:00:00 2001 From: Stephan Herrmann Date: Tue, 14 Jan 2025 00:26:30 +0100 Subject: [PATCH] [24] JEP 494: Module Import Declarations (Second Preview) (#3555) + update: traditional imports shadow module imports Fixes https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2903 --- .../jdt/internal/compiler/lookup/Scope.java | 97 +++++++++------ .../regression/ModuleImportTests.java | 112 +++++++++++++++--- 2 files changed, 156 insertions(+), 53 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java index 2a32526946e..a212439cfb0 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/Scope.java @@ -3604,50 +3604,17 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) { // check on demand imports if (imports != null) { - boolean foundInImport = false; - ReferenceBinding type = null; - for (ImportBinding someImport : imports) { - if (someImport.onDemand) { - Binding resolvedImport = someImport.getResolvedImport(); - ReferenceBinding temp = null; - if (resolvedImport instanceof ModuleBinding) { - temp = findTypeInModule(name, (ModuleBinding) resolvedImport, currentPackage); - } else if (resolvedImport instanceof PackageBinding) { - temp = findType(name, (PackageBinding) resolvedImport, currentPackage); - } else if (someImport.isStatic()) { - // Imports are always resolved in the CU Scope (bug 520874) - temp = compilationUnitScope().findMemberType(name, (ReferenceBinding) resolvedImport); // static imports are allowed to see inherited member types - if (temp != null && !temp.isStatic()) - temp = null; - } else { - temp = compilationUnitScope().findDirectMemberType(name, (ReferenceBinding) resolvedImport); - } - if (TypeBinding.notEquals(temp, type) && temp != null) { - if (temp.isValidBinding()) { - ImportReference importReference = someImport.reference; - if (importReference != null) { - importReference.bits |= ASTNode.Used; - } - if (foundInImport) { - // Answer error binding -- import on demand conflict; name found in two import on demand packages. - temp = new ProblemReferenceBinding(new char[][]{name}, type, ProblemReasons.Ambiguous); - if (typeOrPackageCache != null) - typeOrPackageCache.put(name, temp); - return temp; - } - type = temp; - foundInImport = true; - } else if (foundType == null) { - foundType = temp; - } - } - } + ReferenceBinding type = findTypeInOnDemandImports(imports, currentPackage, name, false, typeOrPackageCache); + if (type == null) { // module imports would otherwise be shadowed + type = findTypeInOnDemandImports(imports, currentPackage, name, true/*module imports*/, typeOrPackageCache); } - if (type != null) { + if (type != null && type.isValidBinding()) { if (typeOrPackageCache != null) typeOrPackageCache.put(name, type); return type; } + if (foundType == null) + foundType = type; } } @@ -3690,6 +3657,58 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) { return foundType; } + private ReferenceBinding findTypeInOnDemandImports(ImportBinding[] imports, PackageBinding currentPackage, char[] name, + boolean inModules, HashtableOfObject typeOrPackageCache) { + // this method is run in two iterations in order to let traditional imports shadow module imports + boolean foundInImport = false; + ReferenceBinding type = null; + ReferenceBinding problemType = null; + for (ImportBinding someImport : imports) { + if (someImport.onDemand) { + Binding resolvedImport = someImport.getResolvedImport(); + ReferenceBinding temp = null; + if (inModules) { + if (resolvedImport instanceof ModuleBinding moduleBinding) { + temp = findTypeInModule(name, moduleBinding, currentPackage); + } + } else { + if (resolvedImport instanceof PackageBinding packageBinding) { + temp = findType(name, packageBinding, currentPackage); + } else if (resolvedImport instanceof ReferenceBinding referenceBinding) { + if (someImport.isStatic()) { + // Imports are always resolved in the CU Scope (bug 520874) + temp = compilationUnitScope().findMemberType(name, referenceBinding); // static imports are allowed to see inherited member types + if (temp != null && !temp.isStatic()) + temp = null; + } else { + temp = compilationUnitScope().findDirectMemberType(name, referenceBinding); + } + } + } + if (TypeBinding.notEquals(temp, type) && temp != null) { + if (temp.isValidBinding()) { + ImportReference importReference = someImport.reference; + if (importReference != null) { + importReference.bits |= ASTNode.Used; + } + if (foundInImport) { + // Answer error binding -- import on demand conflict; name found in two import on demand packages. + temp = new ProblemReferenceBinding(new char[][]{name}, type, ProblemReasons.Ambiguous); + if (typeOrPackageCache != null) + typeOrPackageCache.put(name, temp); + return temp; + } + type = temp; + foundInImport = true; + } else if (problemType == null) { + problemType = temp; + } + } + } + } + return type != null ? type : problemType; + } + private ReferenceBinding findTypeInModule(char[] name, ModuleBinding moduleBinding, PackageBinding currentPackage) { ReferenceBinding type = null; for (PackageBinding packageBinding : moduleBinding.getExports()) { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleImportTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleImportTests.java index e430861c2f5..f4eb1672667 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleImportTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleImportTests.java @@ -419,7 +419,7 @@ void m(Connection c, ConnectionBuilder builder) { // ConnectionBuiler is from ja OUTPUT_DIR); } - public void test008_ambiguous() { + public void test008_shadowing() { String srcDir = OUTPUT_DIR + File.separator + "src"; List files = new ArrayList<>(); writeFileCollecting(files, srcDir + File.separator + "p1", "Connection.java", @@ -440,7 +440,7 @@ public void foo() {} package p2; import module java.sql; import p1.*; - @SuppressWarnings("preview") + @SuppressWarnings({ "preview", "unused" }) // module import is not actually used class Client { void m(Connection c) { c.foo(); // ensure we select p1.Connection @@ -450,21 +450,47 @@ void m(Connection c) { StringBuilder commandLine = new StringBuilder(); commandLine.append(" -24 --enable-preview "); - runNegativeModuleTest( + runConformModuleTest( files, commandLine, "", + ""); + } + + public void test008_shadowing_static_nested() { + String srcDir = OUTPUT_DIR + File.separator + "src"; + List files = new ArrayList<>(); + writeFileCollecting(files, srcDir + File.separator + "p1", "Outer.java", """ - ---------- - 1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p2/Client.java (at line 6) + package p1; + public class Outer { + public static class Connection { + public void foo() {} + } + } + """); + writeFileCollecting(files, srcDir, "module-info.java", + """ + module mod.one { + requires java.sql; + } + """); + writeFileCollecting(files, srcDir + File.separator + "p2", "Client.java", + """ + package p2; + import module java.sql; + import p1.Outer.*; + @SuppressWarnings({ "preview", "unused" }) // module import is not actually used + class Client { void m(Connection c) { - ^^^^^^^^^^ - The type Connection is ambiguous - ---------- - 1 problem (1 error) - """, - "reference to Connection is ambiguous", - OUTPUT_DIR); + c.foo(); // ensure we select p1.Outer.Connection + } + } + """); + StringBuilder commandLine = new StringBuilder(); + commandLine.append(" -24 --enable-preview "); + + runConformModuleTest(files, commandLine, "", ""); } public void test009_ambiguous_modules() { @@ -511,7 +537,7 @@ public class Other{ import module mod.one; @SuppressWarnings("preview") class Client { - Connection conn; // module conflict mod.one java.sql + Connection conn; // module conflict mod.one java.sql (via requires transitive) Other other; // package conflict mod.one/p1 mod.one/p2 } """); @@ -527,7 +553,7 @@ class Client { """ ---------- 1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/p3/Client.java (at line 5) - Connection conn; // module conflict mod.one java.sql + Connection conn; // module conflict mod.one java.sql (via requires transitive) ^^^^^^^^^^ The type Connection is ambiguous ---------- @@ -542,6 +568,64 @@ class Client { OUTPUT_DIR); } + public void test009_ambiguous_modules2() { + // module conflict via separate module imports based on separate requires directly in mod.two + String srcDir = OUTPUT_DIR + File.separator + "src"; + List files = new ArrayList<>(); + String modOneDir = srcDir + File.separator + "mod.one"; + writeFileCollecting(files, modOneDir, "module-info.java", + """ + module mod.one { + exports p1; + } + """); + writeFileCollecting(files, modOneDir + File.separator + "p1", "Connection.java", + """ + package p1; + public class Connection { + } + """); + + String modTwoDir = srcDir + File.separator + "mod.two"; + writeFileCollecting(files, modTwoDir, "module-info.java", + """ + module mod.two { + requires mod.one; + requires java.sql; + } + """); + writeFileCollecting(files, modTwoDir + File.separator + "p3", "Client.java", + """ + package p3; + import module mod.one; + import module java.sql; + @SuppressWarnings("preview") + class Client { + Connection conn; // module conflict mod.one java.sql + } + """); + StringBuilder commandLine = new StringBuilder(); + commandLine.append(" -24 --enable-preview "); + commandLine.append(" --module-source-path \"").append(srcDir).append("\""); + commandLine.append(" -d \"").append(OUTPUT_DIR).append(File.separatorChar).append("bin").append("\""); + + runNegativeModuleTest( + files, + commandLine, + "", + """ + ---------- + 1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.two/p3/Client.java (at line 6) + Connection conn; // module conflict mod.one java.sql + ^^^^^^^^^^ + The type Connection is ambiguous + ---------- + 1 problem (1 error) + """, + "reference to Connection is ambiguous", + OUTPUT_DIR); + } + public void test010_notAccessible() { String srcDir = OUTPUT_DIR + File.separator + "src"; String modOneDir = srcDir + File.separator + "mod.one";