Add new option to `attributes` rule to influence it for attributes with arguments (#4855)
This commit is contained in:
parent
f0138ea1df
commit
7756793356
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -2,7 +2,14 @@
|
||||||
|
|
||||||
#### Breaking
|
#### Breaking
|
||||||
|
|
||||||
* None.
|
* The `attributes` rule now expects attributes with arguments to be placed
|
||||||
|
on their own line above the declaration they are supposed to influence.
|
||||||
|
This applies to attributes with any kinds of arguments including single
|
||||||
|
key path arguments which were previously handled in a different way. This
|
||||||
|
behavior can be turned off by setting `attributes_with_arguments_always_on_line_above`
|
||||||
|
to `false.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4843](https://github.com/realm/SwiftLint/issues/4843)
|
||||||
|
|
||||||
#### Experimental
|
#### Experimental
|
||||||
|
|
||||||
|
@ -59,6 +66,14 @@
|
||||||
[whiteio](https://github.com/whiteio)
|
[whiteio](https://github.com/whiteio)
|
||||||
[#4923](https://github.com/realm/SwiftLint/issues/4923)
|
[#4923](https://github.com/realm/SwiftLint/issues/4923)
|
||||||
|
|
||||||
|
* The `attributes` rule received a new boolean option
|
||||||
|
`attributes_with_arguments_always_on_line_above` which is `true` by default.
|
||||||
|
Setting it to `false` ensures that attributes with arguments like
|
||||||
|
`@Persisted(primaryKey: true)` don't violate the rule if they are on the same
|
||||||
|
line with the variable declaration.
|
||||||
|
[SimplyDanny](https://github.com/SimplyDanny)
|
||||||
|
[#4843](https://github.com/realm/SwiftLint/issues/4843)
|
||||||
|
|
||||||
#### Bug Fixes
|
#### Bug Fixes
|
||||||
|
|
||||||
* Fix `lower_acl_than_parent` rule rewriter by preserving leading whitespace.
|
* Fix `lower_acl_than_parent` rule rewriter by preserving leading whitespace.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
struct AttributesConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
struct AttributesConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||||
var severityConfiguration = SeverityConfiguration(.warning)
|
var severityConfiguration = SeverityConfiguration(.warning)
|
||||||
|
private(set) var attributesWithArgumentsAlwaysOnNewLine = true
|
||||||
private(set) var alwaysOnSameLine = Set<String>()
|
private(set) var alwaysOnSameLine = Set<String>()
|
||||||
private(set) var alwaysOnNewLine = Set<String>()
|
private(set) var alwaysOnNewLine = Set<String>()
|
||||||
|
|
||||||
|
@ -20,6 +21,11 @@ struct AttributesConfiguration: SeverityBasedRuleConfiguration, Equatable {
|
||||||
throw ConfigurationError.unknownConfiguration
|
throw ConfigurationError.unknownConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let attributesWithArgumentsAlwaysOnNewLine
|
||||||
|
= configuration["attributes_with_arguments_always_on_line_above"] as? Bool {
|
||||||
|
self.attributesWithArgumentsAlwaysOnNewLine = attributesWithArgumentsAlwaysOnNewLine
|
||||||
|
}
|
||||||
|
|
||||||
if let alwaysOnSameLine = configuration["always_on_same_line"] as? [String] {
|
if let alwaysOnSameLine = configuration["always_on_same_line"] as? [String] {
|
||||||
self.alwaysOnSameLine = Set(alwaysOnSameLine)
|
self.alwaysOnSameLine = Set(alwaysOnSameLine)
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ private extension AttributeListSyntax {
|
||||||
return (attribute, .sameLineAsDeclaration)
|
return (attribute, .sameLineAsDeclaration)
|
||||||
} else if configuration.alwaysOnNewLine.contains(atPrefixedName) {
|
} else if configuration.alwaysOnNewLine.contains(atPrefixedName) {
|
||||||
return (attribute, .dedicatedLine)
|
return (attribute, .dedicatedLine)
|
||||||
} else if attribute.argument != nil {
|
} else if attribute.argument != nil, configuration.attributesWithArgumentsAlwaysOnNewLine {
|
||||||
return (attribute, .dedicatedLine)
|
return (attribute, .dedicatedLine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,20 +165,9 @@ private extension AttributeListSyntax {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasAttributeWithKeypathArgument: Bool {
|
|
||||||
contains { element in
|
|
||||||
switch element {
|
|
||||||
case .attribute(let attribute):
|
|
||||||
return attribute.hasKeypathArgument
|
|
||||||
case .ifConfigDecl:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// swiftlint:disable:next cyclomatic_complexity
|
// swiftlint:disable:next cyclomatic_complexity
|
||||||
func makeHelper(locationConverter: SourceLocationConverter) -> RuleHelper? {
|
func makeHelper(locationConverter: SourceLocationConverter) -> RuleHelper? {
|
||||||
guard let parent, !hasAttributeWithKeypathArgument else {
|
guard let parent else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,9 +215,3 @@ private extension AttributeListSyntax {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension AttributeSyntax {
|
|
||||||
var hasKeypathArgument: Bool {
|
|
||||||
argument?.as(TupleExprElementListSyntax.self)?.first?.expression.is(KeyPathExprSyntax.self) == true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -82,15 +82,15 @@ internal struct AttributesRuleExamples {
|
||||||
final class AppDelegate: NSAppDelegate {}
|
final class AppDelegate: NSAppDelegate {}
|
||||||
"""),
|
"""),
|
||||||
Example(#"""
|
Example(#"""
|
||||||
|
@_spi(Private) import SomeFramework
|
||||||
|
|
||||||
|
@_spi(Private)
|
||||||
final class MyView: View {
|
final class MyView: View {
|
||||||
@SwiftUI.Environment(\.colorScheme) var colorScheme: ColorScheme
|
@SwiftUI.Environment(\.colorScheme) var first: ColorScheme
|
||||||
|
@Environment(\.colorScheme) var second: ColorScheme
|
||||||
|
@Persisted(primaryKey: true) var id: Int
|
||||||
}
|
}
|
||||||
"""#),
|
"""#, configuration: ["attributes_with_arguments_always_on_line_above": false], excludeFromDocumentation: true)
|
||||||
Example(#"""
|
|
||||||
final class MyView: View {
|
|
||||||
@Environment(\.colorScheme) var colorScheme: ColorScheme
|
|
||||||
}
|
|
||||||
"""#)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
static let triggeringExamples = [
|
static let triggeringExamples = [
|
||||||
|
@ -124,6 +124,16 @@ internal struct AttributesRuleExamples {
|
||||||
Example("@GKInspectable\n ↓var maxSpeed: Float"),
|
Example("@GKInspectable\n ↓var maxSpeed: Float"),
|
||||||
Example("@discardableResult ↓func a() -> Int"),
|
Example("@discardableResult ↓func a() -> Int"),
|
||||||
Example("@objc\n @discardableResult ↓func a() -> Int"),
|
Example("@objc\n @discardableResult ↓func a() -> Int"),
|
||||||
Example("@objc\n\n @discardableResult\n ↓func a() -> Int")
|
Example("@objc\n\n @discardableResult\n ↓func a() -> Int"),
|
||||||
|
Example(#"""
|
||||||
|
struct S: View {
|
||||||
|
@Environment(\.colorScheme) ↓var first: ColorScheme
|
||||||
|
@Persisted var id: Int
|
||||||
|
@FetchRequest(
|
||||||
|
animation: nil
|
||||||
|
)
|
||||||
|
var entities: FetchedResults
|
||||||
|
}
|
||||||
|
"""#, excludeFromDocumentation: true)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue