Skip to content

Commit b095571

Browse files
authored
Make Number non-exhaustive (#564)
* Make Number non-exhaustive * Add CHANGELOG entry
1 parent 8350604 commit b095571

File tree

3 files changed

+43
-5
lines changed

3 files changed

+43
-5
lines changed

.github/workflows/ci.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,10 +97,10 @@ jobs:
9797
cargo update -p serde_bytes --precise 0.11.16
9898
cargo update -p indexmap --precise 2.5.0
9999
cargo update -p once_cell --precise 1.20.3
100-
- run: cargo clippy -- -D warnings
101-
- run: cargo clippy --features integer128 -- -D warnings
102-
- run: cargo clippy --features indexmap -- -D warnings
103-
- run: cargo clippy --all-features -- -D warnings
100+
- run: cargo clippy -- -D warnings -A unknown-lints
101+
- run: cargo clippy --features integer128 -- -D warnings -A unknown-lints
102+
- run: cargo clippy --features indexmap -- -D warnings -A unknown-lints
103+
- run: cargo clippy --all-features -- -D warnings -A unknown-lints
104104

105105
clippy-fuzz:
106106
name: "Clippy: Fuzzer"

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99
### API Changes
1010

1111
- Add `ron::Options::to_io_writer` and `ron::Options::to_io_writer_pretty` to allow writing into an `io::Writer` ([#561](https://github.com/ron-rs/ron/pull/561))
12+
- Breaking: `ron::value::Number` is now non-exhaustive, to avoid breaking `match`es when feature unification enables more of its variants than expected ([#564](https://github.com/ron-rs/ron/pull/564))
1213

1314
## [0.9.0] - 2025-03-18
1415

src/value/number.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,34 @@ use std::{
55

66
use serde::{de::Visitor, Serialize, Serializer};
77

8-
/// A wrapper for any numeric primitive type in Rust
8+
/// A wrapper for any numeric primitive type in Rust.
9+
///
10+
/// Some varints of the `Number` enum are enabled by features:
11+
/// - `Number::I128` and `Number::U128` by the `integer128` feature
12+
///
13+
/// To ensure that feature unification does not break `match`ing over `Number`,
14+
/// the `Number` enum is non-exhaustive.
15+
///
16+
/// <details>
17+
/// <summary>Exhaustively matching on <code>Number</code> in tests</summary>
18+
///
19+
/// If you want to ensure that you exhaustively handle every variant, you can
20+
/// match on the hidden `Number::__NonExhaustive` variant.
21+
///
22+
/// <div class="warning">
23+
/// Matching on this variant means that your code may break when RON is
24+
/// upgraded or when feature unification enables further variants in the
25+
/// <code>Number</code> enum than your code expects.
26+
/// </div>
27+
///
28+
/// It is your responsibility to only *ever* match on `Number::__NonExhaustive`
29+
/// inside tests, e.g. by using `#[cfg(test)]` on the particular match arm, to
30+
/// ensure that only your tests break (e.g. in CI) when your code is not
31+
/// exhaustively matching on every variant, e.g. after a version upgrade or
32+
/// feature unification.
33+
/// </details>
934
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
35+
#[cfg_attr(doc, non_exhaustive)]
1036
pub enum Number {
1137
I8(i8),
1238
I16(i16),
@@ -22,6 +48,14 @@ pub enum Number {
2248
U128(u128),
2349
F32(F32),
2450
F64(F64),
51+
#[cfg(not(doc))]
52+
#[allow(private_interfaces)]
53+
__NonExhaustive(private::Never),
54+
}
55+
56+
mod private {
57+
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)]
58+
pub enum Never {}
2559
}
2660

2761
impl Serialize for Number {
@@ -41,6 +75,7 @@ impl Serialize for Number {
4175
Self::U128(v) => serializer.serialize_u128(*v),
4276
Self::F32(v) => serializer.serialize_f32(v.get()),
4377
Self::F64(v) => serializer.serialize_f64(v.get()),
78+
Self::__NonExhaustive(never) => match *never {},
4479
}
4580
}
4681
}
@@ -65,6 +100,7 @@ impl Number {
65100
Self::U128(v) => visitor.visit_u128(*v),
66101
Self::F32(v) => visitor.visit_f32(v.get()),
67102
Self::F64(v) => visitor.visit_f64(v.get()),
103+
Self::__NonExhaustive(never) => match *never {},
68104
}
69105
}
70106
}
@@ -216,6 +252,7 @@ impl Number {
216252
Number::U128(v) => v as f64,
217253
Number::F32(v) => f64::from(v.get()),
218254
Number::F64(v) => v.get(),
255+
Self::__NonExhaustive(never) => match never {},
219256
}
220257
}
221258
}

0 commit comments

Comments
 (0)