Skip to content

Commit 8bfa50c

Browse files
authored
Reduce (and lint-disallow) panics in main code (#506)
Aside from datagen/sys code, this stops `temporal_rs` from panicking when unexpected things occur. In general a JS engine should not panic; it should throw an error or have GIGO behavior. ICU4X follows a similar policy. It would be nice to make sure none of the arithmetic can panic as well; though at least in release builds it'll wrap or do other GIGO, which seems fine.
1 parent cfc0f77 commit 8bfa50c

File tree

17 files changed

+103
-52
lines changed

17 files changed

+103
-52
lines changed

provider/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
//!
66
77
#![no_std]
8+
#![cfg_attr(
9+
not(test),
10+
warn(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)
11+
)]
812

913
extern crate alloc;
1014

provider/src/posix.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct PosixZone {
1616
}
1717

1818
#[cfg(feature = "datagen")]
19+
#[allow(clippy::unwrap_used, reason = "Datagen only")]
1920
impl From<&PosixTimeZone> for PosixZone {
2021
fn from(value: &PosixTimeZone) -> Self {
2122
let abbr = TinyAsciiStr::<5>::try_from_str(&value.abbr.formatted).unwrap();
@@ -48,6 +49,7 @@ pub struct ZeroPosixTransition {
4849
}
4950

5051
#[cfg(feature = "datagen")]
52+
#[allow(clippy::unwrap_used, reason = "Datagen only")]
5153
impl From<&PosixTransition> for ZeroPosixTransition {
5254
fn from(value: &PosixTransition) -> Self {
5355
let abbr = TinyAsciiStr::<5>::try_from_str(&value.abbr.formatted).unwrap();

provider/src/tzdb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ pub enum IanaDataError {
114114
}
115115

116116
#[cfg(feature = "datagen")]
117+
#[allow(clippy::expect_used, clippy::unwrap_used, reason = "Datagen only")]
117118
impl IanaIdentifierNormalizer<'_> {
118119
pub fn build(tzdata_path: &Path) -> Result<Self, IanaDataError> {
119120
let provider = TzdbDataSource::try_from_zoneinfo_directory(tzdata_path)

provider/src/tzif.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub enum ZoneInfoDataError {
109109
}
110110

111111
#[cfg(feature = "datagen")]
112+
#[allow(clippy::expect_used, clippy::unwrap_used, reason = "Datagen only")]
112113
impl ZoneInfoProvider<'_> {
113114
pub fn build(tzdata: &Path) -> Result<Self, ZoneInfoDataError> {
114115
let tzdb_source = TzdbDataSource::try_from_rearguard_zoneinfo_dir(tzdata).unwrap();

src/builtins/compiled/zoneddatetime.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,17 @@ impl core::fmt::Display for ZonedDateTime {
1818
///
1919
/// Enable with the `compiled_data` feature flag.
2020
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
21-
f.write_str(
22-
&self
23-
.to_ixdtf_string(
24-
DisplayOffset::Auto,
25-
DisplayTimeZone::Auto,
26-
DisplayCalendar::Auto,
27-
ToStringRoundingOptions::default(),
28-
)
29-
.expect("A valid ZonedDateTime string with default options."),
30-
)
21+
let string = self.to_ixdtf_string(
22+
DisplayOffset::Auto,
23+
DisplayTimeZone::Auto,
24+
DisplayCalendar::Auto,
25+
ToStringRoundingOptions::default(),
26+
);
27+
debug_assert!(
28+
string.is_ok(),
29+
"A valid ZonedDateTime string with default options."
30+
);
31+
f.write_str(&string.map_err(|_| Default::default())?)
3132
}
3233
}
3334

src/builtins/core/datetime.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,15 @@ pub struct PlainDateTime {
183183

184184
impl core::fmt::Display for PlainDateTime {
185185
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
186-
let ixdtf_str = self
187-
.to_ixdtf_string(ToStringRoundingOptions::default(), DisplayCalendar::Auto)
188-
.expect("ixdtf default configuration should not fail.");
189-
f.write_str(&ixdtf_str)
186+
let string =
187+
self.to_ixdtf_string(ToStringRoundingOptions::default(), DisplayCalendar::Auto);
188+
189+
debug_assert!(
190+
string.is_ok(),
191+
"Duration must return a valid string with default options."
192+
);
193+
194+
f.write_str(&string.map_err(|_| Default::default())?)
190195
}
191196
}
192197

src/builtins/core/duration.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -299,11 +299,13 @@ pub struct Duration {
299299

300300
impl core::fmt::Display for Duration {
301301
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
302-
f.write_str(
303-
&self
304-
.as_temporal_string(ToStringRoundingOptions::default())
305-
.expect("Duration must return a valid string with default options."),
306-
)
302+
let string = self.as_temporal_string(ToStringRoundingOptions::default());
303+
304+
debug_assert!(
305+
string.is_ok(),
306+
"Duration must return a valid string with default options."
307+
);
308+
f.write_str(&string.map_err(|_| Default::default())?)
307309
}
308310
}
309311

src/builtins/core/duration/normalized.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414
primitive::FiniteF64,
1515
provider::TimeZoneProvider,
1616
rounding::IncrementRounder,
17-
Calendar, TemporalError, TemporalResult, TemporalUnwrap, NS_PER_DAY,
17+
Calendar, TemporalError, TemporalResult, TemporalUnwrap, NS_PER_DAY, NS_PER_DAY_NONZERO,
1818
};
1919

2020
use super::{DateDuration, Duration, Sign, TimeDuration};
@@ -157,7 +157,7 @@ impl NormalizedTimeDuration {
157157
) -> TemporalResult<i64> {
158158
let adjusted_increment = increment
159159
.as_extended_increment()
160-
.saturating_mul(NonZeroU128::new(NS_PER_DAY as u128).expect("cannot fail"));
160+
.saturating_mul(NS_PER_DAY_NONZERO);
161161
let rounded =
162162
IncrementRounder::<i128>::from_signed_num(self.0, adjusted_increment)?.round(mode);
163163
Ok((rounded / NS_PER_DAY_128BIT) as i64)
@@ -831,11 +831,10 @@ impl NormalizedDurationRecord {
831831
// 5. Let done be false.
832832
// 6. Repeat, while unitIndex ≥ largestUnitIndex and done is false,
833833
// a. Let unit be the value in the "Value" column of Table 21 in the row whose ordinal index is unitIndex.
834-
for unit in UNIT_VALUE_TABLE[largest_unit_index..smallest_unit_index]
835-
.iter()
836-
.rev()
837-
.copied()
838-
{
834+
let unit_values = UNIT_VALUE_TABLE
835+
.get(largest_unit_index..smallest_unit_index)
836+
.temporal_unwrap()?;
837+
for unit in unit_values.iter().rev().copied() {
839838
// b. If unit is not week, or largestUnit is week, then
840839
if unit != Unit::Week || largest_unit == Unit::Week {
841840
let end_duration = match unit {

src/builtins/core/zoneddatetime.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,11 +1461,9 @@ pub(crate) fn interpret_isodatetime_offset(
14611461
// c. If matchBehaviour is match-minutes, then
14621462
if match_minutes {
14631463
// i. Let roundedCandidateNanoseconds be RoundNumberToIncrement(candidateOffset, 60 × 10**9, half-expand).
1464-
let rounded_candidate = IncrementRounder::from_signed_num(
1465-
candidate_offset,
1466-
NonZeroU128::new(60_000_000_000).expect("cannot be zero"), // TODO: Add back const { } after MSRV can be bumped
1467-
)?
1468-
.round(RoundingMode::HalfExpand);
1464+
let rounded_candidate =
1465+
IncrementRounder::from_signed_num(candidate_offset, NS_PER_MINUTE_NONZERO)?
1466+
.round(RoundingMode::HalfExpand);
14691467
// ii. If roundedCandidateNanoseconds = offsetNanoseconds, then
14701468
if rounded_candidate == offset.into() {
14711469
// 1. Return candidate.

src/iso.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,9 @@ impl IsoDate {
537537
impl IsoDate {
538538
/// Creates `[[ISOYear]]`, `[[isoMonth]]`, `[[isoDay]]` fields from `ICU4X`'s `Date<Iso>` struct.
539539
pub(crate) fn to_icu4x(self) -> IcuDate<Iso> {
540-
IcuDate::try_new_iso(self.year, self.month, self.day).expect("must not fail.")
540+
let d = IcuDate::try_new_iso(self.year, self.month, self.day);
541+
debug_assert!(d.is_ok(), "ICU4X ISODate conversion must not fail");
542+
d.unwrap_or_else(|_| IcuDate::from_rata_die(icu_calendar::types::RataDie::new(0), Iso))
541543
}
542544
}
543545

0 commit comments

Comments
 (0)