Skip to content

Commit

Permalink
Rework algorithm used in function_default_parameter_at_end rule (#5858
Browse files Browse the repository at this point in the history
)
  • Loading branch information
SimplyDanny authored Nov 16, 2024
1 parent cafed07 commit 9e61e81
Showing 1 changed file with 42 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,88 +42,73 @@ struct FunctionDefaultParameterAtEndRule: OptInRule {
Example("""
func expect<T>(file: String = #file, _ expression: @autoclosure () -> (() throws -> T)) -> Expectation<T> {}
""", excludeFromDocumentation: true),
Example("func foo(bar: Int, baz: Int = 0, z: () -> Void) {}"),
Example("func foo(bar: Int, baz: Int = 0, z: () -> Void, x: Int = 0) {}"),
],
triggeringExamples: [
Example("↓func foo(bar: Int = 0, baz: String) {}"),
Example("private ↓func foo(bar: Int = 0, baz: String) {}"),
Example("public ↓init?(for date: Date = Date(), coordinate: CLLocationCoordinate2D) {}"),
Example("func foo(↓bar: Int = 0, baz: String) {}"),
Example("private func foo(↓bar: Int = 0, baz: String) {}"),
Example("public init?(↓for date: Date = Date(), coordinate: CLLocationCoordinate2D) {}"),
Example("func foo(bar: Int, ↓baz: Int = 0, z: () -> Void, x: Int) {}"),
]
)
}

private extension FunctionDefaultParameterAtEndRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: FunctionDeclSyntax) {
guard !node.modifiers.contains(keyword: .override), node.signature.containsViolation else {
return
if !node.modifiers.contains(keyword: .override) {
collectViolations(for: node.signature)
}

violations.append(node.funcKeyword.positionAfterSkippingLeadingTrivia)
}

override func visitPost(_ node: InitializerDeclSyntax) {
guard !node.modifiers.contains(keyword: .override), node.signature.containsViolation else {
return
if !node.modifiers.contains(keyword: .override) {
collectViolations(for: node.signature)
}

violations.append(node.initKeyword.positionAfterSkippingLeadingTrivia)
}
}
}

private extension FunctionSignatureSyntax {
var containsViolation: Bool {
let params = parameterClause.parameters.filter { param in
!param.isClosure
}

guard params.isNotEmpty else {
return false
}

let defaultParams = params.filter { param in
param.defaultValue != nil
}
guard defaultParams.isNotEmpty else {
return false
}

let lastParameters = params.suffix(defaultParams.count)
let lastParametersWithDefaultValue = lastParameters.filter { param in
param.defaultValue != nil
private func collectViolations(for signature: FunctionSignatureSyntax) {
if signature.parameterClause.parameters.count < 2 {
return
}
var previousWithDefault = true
for param in signature.parameterClause.parameters.reversed() {
if param.isClosure {
continue
}
let hasDefault = param.defaultValue != nil
if !previousWithDefault, hasDefault {
violations.append(param.positionAfterSkippingLeadingTrivia)
}
previousWithDefault = hasDefault
}
}

return lastParameters.count != lastParametersWithDefaultValue.count
}
}

private extension FunctionParameterSyntax {
var isClosure: Bool {
if isEscaping || type.is(FunctionTypeSyntax.self) {
return true
}

if let optionalType = type.as(OptionalTypeSyntax.self),
let tuple = optionalType.wrappedType.as(TupleTypeSyntax.self) {
return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil
}

if let tuple = type.as(TupleTypeSyntax.self) {
return tuple.elements.onlyElement?.type.as(FunctionTypeSyntax.self) != nil
}

if let attrType = type.as(AttributedTypeSyntax.self) {
return attrType.baseType.is(FunctionTypeSyntax.self)
}

return false
isEscaping || type.isFunctionType
}

var isEscaping: Bool {
guard let attrType = type.as(AttributedTypeSyntax.self) else {
return false
}
type.as(AttributedTypeSyntax.self)?.attributes.contains(attributeNamed: "escaping") == true
}
}

return attrType.attributes.contains(attributeNamed: "escaping")
private extension TypeSyntax {
var isFunctionType: Bool {
if `is`(FunctionTypeSyntax.self) {
true
} else if let optionalType = `as`(OptionalTypeSyntax.self) {
optionalType.wrappedType.isFunctionType
} else if let tupleType = `as`(TupleTypeSyntax.self) {
tupleType.elements.onlyElement?.type.isFunctionType == true
} else if let attributedType = `as`(AttributedTypeSyntax.self) {
attributedType.baseType.isFunctionType
} else {
false
}
}
}

0 comments on commit 9e61e81

Please sign in to comment.