Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implements FunctionArgumentsSpacingRule #5337

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4e0e137
wip
u-abyss Nov 3, 2023
9cb3de3
implement rule
u-abyss Nov 8, 2023
a4f2e81
add: description and lil refactoring
u-abyss Nov 8, 2023
c39187c
change: confirm Rule
u-abyss Nov 8, 2023
98ae8b7
add: FunctionArgumentsSpacingRuleGeneratedTests
u-abyss Nov 8, 2023
281e73d
change: move test examples into rule description
u-abyss Nov 27, 2023
f1e35a7
change: consider for function with no parameters
u-abyss Nov 27, 2023
485c606
restore the newline
u-abyss Nov 27, 2023
c744b9d
add: triggering the rule with multiple spaces
u-abyss Nov 27, 2023
8c9e54b
fix: linter is triggered when a comment is included
u-abyss Dec 3, 2023
5715490
update: checks the trivia at each of the variables and at the paren a…
u-abyss Dec 3, 2023
f88686d
add: CHANGELOG
u-abyss Dec 3, 2023
ff605ad
fix: change trailingTrivia to leadingTrivia
u-abyss Dec 3, 2023
5d5c868
handle newline
u-abyss Dec 13, 2023
6493850
fix: pass all tests
u-abyss Dec 13, 2023
c2e50d1
rerun
u-abyss Dec 13, 2023
c319bd2
Update Source/SwiftLintBuiltInRules/Rules/Lint/FunctionArgumentsSpaci…
u-abyss Jan 16, 2024
32bbf0d
change: function name
u-abyss Jan 16, 2024
99c421b
update: passed all current tests
u-abyss Feb 3, 2024
bb4267d
update: trigger comment and tab
u-abyss Feb 4, 2024
df301a7
change: logic
u-abyss Feb 6, 2024
e355ba6
wip
u-abyss Feb 8, 2024
e68a3c6
update: not triggering when argument with linecomment
u-abyss Feb 11, 2024
4035a88
wip
u-abyss Feb 11, 2024
89f32ae
refactoring
u-abyss Feb 11, 2024
8594397
add: test case
u-abyss Feb 11, 2024
2f86326
refactoring
u-abyss Feb 11, 2024
dfbec83
change: name and description
u-abyss Feb 11, 2024
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@

* Make `empty_count` auto-correctable.
[KS1019](https://github.com/KS1019/)
* Add new `no_unnecessary_spaces` rule that No space before the first and after the last argument and exactly one space after every comma.
[u-abyss](https://github.com/u-abyss)
[#5259](https://github.com/realm/SwiftLint/issues/5224)

#### 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 @@ -134,6 +134,7 @@ public let builtInRules: [any Rule.Type] = [
NonOptionalStringDataConversionRule.self,
NonOverridableClassDeclarationRule.self,
NotificationCenterDetachmentRule.self,
NoUnnecessarySpacesRule.self,
NumberSeparatorRule.self,
ObjectLiteralRule.self,
OneDelarationPerFileRule.self,
Expand Down
150 changes: 150 additions & 0 deletions Source/SwiftLintBuiltInRules/Rules/Lint/NoUnnecessarySpacesRule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import SwiftSyntax

@SwiftSyntaxRule
struct NoUnnecessarySpacesRule: Rule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
identifier: "no_unnecessary_spaces",
name: "no Unnecessary Spaces",
description: "No space before the first and after the last argument and exactly one space after every comma",
kind: .lint,
nonTriggeringExamples: [
Example("f()"),
Example("f(true)"),
Example("f(true, false, true)"),
Example("f(a // line comment)"),
Example("f(a /* block comment */)"),
Example("f(true, /* comment */, false // line comment)"),
Example("f(/* comment */ true /* other comment */)"),
Example("f(true /* other comment */, /* comment */ false /* other comment */, false // line comment)"),
Example("""
f(
/* comment */
a: true,
b: true,
)
"""),
Example("""
f(
a: true, // line comment
b: true, // line comment
)
""")
],
triggeringExamples: [
Example("f(↓ )"),
Example("f(↓ )"),
Example("f(↓\t)"),
Example("f(↓ true↓ )"),
Example("f(↓ /* comment */ true /* other comment */ ↓)"),
Example("f(↓ x: 0, y: 0↓ )"),
Example("f(↓ true,↓ false, true↓ )"),
Example("f(↓ true,↓ false,↓ /* other comment */ ↓true↓ )"),
Example("""
f(
a: true,↓ // line comment
b: true,↓ // line comment
)
""")
]
)
}

private extension TriviaPiece {
var isLineComment: Bool {
if case .lineComment = self {
return true
} else {
return false
}
}
var isBlockComment: Bool {
if case .blockComment = self {
return true
} else {
return false
}
}
var isSingleSpace: Bool {
if case .spaces(1) = self {
return true
} else {
return false
}
}
}

private extension NoUnnecessarySpacesRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: FunctionCallExprSyntax) {
guard let leftParen = node.leftParen else { return }
checkSpaces(arguments: node.arguments, leftParen: leftParen)
}
func checkSpaces(arguments: LabeledExprListSyntax?, leftParen: TokenSyntax) {
checkLeftParenTrailingTrivia(leftParen: leftParen)
if let arguments {
arguments.enumerated().forEach { index, arg in
// By trailing trivia in the last argument, the space in front of the right bracket is checked.
if index == arguments.count - 1 {
checkArgumentTrailingTrivia(argument: arguments.last)
}
guard let trailingComma = arg.trailingComma else { return }
checkCommaTrailingTrivia(trailingComma: trailingComma)
}
}
}

