Skip to content

Commit 2423d7b

Browse files
LaurenWhiteallevato
authored andcommitted
Implement DontRepeatTypeInStaticProperties rule and tests. (swiftlang#78)
1 parent 03b1ba3 commit 2423d7b

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed

tools/swift-format/Sources/Rules/DontRepeatTypeInStaticProperties.swift

+72
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,77 @@ import SwiftSyntax
1212
///
1313
/// - SeeAlso: https://google.github.io/swift#static-and-class-properties
1414
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+
}
1583

84+
extension Diagnostic.Message {
85+
static func removeTypeFromName(name: String, type: String) -> Diagnostic.Message {
86+
return .init(.warning, "Remove '\(type)' from '\(name)'")
87+
}
1688
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import Foundation
2+
import SwiftSyntax
3+
import XCTest
4+
5+
@testable import Rules
6+
7+
public class DontRepeatTypeInStaticPropertiesTests: DiagnosingTestCase {
8+
public func testRepetitiveProperties() {
9+
let input =
10+
"""
11+
public class UIColor {
12+
static let redColor: UIColor
13+
public class var blueColor: UIColor
14+
var yellowColor: UIColor
15+
static let green: UIColor
16+
public class var purple: UIColor
17+
}
18+
enum Sandwich {
19+
static let bolognaSandwich: Sandwich
20+
static var hamSandwich: Sandwich
21+
static var turkey: Sandwich
22+
}
23+
protocol RANDPerson {
24+
var oldPerson: Person
25+
static let youngPerson: Person
26+
}
27+
struct TVGame {
28+
static var basketballGame: TVGame
29+
static var baseballGame: TVGame
30+
static let soccer: TVGame
31+
let hockey: TVGame
32+
}
33+
extension URLSession {
34+
class var sharedSession: URLSession
35+
}
36+
"""
37+
performLint(DontRepeatTypeInStaticProperties.self, input: input)
38+
XCTAssertDiagnosed(.removeTypeFromName(name: "redColor", type: "Color"))
39+
XCTAssertDiagnosed(.removeTypeFromName(name: "blueColor", type: "Color"))
40+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "yellowColor", type: "Color"))
41+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "green", type: "Color"))
42+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "purple", type: "Color"))
43+
44+
XCTAssertDiagnosed(.removeTypeFromName(name: "bolognaSandwich", type: "Sandwich"))
45+
XCTAssertDiagnosed(.removeTypeFromName(name: "hamSandwich", type: "Sandwich"))
46+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "turkey", type: "Sandwich"))
47+
48+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "oldPerson", type: "Person"))
49+
XCTAssertDiagnosed(.removeTypeFromName(name: "youngPerson", type: "Person"))
50+
51+
XCTAssertDiagnosed(.removeTypeFromName(name: "basketballGame", type: "Game"))
52+
XCTAssertDiagnosed(.removeTypeFromName(name: "baseballGame", type: "Game"))
53+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "soccer", type: "Game"))
54+
XCTAssertNotDiagnosed(.removeTypeFromName(name: "hockey", type: "Game"))
55+
56+
XCTAssertDiagnosed(.removeTypeFromName(name: "sharedSession", type: "Session"))
57+
}
58+
59+
#if !os(macOS)
60+
static let allTests = [
61+
DontRepeatTypeInStaticPropertiesTests.testRepetitiveProperties,
62+
]
63+
#endif
64+
}
65+

0 commit comments

Comments
 (0)