@@ -398,28 +398,33 @@ private class AttributeRemover: SyntaxRewriter {
398
398
for case . attribute( let attribute) in node {
399
399
if attributesToRemove. contains ( attribute) {
400
400
var leadingTrivia = attribute. leadingTrivia
401
+
402
+ // Don't leave behind an empty line when the attribute being removed is on its own line,
403
+ // based on the following conditions:
404
+ // - Leading trivia ends with a newline followed by arbitrary number of spaces or tabs
405
+ // - All leading trivia pieces after the last newline are just whitespace, ensuring
406
+ // there are no comments or other non-whitespace characters on the same line
407
+ // preceding the attribute.
408
+ // - There is no trailing trivia and the next token has leading trivia.
401
409
if let lastNewline = leadingTrivia. pieces. lastIndex ( where: \. isNewline) ,
402
410
leadingTrivia. pieces [ lastNewline... ] . allSatisfy ( \. isWhitespace) ,
403
411
attribute. trailingTrivia. isEmpty,
404
412
let nextToken = attribute. nextToken ( viewMode: . sourceAccurate) ,
405
413
!nextToken. leadingTrivia. isEmpty
406
414
{
407
- // If the attribute is on its own line based on the following conditions,
408
- // remove the newline from it so we don’t end up with an empty line
409
- // - Leading trivia ends with a newline followed by arbitrary number of spaces or tabs
410
- // - There is no trailing trivia and the next token has no leading trivia
411
415
leadingTrivia = Trivia ( pieces: leadingTrivia. pieces [ ..< lastNewline] )
412
416
}
417
+
413
418
// Drop any spaces or tabs from the trailing trivia because there’s no
414
419
// more attribute they need to separate.
415
420
let trailingTrivia = attribute. trailingTrivia. trimmingPrefix ( while: \. isSpaceOrTab)
416
421
triviaToAttachToNextToken += leadingTrivia + trailingTrivia
417
422
418
- // If the attribute is not separated by the previous attribute using trivia, e.g.
419
- // `@First@Second var x: Int` (yes, that's valid Swift), and removing the `@Second`
420
- // attribute then dropping all trivia of this attribute would join `@First` and `var`
421
- // without any trivia in between, which is invalid. In this case the trailing trivia of the
422
- // attribute is significant and we need to keep it .
423
+ // If the attribute is not separated from the previous attribute by trivia, as in
424
+ // `@First@Second var x: Int` (yes, that's valid Swift), removing the `@Second`
425
+ // attribute and dropping all its trivia would cause `@First` and `var` to join
426
+ // without any trivia in between, which is invalid. In such cases, the trailing trivia
427
+ // of the attribute is significant and must be retained .
423
428
if triviaToAttachToNextToken. isEmpty,
424
429
let previousToken = attribute. previousToken ( viewMode: . sourceAccurate) ,
425
430
previousToken. trailingTrivia. isEmpty
0 commit comments