private func checkLeftParenTrailingTrivia(leftParen: TokenSyntax) {
leftParen.trailingTrivia.pieces.enumerated().forEach { index, trivia in
if trivia.isSpaceOrTab && (index == 0 || leftParen.trailingTrivia.count == 1) {
violations.append(leftParen.endPositionBeforeTrailingTrivia)
} else if trivia.isSingleSpace && leftParen.trailingTrivia.count - 1 == index {
return
} else if trivia.isSpaceOrTab {
violations.append(leftParen.endPosition)
}
}
}

private func checkArgumentTrailingTrivia(argument: LabeledExprListSyntax.Element?) {
if let argument {
guard !argument.trailingTrivia.pieces.isEmpty else { return }

for index in 0 ..< argument.trailingTrivia.pieces.count {
let trivia = argument.trailingTrivia.pieces[index]

if index < argument.trailingTrivia.pieces.count - 1 {
let next = argument.trailingTrivia.pieces[index + 1]
if trivia.isSingleSpace && (next.isBlockComment || next.isLineComment) { continue }
}

if trivia.isSpaceOrTab {
if index == 0 || argument.trailingTrivia.pieces.count == 1 {
violations.append(argument.endPositionBeforeTrailingTrivia)
} else {
violations.append(argument.endPosition)
}
}
}
}
}

private func checkCommaTrailingTrivia(trailingComma: TokenSyntax) {
for index in 0 ..< trailingComma.trailingTrivia.pieces.count {
let trivia = trailingComma.trailingTrivia.pieces[index]

if index < trailingComma.trailingTrivia.pieces.count - 1 {
let next = trailingComma.trailingTrivia.pieces[index + 1]
if trivia.isSingleSpace && (next.isBlockComment || next.isLineComment) { continue }
}

if !trivia.isSingleSpace && (index == 0 || trailingComma.trailingTrivia.count == 1) {
violations.append(trailingComma.endPositionBeforeTrailingTrivia)
} else if !trivia.isSingleSpace && !trivia.isBlockComment && !trivia.isLineComment {
violations.append(trailingComma.endPosition)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal enum OperatorUsageWhitespaceRuleExamples {
Example("""
let something = Something<GenericParameter1,
GenericParameter2>()
""" ),
"""),
Example("""
return path.flatMap { path in
return compileCommands[path] ??
Expand Down
6 changes: 6 additions & 0 deletions Tests/GeneratedTests/GeneratedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,12 @@ class NotificationCenterDetachmentRuleGeneratedTests: SwiftLintTestCase {
}
}

class NoUnnecessarySpacesRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(NoUnnecessarySpacesRule.description)
}
}

class NumberSeparatorRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(NumberSeparatorRule.description)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
@testable import SwiftLintBuiltInRules

class NoUnnecessarySpacesRuleTests: SwiftLintTestCase {
func testNoUnnecessarySpacesRule() {
let description = NoUnnecessarySpacesRule.description
verifyRule(description)
}
}
2 changes: 1 addition & 1 deletion Tests/SwiftLintTestHelpers/TestHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ private func render(violations: [StyleViolation], in contents: String) -> String

private func render(locations: [Location], in contents: String) -> String {
var contents = StringView(contents).lines.map { $0.content }
for location in locations.sorted(by: > ) {
for location in locations.sorted(by: >) {
guard let line = location.line, let character = location.character else { continue }
let content = NSMutableString(string: contents[line - 1])
content.insert("↓", at: character - 1)
Expand Down
Loading