Skip to content

Commit

Permalink
Merge pull request #2581 from realm/mf-nslocalizedstring_key
Browse files Browse the repository at this point in the history
Add `nslocalizedstring_key` opt-in rule
  • Loading branch information
marcelofabri committed Jan 23, 2019
2 parents eca7e6b + 14ef233 commit c2d9a9c
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 1 deletion.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Expand Up @@ -13,7 +13,10 @@

#### Enhancements

* None.
* Add `nslocalizedstring_key` opt-in rule to validate that keys used in
`NSLocalizedString` calls are static strings, so `genstrings` will be
able to find them.
[Marcelo Fabri](https://github.com/marcelofabri)

#### Bug Fixes

Expand Down
38 changes: 38 additions & 0 deletions Rules.md
Expand Up @@ -92,6 +92,7 @@
* [No Fallthrough Only](#no-fallthrough-only)
* [No Grouping Extension](#no-grouping-extension)
* [Notification Center Detachment](#notification-center-detachment)
* [NSLocalizedString Key](#nslocalizedstring-key)
* [Number Separator](#number-separator)
* [Object Literal](#object-literal)
* [Opening Brace Spacing](#opening-brace-spacing)
Expand Down Expand Up @@ -13077,6 +13078,43 @@ class Foo {



## NSLocalizedString Key

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
--- | --- | --- | --- | --- | ---
`nslocalizedstring_key` | Disabled | No | lint | No | 3.0.0

Static strings should be used as key in NSLocalizedString in order to genstrings work.

### Examples

<details>
<summary>Non Triggering Examples</summary>

```swift
NSLocalizedString("key", comment: nil)
```

```swift
NSLocalizedString("key" + "2", comment: nil)
```

</details>
<details>
<summary>Triggering Examples</summary>

```swift
NSLocalizedString(↓method(), comment: nil)
```

```swift
NSLocalizedString(↓"key_\(param)", comment: nil)
```

</details>



## Number Separator

Identifier | Enabled by default | Supports autocorrection | Kind | Analyzer | Minimum Swift Compiler Version
Expand Down
1 change: 1 addition & 0 deletions Source/SwiftLintFramework/Models/MasterRuleList.swift
Expand Up @@ -87,6 +87,7 @@ public let masterRuleList = RuleList(rules: [
MultilineParametersBracketsRule.self,
MultilineParametersRule.self,
MultipleClosuresWithTrailingClosureRule.self,
NSLocalizedStringKeyRule.self,
NestingRule.self,
NimbleOperatorRule.self,
NoExtensionAccessModifierRule.self,
Expand Down
@@ -0,0 +1,44 @@
import Foundation
import SourceKittenFramework

public struct NSLocalizedStringKeyRule: ASTRule, OptInRule, ConfigurationProviderRule, AutomaticTestableRule {
public var configuration = SeverityConfiguration(.warning)

public init() {}

public static let description = RuleDescription(
identifier: "nslocalizedstring_key",
name: "NSLocalizedString Key",
description: "Static strings should be used as key in NSLocalizedString in order to genstrings work.",
kind: .lint,
nonTriggeringExamples: [
"NSLocalizedString(\"key\", comment: nil)",
"NSLocalizedString(\"key\" + \"2\", comment: nil)"
],
triggeringExamples: [
"NSLocalizedString(↓method(), comment: nil)",
"NSLocalizedString(↓\"key_\\(param)\", comment: nil)"
]
)

public func validate(file: File,
kind: SwiftExpressionKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard kind == .call,
dictionary.name == "NSLocalizedString",
let firstArgument = dictionary.enclosedArguments.first,
firstArgument.name == nil,
let offset = firstArgument.offset,
let length = firstArgument.length,
case let kinds = file.syntaxMap.kinds(inByteRange: NSRange(location: offset, length: length)),
!kinds.allSatisfy({ $0 == .string }) else {
return []
}

return [
StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
location: Location(file: file, byteOffset: offset))
]
}
}
4 changes: 4 additions & 0 deletions SwiftLint.xcodeproj/project.pbxproj
Expand Up @@ -227,6 +227,7 @@
D4130D991E16CC1300242361 /* TypeNameRuleExamples.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4130D981E16CC1300242361 /* TypeNameRuleExamples.swift */; };
D414D6AC21D0B77F00960935 /* DiscouragedObjectLiteralRuleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D414D6AB21D0B77F00960935 /* DiscouragedObjectLiteralRuleTests.swift */; };
D414D6AE21D22FF500960935 /* LastWhereRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D414D6AD21D22FF500960935 /* LastWhereRule.swift */; };
D41985E721F85014003BE2B7 /* NSLocalizedStringKeyRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41985E621F85014003BE2B7 /* NSLocalizedStringKeyRule.swift */; };
D41B57781ED8CEE0007B0470 /* ExtensionAccessModifierRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41B57771ED8CEE0007B0470 /* ExtensionAccessModifierRule.swift */; };
D41E7E0B1DF9DABB0065259A /* RedundantStringEnumValueRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = D41E7E0A1DF9DABB0065259A /* RedundantStringEnumValueRule.swift */; };
D4246D6D1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4246D6C1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift */; };
Expand Down Expand Up @@ -688,6 +689,7 @@
D4130D981E16CC1300242361 /* TypeNameRuleExamples.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TypeNameRuleExamples.swift; sourceTree = "<group>"; };
D414D6AB21D0B77F00960935 /* DiscouragedObjectLiteralRuleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiscouragedObjectLiteralRuleTests.swift; sourceTree = "<group>"; };
D414D6AD21D22FF500960935 /* LastWhereRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastWhereRule.swift; sourceTree = "<group>"; };
D41985E621F85014003BE2B7 /* NSLocalizedStringKeyRule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLocalizedStringKeyRule.swift; sourceTree = "<group>"; };
D41B57771ED8CEE0007B0470 /* ExtensionAccessModifierRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionAccessModifierRule.swift; sourceTree = "<group>"; };
D41E7E0A1DF9DABB0065259A /* RedundantStringEnumValueRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RedundantStringEnumValueRule.swift; sourceTree = "<group>"; };
D4246D6C1F30D8620097E658 /* PrivateOverFilePrivateRuleConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrivateOverFilePrivateRuleConfiguration.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1033,6 +1035,7 @@
F9E691272091952E0085B53E /* MissingDocsRule.swift */,
D4DABFD61E2C23B1009617B6 /* NotificationCenterDetachmentRule.swift */,
D4DABFD81E2C59BC009617B6 /* NotificationCenterDetachmentRuleExamples.swift */,
D41985E621F85014003BE2B7 /* NSLocalizedStringKeyRule.swift */,
78F032441D7C877800BE709A /* OverriddenSuperCallRule.swift */,
D40FE89C1F867BFF006433E2 /* OverrideInExtensionRule.swift */,
62DEA1651FB21A9E00BCCCC6 /* PrivateActionRule.swift */,
Expand Down Expand Up @@ -1837,6 +1840,7 @@
E80E018D1B92C0F60078EB70 /* Command.swift in Sources */,
E88198571BEA953300333A11 /* ForceCastRule.swift in Sources */,
D44AD2761C0AA5350048F7B0 /* LegacyConstructorRule.swift in Sources */,
D41985E721F85014003BE2B7 /* NSLocalizedStringKeyRule.swift in Sources */,
D286EC021E02DF6F0003CF72 /* SortedImportsRule.swift in Sources */,
D40E041C1F46E3B30043BC4E /* SuperfluousDisableCommandRule.swift in Sources */,
82F614F42106015100D23904 /* MultilineArgumentsBracketsRule.swift in Sources */,
Expand Down
7 changes: 7 additions & 0 deletions Tests/LinuxMain.swift
Expand Up @@ -793,6 +793,12 @@ extension MultipleClosuresWithTrailingClosureRuleTests {
]
}

extension NSLocalizedStringKeyRuleTests {
static var allTests: [(String, (NSLocalizedStringKeyRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
]
}

extension NestingRuleTests {
static var allTests: [(String, (NestingRuleTests) -> () throws -> Void)] = [
("testWithDefaultConfiguration", testWithDefaultConfiguration)
Expand Down Expand Up @@ -1520,6 +1526,7 @@ XCTMain([
testCase(MultilineParametersBracketsRuleTests.allTests),
testCase(MultilineParametersRuleTests.allTests),
testCase(MultipleClosuresWithTrailingClosureRuleTests.allTests),
testCase(NSLocalizedStringKeyRuleTests.allTests),
testCase(NestingRuleTests.allTests),
testCase(NimbleOperatorRuleTests.allTests),
testCase(NoExtensionAccessModifierRuleTests.allTests),
Expand Down
Expand Up @@ -378,6 +378,12 @@ class MultipleClosuresWithTrailingClosureRuleTests: XCTestCase {
}
}

class NSLocalizedStringKeyRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(NSLocalizedStringKeyRule.description)
}
}

class NestingRuleTests: XCTestCase {
func testWithDefaultConfiguration() {
verifyRule(NestingRule.description)
Expand Down

0 comments on commit c2d9a9c

Please sign in to comment.