Throw error on bad expiring todo date format (#3626)

This commit is contained in:
Chris Hale
2022-04-11 12:20:05 -07:00
committed by GitHub
parent 42a2932883
commit 9beef23175
4 changed files with 49 additions and 9 deletions
+4
View File
@@ -48,6 +48,10 @@
[JP Simard](https://github.com/jpsim)
[#3920](https://github.com/realm/SwiftLint/issues/3920)
* Error by default on bad expiring todo date formatting.
[Christopher Hale](https://github.com/chrispomeroyhale)
[#3636](https://github.com/realm/SwiftLint/pull/3626)
## 0.47.0: Smart Appliance
#### Breaking
@@ -5,6 +5,7 @@ public struct ExpiringTodoRule: ConfigurationProviderRule, OptInRule {
enum ExpiryViolationLevel {
case approachingExpiry
case expired
case badFormatting
var reason: String {
switch self {
@@ -12,13 +13,15 @@ public struct ExpiringTodoRule: ConfigurationProviderRule, OptInRule {
return "TODO/FIXME is approaching its expiry and should be resolved soon."
case .expired:
return "TODO/FIXME has expired and must be resolved."
case .badFormatting:
return "Expiring TODO/FIXME is incorrectly formatted."
}
}
}
public static let description = RuleDescription(
identifier: "expiring_todo",
name: "ExpiringTodo",
name: "Expiring Todo",
description: "TODOs and FIXMEs should be resolved prior to their expiry date.",
kind: .lint,
nonTriggeringExamples: [
@@ -36,7 +39,8 @@ public struct ExpiringTodoRule: ConfigurationProviderRule, OptInRule {
Example("// TODO: [10/14/2019]\n"),
Example("// FIXME: [10/14/2019]\n"),
Example("// FIXME: [1/14/2019]\n"),
Example("// FIXME: [10/4/2019]\n")
Example("// FIXME: [10/14/2019]\n"),
Example("// TODO: [9999/14/10]\n")
]
)
@@ -48,7 +52,7 @@ public struct ExpiringTodoRule: ConfigurationProviderRule, OptInRule {
let regex = #"""
\b(?:TODO|FIXME)(?::|\b)(?:(?!\b(?:TODO|FIXME)(?::|\b)).)*?\#
\\#(configuration.dateDelimiters.opening)\#
(\d{1,4}\\#(configuration.dateSeparator)\d{1,2}\\#(configuration.dateSeparator)\d{1,4})\#
(\d{1,4}\\#(configuration.dateSeparator)\d{1,4}\\#(configuration.dateSeparator)\d{1,4})\#
\\#(configuration.dateDelimiters.closing)
"""#
@@ -57,8 +61,7 @@ public struct ExpiringTodoRule: ConfigurationProviderRule, OptInRule {
syntaxKinds.allSatisfy({ $0.isCommentLike }),
checkingResult.numberOfRanges > 1,
case let range = checkingResult.range(at: 1),
let date = expiryDate(file: file, range: range),
let violationLevel = self.violationLevel(for: date),
let violationLevel = self.violationLevel(for: expiryDate(file: file, range: range)),
let severity = self.severity(for: violationLevel) else {
return nil
}
@@ -90,10 +93,15 @@ public struct ExpiringTodoRule: ConfigurationProviderRule, OptInRule {
return configuration.approachingExpirySeverity.severity
case .expired:
return configuration.expiredSeverity.severity
case .badFormatting:
return configuration.badFormattingSeverity.severity
}
}
private func violationLevel(for expiryDate: Date) -> ExpiryViolationLevel? {
private func violationLevel(for expiryDate: Date?) -> ExpiryViolationLevel? {
guard let expiryDate = expiryDate else {
return .badFormatting
}
guard expiryDate.isAfterToday else {
return .expired
}
@@ -12,14 +12,24 @@ public struct ExpiringTodoConfiguration: RuleConfiguration, Equatable {
}
public var consoleDescription: String {
return "(approaching_expiry_severity) \(approachingExpirySeverity.consoleDescription), " +
"(reached_or_passed_expiry_severity) \(expiredSeverity.consoleDescription)"
let descriptions = [
"approaching_expiry_severity: \(approachingExpirySeverity.consoleDescription)",
"expired_severity: \(expiredSeverity.consoleDescription)",
"bad_formatting_severity: \(badFormattingSeverity.consoleDescription)",
"approaching_expiry_threshold: \(approachingExpiryThreshold)",
"date_format: \(dateFormat)",
"date_delimiters: { opening: \(dateDelimiters.opening)", "closing: \(dateDelimiters.closing) }",
"date_separator: \(dateSeparator)"
]
return descriptions.joined(separator: ", ")
}
private(set) var approachingExpirySeverity: SeverityConfiguration
private(set) var expiredSeverity: SeverityConfiguration
private(set) var badFormattingSeverity: SeverityConfiguration
// swiftlint:disable:next todo
/// The number of days prior to expiry before the TODO emits a violation
private(set) var approachingExpiryThreshold: Int
@@ -33,12 +43,14 @@ public struct ExpiringTodoConfiguration: RuleConfiguration, Equatable {
public init(
approachingExpirySeverity: SeverityConfiguration = .init(.warning),
expiredSeverity: SeverityConfiguration = .init(.error),
badFormattingSeverity: SeverityConfiguration = .init(.error),
approachingExpiryThreshold: Int = 15,
dateFormat: String = "MM/dd/yyyy",
dateDelimiters: DelimiterConfiguration = .default,
dateSeparator: String = "/") {
self.approachingExpirySeverity = approachingExpirySeverity
self.expiredSeverity = expiredSeverity
self.badFormattingSeverity = badFormattingSeverity
self.approachingExpiryThreshold = approachingExpiryThreshold
self.dateDelimiters = dateDelimiters
self.dateFormat = dateFormat
@@ -56,6 +68,9 @@ public struct ExpiringTodoConfiguration: RuleConfiguration, Equatable {
if let expiredConfiguration = configurationDict["expired_severity"] {
try expiredSeverity.apply(configuration: expiredConfiguration)
}
if let badFormattingConfiguration = configurationDict["bad_formatting_severity"] {
try badFormattingSeverity.apply(configuration: badFormattingConfiguration)
}
if let approachingExpiryThreshold = configurationDict["approaching_expiry_threshold"] as? Int {
self.approachingExpiryThreshold = approachingExpiryThreshold
}
@@ -134,6 +134,18 @@ class ExpiringTodoRuleTests: XCTestCase {
XCTAssertEqual(violations[0].location.line, 2)
}
func testBadExpiryTodoFormat() throws {
let ruleConfig: ExpiringTodoConfiguration = .init(
dateFormat: "dd/yyyy/MM"
)
config = makeConfiguration(with: ruleConfig)
let example = Example("fatalError() // TODO: [31/01/2020] Implement")
let violations = self.violations(example)
XCTAssertEqual(violations.count, 1)
XCTAssertEqual(violations.first?.reason, "Expiring TODO/FIXME is incorrectly formatted.")
}
private func violations(_ example: Example) -> [StyleViolation] {
return SwiftLintFrameworkTests.violations(example, config: config)
}
@@ -155,7 +167,7 @@ class ExpiringTodoRuleTests: XCTestCase {
daysToAdvance = ruleConfiguration.approachingExpiryThreshold
case .expired?:
daysToAdvance = 0
case nil:
case .badFormatting?, nil:
daysToAdvance = ruleConfiguration.approachingExpiryThreshold + 1
}
@@ -174,6 +186,7 @@ class ExpiringTodoRuleTests: XCTestCase {
serializedConfig = [
"expired_severity": config.expiredSeverity.severity.rawValue,
"approaching_expiry_severity": config.approachingExpirySeverity.severity.rawValue,
"bad_formatting_severity": config.badFormattingSeverity.severity.rawValue,
"approaching_expiry_threshold": config.approachingExpiryThreshold,
"date_format": config.dateFormat,
"date_delimiters": [