Compare commits
1 Commits
main
...
marcelo/tu
Author | SHA1 | Date |
---|---|---|
![]() |
3a0973a7d3 |
|
@ -30,6 +30,12 @@
|
||||||
`return <expression>` in a function that returns `Void`.
|
`return <expression>` in a function that returns `Void`.
|
||||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
|
||||||
|
* Add back `tuple_pattern` rule to warn against assigning variables
|
||||||
|
through a tuple pattern when the left side of the assignment
|
||||||
|
contains labels.
|
||||||
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
[#3466](https://github.com/realm/SwiftLint/issues/3466)
|
||||||
|
|
||||||
* Don't skip autocorrect on files that have parser warnings. Only files with
|
* Don't skip autocorrect on files that have parser warnings. Only files with
|
||||||
errors reported by the Swift parser will be skipped.
|
errors reported by the Swift parser will be skipped.
|
||||||
[Marcelo Fabri](https://github.com/marcelofabri)
|
[Marcelo Fabri](https://github.com/marcelofabri)
|
||||||
|
|
|
@ -183,6 +183,7 @@ public let primaryRuleList = RuleList(rules: [
|
||||||
TrailingNewlineRule.self,
|
TrailingNewlineRule.self,
|
||||||
TrailingSemicolonRule.self,
|
TrailingSemicolonRule.self,
|
||||||
TrailingWhitespaceRule.self,
|
TrailingWhitespaceRule.self,
|
||||||
|
TuplePatternRule.self,
|
||||||
TypeBodyLengthRule.self,
|
TypeBodyLengthRule.self,
|
||||||
TypeContentsOrderRule.self,
|
TypeContentsOrderRule.self,
|
||||||
TypeNameRule.self,
|
TypeNameRule.self,
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import SourceKittenFramework
|
||||||
|
import SwiftSyntax
|
||||||
|
|
||||||
|
public struct TuplePatternRule: ConfigurationProviderRule, AutomaticTestableRule {
|
||||||
|
public var configuration = SeverityConfiguration(.warning)
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public static let description = RuleDescription(
|
||||||
|
identifier: "tuple_pattern",
|
||||||
|
name: "Tuple Pattern",
|
||||||
|
description: "Assigning variables through a tuple pattern is only permitted if the left-hand side of the " +
|
||||||
|
"assignment is unlabeled.",
|
||||||
|
kind: .idiomatic,
|
||||||
|
minSwiftVersion: .fiveDotOne,
|
||||||
|
nonTriggeringExamples: [
|
||||||
|
Example("let (a, b) = (y: 4, x: 5.0)"),
|
||||||
|
Example("let (a, b) = (4, 5.0)"),
|
||||||
|
Example("let (a, b) = (a: 4, b: 5.0)"),
|
||||||
|
Example("let (a, b) = tuple")
|
||||||
|
],
|
||||||
|
triggeringExamples: [
|
||||||
|
Example("let ↓(x: a, y: b) = (y: 4, x: 5.0)"),
|
||||||
|
Example("let ↓(x: Int, y: Double) = (y: 4, x: 5.0)"),
|
||||||
|
Example("let ↓(x: Int, y: Double) = (y: 4, x: 5.0)")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
public func validate(file: SwiftLintFile) -> [StyleViolation] {
|
||||||
|
guard let syntaxTree = file.syntaxTree else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
let visitor = TuplePatternVisitor()
|
||||||
|
visitor.walk(syntaxTree)
|
||||||
|
return visitor.violations(for: self, in: file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TuplePatternVisitor: SyntaxVisitor {
|
||||||
|
private var positions = [AbsolutePosition]()
|
||||||
|
|
||||||
|
override func visitPost(_ node: PatternBindingSyntax) {
|
||||||
|
guard let tuplePattern = node.pattern.as(TuplePatternSyntax.self),
|
||||||
|
case let leftSideLabels = tuplePattern.labels,
|
||||||
|
!leftSideLabels.compactMap({ $0 }).isEmpty,
|
||||||
|
let rightSideLabels = node.initializer?.tupleElementList?.labels,
|
||||||
|
leftSideLabels != rightSideLabels else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
positions.append(node.positionAfterSkippingLeadingTrivia)
|
||||||
|
}
|
||||||
|
|
||||||
|
func violations(for rule: TuplePatternRule, in file: SwiftLintFile) -> [StyleViolation] {
|
||||||
|
return positions.map { position in
|
||||||
|
StyleViolation(ruleDescription: type(of: rule).description,
|
||||||
|
severity: rule.configuration.severity,
|
||||||
|
location: Location(file: file, byteOffset: ByteCount(position.utf8Offset)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TuplePatternSyntax {
|
||||||
|
var labels: [String?] {
|
||||||
|
return elements.map { element in
|
||||||
|
element.labelName?.withoutTrivia().text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension InitializerClauseSyntax {
|
||||||
|
var tupleElementList: TupleExprElementListSyntax? {
|
||||||
|
if let expr = value.as(TupleExprSyntax.self) {
|
||||||
|
return expr.elementList
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension TupleExprElementListSyntax {
|
||||||
|
var labels: [String?] {
|
||||||
|
return map { element in
|
||||||
|
element.label?.withoutTrivia().text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -785,6 +785,12 @@ class TrailingSemicolonRuleTests: XCTestCase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class TuplePatternRuleTests: XCTestCase {
|
||||||
|
func testWithDefaultConfiguration() {
|
||||||
|
verifyRule(TuplePatternRule.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TypeBodyLengthRuleTests: XCTestCase {
|
class TypeBodyLengthRuleTests: XCTestCase {
|
||||||
func testWithDefaultConfiguration() {
|
func testWithDefaultConfiguration() {
|
||||||
verifyRule(TypeBodyLengthRule.description)
|
verifyRule(TypeBodyLengthRule.description)
|
||||||
|
|
Loading…
Reference in New Issue