diff --git a/CHANGELOG.md b/CHANGELOG.md index 8778f7025b..3fbb7c1e25 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ ### Bug Fixes +* Make `unowned_variable_capture` ignore `unowned(unsafe)` captures by + default and add an `include_unsafe` option for projects that still want them + reported. + [Yurii Bakurov](https://github.com/Yurii201811) + [#6817](https://github.com/realm/SwiftLint/issues/6817) + * Fix baseline writing to store file locations as paths relative to the current working directory, restoring baseline portability and avoiding absolute `file://` paths in generated baseline files. [SimplyDanny](https://github.com/SimplyDanny) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift index 3b48ee16cd..bbbbd008cb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnownedVariableCaptureRule.swift @@ -3,7 +3,7 @@ import SwiftSyntax @SwiftSyntaxRule(optIn: true) struct UnownedVariableCaptureRule: Rule { - var configuration = SeverityConfiguration(.warning) + var configuration = UnownedVariableCaptureConfiguration() static let description = RuleDescription( identifier: "unowned_variable_capture", @@ -15,6 +15,7 @@ struct UnownedVariableCaptureRule: Rule { "foo { [weak self] param in _ }", "foo { [weak bar] in _ }", "foo { [weak bar] param in _ }", + "foo { [unowned(unsafe) self] in _ }", "foo { bar in _ }", "foo { $0 }", """ @@ -30,7 +31,10 @@ struct UnownedVariableCaptureRule: Rule { triggeringExamples: #examples([ "foo { [↓unowned self] in _ }", "foo { [↓unowned bar] in _ }", + "foo { [↓unowned(safe) self] in _ }", "foo { [bar, ↓unowned self] in _ }", + "foo { [↓unowned(unsafe) self] in _ }" + .asExample(configuration: ["include_unsafe": true]), ]) ) } @@ -38,7 +42,13 @@ struct UnownedVariableCaptureRule: Rule { private extension UnownedVariableCaptureRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: TokenSyntax) { - if case .keyword(.unowned) = node.tokenKind, node.parent?.is(ClosureCaptureSpecifierSyntax.self) == true { + guard case .keyword(.unowned) = node.tokenKind, + let specifier = node.parent?.as(ClosureCaptureSpecifierSyntax.self) else { + return + } + + let isUnsafe = specifier.tokens(viewMode: .sourceAccurate).contains { $0.text == "unsafe" } + if !isUnsafe || configuration.includeUnsafe { violations.append(node.positionAfterSkippingLeadingTrivia) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnownedVariableCaptureConfiguration.swift b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnownedVariableCaptureConfiguration.swift new file mode 100644 index 0000000000..8a5444ea20 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/RuleConfigurations/UnownedVariableCaptureConfiguration.swift @@ -0,0 +1,9 @@ +import SwiftLintCore + +@AutoConfigParser +struct UnownedVariableCaptureConfiguration: SeverityBasedRuleConfiguration { + @ConfigurationElement(key: "severity") + private(set) var severityConfiguration = SeverityConfiguration(.warning) + @ConfigurationElement(key: "include_unsafe") + private(set) var includeUnsafe = false +} diff --git a/Tests/IntegrationTests/Resources/default_rule_configurations.yml b/Tests/IntegrationTests/Resources/default_rule_configurations.yml index 59ae9bc2a2..ede8fb479d 100644 --- a/Tests/IntegrationTests/Resources/default_rule_configurations.yml +++ b/Tests/IntegrationTests/Resources/default_rule_configurations.yml @@ -1335,6 +1335,7 @@ unneeded_throws_rethrows: correctable: true unowned_variable_capture: severity: warning + include_unsafe: false meta: opt-in: true correctable: false