Skip to content

Only trim the trailing newline of a %L argument - #2391

Merged
Egorand merged 1 commit into
square:mainfrom
lenamonj:string-arg-trailing-newline
Oct 11, 2026
Merged

Egorand merged 1 commit into
square:mainfrom
lenamonj:string-arg-trailing-newline

Conversation

@lenamonj

Copy link
Copy Markdown
Contributor

Fixes #2381.

trimTrailingNewLine() and ensureEndsWithNewLine() rewrote the last String argument of a trailing placeholder. For %L, which renders its argument as code, that is layout; for %S it is data, so initializer("%S", "cheese\n") emitted "cheese".

Only a %L argument is rewritten; other String arguments are kept and take a requested newline as a format part.

TypeSpecTest.multilineStrings returns to its pre-#1827 expectation; callers relying on the trim see different output.

New tests and multilineStrings fail on main and pass here; ./gradlew build passes.

  • docs/changelog.md has been updated if applicable.
    • Changes not visible to library consumers, such as build script, documentation, or test code updates, don't need to
      be added to the changelog.
  • CLA signed.

@Egorand Egorand left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I believe the logic to trim the %S or %P argument was a design decision, with the assumption that in most cases the trailing newline would not be meaningful and hence safe to delete. But I do now think it should probably be on the library user to pre-process the input before passing it to %S/%P, and the library shouldn't be modifying the input.

I do wonder if we should still trim %L arguments, the justification given in the issue description - "%L renders its argument as code text, so trimming layout newlines there is right" - does not explain why it's right. Maybe we should just never trim placeholder args?

@Egorand
Egorand merged commit 6ff3561 into square:main Oct 11, 2026
7 checks passed
@lenamonj
lenamonj deleted the string-arg-trailing-newline branch October 11, 2026 12:06
@lenamonj

Copy link
Copy Markdown
Contributor Author

The %L trim only affects layout. Both callers manage the final newline themselves: ensureEndsWithNewLine() makes the kdoc end in exactly one before */, and PropertySpec writes its own after trimTrailingNewLine(). Trailing newlines on a String passed to %L collide with that newline: addKdoc("%L", "text\n") would get an empty * line before */ without the trim, and initializer("%L", "foo()\n") would be followed by a blank line. Neither changes the generated program. For %S and %P the newline is part of the literal's value, so trimming it changed the output's meaning, which is why #2391 removed the trim there and kept it for %L.

Never trimming is the cleaner rule and I would take it. A CodeBlock passed to %L was never trimmed, so dropping the String branch applies one rule to every argument: the library emits what it is given, and a caller who passes %L a string ending in a newline trims it themselves, as %S and %P callers now do. The cost is the two blank lines above appearing for existing callers who relied on the trim.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

trimTrailingNewLine rewrites %S, %P and %N arguments, changing the emitted value

2 participants