From 5ef8bba3457aab60741fc96fc28a9574e704ddac Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Sun, 1 May 2022 21:54:34 +0100 Subject: [PATCH] Fix redundantInit removing required init in some cases --- README.md | 2 +- Sources/Rules.swift | 16 +++++++++++++++- Tests/RulesTests+Redundancy.swift | 27 +++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 94f41646..83c989a7 100644 --- a/README.md +++ b/README.md @@ -793,7 +793,7 @@ Known issues * When using the `--self remove` option, the `redundantSelf` rule will remove references to `self` in autoclosure arguments, which may change the meaning of the code, or cause it not to compile. To work around this issue, use the `--selfrequired` option to provide a comma-delimited list of methods to be excluded from the rule. The `expect()` function from the popular [Nimble](https://github.com/Quick/Nimble) unit testing framework is already excluded by default. If you are using the `--self insert` option then this is not an issue. -* If you assign `SomeClass.self` to a variable and then instantiate an instance of the class using that variable, Swift requires that you use an explicit `.init()`, however, the `redundantInit` rule is not currently capable of detecting this situation and will remove the `.init`. To work around this issue, use the `// swiftformat:disable:next redundantInit` comment directive to disable the rule for any affected lines of code (or just disable the `redundantInit` rule completely). +* If you assign `SomeClass.self` to a variable and then instantiate an instance of the class using that variable, Swift requires that you use an explicit `.init()`, however, the `redundantInit` rule is not currently capable of detecting this situation in all cases, and may remove the `.init`. To work around this issue, use the `// swiftformat:disable:next redundantInit` comment directive to disable the rule for any affected lines of code (or just disable the `redundantInit` rule completely). * The `--self insert` option can only recognize locally declared member variables, not ones inherited from superclasses or extensions in other files, so it cannot insert missing `self` references for those. Note that the reverse is not true: `--self remove` should remove *all* redundant `self` references. diff --git a/Sources/Rules.swift b/Sources/Rules.swift index 6552d8d6..cdf40594 100644 --- a/Sources/Rules.swift +++ b/Sources/Rules.swift @@ -4632,7 +4632,21 @@ public struct _FormatRules { firstChar != "$", String(firstChar).uppercased() == String(firstChar) else { return } - + var j = dotIndex + while let prevIndex = formatter.index( + of: prevToken, before: j + ) ?? formatter.index( + of: .startOfScope, before: j + ) { + j = prevIndex + if prevToken == formatter.tokens[prevIndex], + let prevPrevToken = formatter.last( + .nonSpaceOrCommentOrLinebreak, before: prevIndex + ), [.keyword("let"), .keyword("var")].contains(prevPrevToken) + { + return + } + } formatter.removeTokens(in: i + 1 ..< openParenIndex) formatter.removeTokens(in: dotIndex ... i) } diff --git a/Tests/RulesTests+Redundancy.swift b/Tests/RulesTests+Redundancy.swift index 3bcdba7a..6c8c0374 100644 --- a/Tests/RulesTests+Redundancy.swift +++ b/Tests/RulesTests+Redundancy.swift @@ -762,6 +762,33 @@ class RedundancyTests: RulesTests { testFormatting(for: input, rule: FormatRules.redundantInit) } + func testNoRemoveInitForLowercaseType() { + let input = """ + let foo = bar.init() + """ + testFormatting(for: input, rule: FormatRules.redundantInit) + } + + func testNoRemoveInitForLocalLetType() { + let input = """ + let Foo = Foo.self + let foo = Foo.init() + """ + testFormatting(for: input, rule: FormatRules.redundantInit) + } + + func testNoRemoveInitForLocalLetType2() { + let input = """ + let Foo = Foo.self + if x { + return Foo.init(x) + } else { + return Foo.init(y) + } + """ + testFormatting(for: input, rule: FormatRules.redundantInit) + } + // MARK: - redundantLetError func testCatchLetError() {