SwiftLint/Source/SwiftLintFramework/Rules/Lint/NSLocalizedStringRequireBun...

67 lines
2.4 KiB
Swift

import SwiftSyntax
struct NSLocalizedStringRequireBundleRule: SwiftSyntaxRule, OptInRule, ConfigurationProviderRule {
var configuration = SeverityConfiguration(.warning)
static let description = RuleDescription(
identifier: "nslocalizedstring_require_bundle",
name: "NSLocalizedString Require Bundle",
description: "Calls to NSLocalizedString should specify the bundle which contains the strings file",
kind: .lint,
nonTriggeringExamples: [
Example("""
NSLocalizedString("someKey", bundle: .main, comment: "test")
"""),
Example("""
NSLocalizedString("someKey", tableName: "a",
bundle: Bundle(for: A.self),
comment: "test")
"""),
Example("""
NSLocalizedString("someKey", tableName: "xyz",
bundle: someBundle, value: "test"
comment: "test")
"""),
Example("""
arbitraryFunctionCall("something")
""")
],
triggeringExamples: [
Example("""
↓NSLocalizedString("someKey", comment: "test")
"""),
Example("""
↓NSLocalizedString("someKey", tableName: "a", comment: "test")
"""),
Example("""
↓NSLocalizedString("someKey", tableName: "xyz",
value: "test", comment: "test")
""")
]
)
func makeVisitor(file: SwiftLintFile) -> ViolationsSyntaxVisitor {
Visitor(viewMode: .sourceAccurate)
}
}
private extension NSLocalizedStringRequireBundleRule {
final class Visitor: ViolationsSyntaxVisitor {
override func visitPost(_ node: FunctionCallExprSyntax) {
if let identifierExpr = node.calledExpression.as(IdentifierExprSyntax.self),
identifierExpr.identifier.tokenKind == .identifier("NSLocalizedString"),
!node.argumentList.containsArgument(named: "bundle") {
violations.append(node.positionAfterSkippingLeadingTrivia)
}
}
}
}
private extension TupleExprElementListSyntax {
func containsArgument(named name: String) -> Bool {
contains { arg in
arg.label?.tokenKind == .identifier(name)
}
}
}