Skip to content

Commit

Permalink
Make joined_default_parameter rule correctable
Browse files Browse the repository at this point in the history
as described in #1757 and #1746.
  • Loading branch information
ornithocoder authored and marcelofabri committed Aug 17, 2017
1 parent 016fac4 commit b54e223
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
[Marcelo Fabri](https://github.com/marcelofabri)
[#1721](https://github.com/realm/SwiftLint/issues/1721)

* Make `joined_default_parameter` correctable.
[Ornithologist Coder](https://github.com/ornithocoder)
[#1757](https://github.com/realm/SwiftLint/issues/1757)

##### Bug Fixes

* Fix false positive on `force_unwrapping` rule when declaring
Expand Down
2 changes: 1 addition & 1 deletion Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -4824,7 +4824,7 @@ func foo(int: Int!) {}

Identifier | Enabled by default | Supports autocorrection | Kind
--- | --- | --- | ---
`joined_default_parameter` | Disabled | No | idiomatic
`joined_default_parameter` | Disabled | Yes | idiomatic

Discouraged explicit usage of the default separator.

Expand Down
73 changes: 66 additions & 7 deletions Source/SwiftLintFramework/Rules/JoinedDefaultRule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import Foundation
import SourceKittenFramework

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

public init() {}
Expand All @@ -28,19 +28,23 @@ public struct JoinedDefaultParameterRule: ASTRule, ConfigurationProviderRule, Op
"let foo = bar.joined(separator: ↓\"\")",
"let foo = bar.filter(toto)\n" +
" .joined(separator: ↓\"\")"
],
corrections: [
"let foo = bar.joined(↓separator: \"\")": "let foo = bar.joined()",
"let foo = bar.filter(toto)\n.joined(↓separator: \"\")": "let foo = bar.filter(toto)\n.joined()"
]
)

// MARK: - ASTRule

public func validate(file: File,
kind: SwiftExpressionKind,
dictionary: [String: SourceKitRepresentable]) -> [StyleViolation] {
guard
kind == .call,
dictionary.name?.hasSuffix(".joined") == true,
let defaultSeparatorOffset = defaultSeparatorOffset(dictionary: dictionary, file: file)
else {
return []
}
else { return [] }

return [StyleViolation(ruleDescription: type(of: self).description,
severity: configuration.severity,
Expand All @@ -54,11 +58,66 @@ public struct JoinedDefaultParameterRule: ASTRule, ConfigurationProviderRule, Op
let argumentBodyOffset = argument.bodyOffset,
let argumentBodyLength = argument.bodyLength,
argument.name == "separator"
else {
return nil
}
else { return nil }

let body = file.contents.bridge().substringWithByteRange(start: argumentBodyOffset, length: argumentBodyLength)
return body == "\"\"" ? argumentBodyOffset : nil
}

// MARK: - CorrectableRule

public func correct(file: File) -> [Correction] {
let violatingRanges = violationRanges(in: file, dictionary: file.structure.dictionary)
let matches = file.ruleEnabled(violatingRanges: violatingRanges, for: self)
var correctedContents = file.contents
var adjustedLocations: [Int] = []

for violatingRange in matches.reversed() {
if let range = file.contents.nsrangeToIndexRange(violatingRange) {
correctedContents = correctedContents.replacingCharacters(in: range, with: "")
adjustedLocations.insert(violatingRange.location, at: 0)
}
}

file.write(correctedContents)

return adjustedLocations.map {
Correction(ruleDescription: type(of: self).description, location: Location(file: file, characterOffset: $0))
}
}

private func violationRanges(in file: File, dictionary: [String: SourceKitRepresentable]) -> [NSRange] {
return dictionary.substructure.flatMap { subDictionary -> [NSRange] in
let violations = violationRanges(in: file, dictionary: subDictionary)

guard
// is it calling a method '.joined' and passing a single argument?
subDictionary.kind == SwiftExpressionKind.call.rawValue,
subDictionary.name?.hasSuffix(".joined") == true,
subDictionary.enclosedArguments.count == 1
else { return violations }

guard
// is this single argument called 'separator'?
let argument = subDictionary.enclosedArguments.first,
let offset = argument.offset,
let length = argument.length,
argument.name == "separator"
else { return violations }

guard
// is this single argument the default parameter?
let bodyOffset = argument.bodyOffset,
let bodyLength = argument.bodyLength,
let body = file.contents.bridge().substringWithByteRange(start: bodyOffset, length: bodyLength),
body == "\"\""
else { return violations }

guard
let range = file.contents.bridge().byteRangeToNSRange(start: offset, length: length)
else { return violations }

return violations + [range]
}
}
}

0 comments on commit b54e223

Please sign in to comment.