[Java][RestClient] Mark non-required API method parameters @Nullable under JSpecify#24392
Open
seonwooj0810 wants to merge 1 commit into
Conversation
…nder JSpecify
The public `{operationId}` and `{operationId}WithHttpInfo` methods in the
restclient library rendered parameters via `{{>nullable_var_annotations}}
{{{dataType}}}`, which bypasses the `jSpecifyDatatype` lambda. Under
useJspecify the JSpecifyNullableLambda strips the `@Nullable` prefix so it can
be re-injected TYPE_USE-style into the datatype, but without that lambda the
annotation was dropped entirely, leaving non-required parameters unmarked even
though the generated package is @NullMarked.
Use `{{>nullableArgument}}` (as the private *RequestCreation and
*WithResponseSpec methods already do) so non-required params are consistently
annotated. Behavior is unchanged in non-JSpecify mode.
Fixes OpenAPITools#24388
Contributor
There was a problem hiding this comment.
1 issue found across 2 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache">
<violation number="1" location="modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache:183">
P2: Optional generic parameters are annotated at the wrong type-use level after this change: `jSpecifyDatatype` uses the last dot in the whole datatype, so `List<java.time.Instant>` becomes `List<java.time.@Nullable Instant>` instead of annotating the outer `List`. Preserving `nullableArgument` here requires fixing the datatype lambda to place `@Nullable` on the outer parameter type, including nested generic types.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| @Deprecated | ||
| {{/isDeprecated}} | ||
| public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.Resource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.Resource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{>nullableArgument}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{>nullable_var_annotations}} {{{dataType}}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientResponseException { | ||
| public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.Resource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.Resource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{>nullableArgument}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{>nullableArgument}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientResponseException { |
Contributor
There was a problem hiding this comment.
P2: Optional generic parameters are annotated at the wrong type-use level after this change: jSpecifyDatatype uses the last dot in the whole datatype, so List<java.time.Instant> becomes List<java.time.@Nullable Instant> instead of annotating the outer List. Preserving nullableArgument here requires fixing the datatype lambda to place @Nullable on the outer parameter type, including nested generic types.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/resources/Java/libraries/restclient/api.mustache, line 183:
<comment>Optional generic parameters are annotated at the wrong type-use level after this change: `jSpecifyDatatype` uses the last dot in the whole datatype, so `List<java.time.Instant>` becomes `List<java.time.@Nullable Instant>` instead of annotating the outer `List`. Preserving `nullableArgument` here requires fixing the datatype lambda to place `@Nullable` on the outer parameter type, including nested generic types.</comment>
<file context>
@@ -180,7 +180,7 @@ public class {{classname}} {
@Deprecated
{{/isDeprecated}}
- public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.Resource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.Resource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{>nullableArgument}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{>nullable_var_annotations}} {{{dataType}}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientResponseException {
+ public {{#returnType}}{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{#isFile}}{{#useAbstractionForFiles}}{{#collectionFormat}}java.util.Collection<org.springframework.core.io.Resource>{{/collectionFormat}}{{^collectionFormat}}org.springframework.core.io.Resource{{/collectionFormat}}{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{>nullableArgument}}{{/useAbstractionForFiles}}{{/isFile}}{{^isFile}}{{>nullableArgument}}{{/isFile}} {{paramName}}{{^-last}}, {{/-last}}{{/allParams}}) throws RestClientResponseException {
{{#returnType}}ParameterizedTypeReference<{{#isResponseFile}}{{#useAbstractionForFiles}}org.springframework.core.io.Resource{{/useAbstractionForFiles}}{{^useAbstractionForFiles}}{{{returnType}}}{{/useAbstractionForFiles}}{{/isResponseFile}}{{^isResponseFile}}{{{returnType}}}{{/isResponseFile}}> localVarReturnType = new ParameterizedTypeReference<>() {};{{/returnType}}{{^returnType}}ParameterizedTypeReference<Void> localVarReturnType = new ParameterizedTypeReference<>() {};{{/returnType}}
{{#returnType}}return {{/returnType}}{{operationId}}RequestCreation({{#allParams}}{{paramName}}{{^-last}}, {{/-last}}{{/allParams}}).body(localVarReturnType);
</file context>
Chrimle
approved these changes
Jul 23, 2026
Chrimle
left a comment
Contributor
There was a problem hiding this comment.
Looks great, thanks for the contribution! ❤️
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #24388
Root cause
Under
useJspecify, the Javarestclientlibrary rendered the public{operationId}and{operationId}WithHttpInfomethod parameters via{{>nullable_var_annotations}} {{{dataType}}}. That emits@Nullableas a prefix, whichJSpecifyNullableLambdathen strips (so it can be re-injected TYPE_USE-style into the datatype by thejSpecifyDatatypelambda). Because these two methods did not wrap the datatype in{{#lambda.jSpecifyDatatype}}, the stripped@Nullablewas never re-injected and the annotation was dropped entirely — so non-required parameters were left unmarked even though the generated package is@NullMarked. The private*RequestCreationand public*WithResponseSpecmethods already used{{>nullableArgument}}and rendered correctly, which is why the same parameters were@Nullableon those methods but not on{operationId}/{operationId}WithHttpInfo.Change
api.mustache(restclient library): the two affected public methods now use{{>nullableArgument}}— the same partial the correct-behaving methods already use.nullableArgument={{>nullable_var_annotations}} {{#lambda.jSpecifyDatatype}}{{{dataType}}}{{/lambda.jSpecifyDatatype}}, so the@Nullableis correctly placed inside the type (java.time.@Nullable Instant). In non-JSpecify mode the two forms are equivalent, so output is unchanged there.Verification done
masterand regenerated the affected sample (java-restclient-springBoot4-jackson3-jspecify). The only diff is the two public methods now marking the non-requireddtParam/dtQuery/dtCookieasjava.time.@Nullable Instant, byte-for-byte identical to the already-correct privatefooDtParamGetRequestCreationin the same file (which uses the existingorg.jspecify.annotations.Nullableimport). A required parameter (fileIdGet(String id)) correctly stays non-null.java-restclient,java-restclient-springBoot4-jackson3,java-restclient-springBoot4-jackson2): zero diff, confirming the change is behavior-preserving outside JSpecify.Summary by cubic
Marks non-required parameters in generated Java RestClient public methods as
@Nullableunder JSpecify. Fixes #24388 and keeps non-JSpecify output unchanged.{{>nullableArgument}}for params in{operationId}and{operationId}WithHttpInfoto enable type-use placement viajSpecifyDatatype.java.time.@Nullable Instantin@NullMarkedpackages, matching*RequestCreation/*WithResponseSpec.Written for commit 8094378. Summary will update on new commits.