Skip to content

Capture package-level annotations from dependency types via a new JavaType.Package #7721

Description

@timtebeek

Background

The Java LST type model has no representation for packages. JavaType.Class.owningClass only stores enclosing FullyQualified (i.e. class-like) symbols (ReloadableJava21TypeMapping.java:229-231), and the javac/reflection type mappings never read PackageSymbol.getAnnotationMirrors() or Package.getDeclaredAnnotations(). As a result, package-level annotations declared in a dependency's package-info.class are not present anywhere in the LST.

This blocks nullability inference recipes that need to honor:

  • JSpecify @NullMarked / @NullUnmarked
  • JSR-305 @ParametersAreNonnullByDefault / @FieldsAreNonnullByDefault
  • FindBugs/SpotBugs default-scope annotations
  • any other annotation that propagates from package to class to member

As a related symptom, even within source: FindMissingTypesTest.java:172-175 asserts that the type of @NullMarked on a package-info.java resolves to JavaType.Unknown — i.e. package annotations don't resolve their types even when the annotation source is the current project.

Proposal

Introduce JavaType.Package implements JavaType with fullyQualifiedName and annotations: FullyQualified[], accessed via a new @Nullable Package pkg field on JavaType.Class (plus a default getPackage() accessor on FullyQualified).

Scope

  • New JavaType.Package type, with constructor/unsafeSet and the @JsonTypeInfo machinery.
  • Add pkg field + getPackage() to JavaType.Class; extend unsafeSet overloads.
  • Populate from javac (ReloadableJava{8,11,17,21,25}TypeMapping), reflection (JavaReflectionTypeMapping — including a forced load of package-info.class so Package.getDeclaredAnnotations() returns non-empty), Kotlin (KotlinTypeMapping, KotlinIrTypeMapping), Scala (ScalaTypeMapping), and the source-side parser (ReloadableJava{N}ParserVisitor, which already collects getPackageAnnotations() syntactically).
  • Signature builder: add packageSignature(Object) to JavaTypeSignatureBuilder and all implementations; signature "<package> <fqn>" to keep it disjoint from class signatures.
  • Visitor / RPC: add visitPackage to JavaTypeVisitor, wire JavaTypeSender / JavaTypeReceiver.
  • Flip FindMissingTypesTest:172-175 from "expect Unknown" to "expect annotation type resolved".
  • Convenience: TypeUtils.findInheritedAnnotation(JavaType, String) that walks type-use → declaration → enclosing class → package.

Non-goals

  • Module annotations (module-info) — separate problem.
  • Implementing nullability inference itself — this issue only unblocks it.

Compatibility

New optional field, additive. Existing recipes see no behavior change. RPC consumers running an older JavaTypeReceiver will fail on Package payloads — roll out the receiver change first, then the sender.

Acceptance criteria

  • A class loaded from a JAR whose package-info.class is annotated with @org.jspecify.annotations.NullMarked exposes that annotation via cls.getPackage().getAnnotations().
  • Same for reflection-mapped classes.
  • A J.Package.annotations[0].getType() in a parsed package-info.java is no longer JavaType.Unknown.
  • Round-trips correctly through RPC.

Metadata

Metadata

Assignees

No one assigned

    Fields

    No fields configured for Feature.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions