diff --git a/CHANGELOG.md b/CHANGELOG.md index 084a6f952e2..d5a27d68dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ #### Breaking -* None. +* Remove tracking of correction positions. Print the number of corrections made instead. + [SimplyDanny](https://github.com/SimplyDanny) #### Experimental @@ -24,7 +25,7 @@ [SimplyDanny](https://github.com/SimplyDanny) * The artifact bundle name has changed. `SwiftLintBinary-macos.artifactbundle.zip` is now called - `SwiftLintBinary.artifactbundle.zip`. It now includes an AMD64 Linux binary. + `SwiftLintBinary.artifactbundle.zip`. It now includes an AMD64 Linux binary. [Bradley Mackey](https://github.com/bradleymackey) [#5514](https://github.com/realm/SwiftLint/issues/5514) diff --git a/Source/SwiftLintBuiltInRules/Helpers/LegacyFunctionRuleHelper.swift b/Source/SwiftLintBuiltInRules/Helpers/LegacyFunctionRuleHelper.swift index 90074cac7f7..cb6ee81f3e9 100644 --- a/Source/SwiftLintBuiltInRules/Helpers/LegacyFunctionRuleHelper.swift +++ b/Source/SwiftLintBuiltInRules/Helpers/LegacyFunctionRuleHelper.swift @@ -52,12 +52,9 @@ enum LegacyFunctionRuleHelper { let funcName = node.calledExpression.as(DeclReferenceExprSyntax.self)?.baseName.text else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let trimmedArguments = node.arguments.map { $0.trimmingTrailingComma() } let rewriteStrategy = legacyFunctions[funcName] - let expr: ExprSyntax switch rewriteStrategy { case .equal: diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift index 69514185f5e..59192d57dee 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/DuplicateImportsRule.swift @@ -175,8 +175,7 @@ private extension DuplicateImportsRule { if itemsToRemove.isEmpty { return super.visit(node) } - correctionPositions.append(contentsOf: itemsToRemove.map(\.absolutePosition)) - + numberOfCorrections += itemsToRemove.count var copy = node for indexInParent in itemsToRemove.map(\.indexInParent).reversed() { let currentIndex = copy.index(copy.startIndex, offsetBy: indexInParent) @@ -187,7 +186,6 @@ private extension DuplicateImportsRule { } copy.remove(at: currentIndex) } - return super.visit(copy) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift index d8b1acaa708..18f65633b02 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ExplicitInitRule.swift @@ -204,11 +204,11 @@ private extension ExplicitInitRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax { guard let calledExpression = node.calledExpression.as(MemberAccessExprSyntax.self), - let violationPosition = calledExpression.explicitInitPosition, + calledExpression.explicitInitPosition != nil, let calledBase = calledExpression.base else { return super.visit(node) } - correctionPositions.append(violationPosition) + numberOfCorrections += 1 let newNode = node.with(\.calledExpression, calledBase) return super.visit(newNode) } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift index c4a7110dcca..78ac3a2cfa3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/JoinedDefaultParameterRule.swift @@ -49,10 +49,10 @@ private extension JoinedDefaultParameterRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax { - guard let violationPosition = node.violationPosition else { + guard node.violationPosition != nil else { return super.visit(node) } - correctionPositions.append(violationPosition) + numberOfCorrections += 1 let newNode = node.with(\.arguments, []) return super.visit(newNode) } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstantRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstantRule.swift index dc50bdb8ec3..db3edbbf204 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstantRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstantRule.swift @@ -36,7 +36,7 @@ private extension LegacyConstantRule { guard let correction = LegacyConstantRuleExamples.patterns[node.baseName.text] else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return ("\(raw: correction)" as ExprSyntax) .with(\.leadingTrivia, node.leadingTrivia) .with(\.trailingTrivia, node.trailingTrivia) @@ -47,7 +47,7 @@ private extension LegacyConstantRule { let calledExpression = node.calledExpression.as(DeclReferenceExprSyntax.self) else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return ("\(raw: calledExpression.baseName.text).pi" as ExprSyntax) .with(\.leadingTrivia, node.leadingTrivia) .with(\.trailingTrivia, node.trailingTrivia) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstructorRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstructorRule.swift index c61fb0bdeb5..c9a2f2dd758 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstructorRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/LegacyConstructorRule.swift @@ -146,9 +146,7 @@ private extension LegacyConstructorRule { let args = constructorsToArguments[identifier] else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let arguments = LabeledExprListSyntax(node.arguments.enumerated().map { index, elem in elem .with(\.label, .identifier(args[index])) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift index 9538ee445be..2d7ec97bc38 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/NimbleOperatorRule.swift @@ -88,9 +88,7 @@ private extension NimbleOperatorRule { let expectedValueExpr = expectation.expectedValueExpr(for: predicate) else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let elements = ExprListSyntax([ expectation.baseExpr.with(\.trailingTrivia, .space), operatorExpr.with(\.trailingTrivia, .space), diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift index 18f7e1e092f..0f65f46e3a4 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferKeyPathRule.swift @@ -110,7 +110,7 @@ private extension PreferKeyPathRule { let calleeName = node.calleeName else { return super.visit(node) } - correctionPositions.append(closure.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 var node = node.with(\.calledExpression, node.calledExpression.with(\.trailingTrivia, [])) if node.leftParen == nil { node = node.with(\.leftParen, .leftParenToken()) @@ -137,7 +137,7 @@ private extension PreferKeyPathRule { if let expr = node.onlyExprStmt, expr.accesses(identifier: node.onlyParameter) == true, let replacement = expr.asKeyPath { - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = replacement .with(\.leadingTrivia, node.leadingTrivia) .with(\.trailingTrivia, node.trailingTrivia) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift index c595dd0db88..6ae147d8698 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferTypeCheckingRule.swift @@ -71,12 +71,9 @@ private extension PreferTypeCheckingRule { guard let asExpr = node.asExprWithOptionalTypeChecking else { return super.visit(node) } - - correctionPositions.append(asExpr.asKeyword.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let expression = asExpr.expression.trimmed let type = asExpr.type.trimmed - return ExprSyntax(stringLiteral: "\(expression) is \(type)") .with(\.leadingTrivia, node.leadingTrivia) .with(\.trailingTrivia, node.trailingTrivia) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift index 96e31c26946..d8faf71d52b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/PreferZeroOverExplicitInitRule.swift @@ -50,16 +50,12 @@ private extension PreferZeroOverExplicitInitRule { guard node.hasViolation, let name = node.name else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let newNode = MemberAccessExprSyntax(name: "zero") .with(\.base, "\(raw: name)") - return super.visit( - newNode - .with(\.leadingTrivia, node.leadingTrivia) - .with(\.trailingTrivia, node.trailingTrivia) - ) + .with(\.leadingTrivia, node.leadingTrivia) + .with(\.trailingTrivia, node.trailingTrivia) + return super.visit(newNode) } } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift index 22cc886d962..54befe20692 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantNilCoalescingRule.swift @@ -44,9 +44,8 @@ private extension RedundantNilCoalescingRule { else { return super.visit(node) } - + numberOfCorrections += 1 let newNode = ExprListSyntax(node.dropLast(2)).with(\.trailingTrivia, []) - correctionPositions.append(secondToLastExpression.operator.positionAfterSkippingLeadingTrivia) return super.visit(newNode) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantOptionalInitializationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantOptionalInitializationRule.swift index 6937a51dc85..2c689fd39c0 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantOptionalInitializationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantOptionalInitializationRule.swift @@ -138,9 +138,7 @@ private extension RedundantOptionalInitializationRule { guard violations.isNotEmpty else { return super.visit(node) } - - correctionPositions.append(contentsOf: violations.map(\.0)) - + numberOfCorrections += violations.count let violatingBindings = violations.map(\.1) let newBindings = PatternBindingListSyntax(node.bindings.map { binding in guard violatingBindings.contains(binding) else { diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantVoidReturnRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantVoidReturnRule.swift index 432dc89f0da..dd91da179bf 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantVoidReturnRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/RedundantVoidReturnRule.swift @@ -88,25 +88,23 @@ private extension RedundantVoidReturnRule { override func visit(_ node: ClosureSignatureSyntax) -> ClosureSignatureSyntax { guard configuration.includeClosures, let output = node.returnClause, - let tokenBeforeOutput = output.previousToken(viewMode: .sourceAccurate), + output.previousToken(viewMode: .sourceAccurate) != nil, output.containsRedundantVoidViolation else { return super.visit(node) } - - correctionPositions.append(tokenBeforeOutput.endPositionBeforeTrailingTrivia) + numberOfCorrections += 1 return super.visit(node.with(\.returnClause, nil).removingTrailingSpaceIfNeeded()) } override func visit(_ node: FunctionSignatureSyntax) -> FunctionSignatureSyntax { guard let output = node.returnClause, - let tokenBeforeOutput = output.previousToken(viewMode: .sourceAccurate), + output.previousToken(viewMode: .sourceAccurate) != nil, output.containsRedundantVoidViolation else { return super.visit(node) } - - correctionPositions.append(tokenBeforeOutput.endPositionBeforeTrailingTrivia) + numberOfCorrections += 1 return super.visit(node.with(\.returnClause, nil).removingTrailingSpaceIfNeeded()) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift index 72148bb9124..0445058f259 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ReturnValueFromVoidFunctionRule.swift @@ -33,7 +33,7 @@ private extension ReturnValueFromVoidFunctionRule { Syntax(statements).enclosingFunction()?.returnsVoid == true else { return super.visit(statements) } - correctionPositions.append(returnStmt.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let newStmtList = Array(statements.dropLast()) + [ CodeBlockItemSyntax(item: .expr(expr)) .with(\.leadingTrivia, returnStmt.leadingTrivia), diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift index f29726d7b66..4e50af8092b 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ShorthandOptionalBindingRule.swift @@ -95,8 +95,7 @@ private extension ShorthandOptionalBindingRule { guard node.isShadowingOptionalBinding else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let newNode = node .with(\.initializer, nil) .with(\.pattern, node.pattern.with(\.trailingTrivia, node.trailingTrivia)) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/SyntacticSugarRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/SyntacticSugarRule.swift index 2ae8d414994..e3eec3d7e6a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/SyntacticSugarRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/SyntacticSugarRule.swift @@ -31,16 +31,14 @@ struct SyntacticSugarRule: CorrectableRule, SourceKitFreeRule { violations.flatMap { [$0] + flattenViolations($0.children) } } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let visitor = SyntacticSugarRuleVisitor(viewMode: .sourceAccurate) return visitor.walk(file: file) { visitor in var context = CorrectingContext(rule: self, file: file, contents: file.contents) context.correctViolations(visitor.violations) - file.write(context.contents) - - return context.corrections - } + return [context.numberOfCorrections] + }.reduce(0, +) } } @@ -259,7 +257,7 @@ private struct CorrectingContext { let rule: R let file: SwiftLintFile var contents: String - var corrections: [Correction] = [] + var numberOfCorrections = 0 mutating func correctViolations(_ violations: [SyntacticSugarRuleViolation]) { let sortedVolations = violations.sorted(by: { $0.correction.typeStart > $1.correction.typeStart }) @@ -309,9 +307,7 @@ private struct CorrectingContext { correctViolations(violation.children) replaceCharacters(in: leftRange, with: "") } - - let location = Location(file: file, byteOffset: ByteCount(correction.typeStart)) - corrections.append(Correction(ruleDescription: type(of: rule).description, location: location)) + numberOfCorrections += 1 } private func typeIsOpaqueOrExistential(correction: SyntacticSugarRuleViolation.Correction) -> Bool { diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift index 968c799932c..326d1da4d56 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/ToggleBoolRule.swift @@ -45,7 +45,7 @@ private extension ToggleBoolRule { guard node.hasToggleBoolViolation, let firstExpr = node.first, let index = node.index(of: firstExpr) else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let elements = node .with( \.[index], diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/TrailingSemicolonRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/TrailingSemicolonRule.swift index 72a50c3cefe..1a77cde0fe2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/TrailingSemicolonRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/TrailingSemicolonRule.swift @@ -40,8 +40,7 @@ private extension TrailingSemicolonRule { guard node.isTrailingSemicolon else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return .unknown("").with(\.trailingTrivia, node.trailingTrivia) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededBreakInSwitchRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededBreakInSwitchRule.swift index ea46cba797e..df9786f33db 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededBreakInSwitchRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededBreakInSwitchRule.swift @@ -95,7 +95,6 @@ private extension UnneededBreakInSwitchRule { guard let statement = node.unneededBreak else { return } - violations.append(statement.item.positionAfterSkippingLeadingTrivia) } } @@ -103,22 +102,17 @@ private extension UnneededBreakInSwitchRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: SwitchCaseSyntax) -> SwitchCaseSyntax { let stmts = CodeBlockItemListSyntax(node.statements.dropLast()) - guard let breakStatement = node.unneededBreak, let secondLast = stmts.last else { return super.visit(node) } - - correctionPositions.append(breakStatement.item.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let trivia = breakStatement.item.leadingTrivia + breakStatement.item.trailingTrivia - let newNode = node .with(\.statements, stmts) .with(\.statements.trailingTrivia, secondLast.item.trailingTrivia + trivia) .trimmed { !$0.isComment } .formatted() .as(SwitchCaseSyntax.self)! - return super.visit(newNode) } } @@ -131,7 +125,6 @@ private extension SwitchCaseSyntax { breakStatement.label == nil else { return nil } - return statements.last } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift index ddd73209a84..04442fcef33 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UnneededSynthesizedInitializerRule.swift @@ -60,7 +60,7 @@ private extension UnneededSynthesizedInitializerRule { override func visit(_ node: InitializerDeclSyntax) -> DeclSyntax { if unneededInitializers.contains(node) { - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let expr: DeclSyntax = "" return expr .with(\.leadingTrivia, node.leadingTrivia) diff --git a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift index 58ec4b9e9f1..029aab762e1 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Idiomatic/UntypedErrorInCatchRule.swift @@ -124,8 +124,7 @@ private extension UntypedErrorInCatchRule { guard let item = node.catchItems.onlyElement, item.isIdentifierPattern else { return super.visit(node) } - - correctionPositions.append(node.catchKeyword.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return super.visit( node .with(\.catchKeyword, node.catchKeyword.with(\.trailingTrivia, .spaces(1))) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift index c6745cade32..48cdf4113bc 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/LowerACLThanParentRule.swift @@ -96,8 +96,7 @@ private extension LowerACLThanParentRule { guard node.isHigherACLThanParent else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let newNode: DeclModifierSyntax if node.name.tokenKind == .keyword(.open) { newNode = DeclModifierSyntax( diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift index 745ccfcfeb1..d198393897f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/MarkRule.swift @@ -29,9 +29,7 @@ private extension MarkRule { override func visit(_ token: TokenSyntax) -> TokenSyntax { var pieces = token.leadingTrivia.pieces for result in token.violationResults() { - // caution: `correctionPositions` records the positions before the mutations. - // https://github.com/realm/SwiftLint/pull/4297 - correctionPositions.append(result.position) + numberOfCorrections += 1 result.correct(&pieces) } return super.visit(token.with(\.leadingTrivia, Trivia(pieces: pieces))) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift index e3d80657cba..440e9d9da0f 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateSwiftUIStatePropertyRule.swift @@ -117,7 +117,7 @@ private extension PrivateSwiftUIStatePropertyRule { return DeclSyntax(node) } - correctionPositions.append(node.bindingSpecifier.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 // If there are no modifiers present on the current syntax node, // then we should retain the binding specifier's leading trivia diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift index e9dc90514e1..f038b9e3942 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/PrivateUnitTestRule.swift @@ -153,8 +153,7 @@ private extension PrivateUnitTestRule { guard node.isPrivate, node.isXCTestCase(configuration.testParentClasses) else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let (modifiers, declKeyword) = withoutPrivate(modifiers: node.modifiers, declKeyword: node.classKeyword) return super.visit(node.with(\.modifiers, modifiers).with(\.classKeyword, declKeyword)) } @@ -163,8 +162,7 @@ private extension PrivateUnitTestRule { guard node.isTestMethod, node.isPrivate else { return super.visit(node) } - - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let (modifiers, declKeyword) = withoutPrivate(modifiers: node.modifiers, declKeyword: node.funcKeyword) return super.visit(node.with(\.modifiers, modifiers).with(\.funcKeyword, declKeyword)) } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift index d29cdc54eb3..2cf8459bc32 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/RedundantSendableRule.swift @@ -68,7 +68,7 @@ private extension RedundantSendableRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: ActorDeclSyntax) -> DeclSyntax { if node.conformsToSendable { - correctionPositions.append(node.name.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return super.visit(node.withoutSendable) } return super.visit(node) @@ -92,7 +92,7 @@ private extension RedundantSendableRule { private func removeRedundantSendable(from decl: T) -> T { if decl.conformsToSendable, decl.isIsolatedToActor(actors: configuration.globalActors) { - correctionPositions.append(decl.name.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return decl.withoutSendable } return decl diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift index 790f37329f2..5e9d37a92d2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/StrongIBOutletRule.swift @@ -40,15 +40,14 @@ private extension StrongIBOutletRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: VariableDeclSyntax) -> DeclSyntax { - guard let violationPosition = node.violationPosition, + guard node.violationPosition != nil, let weakOrUnownedModifier = node.weakOrUnownedModifier, case let modifiers = node.modifiers else { return super.visit(node) } - let newModifiers = modifiers.filter { $0 != weakOrUnownedModifier } let newNode = node.with(\.modifiers, newModifiers) - correctionPositions.append(violationPosition) + numberOfCorrections += 1 return super.visit(newNode) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift index c9414ff3708..3ccd45d989e 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnneededOverrideRule.swift @@ -49,7 +49,7 @@ private extension UnneededOverrideRule { } private func visitUnneededOverride(_ node: some DeclSyntaxProtocol) -> DeclSyntax { - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let expr: DeclSyntax = "" return expr .with(\.leadingTrivia, node.leadingTrivia) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedClosureParameterRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedClosureParameterRule.swift index fb4f701a681..84868caeb56 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedClosureParameterRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedClosureParameterRule.swift @@ -56,7 +56,7 @@ private extension UnusedClosureParameterRule { let index = params.parameters.index(of: param) else { continue } - correctionPositions.append(name.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let newParameterList = newParams.parameters.with( \.[index], param.with(\.firstName, name.with(\.tokenKind, .wildcard)) @@ -74,7 +74,7 @@ private extension UnusedClosureParameterRule { let index = params.index(of: param) else { continue } - correctionPositions.append(param.name.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 newParams = newParams.with( \.[index], param.with(\.name, param.name.with(\.tokenKind, .wildcard)) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedControlFlowLabelRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedControlFlowLabelRule.swift index 1e36d5f07c1..90e6b03ab39 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedControlFlowLabelRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedControlFlowLabelRule.swift @@ -96,10 +96,10 @@ private extension UnusedControlFlowLabelRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: LabeledStmtSyntax) -> StmtSyntax { - guard let violationPosition = node.violationPosition else { + guard node.violationPosition != nil else { return super.visit(node) } - correctionPositions.append(violationPosition) + numberOfCorrections += 1 return visit(node.statement.with(\.leadingTrivia, node.leadingTrivia)) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift index 6e1951a4821..c6b47b9c4e5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/UnusedImportRule.swift @@ -26,25 +26,23 @@ struct UnusedImportRule: CorrectableRule, AnalyzerRule { } } - func correct(file: SwiftLintFile, compilerArguments: [String]) -> [Correction] { + func correct(file: SwiftLintFile, compilerArguments: [String]) -> Int { let importUsages = importUsage(in: file, compilerArguments: compilerArguments) let matches = file.ruleEnabled(violatingRanges: importUsages.compactMap(\.violationRange), for: self) var contents = file.stringView.nsString - let description = Self.description - var corrections = [Correction]() + var numberOfCorrections = 0 for range in matches.reversed() { contents = contents.replacingCharacters(in: range, with: "").bridge() - let location = Location(file: file, characterOffset: range.location) - corrections.append(Correction(ruleDescription: description, location: location)) + numberOfCorrections += 1 } - if corrections.isNotEmpty { + if numberOfCorrections > 0 { file.write(contents.bridge()) } guard configuration.requireExplicitImports else { - return corrections + return numberOfCorrections } let missingImports = importUsages.compactMap { importUsage -> String? in @@ -57,7 +55,7 @@ struct UnusedImportRule: CorrectableRule, AnalyzerRule { } guard missingImports.isNotEmpty else { - return corrections + return numberOfCorrections } var insertionLocation = 0 @@ -72,14 +70,11 @@ struct UnusedImportRule: CorrectableRule, AnalyzerRule { .joined(separator: "\n") let newContents = contents.replacingCharacters(in: insertionRange, with: missingImportStatements + "\n") file.write(newContents) - let location = Location(file: file, characterOffset: 0) - let missingImportCorrections = missingImports.map { _ in - Correction(ruleDescription: description, location: location) - } - corrections.append(contentsOf: missingImportCorrections) + numberOfCorrections += missingImports.count + // Attempt to sort imports - corrections.append(contentsOf: SortedImportsRule().correct(file: file)) - return corrections + numberOfCorrections += SortedImportsRule().correct(file: file) + return numberOfCorrections } private func importUsage(in file: SwiftLintFile, compilerArguments: [String]) -> [ImportUsage] { diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift index c1166ee473c..6436937ebc6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/EmptyCountRule.swift @@ -112,15 +112,17 @@ private extension EmptyCountRule { return super.visit(node) } - if let (count, position) = node.countNodeAndPosition(onlyAfterDot: configuration.onlyAfterDot) { + if let (count, _) = node.countNodeAndPosition(onlyAfterDot: configuration.onlyAfterDot) { let newNode = if let count = count.as(MemberAccessExprSyntax.self) { ExprSyntax(count.with(\.declName.baseName, "isEmpty").trimmed) } else { ExprSyntax(count.as(DeclReferenceExprSyntax.self)?.with(\.baseName, "isEmpty").trimmed) } - guard let newNode else { return super.visit(node) } - correctionPositions.append(position) + guard let newNode else { + return super.visit(node) + } + numberOfCorrections += 1 return if ["!=", "<", ">"].contains(binaryOperator) { newNode.negated diff --git a/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift b/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift index b18c5ab579e..50cccf7d2f4 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Performance/FinalTestCaseRule.swift @@ -44,7 +44,7 @@ private extension FinalTestCaseRule { override func visit(_ node: ClassDeclSyntax) -> DeclSyntax { var newNode = node if node.isNonFinalTestClass(parentClasses: configuration.testParentClasses) { - correctionPositions.append(node.name.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let finalModifier = DeclModifierSyntax(name: .keyword(.final)) newNode = if node.modifiers.isEmpty { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ClosingBraceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ClosingBraceRule.swift index 0d60cd9e914..c31322d3c83 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ClosingBraceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ClosingBraceRule.swift @@ -37,7 +37,7 @@ private extension ClosingBraceRule { guard node.hasClosingBraceViolation else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return super.visit(node.with(\.trailingTrivia, Trivia())) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ClosureEndIndentationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ClosureEndIndentationRule.swift index 4e760bbc0fd..9782d141af9 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ClosureEndIndentationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ClosureEndIndentationRule.swift @@ -35,7 +35,7 @@ struct ClosureEndIndentationRule: Rule, OptInRule { } extension ClosureEndIndentationRule: CorrectableRule { - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let allViolations = violations(in: file).reversed().filter { violation in guard let nsRange = file.stringView.byteRangeToNSRange(violation.range) else { return false @@ -45,7 +45,7 @@ extension ClosureEndIndentationRule: CorrectableRule { } guard allViolations.isNotEmpty else { - return [] + return 0 } var correctedContents = file.contents @@ -61,16 +61,13 @@ extension ClosureEndIndentationRule: CorrectableRule { } } - var corrections = correctedLocations.map { - Correction(ruleDescription: Self.description, location: Location(file: file, characterOffset: $0)) - } - + var numberOfCorrections = correctedLocations.count file.write(correctedContents) // Re-correct to catch cascading indentation from the first round. - corrections += correct(file: file) + numberOfCorrections += correct(file: file) - return corrections + return numberOfCorrections } private func correct(contents: inout String, expected: NSRange, actual: NSRange) -> Bool { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift index e3cc4e0243d..eea922a5cf4 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ClosureSpacingRule.swift @@ -79,9 +79,8 @@ private extension ClosureSpacingRule { node.rightBrace = node.rightBrace.with(\.trailingTrivia, .spaces(1)) } if violations.hasViolations { - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 } - return super.visit(node) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/CommaRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/CommaRule.swift index 18f7425c597..b91f0cb054a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/CommaRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/CommaRule.swift @@ -120,7 +120,7 @@ struct CommaRule: CorrectableRule, SourceKitFreeRule { } } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let initialNSRanges = Dictionary( uniqueKeysWithValues: violationRanges(in: file) .compactMap { byteRange, shouldAddSpace in @@ -131,21 +131,18 @@ struct CommaRule: CorrectableRule, SourceKitFreeRule { ) let violatingRanges = file.ruleEnabled(violatingRanges: Array(initialNSRanges.keys), for: self) - guard violatingRanges.isNotEmpty else { return [] } + guard violatingRanges.isNotEmpty else { + return 0 + } - let description = Self.description - var corrections = [Correction]() var contents = file.contents for range in violatingRanges.sorted(by: { $0.location > $1.location }) { let contentsNSString = contents.bridge() let shouldAddSpace = initialNSRanges[range] ?? true contents = contentsNSString.replacingCharacters(in: range, with: ",\(shouldAddSpace ? " " : "")") - let location = Location(file: file, characterOffset: range.location) - corrections.append(Correction(ruleDescription: description, location: location)) } - file.write(contents) - return corrections + return violatingRanges.count } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift index 1ba85155d5a..1b408096e43 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ControlStatementRule.swift @@ -114,7 +114,7 @@ private extension ControlStatementRule { guard case let items = node.catchItems, items.containSuperfluousParens == true else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = node .with(\.catchKeyword, node.catchKeyword.with(\.trailingTrivia, .space)) .with(\.catchItems, items.withoutParens) @@ -125,7 +125,7 @@ private extension ControlStatementRule { guard node.conditions.containSuperfluousParens else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = node .with(\.guardKeyword, node.guardKeyword.with(\.trailingTrivia, .space)) .with(\.conditions, node.conditions.withoutParens) @@ -136,7 +136,7 @@ private extension ControlStatementRule { guard node.conditions.containSuperfluousParens else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = node .with(\.ifKeyword, node.ifKeyword.with(\.trailingTrivia, .space)) .with(\.conditions, node.conditions.withoutParens) @@ -147,7 +147,7 @@ private extension ControlStatementRule { guard let tupleElement = node.subject.unwrapped else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = node .with(\.switchKeyword, node.switchKeyword.with(\.trailingTrivia, .space)) .with(\.subject, tupleElement.with(\.trailingTrivia, .space)) @@ -158,7 +158,7 @@ private extension ControlStatementRule { guard node.conditions.containSuperfluousParens else { return super.visit(node) } - correctionPositions.append(node.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = node .with(\.whileKeyword, node.whileKeyword.with(\.trailingTrivia, .space)) .with(\.conditions, node.conditions.withoutParens) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift index 95852f332ba..6f645252ee3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/DirectReturnRule.swift @@ -199,7 +199,7 @@ private extension DirectReturnRule { var initExpression = binding.initializer?.value else { return super.visit(statements) } - correctionPositions.append(binding.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 var newStmtList = Array(statements.dropLast(2)) let newBindingList = bindingList .filter { $0 != binding } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/EmptyEnumArgumentsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/EmptyEnumArgumentsRule.swift index fc58da0b894..ebc815bba47 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/EmptyEnumArgumentsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/EmptyEnumArgumentsRule.swift @@ -131,18 +131,18 @@ private extension EmptyEnumArgumentsRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: SwitchCaseItemSyntax) -> SwitchCaseItemSyntax { - guard let (violationPosition, newPattern) = node.pattern.emptyEnumArgumentsViolation(rewrite: true) else { + guard let (_, newPattern) = node.pattern.emptyEnumArgumentsViolation(rewrite: true) else { return super.visit(node) } - correctionPositions.append(violationPosition) + numberOfCorrections += 1 return super.visit(node.with(\.pattern, newPattern)) } override func visit(_ node: MatchingPatternConditionSyntax) -> MatchingPatternConditionSyntax { - guard let (violationPosition, newPattern) = node.pattern.emptyEnumArgumentsViolation(rewrite: true) else { + guard let (_, newPattern) = node.pattern.emptyEnumArgumentsViolation(rewrite: true) else { return super.visit(node) } - correctionPositions.append(violationPosition) + numberOfCorrections += 1 return super.visit(node.with(\.pattern, newPattern)) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParametersRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParametersRule.swift index 806a7d13df8..6facb7ecacb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParametersRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParametersRule.swift @@ -46,10 +46,10 @@ private extension EmptyParametersRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: FunctionTypeSyntax) -> TypeSyntax { - guard let violationPosition = node.emptyParametersViolationPosition else { + guard node.emptyParametersViolationPosition != nil else { return super.visit(node) } - correctionPositions.append(violationPosition) + numberOfCorrections += 1 return super.visit(node.with(\.parameters, TupleTypeElementListSyntax([]))) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift index 78a76684840..de7c4269505 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/EmptyParenthesesWithTrailingClosureRule.swift @@ -59,15 +59,14 @@ private extension EmptyParenthesesWithTrailingClosureRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax { - guard let violationPosition = node.violationPosition else { + guard node.violationPosition != nil else { return super.visit(node) } - + numberOfCorrections += 1 let newNode = node .with(\.leftParen, nil) .with(\.rightParen, nil) .with(\.trailingClosure, node.trailingClosure?.with(\.leadingTrivia, .spaces(1))) - correctionPositions.append(violationPosition) return super.visit(newNode) } } @@ -80,7 +79,6 @@ private extension FunctionCallExprSyntax { arguments.isEmpty else { return nil } - return leftParen.positionAfterSkippingLeadingTrivia } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift index 7d86574842f..02e8e37cfa9 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ExplicitSelfRule.swift @@ -23,21 +23,18 @@ struct ExplicitSelfRule: CorrectableRule, AnalyzerRule { } } - func correct(file: SwiftLintFile, compilerArguments: [String]) -> [Correction] { + func correct(file: SwiftLintFile, compilerArguments: [String]) -> Int { let violations = violationRanges(in: file, compilerArguments: compilerArguments) let matches = file.ruleEnabled(violatingRanges: violations, for: self) - if matches.isEmpty { return [] } - + if matches.isEmpty { + return 0 + } var contents = file.contents.bridge() - let description = Self.description - var corrections = [Correction]() for range in matches.reversed() { contents = contents.replacingCharacters(in: range, with: "self.").bridge() - let location = Location(file: file, characterOffset: range.location) - corrections.append(Correction(ruleDescription: description, location: location)) } file.write(contents.bridge()) - return corrections + return matches.count } private func violationRanges(in file: SwiftLintFile, compilerArguments: [String]) -> [NSRange] { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/LeadingWhitespaceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/LeadingWhitespaceRule.swift index 329fad61782..a5e3cd3e8e2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/LeadingWhitespaceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/LeadingWhitespaceRule.swift @@ -36,13 +36,13 @@ struct LeadingWhitespaceRule: CorrectableRule, SourceKitFreeRule { ] } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let whitespaceAndNewline = CharacterSet.whitespacesAndNewlines let spaceCount = file.contents.countOfLeadingCharacters(in: whitespaceAndNewline) guard spaceCount > 0, let firstLineRange = file.lines.first?.range, file.ruleEnabled(violatingRanges: [firstLineRange], for: self).isNotEmpty else { - return [] + return 0 } let indexEnd = file.contents.index( @@ -50,7 +50,6 @@ struct LeadingWhitespaceRule: CorrectableRule, SourceKitFreeRule { offsetBy: spaceCount, limitedBy: file.contents.endIndex) ?? file.contents.endIndex file.write(String(file.contents[indexEnd...])) - let location = Location(file: file.path, line: max(file.lines.count, 1)) - return [Correction(ruleDescription: Self.description, location: location)] + return 1 } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/LiteralExpressionEndIndentationRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/LiteralExpressionEndIndentationRule.swift index 29193dba7b1..4c78231ca55 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/LiteralExpressionEndIndentationRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/LiteralExpressionEndIndentationRule.swift @@ -142,42 +142,31 @@ struct LiteralExpressionEndIndentationRule: Rule, OptInRule { } extension LiteralExpressionEndIndentationRule: CorrectableRule { - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let allViolations = violations(in: file).reversed().filter { violation in guard let nsRange = file.stringView.byteRangeToNSRange(violation.range) else { return false } - return file.ruleEnabled(violatingRanges: [nsRange], for: self).isNotEmpty } - guard allViolations.isNotEmpty else { - return [] + return 0 } - var correctedContents = file.contents - var correctedLocations: [Int] = [] - let actualLookup = actualViolationLookup(for: allViolations) - + var numberOfCorrections = 0 for violation in allViolations { let expected = actualLookup(violation).indentationRanges.expected let actual = violation.indentationRanges.actual if correct(contents: &correctedContents, expected: expected, actual: actual) { - correctedLocations.append(actual.location) + numberOfCorrections += 1 } } - - var corrections = correctedLocations.map { - Correction(ruleDescription: Self.description, location: Location(file: file, characterOffset: $0)) - } - file.write(correctedContents) // Re-correct to catch cascading indentation from the first round. - corrections += correct(file: file) - - return corrections + numberOfCorrections += correct(file: file) + return numberOfCorrections } private func correct(contents: inout String, expected: NSRange, actual: NSRange) -> Bool { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ModifierOrderRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ModifierOrderRule.swift index 9e4909706ea..6861f197bad 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ModifierOrderRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ModifierOrderRule.swift @@ -38,14 +38,18 @@ struct ModifierOrderRule: ASTRule, OptInRule, CorrectableRule { return [] } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { file.structureDictionary.traverseDepthFirst { subDict in - guard subDict.declarationKind != nil else { return nil } - return correct(file: file, dictionary: subDict) - } + guard subDict.declarationKind != nil else { + return [0] + } + return [correct(file: file, dictionary: subDict)] + }.reduce(0, +) } - private func correct(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> [Correction] { - guard let offset = dictionary.offset else { return [] } + private func correct(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Int { + guard dictionary.offset != nil else { + return 0 + } let originalContents = file.stringView let violatingRanges = violatingModifiers(dictionary: dictionary) .compactMap { preferred, declared -> (NSRange, NSRange)? in @@ -61,34 +65,19 @@ struct ModifierOrderRule: ASTRule, OptInRule, CorrectableRule { } return (preferredRange, declaredRange) } - - let corrections: [Correction] if violatingRanges.isEmpty { - corrections = [] - } else { - var correctedContents = originalContents.nsString - - violatingRanges.reversed().forEach { arg in - let (preferredModifierRange, declaredModifierRange) = arg - correctedContents = correctedContents.replacingCharacters( - in: declaredModifierRange, - with: originalContents.substring(with: preferredModifierRange) - ).bridge() - } - - file.write(correctedContents.bridge()) - - corrections = [ - Correction( - ruleDescription: Self.description, - location: Location( - file: file, - byteOffset: offset - ) - ), - ] + return 0 + } + var correctedContents = originalContents.nsString + violatingRanges.reversed().forEach { arg in + let (preferredModifierRange, declaredModifierRange) = arg + correctedContents = correctedContents.replacingCharacters( + in: declaredModifierRange, + with: originalContents.substring(with: preferredModifierRange) + ).bridge() } - return corrections + file.write(correctedContents.bridge()) + return violatingRanges.count } private func violatableModifiers(declaredModifiers: [ModifierDescription]) -> [ModifierDescription] { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/NoSpaceInMethodCallRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/NoSpaceInMethodCallRule.swift index 9c185a2aa42..bb572feaa88 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/NoSpaceInMethodCallRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/NoSpaceInMethodCallRule.swift @@ -61,12 +61,9 @@ private extension NoSpaceInMethodCallRule { guard node.hasNoSpaceInMethodCallViolation else { return super.visit(node) } - - correctionPositions.append(node.calledExpression.endPositionBeforeTrailingTrivia) - + numberOfCorrections += 1 let newNode = node .with(\.calledExpression, node.calledExpression.with(\.trailingTrivia, [])) - return super.visit(newNode) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift index 3158e79a2f8..d23963467ae 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/NumberSeparatorRule.swift @@ -55,7 +55,7 @@ private extension NumberSeparatorRule { .floatLiteral(violation.correction) ) ) - correctionPositions.append(violation.position) + numberOfCorrections += 1 return super.visit(newNode) } @@ -64,7 +64,7 @@ private extension NumberSeparatorRule { return super.visit(node) } let newNode = node.with(\.literal, node.literal.with(\.tokenKind, .integerLiteral(violation.correction))) - correctionPositions.append(violation.position) + numberOfCorrections += 1 return super.visit(newNode) } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OperatorUsageWhitespaceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OperatorUsageWhitespaceRule.swift index b7323492839..7e67a3c0909 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OperatorUsageWhitespaceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OperatorUsageWhitespaceRule.swift @@ -35,7 +35,7 @@ struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, SourceKitFreeRul } } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let violatingRanges = violationRanges(file: file) .compactMap { byteRange, correction -> (NSRange, String)? in guard let range = file.stringView.byteRangeToNSRange(byteRange) else { @@ -49,22 +49,15 @@ struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, SourceKitFreeRul } var correctedContents = file.contents - var adjustedLocations = [Int]() - + var numberOfCorrections = 0 for (violatingRange, correction) in violatingRanges.reversed() { if let indexRange = correctedContents.nsrangeToIndexRange(violatingRange) { - correctedContents = correctedContents - .replacingCharacters(in: indexRange, with: correction) - adjustedLocations.insert(violatingRange.location, at: 0) + correctedContents = correctedContents.replacingCharacters(in: indexRange, with: correction) + numberOfCorrections += 1 } } - file.write(correctedContents) - - return adjustedLocations.map { - Correction(ruleDescription: Self.description, - location: Location(file: file, characterOffset: $0)) - } + return numberOfCorrections } private func isAlignedConstant(in byteRange: ByteRange, file: SwiftLintFile) -> Bool { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift index 6a812aa5b22..b6309613403 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/OptionalEnumCaseMatchingRule.swift @@ -185,8 +185,7 @@ private extension OptionalEnumCaseMatchingRule { if let expression = pattern.expression.as(OptionalChainingExprSyntax.self), !expression.expression.isDiscardAssignmentOrBoolLiteral { - let violationPosition = expression.questionMark.positionAfterSkippingLeadingTrivia - correctionPositions.append(violationPosition) + numberOfCorrections += 1 let newPattern = PatternSyntax(pattern.with(\.expression, expression.expression)) let newNode = node .with(\.pattern, newPattern) @@ -203,10 +202,7 @@ private extension OptionalEnumCaseMatchingRule { else { continue } - - let violationPosition = optionalChainingExpression.questionMark.positionAfterSkippingLeadingTrivia - correctionPositions.append(violationPosition) - + numberOfCorrections += 1 let newElement = element.with(\.expression, optionalChainingExpression.expression) if let index = expression.elements.index(of: element) { newExpression.elements = newExpression.elements.with(\.[index], newElement) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift index 3ecd15a5da8..e36f25f4e93 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/PreferSelfTypeOverTypeOfSelfRule.swift @@ -121,9 +121,7 @@ private extension PreferSelfTypeOverTypeOfSelfRule { guard let function = node.base?.as(FunctionCallExprSyntax.self), function.hasViolation else { return super.visit(node) } - - correctionPositions.append(function.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let base = DeclReferenceExprSyntax(baseName: "Self") let baseWithTrivia = base .with(\.leadingTrivia, function.leadingTrivia) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/ProtocolPropertyAccessorsOrderRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/ProtocolPropertyAccessorsOrderRule.swift index 22b161f5358..4bf198c8b54 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/ProtocolPropertyAccessorsOrderRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/ProtocolPropertyAccessorsOrderRule.swift @@ -44,13 +44,9 @@ private extension ProtocolPropertyAccessorsOrderRule { guard node.hasViolation else { return super.visit(node) } - - correctionPositions.append(node.accessors.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let reversedAccessors = AccessorDeclListSyntax(Array(node.accessorsList.reversed())) - return super.visit( - node.with(\.accessors, .accessors(reversedAccessors)) - ) + return super.visit(node.with(\.accessors, .accessors(reversedAccessors))) } } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift index 2d9716754ab..d07022130c2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SelfBindingRule.swift @@ -81,8 +81,7 @@ private extension SelfBindingRule { if let initializerIdentifier = node.initializer?.value.as(DeclReferenceExprSyntax.self), initializerIdentifier.baseName.text == "self" { - correctionPositions.append(identifierPattern.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let newPattern = PatternSyntax( identifierPattern .with(\.identifier, identifierPattern.identifier @@ -94,8 +93,7 @@ private extension SelfBindingRule { if node.initializer == nil, identifierPattern.identifier.text == "self", configuration.bindIdentifier != "self" { - correctionPositions.append(identifierPattern.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let newPattern = PatternSyntax( identifierPattern .with(\.identifier, identifierPattern.identifier diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SortedImportsRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SortedImportsRule.swift index d0f0b165065..705d8d4dec6 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SortedImportsRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SortedImportsRule.swift @@ -125,18 +125,12 @@ struct SortedImportsRule: CorrectableRule, OptInRule { return lhs.importModule().lowercased() <= rhs.importModule().lowercased() } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let groups = importGroups(in: file, filterEnabled: true) - - let corrections = violatingOffsets(inGroups: groups).map { characterOffset -> Correction in - let location = Location(file: file, characterOffset: characterOffset) - return Correction(ruleDescription: Self.description, location: location) - } - - guard corrections.isNotEmpty else { - return [] + let offsets = violatingOffsets(inGroups: groups) + guard offsets.isNotEmpty else { + return 0 } - let correctedContents = NSMutableString(string: file.contents) for group in groups.map({ $0.sorted(by: should(_:comeBefore:)) }) { guard let first = group.first?.contentRange else { @@ -149,7 +143,6 @@ struct SortedImportsRule: CorrectableRule, OptInRule { correctedContents.replaceCharacters(in: union, with: result) } file.write(correctedContents.bridge()) - - return corrections + return offsets.count } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/StatementPositionRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/StatementPositionRule.swift index 9a99e7a276a..342037995b2 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/StatementPositionRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/StatementPositionRule.swift @@ -69,12 +69,12 @@ struct StatementPositionRule: CorrectableRule { } } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { switch configuration.statementMode { case .default: - return defaultCorrect(file: file) + defaultCorrect(file: file) case .uncuddledElse: - return uncuddledCorrect(file: file) + uncuddledCorrect(file: file) } } } @@ -100,22 +100,20 @@ private extension StatementPositionRule { }.compactMap { $0.0 } } - func defaultCorrect(file: SwiftLintFile) -> [Correction] { + func defaultCorrect(file: SwiftLintFile) -> Int { let violations = defaultViolationRanges(in: file, matching: Self.defaultPattern) let matches = file.ruleEnabled(violatingRanges: violations, for: self) - if matches.isEmpty { return [] } + if matches.isEmpty { + return 0 + } let regularExpression = regex(Self.defaultPattern) - let description = Self.description - var corrections = [Correction]() var contents = file.contents for range in matches.reversed() { contents = regularExpression.stringByReplacingMatches(in: contents, options: [], range: range, withTemplate: "} $1") - let location = Location(file: file, characterOffset: range.location) - corrections.append(Correction(ruleDescription: description, location: location)) } file.write(contents) - return corrections + return matches.count } } @@ -179,19 +177,17 @@ private extension StatementPositionRule { return matches.compactMap(validator).filter(filterMatches).map(\.range) } - func uncuddledCorrect(file: SwiftLintFile) -> [Correction] { + func uncuddledCorrect(file: SwiftLintFile) -> Int { var contents = file.contents let syntaxMap = file.syntaxMap let matches = Self.uncuddledRegex.matches(in: file) let validator = Self.uncuddledMatchValidator(contents: file.stringView) let filterRanges = Self.uncuddledMatchFilter(contents: file.stringView, syntaxMap: syntaxMap) - let validMatches = matches.compactMap(validator).filter(filterRanges) .filter { file.ruleEnabled(violatingRanges: [$0.range], for: self).isNotEmpty } - if validMatches.isEmpty { return [] } - let description = Self.uncuddledDescription - var corrections = [Correction]() - + if validMatches.isEmpty { + return 0 + } for match in validMatches.reversed() { let range1 = match.range(at: 1) let range2 = match.range(at: 3) @@ -207,11 +203,8 @@ private extension StatementPositionRule { whitespace.insert("\n", at: whitespace.startIndex) } contents = contents.bridge().replacingCharacters(in: range2, with: whitespace) - let location = Location(file: file, characterOffset: match.range.location) - corrections.append(Correction(ruleDescription: description, location: location)) } - file.write(contents) - return corrections + return validMatches.count } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift index 74b02f875ac..e641aed717a 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/SuperfluousElseRule.swift @@ -266,14 +266,14 @@ private extension SuperfluousElseRule { final class Rewriter: ViolationsSyntaxRewriter { override init(configuration: ConfigurationType, file: SwiftLintFile) { super.init(configuration: configuration, file: file) - let correctionPositions = Visitor(configuration: configuration, file: file).walk(file: file) { - $0.violations.map(\.position) - }.filter { !$0.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) } - self.correctionPositions.append(contentsOf: correctionPositions) + numberOfCorrections += Visitor(configuration: configuration, file: file) + .walk(file: file) { $0.violations.map(\.position) } + .filter { !$0.isContainedIn(regions: disabledRegions, locationConverter: locationConverter) } + .count } override func visitAny(_ node: Syntax) -> Syntax? { - correctionPositions.isEmpty ? node : nil // Avoid skipping all `if` expressions in a code block. + numberOfCorrections == 0 ? node : nil // Avoid skipping all `if` expressions in a code block. } override func visit(_ list: CodeBlockItemListSyntax) -> CodeBlockItemListSyntax { diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift index a2bfa1442d8..09e98c12cdb 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingClosureRule.swift @@ -136,14 +136,14 @@ private extension TrailingClosureRule { guard node.trailingClosure == nil else { return super.visit(node) } if configuration.onlySingleMutedParameter { - if let param = node.singleMutedClosureParameter, - let converted = node.convertToTrailingClosure() { - correctionPositions.append(param.positionAfterSkippingLeadingTrivia) + if node.singleMutedClosureParameter != nil, + let converted = node.convertToTrailingClosure() { + numberOfCorrections += 1 return super.visit(converted) } - } else if let param = node.lastDistinctClosureParameter, + } else if node.lastDistinctClosureParameter != nil, let converted = node.convertToTrailingClosure() { - correctionPositions.append(param.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 return super.visit(converted) } return super.visit(node) diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingCommaRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingCommaRule.swift index 0383ff80512..fb68edaf03c 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingCommaRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingCommaRule.swift @@ -97,7 +97,7 @@ private extension TrailingCommaRule { switch (lastElement.trailingComma, configuration.mandatoryComma) { case (let commaToken?, false): - correctionPositions.append(commaToken.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let newTrailingTrivia = (lastElement.value.trailingTrivia) .appending(trivia: commaToken.leadingTrivia) .appending(trivia: commaToken.trailingTrivia) @@ -110,7 +110,7 @@ private extension TrailingCommaRule { ) return super.visit(newNode) case (nil, true) where !locationConverter.isSingleLine(node: node): - correctionPositions.append(lastElement.endPositionBeforeTrailingTrivia) + numberOfCorrections += 1 let newNode = node .with( \.[index], @@ -132,7 +132,7 @@ private extension TrailingCommaRule { switch (lastElement.trailingComma, configuration.mandatoryComma) { case (let commaToken?, false): - correctionPositions.append(commaToken.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let newNode = node .with( \.[index], @@ -146,7 +146,7 @@ private extension TrailingCommaRule { ) return super.visit(newNode) case (nil, true) where !locationConverter.isSingleLine(node: node): - correctionPositions.append(lastElement.endPositionBeforeTrailingTrivia) + numberOfCorrections += 1 let newNode = node .with( \.[index], diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingNewlineRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingNewlineRule.swift index 1cf4cb22a7d..2f249b55434 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/TrailingNewlineRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/TrailingNewlineRule.swift @@ -53,15 +53,15 @@ struct TrailingNewlineRule: CorrectableRule, SourceKitFreeRule { ] } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { guard let count = file.contents.trailingNewlineCount(), count != 1 else { - return [] + return 0 } guard let lastLineRange = file.lines.last?.range else { - return [] + return 0 } if file.ruleEnabled(violatingRanges: [lastLineRange], for: self).isEmpty { - return [] + return 0 } if count < 1 { file.append("\n") @@ -69,7 +69,6 @@ struct TrailingNewlineRule: CorrectableRule, SourceKitFreeRule { let index = file.contents.index(file.contents.endIndex, offsetBy: 1 - count) file.write(file.contents[.. [Correction] { + func correct(file: SwiftLintFile) -> Int { let whitespaceCharacterSet = CharacterSet.whitespaces var correctedLines = [String]() - var corrections = [Correction]() + var numberOfCorrections = 0 for line in file.lines { guard line.content.hasTrailingWhitespace() else { correctedLines.append(line.content) @@ -77,17 +77,14 @@ struct TrailingWhitespaceRule: CorrectableRule { } if line.content != correctedLine { - let description = Self.description - let location = Location(file: file.path, line: line.index) - corrections.append(Correction(ruleDescription: description, location: location)) + numberOfCorrections += 1 } correctedLines.append(correctedLine) } - if corrections.isNotEmpty { + if numberOfCorrections > 0 { // join and re-add trailing newline file.write(correctedLines.joined(separator: "\n") + "\n") - return corrections } - return [] + return numberOfCorrections } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift index 39e767b9ea7..1089af600ee 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/UnneededParenthesesInClosureArgumentRule.swift @@ -104,8 +104,7 @@ private extension UnneededParenthesesInClosureArgumentRule { ) } - correctionPositions.append(clause.positionAfterSkippingLeadingTrivia) - + numberOfCorrections += 1 let paramList = ClosureShorthandParameterListSyntax(items).with(\.trailingTrivia, .spaces(1)) return super.visit(node.with(\.parameterClause, .init(paramList))) } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift index 9b964d2f003..081b73ed69d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceBetweenCasesRule.swift @@ -191,31 +191,22 @@ extension VerticalWhitespaceBetweenCasesRule: OptInRule { } extension VerticalWhitespaceBetweenCasesRule: CorrectableRule { - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let violatingRanges = file.ruleEnabled(violatingRanges: violationRanges(in: file), for: self) - guard violatingRanges.isNotEmpty else { return [] } - + guard violatingRanges.isNotEmpty else { + return 0 + } let patternRegex = regex(pattern) - let replacementTemplate = "$1\n$2" - let description = Self.description - - var corrections = [Correction]() var fileContents = file.contents - for violationRange in violatingRanges.reversed() { fileContents = patternRegex.stringByReplacingMatches( in: fileContents, options: [], range: violationRange, - withTemplate: replacementTemplate + withTemplate: "$1\n$2" ) - - let location = Location(file: file, characterOffset: violationRange.location) - let correction = Correction(ruleDescription: description, location: location) - corrections.append(correction) } - file.write(fileContents) - return corrections + return violatingRanges.count } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceClosingBracesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceClosingBracesRule.swift index 6ca1625927c..d23484f20f8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceClosingBracesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceClosingBracesRule.swift @@ -37,34 +37,24 @@ struct VerticalWhitespaceClosingBracesRule: CorrectableRule, OptInRule { } } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let pattern = configuration.onlyEnforceBeforeTrivialLines ? self.trivialLinePattern : self.pattern - let violatingRanges = file.ruleEnabled(violatingRanges: file.violatingRanges(for: pattern), for: self) - guard violatingRanges.isNotEmpty else { return [] } - - let patternRegex: NSRegularExpression = regex(pattern) - let replacementTemplate = "$2" - let description = Self.description - - var corrections = [Correction]() + guard violatingRanges.isNotEmpty else { + return 0 + } + let patternRegex = regex(pattern) var fileContents = file.contents - for violationRange in violatingRanges.reversed() { fileContents = patternRegex.stringByReplacingMatches( in: fileContents, options: [], range: violationRange, - withTemplate: replacementTemplate + withTemplate: "$2" ) - - let location = Location(file: file, characterOffset: violationRange.location) - let correction = Correction(ruleDescription: description, location: location) - corrections.append(correction) } - file.write(fileContents) - return corrections + return violatingRanges.count } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceOpeningBracesRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceOpeningBracesRule.swift index b6150b2aac0..585a99aa1b8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceOpeningBracesRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceOpeningBracesRule.swift @@ -171,31 +171,22 @@ extension VerticalWhitespaceOpeningBracesRule: OptInRule { } extension VerticalWhitespaceOpeningBracesRule: CorrectableRule { - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let violatingRanges = file.ruleEnabled(violatingRanges: file.violatingRanges(for: pattern), for: self) - guard violatingRanges.isNotEmpty else { return [] } - - let patternRegex: NSRegularExpression = regex(pattern) - let replacementTemplate = "$1$3" - let description = Self.description - - var corrections = [Correction]() + guard violatingRanges.isNotEmpty else { + return 0 + } + let patternRegex = regex(pattern) var fileContents = file.contents - for violationRange in violatingRanges.reversed() { fileContents = patternRegex.stringByReplacingMatches( in: fileContents, options: [], range: violationRange, - withTemplate: replacementTemplate + withTemplate: "$1$3" ) - - let location = Location(file: file, characterOffset: violationRange.location) - let correction = Correction(ruleDescription: description, location: location) - corrections.append(correction) } - file.write(fileContents) - return corrections + return violatingRanges.count } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceRule.swift index b8b64ca4cd8..2ff363264ae 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VerticalWhitespaceRule.swift @@ -106,9 +106,11 @@ struct VerticalWhitespaceRule: CorrectableRule { return blankLinesSections } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let linesSections = violatingLineSections(in: file) - if linesSections.isEmpty { return [] } + if linesSections.isEmpty { + return 0 + } var indexOfLinesToDelete = [Int]() @@ -119,32 +121,26 @@ struct VerticalWhitespaceRule: CorrectableRule { } var correctedLines = [String]() - var corrections = [Correction]() + var numberOfCorrections = 0 for currentLine in file.lines { // Doesn't correct lines where rule is disabled if file.ruleEnabled(violatingRanges: [currentLine.range], for: self).isEmpty { correctedLines.append(currentLine.content) continue } - // removes lines by skipping them from correctedLines if Set(indexOfLinesToDelete).contains(currentLine.index) { - let description = Self.description - let location = Location(file: file, characterOffset: currentLine.range.location) - // reports every line that is being deleted - corrections.append(Correction(ruleDescription: description, location: location)) + numberOfCorrections += 1 continue // skips line } - // all lines that pass get added to final output file correctedLines.append(currentLine.content) } // converts lines back to file and adds trailing line - if corrections.isNotEmpty { + if numberOfCorrections > 0 { file.write(correctedLines.joined(separator: "\n") + "\n") - return corrections } - return [] + return numberOfCorrections } } diff --git a/Source/SwiftLintBuiltInRules/Rules/Style/VoidReturnRule.swift b/Source/SwiftLintBuiltInRules/Rules/Style/VoidReturnRule.swift index 8fcb01086dc..1bc9e997ac5 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Style/VoidReturnRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Style/VoidReturnRule.swift @@ -62,7 +62,7 @@ private extension VoidReturnRule { final class Rewriter: ViolationsSyntaxRewriter { override func visit(_ node: ReturnClauseSyntax) -> ReturnClauseSyntax { if node.violates { - correctionPositions.append(node.type.positionAfterSkippingLeadingTrivia) + numberOfCorrections += 1 let node = node .with(\.type, TypeSyntax(IdentifierTypeSyntax(name: "Void"))) .with(\.trailingTrivia, node.type.trailingTrivia) diff --git a/Source/SwiftLintCore/Models/Correction.swift b/Source/SwiftLintCore/Models/Correction.swift index 92d7fa14190..ef1eb206f3e 100644 --- a/Source/SwiftLintCore/Models/Correction.swift +++ b/Source/SwiftLintCore/Models/Correction.swift @@ -1,21 +1,26 @@ /// A value describing a SwiftLint violation that was corrected. public struct Correction: Equatable, Sendable { - /// The description of the rule for which this correction was applied. - public let ruleDescription: RuleDescription - /// The location of the original violation that was corrected. - public let location: Location + /// The name of the rule that was corrected. + public let ruleName: String + /// The path to the file that was corrected. + public let filePath: String? + /// The number of corrections that were made. + public let numberOfCorrections: Int /// The console-printable description for this correction. public var consoleDescription: String { - "\(location.file ?? ""): Corrected \(ruleDescription.name)" + let times = numberOfCorrections == 1 ? "time" : "times" + return "\(filePath ?? ""): Corrected \(ruleName) \(numberOfCorrections) \(times)" } /// Memberwise initializer. /// - /// - parameter ruleDescription: The description of the rule for which this correction was applied. - /// - parameter location: The location of the original violation that was corrected. - public init(ruleDescription: RuleDescription, location: Location) { - self.ruleDescription = ruleDescription - self.location = location + /// - parameter ruleName: The name of the rule that was corrected. + /// - parameter filePath: The path to the file that was corrected. + /// - parameter numberOfCorrections: The number of corrections that were made. + public init(ruleName: String, filePath: String?, numberOfCorrections: Int) { + self.ruleName = ruleName + self.filePath = filePath + self.numberOfCorrections = numberOfCorrections } } diff --git a/Source/SwiftLintCore/Protocols/CollectingRule.swift b/Source/SwiftLintCore/Protocols/CollectingRule.swift index eb7bff05417..e19b5919338 100644 --- a/Source/SwiftLintCore/Protocols/CollectingRule.swift +++ b/Source/SwiftLintCore/Protocols/CollectingRule.swift @@ -43,6 +43,8 @@ public protocol CollectingRule: AnyCollectingRule { func validate(file: SwiftLintFile, collectedInfo: [SwiftLintFile: FileInfo]) -> [StyleViolation] } +// MARK: - == Implementations + public extension CollectingRule { func collectInfo(for file: SwiftLintFile, into storage: RuleStorage, compilerArguments: [String]) { storage.collect(info: collectInfo(for: file, compilerArguments: compilerArguments), @@ -89,8 +91,6 @@ public extension CollectingRule where Self: AnalyzerRule { } } -// MARK: - == Implementations - /// :nodoc: public extension Array where Element == any Rule { static func == (lhs: Array, rhs: Array) -> Bool { diff --git a/Source/SwiftLintCore/Protocols/Rule.swift b/Source/SwiftLintCore/Protocols/Rule.swift index 5e0214b678e..0c564eaa90a 100644 --- a/Source/SwiftLintCore/Protocols/Rule.swift +++ b/Source/SwiftLintCore/Protocols/Rule.swift @@ -161,15 +161,15 @@ public protocol CorrectableRule: Rule { /// - parameter file: The file for which to correct violations. /// - parameter compilerArguments: The compiler arguments needed to compile this file. /// - /// - returns: All corrections that were applied. - func correct(file: SwiftLintFile, compilerArguments: [String]) -> [Correction] + /// - returns: Number of corrections that were applied. + func correct(file: SwiftLintFile, compilerArguments: [String]) -> Int /// Attempts to correct the violations to this rule in the specified file. /// /// - parameter file: The file for which to correct violations. /// - /// - returns: All corrections that were applied. - func correct(file: SwiftLintFile) -> [Correction] + /// - returns: Number of corrections that were applied. + func correct(file: SwiftLintFile) -> Int /// Attempts to correct the violations to this rule in the specified file after collecting file info for all files /// and returns all corrections that were applied. @@ -181,14 +181,14 @@ public protocol CorrectableRule: Rule { /// - parameter compilerArguments: The compiler arguments needed to compile this file. /// /// - returns: All corrections that were applied. - func correct(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> [Correction] + func correct(file: SwiftLintFile, using storage: RuleStorage, compilerArguments: [String]) -> Int } public extension CorrectableRule { - func correct(file: SwiftLintFile, compilerArguments _: [String]) -> [Correction] { + func correct(file: SwiftLintFile, compilerArguments _: [String]) -> Int { correct(file: file) } - func correct(file: SwiftLintFile, using _: RuleStorage, compilerArguments: [String]) -> [Correction] { + func correct(file: SwiftLintFile, using _: RuleStorage, compilerArguments: [String]) -> Int { correct(file: file, compilerArguments: compilerArguments) } } @@ -213,24 +213,23 @@ public protocol SubstitutionCorrectableRule: CorrectableRule { } public extension SubstitutionCorrectableRule { - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { let violatingRanges = file.ruleEnabled(violatingRanges: violationRanges(in: file), for: self) - guard violatingRanges.isNotEmpty else { return [] } - - let description = Self.description - var corrections = [Correction]() + guard violatingRanges.isNotEmpty else { + return 0 + } + var numberOfCorrections = 0 var contents = file.contents for range in violatingRanges.sorted(by: { $0.location > $1.location }) { let contentsNSString = contents.bridge() if let (rangeToRemove, substitution) = self.substitution(for: range, in: file) { contents = contentsNSString.replacingCharacters(in: rangeToRemove, with: substitution) - let location = Location(file: file, characterOffset: range.location) - corrections.append(Correction(ruleDescription: description, location: location)) + numberOfCorrections += 1 } } file.write(contents) - return corrections + return numberOfCorrections } } @@ -249,7 +248,7 @@ public extension AnalyzerRule { /// :nodoc: public extension AnalyzerRule where Self: CorrectableRule { - func correct(file _: SwiftLintFile) -> [Correction] { + func correct(file _: SwiftLintFile) -> Int { queuedFatalError("Must call `correct(file:compilerArguments:)` for AnalyzerRule") } } diff --git a/Source/SwiftLintCore/Protocols/SwiftSyntaxCorrectableRule.swift b/Source/SwiftLintCore/Protocols/SwiftSyntaxCorrectableRule.swift index f977d9b0502..85eb3a936b1 100644 --- a/Source/SwiftLintCore/Protocols/SwiftSyntaxCorrectableRule.swift +++ b/Source/SwiftLintCore/Protocols/SwiftSyntaxCorrectableRule.swift @@ -17,33 +17,21 @@ public extension SwiftSyntaxCorrectableRule { nil } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { guard let syntaxTree = preprocess(file: file) else { - return [] + return 0 } if let rewriter = makeRewriter(file: file) { let newTree = rewriter.visit(syntaxTree) - let positions = rewriter.correctionPositions - guard positions.isNotEmpty else { - return [] - } - let corrections = positions - .sorted() - .map { position in - Correction( - ruleDescription: Self.description, - location: Location(file: file, position: position) - ) - } file.write(newTree.description) - return corrections + return rewriter.numberOfCorrections } // There is no rewriter. Falling back to the correction ranges collected by the visitor (if any). let violations = makeVisitor(file: file) .walk(tree: syntaxTree, handler: \.violations) guard violations.isNotEmpty else { - return [] + return 0 } let locationConverter = file.locationConverter @@ -64,19 +52,16 @@ public extension SwiftSyntaxCorrectableRule { lhs.range.location > rhs.range.location } guard correctionRanges.isNotEmpty else { - return [] + return 0 } - var corrections = [Correction]() var contents = file.contents for range in correctionRanges { let contentsNSString = contents.bridge() contents = contentsNSString.replacingCharacters(in: range.range, with: range.correction) - let location = Location(file: file, characterOffset: range.range.location) - corrections.append(Correction(ruleDescription: Self.description, location: location)) } file.write(contents) - return corrections + return correctionRanges.count } } @@ -96,8 +81,8 @@ open class ViolationsSyntaxRewriter: SyntaxRew .compactMap { $0.toSourceRange(locationConverter: locationConverter) } }() - /// Positions in a source file where corrections were applied. - public var correctionPositions = [AbsolutePosition]() + /// The number of corrections made by the rewriter. + public var numberOfCorrections = 0 /// Initializer for a ``ViolationsSyntaxRewriter``. /// diff --git a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift index 3f2bcdfbcce..9c80db6ab79 100644 --- a/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift +++ b/Source/SwiftLintFramework/LintOrAnalyzeCommand.swift @@ -330,6 +330,13 @@ package struct LintOrAnalyzeCommand { if options.useSTDIN { queuedPrint(linter.file.contents) } else { + let corrections = corrections.map { + Correction( + ruleName: $0.0, + filePath: linter.file.path, + numberOfCorrections: $0.1 + ) + } if options.progress { await correctionsBuilder.append(corrections) } else { diff --git a/Source/SwiftLintFramework/Models/Linter.swift b/Source/SwiftLintFramework/Models/Linter.swift index 68f111fdc91..b5e04d39280 100644 --- a/Source/SwiftLintFramework/Models/Linter.swift +++ b/Source/SwiftLintFramework/Models/Linter.swift @@ -364,9 +364,9 @@ public struct CollectedLinter { /// - parameter storage: The storage object containing all collected info. /// /// - returns: All corrections that were applied. - public func correct(using storage: RuleStorage) -> [Correction] { + public func correct(using storage: RuleStorage) -> [String: Int] { if let violations = cachedStyleViolations()?.0, violations.isEmpty { - return [] + return [:] } if let parserDiagnostics = file.parserDiagnostics, parserDiagnostics.isNotEmpty { @@ -374,15 +374,17 @@ public struct CollectedLinter { "warning: Skipping correcting file because it produced Swift parser errors: \(file.path ?? "")" ) queuedPrintError(toJSON(["diagnostics": parserDiagnostics])) - return [] + return [:] } - var corrections = [Correction]() + var corrections = [String: Int]() for rule in rules.compactMap({ $0 as? any CorrectableRule }) { - let newCorrections = rule.correct(file: file, using: storage, compilerArguments: compilerArguments) - corrections += newCorrections - if newCorrections.isNotEmpty, !file.isVirtual { - file.invalidateCache() + let corrected = rule.correct(file: file, using: storage, compilerArguments: compilerArguments) + if corrected != 0 { + corrections[type(of: rule).description.identifier] = corrected + if !file.isVirtual { + file.invalidateCache() + } } } return corrections diff --git a/Tests/CLITests/RulesFilterTests.swift b/Tests/CLITests/RulesFilterTests.swift index 8979db66c19..d4952064330 100644 --- a/Tests/CLITests/RulesFilterTests.swift +++ b/Tests/CLITests/RulesFilterTests.swift @@ -173,7 +173,7 @@ private struct CorrectableRuleMock: CorrectableRule { [] } - func correct(file _: SwiftLintFile) -> [Correction] { - [] + func correct(file _: SwiftLintFile) -> Int { + 0 } } diff --git a/Tests/FrameworkTests/CollectingRuleTests.swift b/Tests/FrameworkTests/CollectingRuleTests.swift index bc160a2670a..7e4b7982358 100644 --- a/Tests/FrameworkTests/CollectingRuleTests.swift +++ b/Tests/FrameworkTests/CollectingRuleTests.swift @@ -92,19 +92,11 @@ final class CollectingRuleTests: SwiftLintTestCase { return [] } - func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: String]) -> [Correction] { - if collectedInfo[file] == "baz" { - return [ - Correction( - ruleDescription: Self.description, - location: Location(file: file, byteOffset: 2) - ), - ] - } - return [] + func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: String]) -> Int { + collectedInfo[file] == "baz" ? 1 : 0 } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { correct(file: file, collectedInfo: [file: collectInfo(for: file)]) } } @@ -131,13 +123,11 @@ final class CollectingRuleTests: SwiftLintTestCase { func correct(file: SwiftLintFile, collectedInfo: [SwiftLintFile: String], - compilerArguments _: [String]) -> [Correction] { - collectedInfo[file] == "baz" - ? [Correction(ruleDescription: Spec.description, location: Location(file: file, byteOffset: 2))] - : [] + compilerArguments _: [String]) -> Int { + collectedInfo[file] == "baz" ? 1 : 0 } - func correct(file: SwiftLintFile) -> [Correction] { + func correct(file: SwiftLintFile) -> Int { correct(file: file, collectedInfo: [file: collectInfo(for: file)], compilerArguments: []) } } diff --git a/Tests/IntegrationTests/IntegrationTests.swift b/Tests/IntegrationTests/IntegrationTests.swift index 877686a120c..971a4fd354a 100644 --- a/Tests/IntegrationTests/IntegrationTests.swift +++ b/Tests/IntegrationTests/IntegrationTests.swift @@ -50,15 +50,10 @@ final class IntegrationTests: SwiftLintTestCase { forceExclude: false, excludeByPrefix: false) let storage = RuleStorage() - let corrections = swiftFiles.parallelFlatMap { + let corrections = swiftFiles.parallelMap { Linter(file: $0, configuration: config).collect(into: storage).correct(using: storage) } - for correction in corrections { - correction.location.file!.withStaticString { - XCTFail(correction.ruleDescription.description, - file: $0, line: UInt(correction.location.line!)) - } - } + XCTAssert(corrections.allSatisfy { $0.isEmpty }, "Unexpected corrections have been applied") } func testDefaultConfigurations() { diff --git a/Tests/TestHelpers/TestHelpers.swift b/Tests/TestHelpers/TestHelpers.swift index 9c4551d5d83..68d6e4b170c 100644 --- a/Tests/TestHelpers/TestHelpers.swift +++ b/Tests/TestHelpers/TestHelpers.swift @@ -82,7 +82,7 @@ public extension Collection where Element == String { .violations(config: config, requiresFileOnDisk: requiresFileOnDisk) } - func corrections(config: Configuration = Configuration.default, requiresFileOnDisk: Bool = false) -> [Correction] { + func corrections(config: Configuration = Configuration.default, requiresFileOnDisk: Bool = false) -> [String: Int] { map { SwiftLintFile.testFile(withContents: $0, persistToDisk: requiresFileOnDisk) } .corrections(config: config, requiresFileOnDisk: requiresFileOnDisk) } @@ -103,17 +103,21 @@ public extension Collection where Element: SwiftLintFile { return requiresFileOnDisk ? violations.withoutFiles() : violations } - func corrections(config: Configuration = Configuration.default, requiresFileOnDisk: Bool = false) -> [Correction] { + func corrections(config: Configuration = Configuration.default, requiresFileOnDisk: Bool = false) -> [String: Int] { let storage = RuleStorage() - let corrections = map({ file in - Linter(file: file, configuration: config, - compilerArguments: requiresFileOnDisk ? file.makeCompilerArguments() : []) - }).map({ linter in - linter.collect(into: storage) - }).flatMap({ linter in - linter.correct(using: storage) - }) - return requiresFileOnDisk ? corrections.withoutFiles() : corrections + var corrections = [String: Int]() + for file in self { + let linter = Linter( + file: file, + configuration: config, + compilerArguments: requiresFileOnDisk ? file.makeCompilerArguments() : [] + ) + let collectedLinter = linter.collect(into: storage) + for (ruleName, numberOfCorrections) in collectedLinter.correct(using: storage) { + corrections[ruleName] = numberOfCorrections + } + } + return corrections } } @@ -127,16 +131,6 @@ private extension Collection where Element == StyleViolation { } } -private extension Collection where Element == Correction { - func withoutFiles() -> [Correction] { - map { correction in - let locationWithoutFile = Location(file: nil, line: correction.location.line, - character: correction.location.character) - return Correction(ruleDescription: correction.ruleDescription, location: locationWithoutFile) - } - } -} - public extension Collection where Element == Example { /// Returns a dictionary with SwiftLint violation markers (↓) removed from keys. /// @@ -200,43 +194,22 @@ private func render(locations: [Location], in contents: String) -> String { private extension Configuration { func assertCorrection(_ before: Example, expected: Example) { - let (cleanedBefore, markerOffsets) = cleanedContentsAndMarkerOffsets(from: before.code) + let (cleanedBefore, _) = cleanedContentsAndMarkerOffsets(from: before.code) let file = SwiftLintFile.testFile(withContents: cleanedBefore, persistToDisk: true) // expectedLocations are needed to create before call `correct()` - let expectedLocations = markerOffsets.map { Location(file: file, characterOffset: $0) } let includeCompilerArguments = self.rules.contains(where: { $0 is any AnalyzerRule }) let compilerArguments = includeCompilerArguments ? file.makeCompilerArguments() : [] let storage = RuleStorage() let collector = Linter(file: file, configuration: self, compilerArguments: compilerArguments) let linter = collector.collect(into: storage) - let corrections = linter.correct(using: storage).sorted { $0.location < $1.location } - if expectedLocations.isEmpty { - XCTAssertGreaterThanOrEqual( - corrections.count, - before.code != expected.code ? 1 : 0, - #function + ".expectedLocationsEmpty", - file: before.file, - line: before.line - ) - } else { - XCTAssertEqual( - corrections.count, - expectedLocations.count, - #function + ".expected locations: \(expectedLocations.count)", - file: before.file, line: before.line - ) - // With SwiftSyntax rewriters, the visitors get called with the new nodes after previous mutations have - // been applied, so it's not straightforward to translate those back into the original source positions. - // So only check the first locations - if let firstCorrection = corrections.first { - XCTAssertEqual( - firstCorrection.location, - expectedLocations.first, - #function + ".correction location", - file: before.file, line: before.line - ) - } - } + let corrections = linter.correct(using: storage) + XCTAssertGreaterThanOrEqual( + corrections.count, + before.code != expected.code ? 1 : 0, + #function + ".expectedLocationsEmpty", + file: before.file, + line: before.line + ) XCTAssertEqual( file.contents, expected.code,