From fb89ab2fb5aa17a80cfd1f0d5ce312219f45e9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danny=20M=C3=B6sch?= Date: Sun, 12 Mar 2023 13:50:58 +0100 Subject: [PATCH] Stop triggering `unused_capture_list` on variables referenced in optional bindings --- CHANGELOG.md | 5 +++ .../Rules/Lint/UnusedCaptureListRule.swift | 34 +++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 484640b8c..5ea92a675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -147,6 +147,11 @@ [SimplyDanny](https://github.com/SimplyDanny) [#4548](https://github.com/realm/SwiftLint/issues/4548) +* Stop triggering `unused_capture_list` on captured variable that is only + referenced by a shorthand optional binding (`if let capturedVar { ... }`). + [SimplyDanny](https://github.com/SimplyDanny) + [#4804](https://github.com/realm/SwiftLint/issues/4804) + * Ensure that negative literals in initializers do not trigger `no_magic_numbers` rule. [SimplyDanny](https://github.com/SimplyDanny) diff --git a/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift b/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift index 335528756..213732323 100644 --- a/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift +++ b/Source/SwiftLintFramework/Rules/Lint/UnusedCaptureListRule.swift @@ -91,6 +91,14 @@ struct UnusedCaptureListRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInR rx.onViewDidAppear.subscribe(onNext: { [unowned self] in doSomething() }).disposed(by: disposeBag) + """), + Example(""" + let closure = { [weak self] in + guard let self else { + return + } + someInstanceFunction() + } """) ], triggeringExamples: [ @@ -128,7 +136,16 @@ struct UnusedCaptureListRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInR } } """), - Example("{ [↓foo] in _ }()") + Example("{ [↓foo] in _ }()"), + Example(""" + let closure = { [↓weak a] in + // The new `a` immediatly shadows the captured `a` which thus isn't needed. + guard let a = getOptionalValue() else { + return + } + someInstanceFunction() + } + """) ] ) @@ -199,7 +216,20 @@ private final class IdentifierReferenceVisitor: SyntaxVisitor { } override func visitPost(_ node: IdentifierExprSyntax) { - let name = node.identifier.text + collectReference(by: node.identifier) + } + + override func visitPost(_ node: IdentifierPatternSyntax) { + // Optional bindings without an initializer like `if let self { ... }` reference the captured `self` + // implicitly. This is handled in here. If we have `if let self = self { ... }` the initializer `self` + // is handled in the `IdentifierExprSyntax` case above. + if let binding = node.parent?.as(OptionalBindingConditionSyntax.self), binding.initializer == nil { + collectReference(by: node.identifier) + } + } + + private func collectReference(by token: TokenSyntax) { + let name = token.text if identifiersToSearch.contains(name) { foundIdentifiers.insert(name) }