Summary
-------
Avoid surprising `null` result from `Elements.getTypeElement(CharSequence)` and `Elements.getPackageElement(CharSequence)` when a (uniquely) visible element clashes with an invisible element.
Problem
-------
Unqualified `Elements.getTypeElement(CharSequence)` and `Elements.getPackageElement(CharSequence)` search for elements across all modules in the module graph, and only return a value when they find exactly one element. This is troublesome, as an element (uniquely) visible from a root module may be "hidden" by an invisible element.
For example, consider:
---m1:
module m1 {}
package test; public class Test {}
---m2:
module m2 { exports test; }
package test; public class Test{}
---mt:
module mt { requires m1; requires m2; }
While compiling "mt", `Elements.getTypeElement("test.Test")` will return `null`, as it will find two copies of `test.Test` (one in each `m1` and `m2`). But, in the context of `mt` compilation, only the element from `m2` makes sense.
Solution
--------
Prefer elements (uniquely) visible from root modules. Only if none is found, search for invisible elements. Root modules are the modules javac is currently compiling.
Specification
-------------
Proposed change to `javax.lang.model.util.Elements`:
diff --git a/src/java.base/share/classes/java/lang/module/package-info.java b/src/java.base/share/classes/java/lang/module/package-info.java
index ff7448c9c7f..2750c99a216 100644
--- a/src/java.base/share/classes/java/lang/module/package-info.java
+++ b/src/java.base/share/classes/java/lang/module/package-info.java
@@ -147,7 +147,7 @@
* <p> Otherwise, resolution succeeds, and the result of resolution is the
* readability graph.
*
- * <h3> Root modules </h3>
+ * <h3><a id="root-modules"></a> Root modules </h3>
*
* <p> The set of root modules at compile-time is usually the set of modules
* being compiled. At run-time, the set of root modules is usually the
diff --git a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
index 81344311869..aad204336e8 100644
--- a/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
+++ b/src/java.compiler/share/classes/javax/lang/model/util/Elements.java
@@ -51,11 +51,30 @@
public interface Elements {
/**
- * Returns a package given its fully qualified name if the package is unique in the environment.
- * If running with modules, all modules in the modules graph are searched for matching packages.
- *
- * @param name fully qualified package name, or an empty string for an unnamed package
- * @return the specified package, or {@code null} if it cannot be uniquely found
+ * Returns a package given its fully qualified name if the package is uniquely
+ * determinable in the environment.
+ *
+ * If running with modules, packages of the given name are searched in a
+ * two-stage process:
+ * <ul>
+ * <li>find non-empty packages with the given name returned by
+ * {@link #getPackageElement(ModuleElement, CharSequence)},
+ * where the provided ModuleSymbol is any
+ * <a href="../../../../../java.base/java/lang/module/package-summary.html#root-modules">root module</a>,
+ * </li>
+ * <li>if the above yields an empty list, search
+ * {@link #getAllModuleElements() all modules} for observable
+ * packages with the given name
+ * </li>
+ * </ul>
+ *
+ * If this process leads to a list with a single element,
+ * the single element is returned, otherwise null is returned.
+ *
+ * @param name fully qualified package name,
+ * or an empty string for an unnamed package
+ * @return the specified package,
+ * or {@code null} if no package can be uniquely determined.
*/
PackageElement getPackageElement(CharSequence name);
@@ -119,12 +138,29 @@ default PackageElement getPackageElement(ModuleElement module, CharSequence name
}
/**
- * Returns a type element given its canonical name if the type element is unique in the environment.
- * If running with modules, all modules in the modules graph are searched for matching
- * type elements.
- *
- * @param name the canonical name
- * @return the named type element, or {@code null} if it cannot be uniquely found
+ * Returns a type element given its canonical name if the type element is uniquely
+ * determinable in the environment.
+ *
+ * If running with modules, type elements of the given name are
+ * searched in a two-stage process:
+ * <ul>
+ * <li>find type elements with the given name returned by
+ * {@link #getTypeElement(ModuleElement, CharSequence)},
+ * where the provided ModuleSymbol is any
+ * <a href="../../../../../java.base/java/lang/module/package-summary.html#root-modules">root module</a>,
+ * </li>
+ * <li>if the above yields an empty list, search
+ * {@link #getAllModuleElements() all modules} for observable
+ * type elements with the given name
+ * </li>
+ * </ul>
+ *
+ * If this process leads to a list with a single element,
+ * the single element is returned, otherwise null is returned.
+ *
+ * @param name the canonical name
+ * @return the named type element,
+ * or {@code null} if no type element can be uniquely determined.
*/
TypeElement getTypeElement(CharSequence name);
Rendered javadoc with these changes is available for convenience here:
* http://cr.openjdk.java.net/~jlahoda/8236842/docsv2/api/java.compiler/javax/lang/model/util/Elements.html#getTypeElement(java.lang.CharSequence)
* http://cr.openjdk.java.net/~jlahoda/8236842/docsv2/api/java.compiler/javax/lang/model/util/Elements.html#getPackageElement(java.lang.CharSequence)