From 66b406452cab5f1e35b4d29ba929855592541fbd Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 5 Dec 2023 08:46:14 +0100 Subject: [PATCH 01/12] rule v1 --- .../Models/BuiltInRules.swift | 1 + .../Lint/AccessibilityFontScaleRule.swift | 101 ++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift diff --git a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift index 4615c17caf..2f8e437745 100644 --- a/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift +++ b/Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift @@ -3,6 +3,7 @@ /// The rule list containing all available rules built into SwiftLint. public let builtInRules: [any Rule.Type] = [ + AccessibilityFontSizeRule.self, AccessibilityLabelForImageRule.self, AccessibilityTraitForButtonRule.self, AnonymousArgumentInMultilineClosureRule.self, diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift new file mode 100644 index 0000000000..5c04a2a9da --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift @@ -0,0 +1,101 @@ +import SourceKittenFramework + +struct AccessibilityFontSizeRule: ASTRule, OptInRule { + var configuration = SeverityConfiguration(.warning) + + static let description = RuleDescription( + identifier: "accessibility_font_size", + name: "Accessibility Font Size", + description: """ + Text may not have a fixed font size + """, + kind: .lint, + minSwiftVersion: .fiveDotOne, + nonTriggeringExamples: AccessibilityLabelForImageRuleExamples.nonTriggeringExamples, + triggeringExamples: AccessibilityLabelForImageRuleExamples.triggeringExamples + ) + + // MARK: AST Rule + + func validate(file: SwiftLintFile, kind: SwiftDeclarationKind, + dictionary: SourceKittenDictionary) -> [StyleViolation] { + // Only proceed to check View structs. + guard (( kind == .struct && dictionary.inheritedTypes.contains("View")) || kind == .extension), + dictionary.substructure.isNotEmpty else { + return [] + } + + return findTextViolations(file: file, substructure: dictionary.substructure) + } + + /// Recursively check a file for image violations, and return all such violations. + private func findTextViolations(file: SwiftLintFile, substructure: [SourceKittenDictionary]) -> [StyleViolation] { + var violations = [StyleViolation]() + for dictionary in substructure { + guard let offset: ByteCount = dictionary.offset else { + continue + } + + // If it's image, and does not hide from accessibility or provide a label, it's a violation. + if dictionary.isText && dictionary.hasStrictFontModifier(in: file) { + violations.append( + StyleViolation(ruleDescription: Self.description, + severity: configuration.severity, + location: Location(file: file, byteOffset: offset)) + ) + } + + // If dictionary did not represent an Image, recursively check substructure, + // unless it's a container that hides its children from accessibility or is labeled. +// else if dictionary.substructure.isNotEmpty { +// if dictionary.hasAccessibilityHiddenModifier(in: file) || +// dictionary.hasAccessibilityElementChildrenIgnoreModifier(in: file) || +// dictionary.hasStrictFontModifier(in: file) { +// continue +// } +// +// violations.append(contentsOf: findTextViolations(file: file, substructure: dictionary.substructure)) +// } + } + + return violations + } +} + +// MARK: SourceKittenDictionary extensions + +private extension SourceKittenDictionary { + /// Whether or not the dictionary represents a SwiftUI Image. + /// Currently only accounts for SwiftUI image literals and not instance variables. + var isText: Bool { + // Image literals will be reported as calls to the initializer. + guard expressionKind == .call else { + return false + } + + if name == "Text" || name == "SwiftUI.Text" { + return true + } + + return substructure.contains(where: { $0.isText }) + } + + /// Whether or not the dictionary represents a SwiftUI View with an `accesibilityLabel(_:)` + /// or `accessibility(label:)` modifier. + func hasStrictFontModifier(in file: SwiftLintFile) -> Bool { + return hasModifier( + anyOf: [ + SwiftUIModifier( + name: "font", + arguments: [ + .init( + name: "", + values: [".system"], + matchType: .prefix) + ] + ), + ], + in: file + ) + } +} From 5fb47664e1d147db1352b69932a4d44b377bb794 Mon Sep 17 00:00:00 2001 From: Martijn Date: Wed, 6 Dec 2023 14:28:30 +0100 Subject: [PATCH 02/12] Improvements + tests --- ....swift => AccessibilityFontSizeRule.swift} | 44 +++++++++++-------- Tests/GeneratedTests/GeneratedTests.swift | 6 +++ 2 files changed, 32 insertions(+), 18 deletions(-) rename Source/SwiftLintBuiltInRules/Rules/Lint/{AccessibilityFontScaleRule.swift => AccessibilityFontSizeRule.swift} (73%) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift similarity index 73% rename from Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift rename to Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index 5c04a2a9da..b1565d2239 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontScaleRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -6,13 +6,28 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { static let description = RuleDescription( identifier: "accessibility_font_size", name: "Accessibility Font Size", - description: """ - Text may not have a fixed font size - """, + description: "Text may not have a fixed font size", kind: .lint, minSwiftVersion: .fiveDotOne, - nonTriggeringExamples: AccessibilityLabelForImageRuleExamples.nonTriggeringExamples, - triggeringExamples: AccessibilityLabelForImageRuleExamples.triggeringExamples + nonTriggeringExamples: [ + Example(""" + struct TestView: View { + var body: some View { + Text("Hello World!") + } + } + """) + ], + triggeringExamples: [ + Example(""" + struct TestView: View { + var body: some View { + Text("Hello World!") + .font(.system(size: 20)) + } + } + """) + ] ) // MARK: AST Rule @@ -20,7 +35,7 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { func validate(file: SwiftLintFile, kind: SwiftDeclarationKind, dictionary: SourceKittenDictionary) -> [StyleViolation] { // Only proceed to check View structs. - guard (( kind == .struct && dictionary.inheritedTypes.contains("View")) || kind == .extension), + guard ( kind == .struct && dictionary.inheritedTypes.contains("View")) || kind == .extension, dictionary.substructure.isNotEmpty else { return [] } @@ -36,7 +51,6 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { continue } - // If it's image, and does not hide from accessibility or provide a label, it's a violation. if dictionary.isText && dictionary.hasStrictFontModifier(in: file) { violations.append( StyleViolation(ruleDescription: Self.description, @@ -47,15 +61,9 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { // If dictionary did not represent an Image, recursively check substructure, // unless it's a container that hides its children from accessibility or is labeled. -// else if dictionary.substructure.isNotEmpty { -// if dictionary.hasAccessibilityHiddenModifier(in: file) || -// dictionary.hasAccessibilityElementChildrenIgnoreModifier(in: file) || -// dictionary.hasStrictFontModifier(in: file) { -// continue -// } -// -// violations.append(contentsOf: findTextViolations(file: file, substructure: dictionary.substructure)) -// } + else if dictionary.substructure.isNotEmpty && dictionary.hasStrictFontModifier(in: file) { + violations.append(contentsOf: findTextViolations(file: file, substructure: dictionary.substructure)) + } } return violations @@ -89,11 +97,11 @@ private extension SourceKittenDictionary { name: "font", arguments: [ .init( - name: "", + name: "", values: [".system"], matchType: .prefix) ] - ), + ) ], in: file ) diff --git a/Tests/GeneratedTests/GeneratedTests.swift b/Tests/GeneratedTests/GeneratedTests.swift index 078c6f614e..0ba9b8a129 100644 --- a/Tests/GeneratedTests/GeneratedTests.swift +++ b/Tests/GeneratedTests/GeneratedTests.swift @@ -8,6 +8,12 @@ import SwiftLintTestHelpers // swiftlint:disable:next blanket_disable_command // swiftlint:disable file_length single_test_class type_name +class AccessibilityFontSizeRuleGeneratedTests: SwiftLintTestCase { + func testWithDefaultConfiguration() { + verifyRule(AccessibilityFontSizeRule.description) + } +} + class AccessibilityLabelForImageRuleGeneratedTests: SwiftLintTestCase { func testWithDefaultConfiguration() { verifyRule(AccessibilityLabelForImageRule.description) From 7e3641fc9581f25fa2b2874dc434f4f8df8fe35c Mon Sep 17 00:00:00 2001 From: Martijn Date: Mon, 11 Dec 2023 14:11:07 +0100 Subject: [PATCH 03/12] Updated comments --- .../Rules/Lint/AccessibilityFontSizeRule.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index b1565d2239..780bd5bfff 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -59,7 +59,7 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { ) } - // If dictionary did not represent an Image, recursively check substructure, + // If dictionary did not represent an Text, recursively check substructure, // unless it's a container that hides its children from accessibility or is labeled. else if dictionary.substructure.isNotEmpty && dictionary.hasStrictFontModifier(in: file) { violations.append(contentsOf: findTextViolations(file: file, substructure: dictionary.substructure)) @@ -73,10 +73,10 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { // MARK: SourceKittenDictionary extensions private extension SourceKittenDictionary { - /// Whether or not the dictionary represents a SwiftUI Image. - /// Currently only accounts for SwiftUI image literals and not instance variables. + /// Whether or not the dictionary represents a SwiftUI Text. + /// Currently only accounts for SwiftUI text literals and not instance variables. var isText: Bool { - // Image literals will be reported as calls to the initializer. + // Text literals will be reported as calls to the initializer. guard expressionKind == .call else { return false } @@ -88,8 +88,7 @@ private extension SourceKittenDictionary { return substructure.contains(where: { $0.isText }) } - /// Whether or not the dictionary represents a SwiftUI View with an `accesibilityLabel(_:)` - /// or `accessibility(label:)` modifier. + /// Whether or not the dictionary represents a SwiftUI View with an `font(.system())` modifier. func hasStrictFontModifier(in file: SwiftLintFile) -> Bool { return hasModifier( anyOf: [ From 5cf18461bb287923a96a4e23a1e80878789df82c Mon Sep 17 00:00:00 2001 From: Martijn Date: Mon, 11 Dec 2023 15:31:52 +0100 Subject: [PATCH 04/12] removed unused code --- .../Rules/Lint/AccessibilityFontSizeRule.swift | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index 780bd5bfff..51eb9dbe30 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -43,7 +43,7 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { return findTextViolations(file: file, substructure: dictionary.substructure) } - /// Recursively check a file for image violations, and return all such violations. + /// Recursively check a file for font violations, and return all such violations. private func findTextViolations(file: SwiftLintFile, substructure: [SourceKittenDictionary]) -> [StyleViolation] { var violations = [StyleViolation]() for dictionary in substructure { @@ -59,9 +59,8 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { ) } - // If dictionary did not represent an Text, recursively check substructure, - // unless it's a container that hides its children from accessibility or is labeled. - else if dictionary.substructure.isNotEmpty && dictionary.hasStrictFontModifier(in: file) { + // If dictionary did not represent an Text, recursively check substructure. + else if dictionary.substructure.isNotEmpty { violations.append(contentsOf: findTextViolations(file: file, substructure: dictionary.substructure)) } } From 28ea2c188a81ff497811a56a2701eefb8b631f13 Mon Sep 17 00:00:00 2001 From: Martijn Date: Mon, 18 Dec 2023 15:41:15 +0100 Subject: [PATCH 05/12] Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 806163ee4c..f31881f734 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -109,6 +109,10 @@ #### Enhancements +* Add `Accessibility Font Size` rule to warn if a SwiftUI Text + has a fixed font size. + [MartijnAmbagtsheer](https://github.com/MartijnAmbagtsheer) + * Add `only` configuration option to `todo` rule which allows to specify whether the rule shall trigger on `TODO`s, `FIXME`s or both. [gibachan](https://github.com/gibachan) From 5c562164403fc8fc8b3db90aa945c2f474d69b2b Mon Sep 17 00:00:00 2001 From: Martijn Date: Wed, 20 Dec 2023 10:39:49 +0100 Subject: [PATCH 06/12] Added extra char to check --- .../Rules/Lint/AccessibilityFontSizeRule.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index 51eb9dbe30..b490fac083 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -96,7 +96,7 @@ private extension SourceKittenDictionary { arguments: [ .init( name: "", - values: [".system"], + values: [".system("], matchType: .prefix) ] ) From f6ba07f52903e357faf51ada305729948b23837b Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 16 Jan 2024 11:08:03 +0100 Subject: [PATCH 07/12] New method to check for substructures modifiers --- .../Lint/AccessibilityFontSizeRule.swift | 62 +++++++++++++++---- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index b490fac083..c4cd611088 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -51,22 +51,41 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { continue } - if dictionary.isText && dictionary.hasStrictFontModifier(in: file) { - violations.append( - StyleViolation(ruleDescription: Self.description, - severity: configuration.severity, - location: Location(file: file, byteOffset: offset)) - ) + guard dictionary.isText else { + continue } - // If dictionary did not represent an Text, recursively check substructure. - else if dictionary.substructure.isNotEmpty { - violations.append(contentsOf: findTextViolations(file: file, substructure: dictionary.substructure)) + if checkForViolations(dictionaries: [dictionary], in: file) { + violations.append(getViolation(in: file, onOffset: offset)) } } return violations } + + private func getViolation(in file: SwiftLintFile, onOffset offset: ByteCount) -> StyleViolation { + return StyleViolation( + ruleDescription: Self.description, + severity: configuration.severity, + location: Location(file: file, byteOffset: offset) + ) + } + + private func checkForViolations(dictionaries: [SourceKittenDictionary], in file: SwiftLintFile) -> Bool { + for dictionary in dictionaries { + if dictionary.hasSystemFontModifier(in: file) || dictionary.hasCustomFontModifierWithFixedSize(in: file) { + return true + } + + else if dictionary.substructure.isNotEmpty { + if checkForViolations(dictionaries: dictionary.substructure, in: file) { + return true + } + } + } + + return false + } } // MARK: SourceKittenDictionary extensions @@ -87,17 +106,34 @@ private extension SourceKittenDictionary { return substructure.contains(where: { $0.isText }) } + func hasCustomFontModifierWithFixedSize(in file: SwiftLintFile) -> Bool { + return hasModifier( + anyOf: [ + SwiftUIModifier( + name: ".custom", + arguments: [ + .init( + name: "fixedSize", + values: [], + matchType: .substring) + ] + ) + ], + in: file + ) + } + /// Whether or not the dictionary represents a SwiftUI View with an `font(.system())` modifier. - func hasStrictFontModifier(in file: SwiftLintFile) -> Bool { + func hasSystemFontModifier(in file: SwiftLintFile) -> Bool { return hasModifier( anyOf: [ SwiftUIModifier( - name: "font", + name: "system", arguments: [ .init( name: "", - values: [".system("], - matchType: .prefix) + values: ["size"], + matchType: .substring) ] ) ], From 0ff98397e13f5e2339ee76849c1b8d1a5104c58f Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 16 Jan 2024 11:23:46 +0100 Subject: [PATCH 08/12] Added recursion for when not text --- .../Lint/AccessibilityFontSizeRule.swift | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index c4cd611088..ff559205f0 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -52,28 +52,38 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { } guard dictionary.isText else { + if dictionary.substructure.isNotEmpty { + violations.append( + contentsOf: findTextViolations( + file: file, + substructure: dictionary.substructure + ) + ) + } + continue } if checkForViolations(dictionaries: [dictionary], in: file) { - violations.append(getViolation(in: file, onOffset: offset)) + violations.append( + StyleViolation( + ruleDescription: Self.description, + severity: configuration.severity, + location: Location(file: file, byteOffset: offset) + ) + ) } } return violations } - private func getViolation(in file: SwiftLintFile, onOffset offset: ByteCount) -> StyleViolation { - return StyleViolation( - ruleDescription: Self.description, - severity: configuration.severity, - location: Location(file: file, byteOffset: offset) - ) - } - private func checkForViolations(dictionaries: [SourceKittenDictionary], in file: SwiftLintFile) -> Bool { for dictionary in dictionaries { - if dictionary.hasSystemFontModifier(in: file) || dictionary.hasCustomFontModifierWithFixedSize(in: file) { + if ( + dictionary.hasSystemFontModifier(in: file) || + dictionary.hasCustomFontModifierWithFixedSize(in: file) + ) { return true } From 922061d94be1d35a708882f3d56cdb3dfe8e0d42 Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 16 Jan 2024 13:44:56 +0100 Subject: [PATCH 09/12] Created example file + renaming to font --- .../Lint/AccessibilityFontSizeRule.swift | 37 ++--- .../AccessibilityFontSizeRuleExamples.swift | 155 ++++++++++++++++++ 2 files changed, 165 insertions(+), 27 deletions(-) create mode 100644 Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRuleExamples.swift diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index ff559205f0..d70faf21c3 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -6,28 +6,11 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { static let description = RuleDescription( identifier: "accessibility_font_size", name: "Accessibility Font Size", - description: "Text may not have a fixed font size", + description: "Fonts may not have a fixed size", kind: .lint, minSwiftVersion: .fiveDotOne, - nonTriggeringExamples: [ - Example(""" - struct TestView: View { - var body: some View { - Text("Hello World!") - } - } - """) - ], - triggeringExamples: [ - Example(""" - struct TestView: View { - var body: some View { - Text("Hello World!") - .font(.system(size: 20)) - } - } - """) - ] + nonTriggeringExamples: AccessibilityFontSizeRuleExamples.nonTriggeringExamples, + triggeringExamples: AccessibilityFontSizeRuleExamples.triggeringExamples ) // MARK: AST Rule @@ -40,21 +23,21 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { return [] } - return findTextViolations(file: file, substructure: dictionary.substructure) + return findFontViolations(file: file, substructure: dictionary.substructure) } /// Recursively check a file for font violations, and return all such violations. - private func findTextViolations(file: SwiftLintFile, substructure: [SourceKittenDictionary]) -> [StyleViolation] { + private func findFontViolations(file: SwiftLintFile, substructure: [SourceKittenDictionary]) -> [StyleViolation] { var violations = [StyleViolation]() for dictionary in substructure { guard let offset: ByteCount = dictionary.offset else { continue } - guard dictionary.isText else { + guard dictionary.isFontModifier else { if dictionary.substructure.isNotEmpty { violations.append( - contentsOf: findTextViolations( + contentsOf: findFontViolations( file: file, substructure: dictionary.substructure ) @@ -103,17 +86,17 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { private extension SourceKittenDictionary { /// Whether or not the dictionary represents a SwiftUI Text. /// Currently only accounts for SwiftUI text literals and not instance variables. - var isText: Bool { + var isFontModifier: Bool { // Text literals will be reported as calls to the initializer. guard expressionKind == .call else { return false } - if name == "Text" || name == "SwiftUI.Text" { + if name != nil && name!.contains(".font") { return true } - return substructure.contains(where: { $0.isText }) + return substructure.contains(where: { $0.isFontModifier }) } func hasCustomFontModifierWithFixedSize(in file: SwiftLintFile) -> Bool { diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRuleExamples.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRuleExamples.swift new file mode 100644 index 0000000000..cb50710fd2 --- /dev/null +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRuleExamples.swift @@ -0,0 +1,155 @@ +internal struct AccessibilityFontSizeRuleExamples { + static let nonTriggeringExamples = [ + Example(""" + struct TestView: View { + var body: some View { + Text("Hello World!") + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + Text("Hello World!") + .font(.system(.largeTitle)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + TextField("Username", text: .constant("")) + .font(.system(.largeTitle)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + SecureField("Password", text: .constant("")) + .font(.system(.largeTitle)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + Button("Login") {} + .font(.system(.largeTitle)) + } + } + """) + ] + + static let triggeringExamples = [ + Example(""" + struct TestView: View { + var body: some View { + ↓Text("Hello World!") + .font(.system(size: 20)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓Text("Hello World!") + .italic() + .font(.system(size: 20)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓TextField("Username", text: .constant("")) + .font(.system(size: 15)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓SecureField("Password", text: .constant("")) + .font(.system(size: 15)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓Button("Login") {} + .font(.system(size: 15)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓Text("Hello World!") + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓Text("Hello World!") + .italic() + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓TextField("Username", text: .constant("")) + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓TextField("Username", text: .constant("")) + .italic() + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓SecureField("Password", text: .constant("")) + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓SecureField("Password", text: .constant("")) + .italic() + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓Button("Login") {} + .font(.custom("Havana", fixedSize: 16)) + } + } + """), + Example(""" + struct TestView: View { + var body: some View { + ↓Button("Login") {} + .italic() + .font(.custom("Havana", fixedSize: 16)) + } + } + """) + ] +} From 497cb5a0ce760823a4b6d7dd223775bbfc5c9c74 Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 16 Jan 2024 13:58:11 +0100 Subject: [PATCH 10/12] Style change --- .../Rules/Lint/AccessibilityFontSizeRule.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index d70faf21c3..8c05c6671d 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -63,10 +63,7 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { private func checkForViolations(dictionaries: [SourceKittenDictionary], in file: SwiftLintFile) -> Bool { for dictionary in dictionaries { - if ( - dictionary.hasSystemFontModifier(in: file) || - dictionary.hasCustomFontModifierWithFixedSize(in: file) - ) { + if dictionary.hasSystemFontModifier(in: file) || dictionary.hasCustomFontModifierWithFixedSize(in: file) { return true } From 0cde21fc52061433991876b100fe2e03d5a4a24b Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 16 Jan 2024 14:05:51 +0100 Subject: [PATCH 11/12] Styling --- .../Rules/Lint/AccessibilityFontSizeRule.swift | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index 8c05c6671d..500e0a94b8 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -65,9 +65,7 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { for dictionary in dictionaries { if dictionary.hasSystemFontModifier(in: file) || dictionary.hasCustomFontModifierWithFixedSize(in: file) { return true - } - - else if dictionary.substructure.isNotEmpty { + } else if dictionary.substructure.isNotEmpty { if checkForViolations(dictionaries: dictionary.substructure, in: file) { return true } From db88658be29475590ba217385f6d6c86516f9e54 Mon Sep 17 00:00:00 2001 From: Martijn Date: Tue, 16 Jan 2024 15:34:35 +0100 Subject: [PATCH 12/12] Fixed bug where it picks the wrong view element to report --- .../Rules/Lint/AccessibilityFontSizeRule.swift | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift index 500e0a94b8..5ddb420dc7 100644 --- a/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift +++ b/Source/SwiftLintBuiltInRules/Rules/Lint/AccessibilityFontSizeRule.swift @@ -34,7 +34,7 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { continue } - guard dictionary.isFontModifier else { + guard dictionary.isFontModifier(in: file) else { if dictionary.substructure.isNotEmpty { violations.append( contentsOf: findFontViolations( @@ -81,17 +81,25 @@ struct AccessibilityFontSizeRule: ASTRule, OptInRule { private extension SourceKittenDictionary { /// Whether or not the dictionary represents a SwiftUI Text. /// Currently only accounts for SwiftUI text literals and not instance variables. - var isFontModifier: Bool { + func isFontModifier(in file: SwiftLintFile) -> Bool { // Text literals will be reported as calls to the initializer. guard expressionKind == .call else { return false } - if name != nil && name!.contains(".font") { + if hasModifier( + anyOf: [ + SwiftUIModifier( + name: ".font", + arguments: [] + ) + ], + in: file + ) { return true } - return substructure.contains(where: { $0.isFontModifier }) + return substructure.contains(where: { $0.isFontModifier(in: file) }) } func hasCustomFontModifierWithFixedSize(in file: SwiftLintFile) -> Bool {