Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public final class CasingConfiguration {
"else",
"enum",
"extends",
"false",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Java generator fix ships without a changelog/version entry

The Java generator bug fix changes shared casing logic (JAVA_RESERVED_KEYWORDS at generators/java/generator-utils/src/main/java/com/fern/java/utils/CasingConfiguration.java:62) without adding a corresponding release entry, so users get no released version containing the fix and no record of it.
Impact: The fix is not published or documented for users of the Java generator.

Repository rule requiring a versions.yml entry for generator changes

REVIEW.md states: "Generator changes (bug fixes or features) should include a versions.yml entry in the appropriate generator directory" and "The type field in changelog entries should match the PR type: fix for bug fixes". This PR only touches CasingConfiguration.java and its test; no entry was added to generators/java/sdk/versions.yml (or the other Java generator directories that consume generator-utils).

Prompt for agents
REVIEW.md requires generator changes to include a versions.yml changelog entry in the appropriate generator directory with type matching the PR type. This PR fixes a Java generator crash caused by unescaped null/true/false discriminator values in CasingConfiguration, but no entry was added. Add a new version entry with a `fix` changelog summary to generators/java/sdk/versions.yml (and any other Java generator versions.yml files that ship generator-utils, e.g. spring/model, if applicable), following the formatting of existing entries.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@ChoMinGi ChoMinGi Aug 15, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.
added a changelog entry at generators/java/sdk/changes/unreleased/fix-null-true-false-discriminator.yml per the REVIEW.md convention.

"final",
"finally",
"float",
Expand All @@ -73,6 +74,7 @@ public final class CasingConfiguration {
"long",
"native",
"new",
"null",
"package",
"private",
"protected",
Expand All @@ -88,6 +90,7 @@ public final class CasingConfiguration {
"throw",
"throws",
"transient",
"true",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 suggestion

Two independent reserved-word lists (KeyWordUtils and this one) drifting apart is exactly how this bug happened. Consider having one derive from the other, or at minimum add a test asserting JAVA_RESERVED_KEYWORDS is a superset of KeyWordUtils' set so the next divergence fails in CI instead of in a user's generate run.

@ChoMinGi ChoMinGi Aug 15, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried that — turns out _ doesn't work the same way.

KeyWordUtils does treat _ as reserved, but in CasingConfiguration it never gets that far. splitWords()'s regex doesn't match underscores at all, so computeName("_") collapses to an empty string before sanitizeName ever runs — I checked, and camelSafe comes back as "", not "_". So adding _ to JAVA_RESERVED_KEYWORDS would just be a no-op.

That looks like a bug in the word-splitting logic rather than the list drifting, so I'd rather leave it out of this PR. Happy to file it separately if that's useful.

"try",
"void",
"volatile",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -957,4 +957,24 @@ private static CasingConfiguration buildConfig(boolean smartCasing, String langu
root.set("casingsConfig", casingsConfig);
return CasingConfiguration.fromIrJson(root);
}

// ===== keyword sanitization: Java literals =====

@Nested
class JavaLiteralKeywordTests {

@ParameterizedTest
@CsvSource({"'null',null_", "'true',true_", "'false',false_"})
void computeName_escapesJavaLiterals(String input, String expectedSafeName) {
CasingConfiguration config = buildConfig(true, "java", null);
CasingConfiguration.NameParts parts = config.computeName(input);
assertThat(parts.camelSafe).isEqualTo(expectedSafeName);
}

@Test
void computeName_ordinaryKeywordStillEscaped() {
CasingConfiguration config = buildConfig(true, "java", null);
assertThat(config.computeName("default").camelSafe).isEqualTo("default_");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# yaml-language-server: $schema=../../../../../fern-changes-yml.schema.json

- summary: |
Escape the Java literals `null`, `true`, and `false` when they're used as a
discriminated union's discriminator value. Previously the generated Java
identifier was left unescaped (e.g. a static factory method literally named
`null`), which JavaPoet rejected with `not a valid name: null`, aborting
generation. The identifier is now escaped to `null_`/`true_`/`false_`
(matching the existing convention for other reserved words like `static`),
while the serialized discriminator value is unchanged.
type: fix
Loading