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: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
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)

* Add `prefer_scaled_to_fit` opt-in rule to prefer `scaledToFit()` and
`scaledToFill()` over `aspectRatio(contentMode:)` with a constant
content mode.
[DemiDevv](https://github.com/DemiDevv)
[#5713](https://github.com/realm/SwiftLint/issues/5713)

* Support access level modifiers on imports in `unused_imports` rule.
[SimplyDanny](https://github.com/SimplyDanny)
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 @@ -158,6 +158,7 @@ public let builtInRules: [any Rule.Type] = [
PreferConditionListRule.self,
PreferKeyPathRule.self,
PreferNimbleRule.self,
PreferScaledToFitRule.self,
PreferSelfInStaticReferencesRule.self,
PreferSelfTypeOverTypeOfSelfRule.self,
PreferTypeCheckingRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import SwiftLintCore
import SwiftSyntax

@SwiftSyntaxRule(optIn: true)
struct PreferScaledToFitRule: Rule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
identifier: "prefer_scaled_to_fit",
name: "Prefer Scaled To Fit",
description: "Prefer `scaledToFit()` or `scaledToFill()` over " +
"`aspectRatio(contentMode:)` with a constant content mode",
kind: .idiomatic,
nonTriggeringExamples: [
Example("view.aspectRatio(ratio, contentMode: .fit)"),
Example("view.aspectRatio(ratio, contentMode: .fill)"),
Example("view.aspectRatio(contentMode: contentMode)"),
Example("view.aspectRatio(contentMode: shouldFit ? .fit : .fill)"),
Example("view.scaledToFit()"),
Example("view.scaledToFill()"),
],
triggeringExamples: [
Example("view.↓aspectRatio(contentMode: .fit)"),
Example("view.↓aspectRatio(contentMode: .fill)"),
Example("↓aspectRatio(contentMode: .fit)"),
Example("↓aspectRatio(contentMode: .fill)"),
]
)
}

private extension PreferScaledToFitRule {
final class Visitor: ViolationsSyntaxVisitor<ConfigurationType> {
override func visitPost(_ node: FunctionCallExprSyntax) {
let functionName: String
let violationPosition: AbsolutePosition

if let memberAccess = node.calledExpression.as(MemberAccessExprSyntax.self) {
functionName = memberAccess.declName.baseName.text
violationPosition = memberAccess.declName.baseName.positionAfterSkippingLeadingTrivia
} else if let declRef = node.calledExpression.as(DeclReferenceExprSyntax.self) {
functionName = declRef.baseName.text
violationPosition = declRef.baseName.positionAfterSkippingLeadingTrivia
} else {
return
}

guard functionName == "aspectRatio" else {
return
}

guard
node.arguments.count == 1,
let argument = node.arguments.first,
argument.label?.text == "contentMode"
else {
return
}

guard
let memberValue = argument.expression.as(MemberAccessExprSyntax.self),
let valueName = memberValue.declName.baseName.text as String?,
valueName == "fit" || valueName == "fill"
else {
return
}

violations.append(violationPosition)
}
}
}
12 changes: 6 additions & 6 deletions Tests/GeneratedTests/GeneratedTests_07.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ final class PreferNimbleRuleGeneratedTests: SwiftLintTestCase {
}
}

final class PreferScaledToFitRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(PreferScaledToFitRule.description)
}
}

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

final class ReduceBooleanRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(ReduceBooleanRule.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 ReduceBooleanRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(ReduceBooleanRule.description)
}
}

final class ReduceIntoRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(ReduceIntoRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class SortedImportsRuleGeneratedTests: SwiftLintTestCase {
verifyRule(SortedImportsRule.description)
}
}

final class StatementPositionRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(StatementPositionRule.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 StatementPositionRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(StatementPositionRule.description)
}
}

final class StaticOperatorRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(StaticOperatorRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class UnneededBreakInSwitchRuleGeneratedTests: SwiftLintTestCase {
verifyRule(UnneededBreakInSwitchRule.description)
}
}

final class UnneededEscapingRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnneededEscapingRule.description)
}
}
12 changes: 6 additions & 6 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 UnneededEscapingRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnneededEscapingRule.description)
}
}

final class UnneededOverrideRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnneededOverrideRule.description)
Expand Down Expand Up @@ -150,9 +156,3 @@ final class VoidReturnRuleGeneratedTests: SwiftLintTestCase {
verifyRule(VoidReturnRule.description)
}
}

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

final class WeakDelegateRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(WeakDelegateRule.description)
}
}

final class XCTFailMessageRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(XCTFailMessageRule.description)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -914,6 +914,11 @@ prefer_nimble:
meta:
opt-in: true
correctable: false
prefer_scaled_to_fit:
severity: warning
meta:
opt-in: true
correctable: false
prefer_self_in_static_references:
severity: warning
meta:
Expand Down
Loading