From db0c7cb1dfc0e1cb56f13d0ae13babe2a2c42799 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sat, 5 Aug 2023 22:40:09 +0100 Subject: [PATCH] Update for 0.51.14 release --- CHANGELOG.md | 6 ++ CommandLineTool/swiftformat | Bin 3819856 -> 3819856 bytes Sources/Formatter.swift | 2 +- Sources/Rules.swift | 12 +-- Sources/SwiftFormat.swift | 2 +- Sources/Tokenizer.swift | 37 +++------- SwiftFormat.podspec.json | 4 +- SwiftFormat.xcodeproj/project.pbxproj | 12 +-- Tests/RulesTests+Linebreaks.swift | 54 ++++++++++---- Tests/RulesTests+Redundancy.swift | 45 ------------ Tests/RulesTests+Spacing.swift | 48 ------------ Tests/TokenizerTests.swift | 101 ++------------------------ 12 files changed, 74 insertions(+), 249 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9844f2b7..8d0d12833 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## [0.51.14](https://github.com/nicklockwood/SwiftFormat/releases/tag/0.51.14) (2023-08-05) + +- Fixed regression in `unusedArguments` rule that caused used parameters to be marked unused +- Fixed some additional cases where regex literal was mistaken for `/` operator +- Vertical tab and form feed characters in source file no longer cause spurious errors + ## [0.51.13](https://github.com/nicklockwood/SwiftFormat/releases/tag/0.51.13) (2023-07-18) - Fixed bug where importing a type caused the `redundantSelf` rule to be silently disabled diff --git a/CommandLineTool/swiftformat b/CommandLineTool/swiftformat index 9834f51b9c431de3c08b2ccc2412b70e4a8d9f06..73be20cd3796e5199b8d739eb54e52a5d6f91c7f 100755 GIT binary patch delta 1435 zcmajdeLT~70KjqEdD^OF9-3^{@|ZC;wr0}VESn20m4^yP+Rj5urcfLzS%@B4Qb+Re zY93yjj^Fb1Dv~@jgiYeU7gp)ddr`~+wMT7Wj71Ly(>012!BRsyS_+ECOpUl>)K zQ;Pyt19|`&&<6|vL%;}F17M(>TH`b<=ut{zU@c$*U;!M2Qkqt46P{Rz2Tx-sPxa@^ z%$<_?70(g9v;-BAdfxt^Fe-9kKr%9DN+8_ejBZdih{V6_-77ol=&JuVh8 za-MOIoV>!$p4+`xej%u6nHQKK(ZXYJo1DosM+%irW9%V2Xd{Rj zo*8qiUvUgbSIXLp-Bf<_uaoOIUTuuO45^IikN%b)IjlsVx6YwhgSa-LbExuQD z-F($_Sy`7#H zqP~Oo&`Q?5dT#}ii23=LPu*vYtR2zVPmfJ88prEx4|HU9w<|IFL|(<}N9R8f+tm*P z@^ZbPoYbtQ{x22eq2TK3fSmZ(Eu*={!ij82Nh!mu!(?KG`-X&;$mcsB^jqdHqb=GWe7VMUJM7EJRA*Ws>y}#f9p_(XUp(WY zI#*80-$bWo2*35bmr9d{+QxHS2j1b_K7GWGsNqaIIi=T4&dk8pr{UkM)Y~fBm8h-d zqEm%kEY4kO(}%vE!B^SBTE%4wJg^QR0P6uWz#OmuECDOP8rT5XKxGOdrV*AYZjQ4B z>;MvA4>$m1fC4x|&2df%k6>wH8ixwdfQ&L5``!At zJE4&-M(A2X;Cz2sT*57BU%GK3(X2zgu&l0{$cgs#X|2Ge=0dpz~}NJcsxIcV}}zcEVgKm?bf*O7gNKy z>ESgYbSLi`w&@lToWDe@WDKW#Fw#TQ+K}@kG`lKBy_wfMk6HKksTbseSw)s!*}=lhM%qW!K}OeEyjn@!HsltUA$88*)=rg4GPX>euB zeuL7FBh$JwJ_m+_j@FQL$myx(zj{1v>pEQ0?@-CSpRN2WwpZY4{+V8IRji+aux_&2 zk#F<5uTQZ(>4dbiSU-1$+3PV|Klzpw8GA~-VZiy&yOFb_75(vqTF z#?qXb38q+@6KYM()V1=Nnl7Ctc|09^oJXtnd)E8);`8qN@;g-V3N{2ZpW&fvYH^8F z^Q{W;ecf?k?AUTk4G&`u3(i-?6{+H2fF7_O&<6|vIDh~Q0VBW|*Z`OSNVO^sH5v+Y zFDg}`08;=BU;s0~9IyZ^fsKHbx>RK?#(^F+U=3ga8vqB`s!;>@n{a}Ka6+fOMOqE- zoqTNm+Vq_d^gKNH+%S3@_hYqT)IgYQ>?xi=I2AS;fzKm8HR;d%2x|y)%B_lYz;_|s z+I+j;Woa5r)wpMPu`QudeIPxw0mbW;>{4*%)0~$?EX55op|U&Yl1xTjY2)jpSvp(gXMLj~t56;PJynj6|-VEZrX3n%CHiNE$d)ByzvI)I77Mtjm3K^?_>t&m+YyzIXPNZ%dr! zcyI>uUPj6&^C?wSf1AvO^uV0IBTn;&OfiR2W69(IeBI0pRT16e+6H&6Rju9L7-T<( z`SGIUo4u0($pdY6OrPG^yo&(~T6w1+YF&F&1r%JCGBsi0+NkT>PkYkcL3%jAFc>fR zr~ef_f7zl+->ICGIC`)e+wu%=-LpDZHq@u*7O12T`Y3%1acyEqXM(n8dBRq(9LuXh z=sd!N^Gv-`0yVn8Bt^CD5MT!ofK31qum?!MX21a;1CD@`y0+cfssqL^x+ib}D1a;A z22cSSKnED=djfaS6IfgkGjI#Q1XzFv;HhQ?vK>wnx?cLI;fz*KfpdTZ**<)oXtn38 zD1O*n=_5cB5$BU-S$%9@0w?;n+v8{0({A$lCen@ej!1@N@lD#Sb%3$spUCE48S1e< zb{v)fAFdFXa21F3^e*9|?qAc|%*JT%rFoD0b3JL^>(AAzW2V_+41ue&)yxi3I%gkT z3o$#P@_|dYD_?LHQBB7}S}_!@M*V(?z5KryV=^5-y`3sf-DT4?FlW|};rflTAAX0| z$`@`YJ#L~8f4=yO)qfXrBh%8T)F3{82lJ~P$2~$=M}v9Xy%GM5#H7y`=lfC|kx6&u zf2B&b6z|R^)Q++bg@?Tg=1$gyV4*C3{`UH{mKQ6fJi9*@Ns?ObwMWvMzeJXZ0LZMEQRudtgJFTnQ;s2?AMa3yYNORQTkR&%eI-!Czo7_3DO*?oIZoao_e;%Wa~CulI#o(DITlqj#TYo*>V5haB|^ zD>lw9j96YM&=9VOO$j3gpH_z08%ifHPi)Acj`ON?6JpFp+Qbv->m!x String { - readCharacters { !$0.isSpaceOrLinebreak } ?? "" + readCharacters { !$0.isSpace && !"\n\r".unicodeScalars.contains($0) } ?? "" } } @@ -742,18 +738,13 @@ private extension UnicodeScalarView { } mutating func parseLineBreak() -> Token? { - switch first { - case "\r": - removeFirst() + if read("\r") { if read("\n") { return .linebreak("\r\n", 0) } return .linebreak("\r", 0) - case "\n", "\u{000B}", "\u{000C}": - return .linebreak(String(removeFirst()), 0) - default: - return nil } + return read("\n") ? .linebreak("\n", 0) : nil } mutating func parseDelimiter() -> Token? { @@ -1476,10 +1467,8 @@ public func tokenize(_ source: String) -> [Token] { return } guard let prevNonSpaceIndex = index(of: .nonSpaceOrCommentOrLinebreak, before: i) else { - if string == "/" { - tokens[i] = .startOfScope("/") - } else if tokens.count > i + 1 { - tokens[i] = .operator(string, .prefix) + if tokens.count > i + 1 { + tokens[i] = string == "/" ? .startOfScope("/") : .operator(string, .prefix) } return } @@ -1549,8 +1538,11 @@ public func tokenize(_ source: String) -> [Token] { guard let nextNonSpaceToken = index(of: .nonSpaceOrCommentOrLinebreak, after: i).map({ tokens[$0] }) else { + if prevToken.isLvalue { + type = .postfix + break + } if token == .operator("/", .none), - prevToken.isSpaceOrLinebreak || prevNonSpaceToken.isOperator(ofType: .infix) || ( prevNonSpaceToken.isUnwrapOperator && prevNonSpaceIndex > 0 && @@ -1562,9 +1554,6 @@ public func tokenize(_ source: String) -> [Token] { ].contains(prevNonSpaceToken) { tokens[i] = .startOfScope("/") - } else if prevToken.isLvalue { - type = .postfix - break } return } @@ -1875,12 +1864,6 @@ public func tokenize(_ source: String) -> [Token] { token = tokens[count - 1] switch token { case .startOfScope("/"): - if let next = characters.first, next.isSpaceOrLinebreak { - // Misidentified as regex - token = .operator("/", .none) - tokens[count - 1] = token - return - } scopeIndexStack.append(count - 1) let start = characters processStringBody(regex: true, hashCount: 0) diff --git a/SwiftFormat.podspec.json b/SwiftFormat.podspec.json index 4c148c5fa..b64058c71 100644 --- a/SwiftFormat.podspec.json +++ b/SwiftFormat.podspec.json @@ -1,6 +1,6 @@ { "name": "SwiftFormat", - "version": "0.51.13", + "version": "0.51.14", "license": { "type": "MIT", "file": "LICENSE.md" @@ -10,7 +10,7 @@ "authors": "Nick Lockwood", "source": { "git": "https://github.com/nicklockwood/SwiftFormat.git", - "tag": "0.51.13" + "tag": "0.51.14" }, "default_subspecs": "Core", "subspecs": [ diff --git a/SwiftFormat.xcodeproj/project.pbxproj b/SwiftFormat.xcodeproj/project.pbxproj index 2b3e8c80b..f47602619 100644 --- a/SwiftFormat.xcodeproj/project.pbxproj +++ b/SwiftFormat.xcodeproj/project.pbxproj @@ -1111,7 +1111,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.51.13; + MARKETING_VERSION = 0.51.14; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = com.charcoaldesign.SwiftFormat; @@ -1144,7 +1144,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.51.13; + MARKETING_VERSION = 0.51.14; MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; PRODUCT_BUNDLE_IDENTIFIER = com.charcoaldesign.SwiftFormat; @@ -1251,7 +1251,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.51.13; + MARKETING_VERSION = 0.51.14; PRODUCT_BUNDLE_IDENTIFIER = "com.charcoaldesign.SwiftFormat-for-Xcode"; PRODUCT_NAME = "SwiftFormat for Xcode"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1282,7 +1282,7 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.51.13; + MARKETING_VERSION = 0.51.14; PRODUCT_BUNDLE_IDENTIFIER = "com.charcoaldesign.SwiftFormat-for-Xcode"; PRODUCT_NAME = "SwiftFormat for Xcode"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1310,7 +1310,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.51.13; + MARKETING_VERSION = 0.51.14; PRODUCT_BUNDLE_IDENTIFIER = "com.charcoaldesign.SwiftFormat-for-Xcode.SourceEditorExtension"; PRODUCT_NAME = SwiftFormat; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1340,7 +1340,7 @@ "@executable_path/../../../../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.14; - MARKETING_VERSION = 0.51.13; + MARKETING_VERSION = 0.51.14; PRODUCT_BUNDLE_IDENTIFIER = "com.charcoaldesign.SwiftFormat-for-Xcode.SourceEditorExtension"; PRODUCT_NAME = SwiftFormat; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/Tests/RulesTests+Linebreaks.swift b/Tests/RulesTests+Linebreaks.swift index 04a181de7..30783f194 100644 --- a/Tests/RulesTests+Linebreaks.swift +++ b/Tests/RulesTests+Linebreaks.swift @@ -10,30 +10,52 @@ import XCTest @testable import SwiftFormat class LinebreakTests: RulesTests { - // MARK: - linebreaks + // MARK: - trailingSpace - func testCarriageReturn() { - let input = "foo\rbar" + // truncateBlankLines = true + + func testTrailingSpace() { + let input = "foo \nbar" let output = "foo\nbar" - testFormatting(for: input, output, rule: FormatRules.linebreaks) + testFormatting(for: input, output, rule: FormatRules.trailingSpace) } - func testCarriageReturnLinefeed() { - let input = "foo\r\nbar" - let output = "foo\nbar" - testFormatting(for: input, output, rule: FormatRules.linebreaks) + func testTrailingSpaceAtEndOfFile() { + let input = "foo " + let output = "foo" + testFormatting(for: input, output, rule: FormatRules.trailingSpace) } - func testVerticalTab() { - let input = "foo\u{000B}bar" - let output = "foo\nbar" - testFormatting(for: input, output, rule: FormatRules.linebreaks) + func testTrailingSpaceInMultilineComments() { + let input = "/* foo \n bar */" + let output = "/* foo\n bar */" + testFormatting(for: input, output, rule: FormatRules.trailingSpace) } - func testFormfeed() { - let input = "foo\u{000C}bar" - let output = "foo\nbar" - testFormatting(for: input, output, rule: FormatRules.linebreaks) + func testTrailingSpaceInSingleLineComments() { + let input = "// foo \n// bar " + let output = "// foo\n// bar" + testFormatting(for: input, output, rule: FormatRules.trailingSpace) + } + + func testTruncateBlankLine() { + let input = "foo {\n // bar\n \n // baz\n}" + let output = "foo {\n // bar\n\n // baz\n}" + testFormatting(for: input, output, rule: FormatRules.trailingSpace) + } + + func testTrailingSpaceInArray() { + let input = "let foo = [\n 1,\n \n 2,\n]" + let output = "let foo = [\n 1,\n\n 2,\n]" + testFormatting(for: input, output, rule: FormatRules.trailingSpace, exclude: ["redundantSelf"]) + } + + // truncateBlankLines = false + + func testNoTruncateBlankLine() { + let input = "foo {\n // bar\n \n // baz\n}" + let options = FormatOptions(truncateBlankLines: false) + testFormatting(for: input, rule: FormatRules.trailingSpace, options: options) } // MARK: - consecutiveBlankLines diff --git a/Tests/RulesTests+Redundancy.swift b/Tests/RulesTests+Redundancy.swift index b4755a347..ea3c9b2c6 100644 --- a/Tests/RulesTests+Redundancy.swift +++ b/Tests/RulesTests+Redundancy.swift @@ -6482,34 +6482,6 @@ class RedundancyTests: RulesTests { testFormatting(for: input, output, rule: FormatRules.unusedArguments) } - func testClosureArgumentUsedInGuardNotRemoved() { - let input = """ - bar(for: quux) { _, _, foo in - guard - let baz = quux.baz, - foo.contains(where: { $0.baz == baz }) - else { - return - } - } - """ - testFormatting(for: input, rule: FormatRules.unusedArguments) - } - - func testClosureArgumentUsedInIfNotRemoved() { - let input = """ - foo = { reservations, _ in - if let reservations, eligibleToShow( - reservations, - accountService: accountService - ) { - coordinator.startFlow() - } - } - """ - testFormatting(for: input, rule: FormatRules.unusedArguments) - } - // init func testParameterUsedInInit() { @@ -7095,23 +7067,6 @@ class RedundancyTests: RulesTests { testFormatting(for: input, output, rule: FormatRules.unusedArguments) } - func testFunctionArgumentUsedInGuardNotRemoved() { - let input = """ - func scrollViewDidEndDecelerating(_ visibleDayRange: DayRange) { - guard - store.state.request.isIdle, - let nextDayToLoad = store.state.request.nextCursor?.lowerBound, - visibleDayRange.upperBound.distance(to: nextDayToLoad) < 30 - else { - return - } - - store.handle(.loadNext) - } - """ - testFormatting(for: input, rule: FormatRules.unusedArguments) - } - // functions (closure-only) func testNoMarkFunctionArgument() { diff --git a/Tests/RulesTests+Spacing.swift b/Tests/RulesTests+Spacing.swift index 25648992f..604a09a4b 100644 --- a/Tests/RulesTests+Spacing.swift +++ b/Tests/RulesTests+Spacing.swift @@ -1354,54 +1354,6 @@ class SpacingTests: RulesTests { testFormatting(for: input, rule: FormatRules.consecutiveSpaces) } - // MARK: - trailingSpace - - // truncateBlankLines = true - - func testTrailingSpace() { - let input = "foo \nbar" - let output = "foo\nbar" - testFormatting(for: input, output, rule: FormatRules.trailingSpace) - } - - func testTrailingSpaceAtEndOfFile() { - let input = "foo " - let output = "foo" - testFormatting(for: input, output, rule: FormatRules.trailingSpace) - } - - func testTrailingSpaceInMultilineComments() { - let input = "/* foo \n bar */" - let output = "/* foo\n bar */" - testFormatting(for: input, output, rule: FormatRules.trailingSpace) - } - - func testTrailingSpaceInSingleLineComments() { - let input = "// foo \n// bar " - let output = "// foo\n// bar" - testFormatting(for: input, output, rule: FormatRules.trailingSpace) - } - - func testTruncateBlankLine() { - let input = "foo {\n // bar\n \n // baz\n}" - let output = "foo {\n // bar\n\n // baz\n}" - testFormatting(for: input, output, rule: FormatRules.trailingSpace) - } - - func testTrailingSpaceInArray() { - let input = "let foo = [\n 1,\n \n 2,\n]" - let output = "let foo = [\n 1,\n\n 2,\n]" - testFormatting(for: input, output, rule: FormatRules.trailingSpace, exclude: ["redundantSelf"]) - } - - // truncateBlankLines = false - - func testNoTruncateBlankLine() { - let input = "foo {\n // bar\n \n // baz\n}" - let options = FormatOptions(truncateBlankLines: false) - testFormatting(for: input, rule: FormatRules.trailingSpace, options: options) - } - // MARK: - emptyBraces func testLinebreaksRemovedInsideBraces() { diff --git a/Tests/TokenizerTests.swift b/Tests/TokenizerTests.swift index 88e2c11c4..bf35e7ccb 100644 --- a/Tests/TokenizerTests.swift +++ b/Tests/TokenizerTests.swift @@ -219,32 +219,6 @@ class TokenizerTests: XCTestCase { XCTAssertEqual(tokenize(input), output) } - // MARK: Linebreaks - - func testCarriageReturnLinefeed() { - let input = "\r\n" - let output: [Token] = [ - .linebreak("\r\n", 1), - ] - XCTAssertEqual(tokenize(input), output) - } - - func testVerticalTab() { - let input = "\u{000B}" - let output: [Token] = [ - .linebreak("\u{000B}", 1), - ] - XCTAssertEqual(tokenize(input), output) - } - - func testFormfeed() { - let input = "\u{000C}" - let output: [Token] = [ - .linebreak("\u{000C}", 1), - ] - XCTAssertEqual(tokenize(input), output) - } - // MARK: Strings func testEmptyString() { @@ -803,8 +777,8 @@ class TokenizerTests: XCTestCase { XCTAssertEqual(tokenize(input), output) } - func testSingleLineRegexLiteralWithEscapedClosingParen() { - let input = "let regex = /\\)/" + func testSingleLineRegexLiteralPrecededByTry() { + let input = "let regex = try /foo/" let output: [Token] = [ .keyword("let"), .space(" "), @@ -812,52 +786,8 @@ class TokenizerTests: XCTestCase { .space(" "), .operator("=", .infix), .space(" "), - .startOfScope("/"), - .stringBody("\\)"), - .endOfScope("/"), - ] - XCTAssertEqual(tokenize(input), output) - } - - func testSingleLineRegexLiteralWithEscapedClosingParenAtStartOfFile() { - let input = "/\\)/" - let output: [Token] = [ - .startOfScope("/"), - .stringBody("\\)"), - .endOfScope("/"), - ] - XCTAssertEqual(tokenize(input), output) - } - - func testSingleLineRegexLiteralWithEscapedClosingParenAtStartOfLine() { - let input = """ - let a = b - /\\)/ - """ - let output: [Token] = [ - .keyword("let"), - .space(" "), - .identifier("a"), - .space(" "), - .operator("=", .infix), - .space(" "), - .identifier("b"), - .linebreak("\n", 1), - .startOfScope("/"), - .stringBody("\\)"), - .endOfScope("/"), - ] - XCTAssertEqual(tokenize(input), output) - } - - func testSingleLineRegexLiteralPrecededByTry() { - let input = "let regex=try/foo/" - let output: [Token] = [ - .keyword("let"), - .space(" "), - .identifier("regex"), - .operator("=", .infix), .keyword("try"), + .space(" "), .startOfScope("/"), .stringBody("foo"), .endOfScope("/"), @@ -866,14 +796,17 @@ class TokenizerTests: XCTestCase { } func testSingleLineRegexLiteralPrecededByOptionalTry() { - let input = "let regex=try?/foo/" + let input = "let regex = try? /foo/" let output: [Token] = [ .keyword("let"), .space(" "), .identifier("regex"), + .space(" "), .operator("=", .infix), + .space(" "), .keyword("try"), .operator("?", .postfix), + .space(" "), .startOfScope("/"), .stringBody("foo"), .endOfScope("/"), @@ -972,26 +905,6 @@ class TokenizerTests: XCTestCase { XCTAssertEqual(tokenize(input), output) } - func testDivisionFollowedByCommentNotMistakenForRegexLiteral() { - let input = "foo = bar / 100 // baz" - let output: [Token] = [ - .identifier("foo"), - .space(" "), - .operator("=", .infix), - .space(" "), - .identifier("bar"), - .space(" "), - .operator("/", .infix), - .space(" "), - .number("100", .integer), - .space(" "), - .startOfScope("//"), - .space(" "), - .commentBody("baz"), - ] - XCTAssertEqual(tokenize(input), output) - } - func testPrefixPostfixSlashOperatorNotPermitted() { let input = "let x = /0; let y = 1/" let output: [Token] = [