         ]
     )
 
-    public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
-                         dictionary: SourceKittenDictionary) -> [StyleViolation] {
-        guard kind == .varInstance else {
-            return []
-        }
-
-        // Check if IBInspectable
-        let isIBInspectable = dictionary.enclosedSwiftAttributes.contains(.ibinspectable)
-        guard isIBInspectable else {
-            return []
-        }
-
-        let shouldMakeViolation: Bool
-        if !file.isMutableProperty(dictionary) {
-            shouldMakeViolation = true
-        } else if let type = dictionary.typeName,
-            Self.supportedTypes.contains(type) {
-            shouldMakeViolation = false
-        } else {
-            // Variable should have explicit type or IB won't recognize it
-            // Variable should be of one of the supported types
-            shouldMakeViolation = true
-        }
-
-        guard shouldMakeViolation else {
-            return []
-        }
-
-        let location: Location
-        if let offset = dictionary.offset {
-            location = Location(file: file, byteOffset: offset)
-        } else {
-            location = Location(file: file.path)
-        }
-
-        return [
-            StyleViolation(ruleDescription: Self.description,
-                           severity: configuration.severity,
-                           location: location)
-        ]
-    }
-
-    private static func createSupportedTypes() -> [String] {
+    public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
+        Visitor(viewMode: .sourceAccurate)
+    }
+
+    fileprivate static var supportedTypes: Set<String> = {
         // "You can add the IBInspectable attribute to any property in a class declaration,
         // class extension, or category of type: boolean, integer or floating point number, string,
         // localized string, rectangle, point, size, color, range, and nil."

import Foundation
import SourceKittenFramework

public struct ValidIBInspectableRule: ASTRule, ConfigurationProviderRule {
    public var configuration = SeverityConfiguration(.warning)

    private static let supportedTypes = Self.createSupportedTypes()

    public init() {}


        let expandToIncludeOptionals: (String) -> [String] = { [$0, $0 + "!", $0 + "?"] }

        // It seems that only reference types can be used as ImplicitlyUnwrappedOptional or Optional
        return Set(referenceTypes.flatMap(expandToIncludeOptionals) + types + intTypes)
    }()
}

private extension ValidIBInspectableRule {
    final class Visitor: SyntaxVisitor, ViolationsSyntaxVisitor {
        private(set) var violationPositions: [AbsolutePosition] = []

        override func visitPost(_ node: VariableDeclSyntax) {
            if node.isInstanceVariable, node.isIBInspectable, node.hasViolation {
                violationPositions.append(node.letOrVarKeyword.positionAfterSkippingLeadingTrivia)
            }
        }

        override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
            .skipChildren
        }
    }
}

private extension VariableDeclSyntax {
    var isInstanceVariable: Bool {
        guard let modifiers = modifiers else {
            return true
        }

        return !modifiers.contains { modifier in
            modifier.name.text == "static" || modifier.name.text == "class"
        }
    }

    var isIBInspectable: Bool {
        attributes?.contains { attr in
            attr.as(AttributeSyntax.self)?.attributeName.text == "IBInspectable"
        } ?? false
    }

    var hasViolation: Bool {
        isReadOnlyProperty || !isSupportedType
    }

    var isReadOnlyProperty: Bool {
        if letOrVarKeyword.tokenKind == .letKeyword {
            return true
        }

        let computedProperty = bindings.contains { binding in
            binding.accessor != nil
        }

        if !computedProperty {
            return false
        }

        return bindings.allSatisfy { binding in
            guard let accessorBlock = binding.accessor?.as(AccessorBlockSyntax.self) else {
                return true
            }

            // if it has a `get`, it needs to have a `set`, otherwise it's readonly
            if accessorBlock.containsGetAccessor {
                return !accessorBlock.containsSetAccessor
            }

            return false
        }
    }

    var isSupportedType: Bool {
        bindings.allSatisfy { binding in
            guard let type = binding.typeAnnotation else {
                return false
            }

            return ValidIBInspectableRule.supportedTypes.contains(type.type.withoutTrivia().description)
        }
    }
}

private extension AccessorBlockSyntax {
    var containsSetAccessor: Bool {
        return accessors.contains { accessor in
            accessor.accessorKind.tokenKind == .contextualKeyword("set")
        }
    }

    var containsGetAccessor: Bool {
        return accessors.contains { accessor in
            accessor.accessorKind.tokenKind == .contextualKeyword("get")
        }
    }
}
