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.
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());
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:
- Add a dedicated golden test case capturing the behavior of nullable inner classes and generic types to ensure we prevent regressions.
- Refactor
VaporReference and ConcreteReference to use a consistent representation of nullability formatting in name().
- 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>).
Issue Details
There is a behavioral inconsistency in how JSpecify
@Nullableannotations are prepended to class names betweenConcreteReferenceandVaporReferencein thegapic-generator-javaAST. This technical debt is captured in the code via // TODO(miraleung): Give this behavioral parity with ConcreteReference.ConcreteReference.name()manually and dynamically prepends@Nullableto the class name ifisNullable()is true:VaporReference.name()does not include this check in its name formation. Instead, nullability prepending forVaporReferencewas implemented purely inside the writer phase inJavaWriterVisitor.visit(VaporReference).This inconsistency causes issues when reference names are dynamically constructed or compared (for example, when a
VaporReferenceorConcreteReferenceis nested as a generic parameter inside another reference, since the parent'sbuild()method callsr.name()on its generics to build its own name string).To unblock the initial JSpecify
@NullablePR, a temporary patch was applied to copy over the minimal behavior, but we need a clean, long-term alignment.Proposed Follow-up Work:
VaporReferenceandConcreteReferenceto use a consistent representation of nullability formatting inname().ConcreteReferenceby removing the manual string formatting inside the AST class entirely, centralizing the nullability formatting logic inside theJavaWriterVisitor.Environment
sdk-platform-java/gapic-generator-javaDependencies
com.google.api:gapic-generator-javaReproducer
The issue can be reproduced by comparing the string returned by
name()for a nullableVaporReferencevs a nullableConcreteReference:This causes formatting differences when these references are nested inside collection types (e.g.
List<@Nullable String>vsList<MyClass>).