Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@

### Enhancements

* None.
* Add `legacy_uigraphics_functions` rule to encourage the use of modern
UIGraphicsImageRenderer instead of legacy UIGraphics{Begin|End}ImageContext.
The modern replacement is safer, cleaner, Retina-aware and more performant.
Comment on lines +31 to +33
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Add `legacy_uigraphics_functions` rule to encourage the use of modern
UIGraphicsImageRenderer instead of legacy UIGraphics{Begin|End}ImageContext.
The modern replacement is safer, cleaner, Retina-aware and more performant.
* Add `legacy_uigraphics_functions` rule to encourage the use of modern
`UIGraphicsImageRenderer `instead of legacy `UIGraphics{Begin|End}ImageContext`.
The modern replacement is safer, cleaner, Retina-aware and more performant.

[Dimitri Dupuis-Latour](https://github.com/DimDL)
[#6268](https://github.com/realm/SwiftLint/issues/6268)

### Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public let builtInRules: [any Rule.Type] = [
LegacyNSGeometryFunctionsRule.self,
LegacyObjcTypeRule.self,
LegacyRandomRule.self,
LegacyUIGraphicsFunctionsRule.self,
LetVarWhitespaceRule.self,
LineLengthRule.self,
LiteralExpressionEndIndentationRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import SwiftSyntax

@SwiftSyntaxRule
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rule should be opt-in as you have described in the issue:

Suggested change
@SwiftSyntaxRule
@SwiftSyntaxRule(optIn: true)

struct LegacyUIGraphicsFunctionsRule: Rule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
identifier: "legacy_uigraphics_functions",
name: "Legacy UIGraphics Functions",
description: "Prefer using `UIGraphicsImageRenderer` over legacy functions",
rationale: "The modern replacement is safer, cleaner, Retina-aware and more performant",
kind: .idiomatic,
nonTriggeringExamples: [
Example("""
let renderer = UIGraphicsImageRenderer(size: bounds.size)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests can be a bit easier, as the rule effectively only checks function names. So the let screenshot = ... part isn't actually necessary.

let screenshot = renderer.image { _ in
myUIView.drawHierarchy(in: bounds, afterScreenUpdates: true)
}
"""),

Example("""
let renderer = UIGraphicsImageRenderer(size: newSize)
let combined = renderer.image { _ in
background.draw(in: CGRect(origin: .zero, size: newSize))
watermark.draw(in: CGRect(origin: .zero, size: watermarkSize))
}
"""),

Example("""
let format = UIGraphicsImageRendererFormat()
format.scale = 1.0
format.opaque = true

let renderer = UIGraphicsImageRenderer(size: newSize, format: format)
return renderer.image { _ in
image.draw(in: CGRect(origin: .zero, size: newSize))
}
"""),
],
triggeringExamples: [
Example("""
↓UIGraphicsBeginImageContext(newSize)
myUIView.drawHierarchy(in: bounds, afterScreenUpdates: false)
let optionalScreenshot = ↓UIGraphicsGetImageFromCurrentImageContext()
↓UIGraphicsEndImageContext()
"""),

Example("""
↓UIGraphicsBeginImageContext(newSize)
background.draw(in: CGRect(origin: .zero, size: newSize))
watermark.draw(in: CGRect(origin: .zero, size: watermarkSize))
let optionalOutput = ↓UIGraphicsGetImageFromCurrentImageContext()
↓UIGraphicsEndImageContext()
"""),

Example("""
↓UIGraphicsBeginImageContextWithOptions(newSize, true, 1.0)
image.draw(in: CGRect(origin: .zero, size: newSize))
let optionalOutput = ↓UIGraphicsGetImageFromCurrentImageContext()
↓UIGraphicsEndImageContext()
"""),
]
)
}

private extension LegacyUIGraphicsFunctionsRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
private static let legacyUIGraphicsFunctions: Set<String> = [
"UIGraphicsBeginImageContext",
"UIGraphicsBeginImageContextWithOptions",
"UIGraphicsGetImageFromCurrentImageContext",
"UIGraphicsEndImageContext",
]

override func visitPost(_ node: FunctionCallExprSyntax) {
if let function = node.calledExpression.as(DeclReferenceExprSyntax.self)?.baseName.text,
Self.legacyUIGraphicsFunctions.contains(function) {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}
}
}
12 changes: 6 additions & 6 deletions Tests/GeneratedTests/GeneratedTests_05.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ final class LegacyRandomRuleGeneratedTests: SwiftLintTestCase {
}
}

final class LegacyUIGraphicsFunctionsRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(LegacyUIGraphicsFunctionsRule.description)
}
}

final class LetVarWhitespaceRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(LetVarWhitespaceRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class NSObjectPreferIsEqualRuleGeneratedTests: SwiftLintTestCase {
verifyRule(NSObjectPreferIsEqualRule.description)
}
}

final class NestingRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(NestingRule.description)
}
}
12 changes: 6 additions & 6 deletions Tests/GeneratedTests/GeneratedTests_06.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
@testable import SwiftLintCore
import TestHelpers

final class NestingRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(NestingRule.description)
}
}

final class NimbleOperatorRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(NimbleOperatorRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class PreferKeyPathRuleGeneratedTests: SwiftLintTestCase {
verifyRule(PreferKeyPathRule.description)
}
}

final class PreferNimbleRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(PreferNimbleRule.description)
}
}
12 changes: 6 additions & 6 deletions Tests/GeneratedTests/GeneratedTests_07.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
@testable import SwiftLintCore
import TestHelpers

final class PreferNimbleRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(PreferNimbleRule.description)
}
}

final class PreferSelfInStaticReferencesRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(PreferSelfInStaticReferencesRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class RedundantSelfInClosureRuleGeneratedTests: SwiftLintTestCase {
verifyRule(RedundantSelfInClosureRule.description)
}
}

final class RedundantSendableRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(RedundantSendableRule.description)
}
}
12 changes: 6 additions & 6 deletions Tests/GeneratedTests/GeneratedTests_08.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
@testable import SwiftLintCore
import TestHelpers

final class RedundantSendableRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(RedundantSendableRule.description)
}
}

final class RedundantSetAccessControlRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(RedundantSetAccessControlRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class SwitchCaseAlignmentRuleGeneratedTests: SwiftLintTestCase {
verifyRule(SwitchCaseAlignmentRule.description)
}
}

final class SwitchCaseOnNewlineRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(SwitchCaseOnNewlineRule.description)
}
}
12 changes: 6 additions & 6 deletions Tests/GeneratedTests/GeneratedTests_09.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
@testable import SwiftLintCore
import TestHelpers

final class SwitchCaseOnNewlineRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(SwitchCaseOnNewlineRule.description)
}
}

final class SyntacticSugarRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(SyntacticSugarRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class UnusedControlFlowLabelRuleGeneratedTests: SwiftLintTestCase {
verifyRule(UnusedControlFlowLabelRule.description)
}
}

final class UnusedDeclarationRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnusedDeclarationRule.description)
}
}
6 changes: 6 additions & 0 deletions Tests/GeneratedTests/GeneratedTests_10.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
@testable import SwiftLintCore
import TestHelpers

final class UnusedDeclarationRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnusedDeclarationRule.description)
}
}

final class UnusedEnumeratedRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnusedEnumeratedRule.description)
Expand Down
5 changes: 5 additions & 0 deletions Tests/IntegrationTests/default_rule_configurations.yml
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,11 @@ legacy_random:
meta:
opt-in: false
correctable: false
legacy_uigraphics_functions:
severity: warning
meta:
opt-in: false
correctable: false
let_var_whitespace:
severity: warning
meta:
Expand Down