Skip to content

Commit

Permalink
Add UIImage Requires Bundle Rule
Browse files Browse the repository at this point in the history
  • Loading branch information
danielPeloton committed Aug 15, 2023
1 parent 1f65377 commit 48577fe
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
1 change: 1 addition & 0 deletions Source/SwiftLintBuiltInRules/Models/BuiltInRules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ public let builtInRules: [Rule.Type] = [
UnavailableConditionRule.self,
UnavailableFunctionRule.self,
UnhandledThrowingTaskRule.self,
UIImageIncludesBundleRule.self,
UnneededBreakInSwitchRule.self,
UnneededOverrideRule.self,
UnneededParenthesesInClosureArgumentRule.self,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import SwiftSyntax

struct UIImageIncludesBundleRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration<Self>(.warning)

static let description = RuleDescription(
identifier: "uiimage_requires_bundle",
name: "UIImage Requires Bundle",
description: "`UIImage(named:) must specify a bundle via the `in:` parameter",
kind: .lint,
nonTriggeringExamples: [
Example("""
UIImage(named: "image", in: Bundle.main)
"""),
Example("""
UIImageView(image: UIImage(named: "image", in: Bundle(for A.self)))
"""),
Example("""
UIImage(named: "image", in: Bundle(for A.self))
"""),
Example("""
UIImage(named: "image", in: Bundle.main, compatibleWith: aCollection)
"""),
Example("""
UIImage(named: "image", in: Bundle.main, with: aConfiguration)
"""),
Example("""
UIImage(systemName: "systemImage")
"""),
Example("""
UIImage(systemName: "systemImage", compatibleWith: aCollection)
"""),
Example("""
UIImage(systemName: "systemImage", withConfiguration: aConfiguration)
"""),
Example("""
UIImage(systemName: "systemImage", variableValue: 0.5)
"""),
Example("""
UIImage(systemName: "systemImage", variableValue: 0.5, configuration: aConfiguration)
"""),
Example("""
UIImage(
named: "image",
in: Bundle.main
)
"""),
Example("""
arbitraryFunctionCall("something")
"""),
Example("""
UIImageView(image: UIImage(named: "hashtag", in: Bundle.main))
""")
],
triggeringExamples: [
Example("""
↓UIImage(named: "image")
"""),
Example("""
UIImageView(image: ↓UIImage(named: "image"))
"""),
Example("""
↓UIImage(named: "image", compatibleWith: aCollection)
"""),
Example("""
↓UIImage(named: "image", with: aConfiguration)
"""),
Example("""
↓UIImage(
named: "image"
)
""")
]
)

func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}
}

private extension UIImageIncludesBundleRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: FunctionCallExprSyntax) {
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
identifierExpr.identifier.tokenKind == .identifier("UIImage"),
node.argumentList.containsArgument(named: "named"),
!node.argumentList.containsArgument(named: "in") {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}
}
}

private extension TupleExprElementListSyntax {
func containsArgument(named name: String) -> Bool {
contains { arg in
arg.label?.tokenKind == .identifier(name)
}
}
}
6 changes: 6 additions & 0 deletions Tests/GeneratedTests/GeneratedTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,12 @@ class UnhandledThrowingTaskRuleGeneratedTests: SwiftLintTestCase {
}
}

class UIImageIncludesBundleRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UIImageIncludesBundleRule.description)
}
}

class UnneededBreakInSwitchRuleGeneratedTests: SwiftLintTestCase {
func testWithDefaultConfiguration() {
verifyRule(UnneededBreakInSwitchRule.description)
Expand Down

0 comments on commit 48577fe

Please sign in to comment.