From 6aa8db51b9949c46b352579c9e951056b8ce333f Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Sat, 9 Sep 2023 01:33:51 -0400 Subject: [PATCH] Require internal macros be explicitly imported --- time/src/date.rs | 4 + time/src/date_time.rs | 4 + time/src/duration.rs | 3 + time/src/error/mod.rs | 2 + time/src/error/parse.rs | 1 + time/src/format_description/parse/ast.rs | 1 + .../format_description/parse/format_item.rs | 1 + time/src/instant.rs | 1 + time/src/internal_macros.rs | 197 ++++++++++++++++++ time/src/lib.rs | 186 +---------------- time/src/offset_date_time.rs | 1 + time/src/parsing/parsable.rs | 1 + time/src/parsing/parsed.rs | 1 + time/src/primitive_date_time.rs | 1 + time/src/time.rs | 1 + time/src/utc_offset.rs | 1 + 16 files changed, 221 insertions(+), 185 deletions(-) create mode 100644 time/src/internal_macros.rs diff --git a/time/src/date.rs b/time/src/date.rs index 2a89abaa2..96a5d8b98 100644 --- a/time/src/date.rs +++ b/time/src/date.rs @@ -11,6 +11,10 @@ use deranged::RangedI32; use crate::convert::*; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{ + cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign, + impl_sub_assign, +}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year}; diff --git a/time/src/date_time.rs b/time/src/date_time.rs index 238a7d41a..048d2a029 100644 --- a/time/src/date_time.rs +++ b/time/src/date_time.rs @@ -22,6 +22,10 @@ use crate::convert::*; use crate::date::{MAX_YEAR, MIN_YEAR}; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{ + bug, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, impl_add_assign, + impl_sub_assign, +}; #[cfg(feature = "parsing")] use crate::parsing::{Parsable, Parsed}; use crate::{error, util, Date, Duration, Month, Time, UtcOffset, Weekday}; diff --git a/time/src/duration.rs b/time/src/duration.rs index 8c0f0245f..e7eacfc2b 100644 --- a/time/src/duration.rs +++ b/time/src/duration.rs @@ -10,6 +10,9 @@ use deranged::RangedI32; use crate::convert::*; use crate::error; +use crate::internal_macros::{ + const_try_opt, expect_opt, impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, +}; #[cfg(feature = "std")] use crate::Instant; diff --git a/time/src/error/mod.rs b/time/src/error/mod.rs index e53f82f75..633909306 100644 --- a/time/src/error/mod.rs +++ b/time/src/error/mod.rs @@ -36,6 +36,8 @@ pub use parse_from_description::ParseFromDescription; #[cfg(feature = "parsing")] pub use try_from_parsed::TryFromParsed; +use crate::internal_macros::bug; + /// A unified error type for anything returned by a method in the time crate. /// /// This can be used when you either don't know or don't care about the exact error returned. diff --git a/time/src/error/parse.rs b/time/src/error/parse.rs index b91749b5e..09935b1c9 100644 --- a/time/src/error/parse.rs +++ b/time/src/error/parse.rs @@ -3,6 +3,7 @@ use core::fmt; use crate::error::{self, ParseFromDescription, TryFromParsed}; +use crate::internal_macros::bug; /// An error that occurred at some stage of parsing. #[allow(variant_size_differences)] diff --git a/time/src/format_description/parse/ast.rs b/time/src/format_description/parse/ast.rs index 4957fe096..c7fc5e05a 100644 --- a/time/src/format_description/parse/ast.rs +++ b/time/src/format_description/parse/ast.rs @@ -6,6 +6,7 @@ use alloc::vec::Vec; use core::iter; use super::{lexer, unused, Error, Location, Spanned, SpannedValue, Unused}; +use crate::internal_macros::bug; /// One part of a complete format description. pub(super) enum Item<'a> { diff --git a/time/src/format_description/parse/format_item.rs b/time/src/format_description/parse/format_item.rs index c54a274ab..f33078ec4 100644 --- a/time/src/format_description/parse/format_item.rs +++ b/time/src/format_description/parse/format_item.rs @@ -6,6 +6,7 @@ use core::num::NonZeroU16; use core::str::{self, FromStr}; use super::{ast, unused, Error, Span, Spanned}; +use crate::internal_macros::bug; /// Parse an AST iterator into a sequence of format items. pub(super) fn parse<'a>( diff --git a/time/src/instant.rs b/time/src/instant.rs index 58579b101..706c759c4 100644 --- a/time/src/instant.rs +++ b/time/src/instant.rs @@ -6,6 +6,7 @@ use core::ops::{Add, Sub}; use core::time::Duration as StdDuration; use std::time::Instant as StdInstant; +use crate::internal_macros::{impl_add_assign, impl_sub_assign}; use crate::Duration; /// A measurement of a monotonically non-decreasing clock. Opaque and useful only with [`Duration`]. diff --git a/time/src/internal_macros.rs b/time/src/internal_macros.rs new file mode 100644 index 000000000..857138983 --- /dev/null +++ b/time/src/internal_macros.rs @@ -0,0 +1,197 @@ +//! Macros for use within the library. They are not publicly available. + +/// Helper macro for easily implementing `OpAssign`. +macro_rules! __impl_assign { + ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$( + #[allow(unused_qualifications)] + $(#[$attr])* + impl core::ops::$op<$t> for $target { + fn $fn(&mut self, rhs: $t) { + *self = *self $sym rhs; + } + } + )+}; +} + +/// Implement `AddAssign` for the provided types. +macro_rules! impl_add_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + + AddAssign add_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `SubAssign` for the provided types. +macro_rules! impl_sub_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + - SubAssign sub_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `MulAssign` for the provided types. +macro_rules! impl_mul_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + * MulAssign mul_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Implement `DivAssign` for the provided types. +macro_rules! impl_div_assign { + ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { + $crate::internal_macros::__impl_assign!( + / DivAssign div_assign $target : $($(#[$attr])* $t),+ + ); + }; +} + +/// Division of integers, rounding the resulting value towards negative infinity. +macro_rules! div_floor { + ($a:expr, $b:expr) => {{ + let _a = $a; + let _b = $b; + + let (_quotient, _remainder) = (_a / _b, _a % _b); + + if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) { + _quotient - 1 + } else { + _quotient + } + }}; +} + +/// Cascade an out-of-bounds value. +macro_rules! cascade { + (@ordinal ordinal) => {}; + (@year year) => {}; + + // Cascade an out-of-bounds value from "from" to "to". + ($from:ident in $min:literal.. $max:expr => $to:tt) => { + #[allow(unused_comparisons, unused_assignments)] + let min = $min; + let max = $max; + if $from >= max { + $from -= max - min; + $to += 1; + } else if $from < min { + $from += max - min; + $to -= 1; + } + }; + + // Special case the ordinal-to-year cascade, as it has different behavior. + ($ordinal:ident => $year:ident) => { + // We need to actually capture the idents. Without this, macro hygiene causes errors. + cascade!(@ordinal $ordinal); + cascade!(@year $year); + #[allow(unused_assignments)] + if $ordinal > crate::util::days_in_year($year) as i16 { + $ordinal -= crate::util::days_in_year($year) as i16; + $year += 1; + } else if $ordinal < 1 { + $year -= 1; + $ordinal += crate::util::days_in_year($year) as i16; + } + }; +} + +/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range. +macro_rules! ensure_ranged { + ($type:ident : $value:ident) => { + match $type::new($value) { + Some(val) => val, + None => { + #[allow(trivial_numeric_casts)] + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as _, + maximum: $type::MAX.get() as _, + value: $value as _, + conditional_range: false, + }); + } + } + }; + + ($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => { + match ($value $(as $as_type)?).checked_mul($factor) { + Some(val) => match $type::new(val) { + Some(val) => val, + None => { + #[allow(trivial_numeric_casts)] + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64 / $factor as i64, + maximum: $type::MAX.get() as i64 / $factor as i64, + value: $value as _, + conditional_range: false, + }); + } + }, + None => { + return Err(crate::error::ComponentRange { + name: stringify!($value), + minimum: $type::MIN.get() as i64 / $factor as i64, + maximum: $type::MAX.get() as i64 / $factor as i64, + value: $value as _, + conditional_range: false, + }); + } + } + }; +} + +/// Try to unwrap an expression, returning if not possible. +/// +/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is +/// usable in `const` contexts. +macro_rules! const_try { + ($e:expr) => { + match $e { + Ok(value) => value, + Err(error) => return Err(error), + } + }; +} + +/// Try to unwrap an expression, returning if not possible. +/// +/// This is similar to the `?` operator, but is usable in `const` contexts. +macro_rules! const_try_opt { + ($e:expr) => { + match $e { + Some(value) => value, + None => return None, + } + }; +} + +/// Try to unwrap an expression, panicking if not possible. +/// +/// This is similar to `$e.expect($message)`, but is usable in `const` contexts. +macro_rules! expect_opt { + ($e:expr, $message:literal) => { + match $e { + Some(value) => value, + None => crate::expect_failed($message), + } + }; +} + +/// `unreachable!()`, but better. +macro_rules! bug { + () => { compile_error!("provide an error message to help fix a possible bug") }; + ($descr:literal $($rest:tt)?) => { + panic!(concat!("internal error: ", $descr) $($rest)?) + } +} + +pub(crate) use { + __impl_assign, bug, cascade, const_try, const_try_opt, div_floor, ensure_ranged, expect_opt, + impl_add_assign, impl_div_assign, impl_mul_assign, impl_sub_assign, +}; diff --git a/time/src/lib.rs b/time/src/lib.rs index b8d98ac9d..9452c4f6a 100644 --- a/time/src/lib.rs +++ b/time/src/lib.rs @@ -86,191 +86,6 @@ #[cfg(feature = "alloc")] extern crate alloc; -// region: macros -/// Helper macro for easily implementing `OpAssign`. -macro_rules! __impl_assign { - ($sym:tt $op:ident $fn:ident $target:ty : $($(#[$attr:meta])* $t:ty),+) => {$( - #[allow(unused_qualifications)] - $(#[$attr])* - impl core::ops::$op<$t> for $target { - fn $fn(&mut self, rhs: $t) { - *self = *self $sym rhs; - } - } - )+}; -} - -/// Implement `AddAssign` for the provided types. -macro_rules! impl_add_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(+ AddAssign add_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Implement `SubAssign` for the provided types. -macro_rules! impl_sub_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(- SubAssign sub_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Implement `MulAssign` for the provided types. -macro_rules! impl_mul_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(* MulAssign mul_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Implement `DivAssign` for the provided types. -macro_rules! impl_div_assign { - ($target:ty : $($(#[$attr:meta])* $t:ty),+ $(,)?) => { - __impl_assign!(/ DivAssign div_assign $target : $($(#[$attr])* $t),+); - }; -} - -/// Division of integers, rounding the resulting value towards negative infinity. -macro_rules! div_floor { - ($a:expr, $b:expr) => {{ - let _a = $a; - let _b = $b; - - let (_quotient, _remainder) = (_a / _b, _a % _b); - - if (_remainder > 0 && _b < 0) || (_remainder < 0 && _b > 0) { - _quotient - 1 - } else { - _quotient - } - }}; -} - -/// Cascade an out-of-bounds value. -macro_rules! cascade { - (@ordinal ordinal) => {}; - (@year year) => {}; - - // Cascade an out-of-bounds value from "from" to "to". - ($from:ident in $min:literal.. $max:expr => $to:tt) => { - #[allow(unused_comparisons, unused_assignments)] - let min = $min; - let max = $max; - if $from >= max { - $from -= max - min; - $to += 1; - } else if $from < min { - $from += max - min; - $to -= 1; - } - }; - - // Special case the ordinal-to-year cascade, as it has different behavior. - ($ordinal:ident => $year:ident) => { - // We need to actually capture the idents. Without this, macro hygiene causes errors. - cascade!(@ordinal $ordinal); - cascade!(@year $year); - #[allow(unused_assignments)] - if $ordinal > crate::util::days_in_year($year) as i16 { - $ordinal -= crate::util::days_in_year($year) as i16; - $year += 1; - } else if $ordinal < 1 { - $year -= 1; - $ordinal += crate::util::days_in_year($year) as i16; - } - }; -} - -/// Constructs a ranged integer, returning a `ComponentRange` error if the value is out of range. -macro_rules! ensure_ranged { - ($type:ident : $value:ident) => { - match $type::new($value) { - Some(val) => val, - None => { - #[allow(trivial_numeric_casts)] - return Err(crate::error::ComponentRange { - name: stringify!($value), - minimum: $type::MIN.get() as _, - maximum: $type::MAX.get() as _, - value: $value as _, - conditional_range: false, - }); - } - } - }; - - ($type:ident : $value:ident $(as $as_type:ident)? * $factor:expr) => { - match ($value $(as $as_type)?).checked_mul($factor) { - Some(val) => match $type::new(val) { - Some(val) => val, - None => { - #[allow(trivial_numeric_casts)] - return Err(crate::error::ComponentRange { - name: stringify!($value), - minimum: $type::MIN.get() as i64 / $factor as i64, - maximum: $type::MAX.get() as i64 / $factor as i64, - value: $value as _, - conditional_range: false, - }); - } - }, - None => { - return Err(crate::error::ComponentRange { - name: stringify!($value), - minimum: $type::MIN.get() as i64 / $factor as i64, - maximum: $type::MAX.get() as i64 / $factor as i64, - value: $value as _, - conditional_range: false, - }); - } - } - }; -} - -/// Try to unwrap an expression, returning if not possible. -/// -/// This is similar to the `?` operator, but does not perform `.into()`. Because of this, it is -/// usable in `const` contexts. -macro_rules! const_try { - ($e:expr) => { - match $e { - Ok(value) => value, - Err(error) => return Err(error), - } - }; -} - -/// Try to unwrap an expression, returning if not possible. -/// -/// This is similar to the `?` operator, but is usable in `const` contexts. -macro_rules! const_try_opt { - ($e:expr) => { - match $e { - Some(value) => value, - None => return None, - } - }; -} - -/// Try to unwrap an expression, panicking if not possible. -/// -/// This is similar to `$e.expect($message)`, but is usable in `const` contexts. -macro_rules! expect_opt { - ($e:expr, $message:literal) => { - match $e { - Some(value) => value, - None => crate::expect_failed($message), - } - }; -} - -/// `unreachable!()`, but better. -macro_rules! bug { - () => { compile_error!("provide an error message to help fix a possible bug") }; - ($descr:literal $($rest:tt)?) => { - panic!(concat!("internal error: ", $descr) $($rest)?) - } -} -// endregion macros - mod date; mod date_time; mod duration; @@ -282,6 +97,7 @@ pub mod format_description; pub mod formatting; #[cfg(feature = "std")] mod instant; +mod internal_macros; #[cfg(feature = "macros")] pub mod macros; mod month; diff --git a/time/src/offset_date_time.rs b/time/src/offset_date_time.rs index d979174e1..e88097fe3 100644 --- a/time/src/offset_date_time.rs +++ b/time/src/offset_date_time.rs @@ -16,6 +16,7 @@ use std::time::SystemTime; use crate::date_time::offset_kind; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{const_try, const_try_opt}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::{error, Date, DateTime, Duration, Month, PrimitiveDateTime, Time, UtcOffset, Weekday}; diff --git a/time/src/parsing/parsable.rs b/time/src/parsing/parsable.rs index b153e850e..30fda4db2 100644 --- a/time/src/parsing/parsable.rs +++ b/time/src/parsing/parsable.rs @@ -9,6 +9,7 @@ use crate::format_description::well_known::{Iso8601, Rfc2822, Rfc3339}; use crate::format_description::FormatItem; #[cfg(feature = "alloc")] use crate::format_description::OwnedFormatItem; +use crate::internal_macros::bug; use crate::parsing::{Parsed, ParsedItem}; use crate::{error, Date, DateTime, Month, Time, UtcOffset, Weekday}; diff --git a/time/src/parsing/parsed.rs b/time/src/parsing/parsed.rs index c382fd3a6..66d51227d 100644 --- a/time/src/parsing/parsed.rs +++ b/time/src/parsing/parsed.rs @@ -14,6 +14,7 @@ use crate::error::TryFromParsed::InsufficientInformation; #[cfg(feature = "alloc")] use crate::format_description::OwnedFormatItem; use crate::format_description::{modifier, Component, FormatItem}; +use crate::internal_macros::const_try_opt; use crate::parsing::component::{ parse_day, parse_end, parse_hour, parse_ignore, parse_minute, parse_month, parse_offset_hour, parse_offset_minute, parse_offset_second, parse_ordinal, parse_period, parse_second, diff --git a/time/src/primitive_date_time.rs b/time/src/primitive_date_time.rs index 9850f96d4..b985867d1 100644 --- a/time/src/primitive_date_time.rs +++ b/time/src/primitive_date_time.rs @@ -9,6 +9,7 @@ use std::io; use crate::date_time::offset_kind; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{const_try, const_try_opt}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::{error, Date, DateTime, Duration, Month, OffsetDateTime, Time, UtcOffset, Weekday}; diff --git a/time/src/time.rs b/time/src/time.rs index e53473347..cfee25cb9 100644 --- a/time/src/time.rs +++ b/time/src/time.rs @@ -11,6 +11,7 @@ use deranged::{RangedU32, RangedU8}; use crate::convert::*; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::{cascade, ensure_ranged, impl_add_assign, impl_sub_assign}; #[cfg(feature = "parsing")] use crate::parsing::Parsable; use crate::util::DateAdjustment; diff --git a/time/src/utc_offset.rs b/time/src/utc_offset.rs index 27efdf354..01c948536 100644 --- a/time/src/utc_offset.rs +++ b/time/src/utc_offset.rs @@ -11,6 +11,7 @@ use crate::convert::*; use crate::error; #[cfg(feature = "formatting")] use crate::formatting::Formattable; +use crate::internal_macros::ensure_ranged; #[cfg(feature = "parsing")] use crate::parsing::Parsable; #[cfg(feature = "local-offset")]