diff --git a/CHANGELOG.md b/CHANGELOG.md index 735984b80f..188a67e8ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,11 @@ [Marcelo Fabri](https://github.com/marcelofabri) [#1159](https://github.com/realm/SwiftLint/issues/1159) +* Fix false positive on `operator_usage_whitespace` rule with decimal + literals in exponent format. + [Marcelo Fabri](https://github.com/marcelofabri) + [#1153](https://github.com/realm/SwiftLint/issues/1153) + ## 0.16.0: Maximum Energy Efficiency Setting ##### Breaking diff --git a/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift b/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift index f92734d341..d71d54014e 100644 --- a/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift +++ b/Source/SwiftLintFramework/Rules/OperatorUsageWhitespaceRule.swift @@ -34,7 +34,8 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura "#if swift(>=3.0)\n", "array.removeAtIndex(-200)\n", "let name = \"image-1\"\n", - "button.setImage(#imageLiteral(resourceName: \"image-1\"), for: .normal)\n" + "button.setImage(#imageLiteral(resourceName: \"image-1\"), for: .normal)\n", + "let doubleValue = -9e-11\n" ], triggeringExamples: [ "let foo = 1↓+2\n", @@ -107,36 +108,49 @@ public struct OperatorUsageWhitespaceRule: OptInRule, CorrectableRule, Configura zeroSpaces + trailingVariableOrNumber let excludingPattern = "(?:\(genericPattern)|\(validRangePattern))" - let kinds = SyntaxKind.commentAndStringKinds() + [.objectLiteral] + let excludingKinds = SyntaxKind.commentAndStringKinds() + [.objectLiteral] - return file.match(pattern: pattern, excludingSyntaxKinds: kinds, - excludingPattern: excludingPattern).flatMap { + return file.match(pattern: pattern, excludingSyntaxKinds: excludingKinds, + excludingPattern: excludingPattern).flatMap { range in + + // if it's only a number (i.e. -9e-11), it shouldn't trigger + guard kinds(in: range, file: file) != [.number] else { + return nil + } let spacesPattern = oneSpace + "*" let rangeRegex = regex(spacesPattern + rangePattern + spacesPattern) // if it's a range operator, the correction shouldn't have spaces - if let range = rangeRegex.firstMatch(in: file.contents, - options: [], range: $0)?.range { - let correction = operatorInRange(file: file, range: range) - return (range, correction) + if let matchRange = rangeRegex.firstMatch(in: file.contents, options: [], range: range)?.range { + let correction = operatorInRange(file: file, range: matchRange) + return (matchRange, correction) } let pattern = spacesPattern + operators + spacesPattern let operatorsRegex = regex(pattern) - guard let range = operatorsRegex.firstMatch(in: file.contents, - options: [], range: $0)?.range else { + guard let matchRange = operatorsRegex.firstMatch(in: file.contents, + options: [], range: range)?.range else { return nil } - let operatorContent = operatorInRange(file: file, range: range) + let operatorContent = operatorInRange(file: file, range: matchRange) let correction = " " + operatorContent + " " - return (range, correction) + return (matchRange, correction) } } + private func kinds(in range: NSRange, file: File) -> [SyntaxKind] { + let contents = file.contents.bridge() + guard let byteRange = contents.NSRangeToByteRange(start: range.location, length: range.length) else { + return [] + } + + return file.syntaxMap.tokens(inByteRange: byteRange).flatMap { SyntaxKind(rawValue: $0.type) } + } + private func operatorInRange(file: File, range: NSRange) -> String { return file.contents.bridge().substring(with: range).trimmingCharacters(in: .whitespaces) }