<commit_before>import SourceKittenFramework

public struct DuplicateEnumCasesRule: ConfigurationProviderRule, ASTRule {
    public var configuration = SeverityConfiguration(.error)

    public init() {}

    public static let description = RuleDescription(
        identifier: "duplicate_enum_cases",
        name: "Duplicate Enum Cases",
        description: "Enum can't contain multiple cases with the same name.",
        kind: .lint,
        nonTriggeringExamples: [
            Example("""
            enum PictureImport {
                case addImage(image: UIImage)
                case addData(data: Data)
            }
            """),
            Example("""
            enum A {
                case add(image: UIImage)
            }
            enum B {
                case add(image: UIImage)
            }
            """)
        ],
        triggeringExamples: [
            Example("""
            enum PictureImport {
                case ↓add(image: UIImage)
                case addURL(url: URL)
                case ↓add(data: Data)
            }
            """)
        ]
    )

    public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
                         dictionary: SourceKittenDictionary) -> [StyleViolation] {
        guard kind == .enum else {
            return []
        }

        let enumElements = substructureElements(of: dictionary, matching: .enumcase)
            .compactMap { substructureElements(of: $0, matching: .enumelement) }
            .flatMap { $0 }

        var elementsByName: [String: [ByteCount]] = [:]
        for element in enumElements {
            guard let name = element.name,
                let nameWithoutParameters = name.split(separator: "(").first,
                let offset = element.offset
            else {
                continue
            }

            elementsByName[String(nameWithoutParameters), default: []].append(offset)
        }

        return elementsByName.filter { $0.value.count > 1 }
            .flatMap { $0.value }
            .map {
                StyleViolation(ruleDescription: Self.description,
                               severity: configuration.severity,
                               location: Location(file: file, byteOffset: $0))
            }
    }

    private func substructureElements(of dict: SourceKittenDictionary,
                                      matching kind: SwiftDeclarationKind) -> [SourceKittenDictionary] {
        return dict.substructure
            .filter { $0.declarationKind == kind }<commit_msg>Convert `duplicate_enum_cases` rule to SwiftSyntax (#4263)<commit_after>import SwiftSyntax

public struct DuplicateEnumCasesRule: ConfigurationProviderRule, SwiftSyntaxRule {
    public var configuration = SeverityConfiguration(.error)

    public init() {}

    public static let description = RuleDescription(
        identifier: "duplicate_enum_cases",
        name: "Duplicate Enum Cases",
        description: "Enum can't contain multiple cases with the same name.",
        kind: .lint,
        nonTriggeringExamples: [
            Example("""
            enum PictureImport {
                case addImage(image: UIImage)
                case addData(data: Data)
            }
            """),
            Example("""
            enum A {
                case add(image: UIImage)
            }
            enum B {
                case add(image: UIImage)
            }
            """)
        ],
        triggeringExamples: [
            Example("""
            enum PictureImport {
                case ↓add(image: UIImage)
                case addURL(url: URL)
                case ↓add(data: Data)
            }
            """)
        ]
    )

    public func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor? {
        Visitor(viewMode: .sourceAccurate)
    }
}

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

        override func visitPost(_ node: EnumDeclSyntax) {
            let enumElements = node.members.members
                .flatMap { member -> EnumCaseElementListSyntax in
                    guard let enumCaseDecl = member.decl.as(EnumCaseDeclSyntax.self) else {
                        return EnumCaseElementListSyntax([])
                    }

                    return enumCaseDecl.elements
                }

            var elementsByName: [String: [AbsolutePosition]] = [:]
            for element in enumElements {
                let name = element.identifier.text
                elementsByName[String(name), default: []].append(element.positionAfterSkippingLeadingTrivia)
            }

            let duplicatedElementPositions = elementsByName
                .filter { $0.value.count > 1 }
                .flatMap { $0.value }

            violationPositions.append(contentsOf: duplicatedElementPositions)
        }
{'fraction_non_alphanumeric': 0.08528225806451613, 'fraction_numerical': 0.00282258064516129, 'mean_word_length': 1.6515232495991448, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2276087', 'n_tokens_mistral': 1238, 'n_tokens_neox': 1167, 'n_words': 290}