Copilot commented on code in PR #2745:
URL: https://github.com/apache/groovy/pull/2745#discussion_r3670595002
##########
src/main/java/org/codehaus/groovy/control/ClassNodeResolver.java:
##########
@@ -160,6 +174,76 @@ public ClassNode getFromClassCache(final String name) {
return cached;
}
+ /**
+ * Resolves a package name to a {@link PackageNode} carrying the
annotations found on the
+ * package's compiled {@code package-info.class}, if any (GROOVY-12207).
This makes
+ * package-level annotations of precompiled dependencies (e.g. JSpecify's
{@code @NullMarked})
+ * visible to type checkers and AST transforms.
+ * <p>
+ * The {@code package-info.class} is located on the compilation unit's
class path and decompiled
+ * on demand using the same ASM infrastructure as ordinary classes;
results are cached per
+ * resolver, including a negative cache for packages that have no
(annotation-bearing)
+ * package-info. Returns {@code null} if the package has no such metadata.
+ *
+ * @param packageName the fully qualified package name (no trailing dot),
e.g. {@code "foo.bar"}
+ * @param compilationUnit the current {@link CompilationUnit}
+ * @return a {@link PackageNode} with the package's annotations, or {@code
null} if none
+ */
+ public synchronized PackageNode resolvePackage(final String packageName,
final CompilationUnit compilationUnit) {
+ if (packageName == null || packageName.isEmpty() || compilationUnit ==
null) {
+ return null;
+ }
+ PackageNode cached = cachedPackages.get(packageName);
+ if (cached != null) {
+ return cached == NO_PACKAGE ? null : cached;
+ }
+ PackageNode result = findPackageInfo(packageName, compilationUnit);
+ cachedPackages.put(packageName, result == null ? NO_PACKAGE : result);
+ return result;
+ }
+
+ /**
+ * Loads and decompiles {@code <packageName>/package-info.class} from the
compilation unit's
+ * class loader, copying its (class-level) annotations onto a fresh {@link
PackageNode}. The
+ * annotations of a {@code package-info} type are stored as ordinary class
annotations in the
+ * bytecode, so the existing decompiler pipeline reads them without
special handling.
+ *
+ * @return a populated {@link PackageNode}, or {@code null} if there is no
package-info, it has
+ * no annotations, or it could not be read
+ */
+ private PackageNode findPackageInfo(final String packageName, final
CompilationUnit compilationUnit) {
+ GroovyClassLoader loader = compilationUnit.getClassLoader();
+ if (loader == null) {
+ return null;
+ }
+ String fileName = packageName.replace('.', '/') +
"/package-info.class";
+ URL resource = loader.getResource(fileName);
+ if (resource == null) {
+ return null;
+ }
+ try {
+ DecompiledClassNode packageInfo = new DecompiledClassNode(
+ AsmDecompiler.parseClass(resource), new
AsmReferenceResolver(this, compilationUnit));
+ if (!packageInfo.getName().equals(packageName + ".package-info")) {
+ // this may happen under Windows/macOS because getResource is
case-insensitive there!
+ return null;
+ }
+ List<AnnotationNode> annotations = packageInfo.getAnnotations();
+ if (annotations == null || annotations.isEmpty()) {
+ return null;
+ }
+ PackageNode packageNode = new PackageNode(packageName + ".");
Review Comment:
`resolvePackage` documents `packageName` as having no trailing dot, but
`findPackageInfo` constructs `PackageNode` with `packageName + "."`. This makes
`PackageNode.getName()` inconsistent with the rest of the AST (and would render
as `package foo.bar.` via `PackageNode#getText`). Create the `PackageNode` with
the plain package name instead.
##########
src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java:
##########
@@ -164,6 +165,23 @@ public long getCompilationTimeStamp() {
* @throws org.codehaus.groovy.GroovyBugError if the class cannot be
resolved at runtime
* @see AsmReferenceResolver#resolveJvmClass(String)
*/
+ /**
+ * Returns the {@link PackageNode} for this decompiled class, carrying any
annotations declared
+ * on the package's compiled {@code package-info.class} (GROOVY-12207).
Unlike a source-based
+ * {@link ClassNode}, a decompiled class has no owning module, so the
package (and its
+ * annotations) is resolved on demand from the class path via the
reference resolver's
Review Comment:
There are now two consecutive Javadoc blocks before `getPackage()`. The
first block (about resolving the runtime JVM `Class`) is no longer attached to
`getTypeClass()` and can trigger dangling-Javadoc warnings and misleading
generated docs. Removing that stray Javadoc block (or relocating it directly
above `getTypeClass()`) will keep Javadoc output clean.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]