fix(streaming): replace the token-kind trap with an exhaustive switch

The 22-case TokenType mapping was split across three initializers to
stay under the complexity lint, leaving a preconditionFailure default
the types could not prove unreachable, which also tripped the
forbidden-patterns gate on every push. One exhaustive switch makes a
new TokenType case a compile error instead of a runtime trap; the lint
now ignores case statements when counting complexity, since exhaustive
closed-enum switches are the point, not a smell.
This commit is contained in:
Mihaela Mihaljevic 2026-07-12 13:26:06 +02:00
parent b48a30f978
commit 20f080a9f7
2 changed files with 10 additions and 20 deletions

View File

@ -14,5 +14,10 @@ force_cast: error
line_length: line_length:
warning: 180 warning: 180
error: 180 error: 180
# Exhaustive switches over closed enums are the intended way to make
# impossible states unrepresentable; counting their cases as complexity
# pushes code toward split mappings with trapping defaults instead.
cyclomatic_complexity:
ignores_case_statements: true
trailing_comma: trailing_comma:
mandatory_comma: true mandatory_comma: true

View File

@ -56,13 +56,11 @@ extension PureYAML.Streaming.Token {
} }
extension PureYAML.Streaming.Token.Kind { extension PureYAML.Streaming.Token.Kind {
/// A mechanical 1:1 mapping over all 22 `TokenType` cases, split across /// A mechanical 1:1 mapping over all 22 `TokenType` cases in one
/// three initializers purely to keep each switch's cyclomatic complexity /// exhaustive switch: no `default`, so adding a `TokenType` case is a
/// under the project's lint limit; the split carries no meaning beyond /// compile error here instead of a runtime trap. The lint's complexity
/// that; each `default:` either forwards to the next initializer (the /// metric ignores case statements precisely for closed-enum mappings
/// cases it doesn't itself handle) or, in the last one, is truly /// like this one.
/// unreachable because the first two together already handle every
/// non-payload-bearing case.
init(_ type: PureYAML.Parsing.LibYAML.TokenType) { init(_ type: PureYAML.Parsing.LibYAML.TokenType) {
switch type { switch type {
case .streamStart: self = .streamStart case .streamStart: self = .streamStart
@ -73,12 +71,6 @@ extension PureYAML.Streaming.Token.Kind {
case .blockMappingStart: self = .blockMappingStart case .blockMappingStart: self = .blockMappingStart
case .blockEnd: self = .blockEnd case .blockEnd: self = .blockEnd
case .blockEntry: self = .blockEntry case .blockEntry: self = .blockEntry
default: self = Self(structural: type)
}
}
private init(structural type: PureYAML.Parsing.LibYAML.TokenType) {
switch type {
case .flowSequenceStart: self = .flowSequenceStart case .flowSequenceStart: self = .flowSequenceStart
case .flowMappingStart: self = .flowMappingStart case .flowMappingStart: self = .flowMappingStart
case .flowSequenceEnd: self = .flowSequenceEnd case .flowSequenceEnd: self = .flowSequenceEnd
@ -87,19 +79,12 @@ extension PureYAML.Streaming.Token.Kind {
case .key: self = .key case .key: self = .key
case .value: self = .value case .value: self = .value
case .reservedDirective: self = .reservedDirective case .reservedDirective: self = .reservedDirective
default: self = Self(payload: type)
}
}
private init(payload type: PureYAML.Parsing.LibYAML.TokenType) {
switch type {
case let .versionDirective(major, minor): self = .versionDirective(major: major, minor: minor) case let .versionDirective(major, minor): self = .versionDirective(major: major, minor: minor)
case let .tagDirective(handle, prefix): self = .tagDirective(handle: handle, prefix: prefix) case let .tagDirective(handle, prefix): self = .tagDirective(handle: handle, prefix: prefix)
case let .anchor(name): self = .anchor(name: name) case let .anchor(name): self = .anchor(name: name)
case let .alias(name): self = .alias(name: name) case let .alias(name): self = .alias(name: name)
case let .tag(handle, suffix): self = .tag(handle: handle, suffix: suffix) case let .tag(handle, suffix): self = .tag(handle: handle, suffix: suffix)
case let .scalar(value, style): self = .scalar(value: value, style: PureYAML.Streaming.Token.ScalarStyle(style)) case let .scalar(value, style): self = .scalar(value: value, style: PureYAML.Streaming.Token.ScalarStyle(style))
default: preconditionFailure("unreachable: init(_:) and init(structural:) handle every non-payload TokenType case")
} }
} }
} }