         ]
     )
 
-    private static let terminatingFunctions = Set([
-        "abort",
-        "fatalError",
-        "preconditionFailure"
-    ])
+    public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
+        Visitor(viewMode: .sourceAccurate)
+    }
+}
 
-    public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
-                         dictionary: SourceKittenDictionary) -> [StyleViolation] {
-        guard SwiftDeclarationKind.functionKinds.contains(kind) else {
-            return []
+private extension UnavailableFunctionRule {
+    final class Visitor: ViolationsSyntaxVisitor {
+        override func visitPost(_ node: FunctionDeclSyntax) {
+            guard !node.returnsNever,
+                  !node.attributes.hasUnavailableAttribute,
+                  node.body.containsTerminatingCall,
+                  !node.body.containsReturn else {
+                return
+            }
+
+            violations.append(node.funcKeyword.positionAfterSkippingLeadingTrivia)
         }
 
-        let containsTerminatingCall = dictionary.substructure.contains { dict -> Bool in
-            return dict.expressionKind == .call && (dict.name.map { name in
-                Self.terminatingFunctions.contains(name)
-            } ?? false)
-        }
-
-        let hasReturnTypeNever = dictionary.typeName == "Never"
-
-        guard
-            containsTerminatingCall,
-            !hasReturnTypeNever,
-            !isFunctionUnavailable(file: file, dictionary: dictionary),
-            let offset = dictionary.offset,
-            let bodyRange = dictionary.bodyByteRange,
-            let range = file.stringView.byteRangeToNSRange(bodyRange),
-            file.match(pattern: "\\breturn\\b", with: [.keyword], range: range).isEmpty
-        else {
-            return []
-        }
-
-        return [
-            StyleViolation(ruleDescription: Self.description,
-                           severity: configuration.severity,
-                           location: Location(file: file, byteOffset: offset))
-        ]
-    }
-
-    private func isFunctionUnavailable(file: SwiftLintFile, dictionary: SourceKittenDictionary) -> Bool {
-        return dictionary.swiftAttributes.contains { dict -> Bool in
-            guard dict.attribute.flatMap(SwiftDeclarationAttributeKind.init(rawValue:)) == .available,
-                let byteRange = dict.byteRange,
-                let contents = file.stringView.substringWithByteRange(byteRange) else {
-                    return false
+        override func visitPost(_ node: InitializerDeclSyntax) {
+            guard !node.attributes.hasUnavailableAttribute,
+                  node.body.containsTerminatingCall,
+                  !node.body.containsReturn else {
+                return
             }
 
-            return contents.contains("unavailable")
+            violations.append(node.initKeyword.positionAfterSkippingLeadingTrivia)
         }
     }
 }
+
+private extension FunctionDeclSyntax {
+    var returnsNever: Bool {
+        if let expr = signature.output?.returnType.as(SimpleTypeIdentifierSyntax.self) {
+            return expr.name.withoutTrivia().text == "Never"
+        }
+        return false
+    }
+}
+
+private extension AttributeListSyntax? {
+    var hasUnavailableAttribute: Bool {
+        guard let attrs = self else {
+            return false
+        }
+
+        return attrs.contains { elem in
+            guard let attr = elem.as(AttributeSyntax.self),
+                    let arguments = attr.argument?.as(AvailabilitySpecListSyntax.self) else {
+                return false
+            }
+
+            return attr.attributeName.tokenKind == .contextualKeyword("available") && arguments.contains { arg in
+                arg.entry.as(TokenSyntax.self)?.tokenKind == .contextualKeyword("unavailable")
+            }
+        }
+    }
+}
+
+private extension CodeBlockSyntax? {
+    var containsTerminatingCall: Bool {
+        guard let statements = self?.statements else {
+            return false
+        }
+
+        let terminatingFunctions: Set = [
+            "abort",
+            "fatalError",
+            "preconditionFailure"
+        ]
+
+        return statements.contains { item in
+            guard let function = item.item.as(FunctionCallExprSyntax.self),
+                  let identifierExpr = function.calledExpression.as(IdentifierExprSyntax.self) else {
+                return false
+            }
+
+            return terminatingFunctions.contains(identifierExpr.identifier.withoutTrivia().text)
+        }
+    }
+
+    var containsReturn: Bool {
+        guard let statements = self?.statements else {
+            return false
+        }
+
+        return ReturnFinderVisitor(viewMode: .sourceAccurate)
+            .walk(tree: statements, handler: \.containsReturn)
+    }
+}
+
+private final class ReturnFinderVisitor: SyntaxVisitor {
+    private(set) var containsReturn = false
+
+    override func visitPost(_ node: ReturnStmtSyntax) {
+        containsReturn = true
+    }
+
+    override func visit(_ node: ClosureExprSyntax) -> SyntaxVisitorContinueKind {
+        .skipChildren
+    }
+
+    override func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind {
+        .skipChildren
+    }
+}

            }
            """),
            Example("""
            func resetOnboardingStateAndCrash() {
                resetUserDefaults()
                // Crash the app to re-start the onboarding flow.
                fatalError("Onboarding re-start crash.")

import SwiftSyntax

public struct UnavailableFunctionRule: SwiftSyntaxRule, ConfigurationProviderRule, OptInRule {
    public var configuration = SeverityConfiguration(.warning)

    public init() {}
