Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
* Add new `unneeded_escaping` rule that detects closure parameters marked with
`@escaping` that are never stored or captured escapingly.
[SimplyDanny](https://github.com/SimplyDanny)

* Add `multiline_call_arguments` opt-in rule to enforce consistent multiline
formatting for function and method call arguments.
[GandaLF2006](https://github.com/GandaLF2006)

### Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public let builtInRules: [any Rule.Type] = [
ModifierOrderRule.self,
MultilineArgumentsBracketsRule.self,
MultilineArgumentsRule.self,
MultilineCallArgumentsRule.self,
MultilineFunctionChainsRule.self,
MultilineLiteralBracketsRule.self,
MultilineParametersBracketsRule.self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,7 @@ private func wrapExample(_ type: String,
\(type) Foo {
\(test)
}
""", file: file, line: line)
""",
file: file,
line: line)
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ struct ImplicitlyUnwrappedOptionalRule: Rule {
@IBOutlet
weak var bar: SomeObject!
}
""", configuration: ["mode": "all_except_iboutlets"], excludeFromDocumentation: true),
""",
configuration: ["mode": "all_except_iboutlets"],
excludeFromDocumentation: true
),
],
triggeringExamples: [
Example("let label: ↓UILabel!"),
Expand Down
42 changes: 30 additions & 12 deletions Source/SwiftLintBuiltInRules/Rules/Idiomatic/TypeNameRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,21 @@ struct TypeNameRule: Rule {
private extension TypeNameRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: StructDeclSyntax) {
if let violation = violation(identifier: node.name, modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes) {
if let violation = violation(
identifier: node.name,
modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes
) {
violations.append(violation)
}
}

override func visitPost(_ node: ClassDeclSyntax) {
if let violation = violation(identifier: node.name, modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes) {
if let violation = violation(
identifier: node.name,
modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes
) {
violations.append(violation)
}
}
Expand All @@ -42,30 +48,42 @@ private extension TypeNameRule {
}

override func visitPost(_ node: AssociatedTypeDeclSyntax) {
if let violation = violation(identifier: node.name, modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes) {
if let violation = violation(
identifier: node.name,
modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes
) {
violations.append(violation)
}
}

override func visitPost(_ node: EnumDeclSyntax) {
if let violation = violation(identifier: node.name, modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes) {
if let violation = violation(
identifier: node.name,
modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes
) {
violations.append(violation)
}
}

override func visitPost(_ node: ActorDeclSyntax) {
if let violation = violation(identifier: node.name, modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes) {
if let violation = violation(
identifier: node.name,
modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes
) {
violations.append(violation)
}
}

override func visitPost(_ node: ProtocolDeclSyntax) {
if configuration.validateProtocols,
let violation = violation(identifier: node.name, modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes) {
let violation = violation(
identifier: node.name,
modifiers: node.modifiers,
inheritedTypes: node.inheritanceClause?.inheritedTypes
) {
violations.append(violation)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ private func embedInSwitch(
\(`case`):
\(text)
}
""", file: file, line: line)
""",
file: file,
line: line)
}

@SwiftSyntaxRule(explicitRewriter: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ private extension DeploymentTargetRule {
for elem in node.availabilityArguments {
guard let restriction = elem.argument.as(PlatformVersionSyntax.self),
let versionString = restriction.version?.description,
let reason = reason(platform: restriction.platform, version: versionString,
violationType: violationType) else {
let reason = reason(
platform: restriction.platform,
version: versionString,
violationType: violationType
) else {
continue
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import SwiftLintCore
import SwiftSyntax

@SwiftSyntaxRule(optIn: true)
struct MultilineCallArgumentsRule: Rule {
var configuration = MultilineCallArgumentsConfiguration()

static let description = RuleDescription(
identifier: "multiline_call_arguments",
name: "Multiline Call Arguments",
description: """
Arguments in function and method calls should be either on the same line \
or one per line when the call spans multiple lines
""",
kind: .style,
nonTriggeringExamples: [
Example("""
foo(
param1: "param1",
param2: false,
param3: []
)
""", configuration: ["max_number_of_single_line_parameters": 2]),
Example("""
foo(param1: 1,
param2: false,
param3: [])
""", configuration: ["max_number_of_single_line_parameters": 1]),
Example("foo(param1: 1, param2: false)",
configuration: ["max_number_of_single_line_parameters": 2]),
Example("Enum.foo(param1: 1, param2: false)",
configuration: ["max_number_of_single_line_parameters": 2]),
Example("""
foo(param1: 1)
""",
configuration: [
"allows_single_line": false
]),
Example("""
Enum.foo(param1: 1)
""",
configuration: [
"allows_single_line": false
]),
Example("""
Enum.foo(param1: 1, param2: 2, param3: 3)
""",
configuration: [
"allows_single_line": true
]),
Example("""
foo(
param1: 1,
param2: 2,
param3: 3
)
""",
configuration: [
"allows_single_line": false
]),
],
triggeringExamples: [
Example("↓foo(param1: 1, param2: false, param3: [])",
configuration: [
"max_number_of_single_line_parameters": 2
]),
Example("↓Enum.foo(param1: 1, param2: false, param3: [])",
configuration: [
"max_number_of_single_line_parameters": 2
]),
Example("""
↓foo(param1: 1, param2: false,
param3: [])
""", configuration: [
"max_number_of_single_line_parameters": 3
]),
Example("""
↓Enum.foo(param1: 1, param2: false,
param3: [])
""", configuration: [
"max_number_of_single_line_parameters": 3
]),
Example("""
↓foo(param1: 1, param2: false)
""",
configuration: [
"allows_single_line": false
]),
Example("""
↓Enum.foo(param1: 1, param2: false)
""",
configuration: [
"allows_single_line": false
]),
]
)
}

private extension MultilineCallArgumentsRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: FunctionCallExprSyntax) {
guard node.arguments.isNotEmpty else { return }
guard node.trailingClosure == nil else { return }

if containsViolation(for: node.arguments) {
let anchor = node.calledExpression.positionAfterSkippingLeadingTrivia
violations.append(anchor)
}
}

private func containsViolation(for arguments: LabeledExprListSyntax) -> Bool {
let argumentPositions = arguments.map(\.positionAfterSkippingLeadingTrivia)
return containsViolation(parameterPositions: argumentPositions)
}

private func containsViolation(parameterPositions: [AbsolutePosition]) -> Bool {
guard parameterPositions.isNotEmpty else {
return false
}

var numberOfParameters = 0
var linesWithParameters: Set<Int> = []
var hasMultipleParametersOnSameLine = false

for position in parameterPositions {
let line = locationConverter.location(for: position).line

if !linesWithParameters.insert(line).inserted {
hasMultipleParametersOnSameLine = true
}

numberOfParameters += 1
}

if linesWithParameters.count == 1 {
guard configuration.allowsSingleLine else {
return numberOfParameters > 1
}

if let maxNumberOfSingleLineParameters = configuration.maxNumberOfSingleLineParameters {
return numberOfParameters > maxNumberOfSingleLineParameters
}

return false
}

return hasMultipleParametersOnSameLine
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ struct PrivateOutletRule: Rule {
ellipsisButtonDidTouch?(self)
}
}
""", configuration: ["allow_private_set": false], excludeFromDocumentation: true),
""",
configuration: ["allow_private_set": false],
excludeFromDocumentation: true),
]
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,7 @@ private func wrapExample(_ text: String, file: StaticString = #filePath, line: U
class ViewController: UIViewController {
\(text)
}
""", file: file, line: line)
""",
file: file,
line: line)
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,7 @@ private func wrapExample(_ text: String, file: StaticString = #filePath, line: U
class ViewController: UIViewController {
\(text)
}
""", file: file, line: line)
""",
file: file,
line: line)
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,12 @@ private extension SwiftLintFile {
compilerArguments: [String],
configuration: UnusedDeclarationConfiguration) -> Set<UnusedDeclarationRule.DeclaredUSR> {
Set(index.traverseEntitiesDepthFirst { _, indexEntity in
self.declaredUSR(indexEntity: indexEntity, editorOpen: editorOpen, compilerArguments: compilerArguments,
configuration: configuration)
self.declaredUSR(
indexEntity: indexEntity,
editorOpen: editorOpen,
compilerArguments: compilerArguments,
configuration: configuration
)
})
}

Expand Down
Loading