@@ -12,5 +12,77 @@ import SwiftSyntax
12
12
///
13
13
/// - SeeAlso: https://google.github.io/swift#static-and-class-properties
14
14
public final class DontRepeatTypeInStaticProperties : SyntaxLintRule {
15
+
16
+ public override func visit( _ node: ClassDeclSyntax ) {
17
+ determinePropertyNameViolations ( members: node. members. members, nodeId: node. identifier. text)
18
+ }
19
+
20
+ public override func visit( _ node: EnumDeclSyntax ) {
21
+ determinePropertyNameViolations ( members: node. members. members, nodeId: node. identifier. text)
22
+ }
23
+
24
+ public override func visit( _ node: ProtocolDeclSyntax ) {
25
+ determinePropertyNameViolations ( members: node. members. members, nodeId: node. identifier. text)
26
+ }
27
+
28
+ public override func visit( _ node: StructDeclSyntax ) {
29
+ determinePropertyNameViolations ( members: node. members. members, nodeId: node. identifier. text)
30
+ }
31
+
32
+ public override func visit( _ node: ExtensionDeclSyntax ) {
33
+ determinePropertyNameViolations ( members: node. members. members, nodeId: node. extendedType. description)
34
+ }
35
+
36
+ func determinePropertyNameViolations( members: MemberDeclListSyntax , nodeId: String ) {
37
+ for member in members {
38
+ guard let decl = member. decl as? VariableDeclSyntax else { continue }
39
+ guard let modifiers = decl. modifiers else { continue }
40
+ guard containsClassOrStatic ( modifiers: modifiers) else { continue }
41
+
42
+ let typeName = withoutPrefix ( name: nodeId)
43
+ var varName = " "
44
+
45
+ for binding in decl. bindings {
46
+ guard let exp = binding. pattern as? IdentifierPatternSyntax else { continue }
47
+ varName = exp. identifier. text
48
+ }
49
+
50
+ if varName. contains ( typeName) {
51
+ diagnose ( . removeTypeFromName( name: varName, type: typeName) , on: decl)
52
+ }
53
+ }
54
+ }
55
+
56
+ func containsClassOrStatic( modifiers: ModifierListSyntax ) -> Bool {
57
+ for modifier in modifiers {
58
+ let name = modifier. name. text
59
+ if name == " class " || name == " static " { return true }
60
+ }
61
+ return false
62
+ }
63
+
64
+ // Returns the given string without capitalized prefix in the beginning
65
+ func withoutPrefix( name: String ) -> String {
66
+ let formattedName = name. trimmingCharacters ( in: CharacterSet . whitespaces)
67
+ let upperCase = Array ( formattedName. uppercased ( ) )
68
+ let original = Array ( formattedName)
69
+ guard original [ 0 ] == upperCase [ 0 ] else { return name }
70
+
71
+ var prefixEndsAt = 0
72
+ var idx = 0
73
+ while idx <= name. count - 2 {
74
+ if original [ idx] == upperCase [ idx] &&
75
+ original [ idx + 1 ] != upperCase [ idx + 1 ] {
76
+ prefixEndsAt = idx
77
+ }
78
+ idx += 1
79
+ }
80
+ return String ( formattedName. dropFirst ( prefixEndsAt) )
81
+ }
82
+ }
15
83
84
+ extension Diagnostic . Message {
85
+ static func removeTypeFromName( name: String , type: String ) -> Diagnostic . Message {
86
+ return . init( . warning, " Remove ' \( type) ' from ' \( name) ' " )
87
+ }
16
88
}
0 commit comments