             Example("do {\n    try foo() \n} ↓catch (let error) {}"): Example("do {\n    try foo() \n} catch {}")
         ])
 
-    public func validate(file: SwiftLintFile) -> [StyleViolation] {
-        return violationRanges(in: file).map {
-            return StyleViolation(ruleDescription: Self.description,
-                                  severity: configuration.severity,
-                                  location: Location(file: file, characterOffset: $0.location))
-        }
+    public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
+        UntypedErrorInCatchRuleVisitor()
     }
 
-    fileprivate func violationRanges(in file: SwiftLintFile) -> [NSRange] {
-        return file.match(pattern: Self.regularExpression,
-                          with: [.keyword, .keyword, .identifier])
+    public func makeRewriter(file: SwiftLintFile) -> ViolationsSyntaxRewriter? {
+        file.locationConverter.map { locationConverter in
+            UntypedErrorInCatchRuleRewriter(
+                locationConverter: locationConverter,
+                disabledRegions: disabledRegions(file: file)
+            )
+        }
     }
 }
 
-extension UntypedErrorInCatchRule: CorrectableRule {
-    public func correct(file: SwiftLintFile) -> [Correction] {
-        let violations = violationRanges(in: file)
-        let matches = file.ruleEnabled(violatingRanges: violations, for: self)
-        if matches.isEmpty { return [] }
+private extension CatchItemSyntax {
+    var isIdentifierPattern: Bool {
+        guard whereClause == nil else {
+            return false
+        }
 
-        var contents = file.contents.bridge()
-        let description = Self.description
-        var corrections = [Correction]()
+        if let pattern = pattern?.as(ValueBindingPatternSyntax.self) {
+            return pattern.valuePattern.is(IdentifierPatternSyntax.self)
+        }
 
-        for range in matches.reversed() where contents.substring(with: range).contains("let error") {
-            contents = contents.replacingCharacters(in: range, with: "catch {").bridge()
-            let location = Location(file: file, characterOffset: range.location)
-            corrections.append(Correction(ruleDescription: description, location: location))
+        if let pattern = pattern?.as(ExpressionPatternSyntax.self),
+           let tupleExpr = pattern.expression.as(TupleExprSyntax.self),
+           tupleExpr.elementList.count == 1,
+           let tupleElement = tupleExpr.elementList.first,
+           let unresolvedPattern = tupleElement.expression.as(UnresolvedPatternExprSyntax.self),
+           let valueBindingPattern = unresolvedPattern.pattern.as(ValueBindingPatternSyntax.self) {
+            return valueBindingPattern.valuePattern.is(IdentifierPatternSyntax.self)
         }
-        file.write(contents.bridge())
-        return corrections
+
+        return false
     }
 }
+
+private final class UntypedErrorInCatchRuleVisitor: SyntaxVisitor, ViolationsSyntaxVisitor {
+    private(set) var violationPositions: [AbsolutePosition] = []
+
+    override func visitPost(_ node: CatchClauseSyntax) {
+        guard node.catchItems?.count == 1,
+              let item = node.catchItems?.first,
+              item.isIdentifierPattern else {
+            return
+        }
+
+        violationPositions.append(node.catchKeyword.positionAfterSkippingLeadingTrivia)
+    }
+}
+
+private final class UntypedErrorInCatchRuleRewriter: SyntaxRewriter, ViolationsSyntaxRewriter {
+    private(set) var correctionPositions: [AbsolutePosition] = []
+    let locationConverter: SourceLocationConverter
+    let disabledRegions: [SourceRange]
+
+    init(locationConverter: SourceLocationConverter, disabledRegions: [SourceRange]) {
+        self.locationConverter = locationConverter
+        self.disabledRegions = disabledRegions
+    }
+
+    override func visit(_ node: CatchClauseSyntax) -> Syntax {
+        guard node.catchItems?.count == 1,
+              let item = node.catchItems?.first,
+              item.isIdentifierPattern else {
+            return super.visit(node)
+        }
+
+        let isInDisabledRegion = disabledRegions.contains { region in
+            region.contains(node.positionAfterSkippingLeadingTrivia, locationConverter: locationConverter)
+        }
+
+        guard !isInDisabledRegion else {
+            return super.visit(node)
+        }
+
+        correctionPositions.append(node.catchKeyword.positionAfterSkippingLeadingTrivia)
+        return super.visit(
+            node
+                .withCatchKeyword(node.catchKeyword.withTrailingTrivia(.spaces(1)))
+                .withCatchItems(SyntaxFactory.makeBlankCatchItemList())
+        )
+    }
+}

              try foo()
            } catch var error as MyError {
            } catch {}
            """)
        ],
        triggeringExamples: [

import SwiftSyntax

public struct UntypedErrorInCatchRule: OptInRule, ConfigurationProviderRule, SwiftSyntaxCorrectableRule {
    public var configuration = SeverityConfiguration(.warning)

    public init() {}

    public static let description = RuleDescription(
        identifier: "untyped_error_in_catch",
