import SwiftSyntax @SwiftSyntaxRule(optIn: true) struct DiscouragedNoneNameRule: Rule { var configuration = SeverityConfiguration(.warning) static let description = RuleDescription( identifier: "discouraged_none_name", name: "Discouraged None Name", description: "Enum cases and static members named `none` are discouraged as they can conflict with " + "`Optional.none`.", kind: .idiomatic, nonTriggeringExamples: [ // Should not trigger unless exactly matches "none" Example(""" enum MyEnum { case nOne } """), Example(""" enum MyEnum { case _none } """), Example(""" enum MyEnum { case none_ } """), Example(""" enum MyEnum { case none(Any) } """), Example(""" enum MyEnum { case nonenone } """), Example(""" class MyClass { class var nonenone: MyClass { MyClass() } } """), Example(""" class MyClass { static var nonenone = MyClass() } """), Example(""" class MyClass { static let nonenone = MyClass() } """), Example(""" struct MyStruct { static var nonenone = MyStruct() } """), Example(""" struct MyStruct { static let nonenone = MyStruct() } """), // Should not trigger if not an enum case or static/class member Example(""" struct MyStruct { let none = MyStruct() } """), Example(""" struct MyStruct { var none = MyStruct() } """), Example(""" class MyClass { let none = MyClass() } """), Example(""" class MyClass { var none = MyClass() } """), ], triggeringExamples: [ Example(""" enum MyEnum { case ↓none } """), Example(""" enum MyEnum { case a, ↓none } """), Example(""" enum MyEnum { case ↓none, b } """), Example(""" enum MyEnum { case a, ↓none, b } """), Example(""" enum MyEnum { case a case ↓none } """), Example(""" enum MyEnum { case ↓none case b } """), Example(""" enum MyEnum { case a case ↓none case b } """), Example(""" class MyClass { ↓static let none = MyClass() } """), Example(""" class MyClass { ↓static let none: MyClass = MyClass() } """), Example(""" class MyClass { ↓static var none: MyClass = MyClass() } """), Example(""" class MyClass { ↓class var none: MyClass { MyClass() } } """), Example(""" struct MyStruct { ↓static var none = MyStruct() } """), Example(""" struct MyStruct { ↓static var none: MyStruct = MyStruct() } """), Example(""" struct MyStruct { ↓static var none = MyStruct() } """), Example(""" struct MyStruct { ↓static var none: MyStruct = MyStruct() } """), Example(""" struct MyStruct { ↓static var a = MyStruct(), none = MyStruct() } """), Example(""" struct MyStruct { ↓static var none = MyStruct(), a = MyStruct() } """), ] ) } private extension DiscouragedNoneNameRule { final class Visitor: ViolationsSyntaxVisitor { override func visitPost(_ node: EnumCaseElementSyntax) { let emptyParams = node.parameterClause?.parameters.isEmpty ?? true if emptyParams, node.name.isNone { violations.append(ReasonedRuleViolation( position: node.positionAfterSkippingLeadingTrivia, reason: reason(type: "`case`") )) } } override func visitPost(_ node: VariableDeclSyntax) { let type: String? = { if node.modifiers.contains(keyword: .class) { return "`class` member" } if node.modifiers.contains(keyword: .static) { return "`static` member" } return nil }() guard let type else { return } for binding in node.bindings { guard let pattern = binding.pattern.as(IdentifierPatternSyntax.self), pattern.identifier.isNone else { continue } violations.append(ReasonedRuleViolation( position: node.positionAfterSkippingLeadingTrivia, reason: reason(type: type) )) return } } private func reason(type: String) -> String { let reason = "Avoid naming \(type) `none` as the compiler can think you mean `Optional.none`" let recommendation = "consider using an Optional value instead" return "\(reason); \(recommendation)" } } } private extension TokenSyntax { var isNone: Bool { tokenKind == .identifier("none") || tokenKind == .identifier("`none`") } }