Skip to content

Commit

Permalink
[24] JEP 494: Module Import Declarations (Second Preview) (eclipse-jd…
Browse files Browse the repository at this point in the history
…t#3555)

+ update: traditional imports shadow module imports

Fixes eclipse-jdt#2903
  • Loading branch information
stephan-herrmann authored Jan 13, 2025
1 parent 6c5fe91 commit a2826b9
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> files = new ArrayList<>();
writeFileCollecting(files, srcDir + File.separator + "p1", "Connection.java",
Expand All @@ -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
Expand All @@ -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<String> 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() {
Expand Down Expand Up @@ -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
}
""");
Expand All @@ -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
----------
Expand All @@ -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<String> 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";
Expand Down

0 comments on commit a2826b9

Please sign in to comment.