Skip to content

Align VaporReference and ConcreteReference Nullability name() Formatting #13798

Description

@nnicolee

Issue Details

There is a behavioral inconsistency in how JSpecify @Nullable annotations are prepended to class names between ConcreteReference and VaporReference in the gapic-generator-java AST. This technical debt is captured in the code via // TODO(miraleung): Give this behavioral parity with ConcreteReference.

  1. ConcreteReference.name() manually and dynamically prepends @Nullable to the class name if isNullable() is true:
    if (isNullable()) {
      sb.append("@Nullable");
      sb.append(SPACE);
    }
    sb.append(simpleName());
  2. VaporReference.name() does not include this check in its name formation. Instead, nullability prepending for VaporReference was implemented purely inside the writer phase in JavaWriterVisitor.visit(VaporReference).

This inconsistency causes issues when reference names are dynamically constructed or compared (for example, when a VaporReference or ConcreteReference is nested as a generic parameter inside another reference, since the parent's build() method calls r.name() on its generics to build its own name string).

To unblock the initial JSpecify @Nullable PR, a temporary patch was applied to copy over the minimal behavior, but we need a clean, long-term alignment.

Proposed Follow-up Work:

  1. Add a dedicated golden test case capturing the behavior of nullable inner classes and generic types to ensure we prevent regressions.
  2. Refactor VaporReference and ConcreteReference to use a consistent representation of nullability formatting in name().
  3. Investigate if we can simplify ConcreteReference by removing the manual string formatting inside the AST class entirely, centralizing the nullability formatting logic inside the JavaWriterVisitor.

Environment

  • OS Type and Version: Linux
  • Java Version and JDK Vendor: Java 11+
  • Module: sdk-platform-java/gapic-generator-java

Dependencies

  • com.google.api:gapic-generator-java

Reproducer

The issue can be reproduced by comparing the string returned by name() for a nullable VaporReference vs a nullable ConcreteReference:

// ConcreteReference name will contain "@Nullable"
ConcreteReference.builder()
    .setClazz(String.class)
    .setIsNullable(true)
    .build()
    .name(); // Returns "@Nullable String"

// VaporReference name will NOT contain "@Nullable"
VaporReference.builder()
    .setName("MyClass")
    .setPakkage("com.google")
    .setIsNullable(true)
    .build()
    .name(); // Returns "MyClass"

This causes formatting differences when these references are nested inside collection types (e.g. List<@Nullable String> vs List<MyClass>).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions