From b783eb466b5b76fe780201d7b5e3c62e31824bb2 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Feb 2025 12:59:17 -0500 Subject: [PATCH 1/5] Makes use of the nightly `doc_cfg` feature to automatically mark feature-gated items as requiring that feature. This is possible thanks to the fact that docs.rs runs on nightly. While this may not be stabilized (and therefore may eventually reverse), I think it's extremely useful to users and only requires small additional configurations that would be easy to remove in the future. --- .github/workflows/ci.yaml | 2 +- Cargo.toml | 3 +++ src/array_approx.rs | 6 ++---- src/arraytraits.rs | 1 + src/error.rs | 1 + src/impl_constructors.rs | 4 ++++ src/lib.rs | 6 ++++++ src/linalg_traits.rs | 5 +++++ src/numeric/impl_float_maths.rs | 4 +--- src/numeric/impl_numeric.rs | 4 ++++ src/parallel/impl_par_methods.rs | 2 -- src/partial.rs | 3 +++ 12 files changed, 31 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ae74aeb45..27289853d 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -161,7 +161,7 @@ jobs: strategy: matrix: rust: - - stable + - nightly # This is what docs.rs runs on, and is needed for the feature flags name: docs/${{ matrix.rust }} env: RUSTDOCFLAGS: "-Dwarnings" diff --git a/Cargo.toml b/Cargo.toml index 5c7217025..0f487b3b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -121,5 +121,8 @@ opt-level = 2 no-dev-version = true tag-name = "{{version}}" +# Config specific to docs.rs [package.metadata.docs.rs] features = ["docs"] +# Define the configuration attribute `docsrs` +rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/array_approx.rs b/src/array_approx.rs index 493864c7e..c6fd174d1 100644 --- a/src/array_approx.rs +++ b/src/array_approx.rs @@ -1,4 +1,5 @@ #[cfg(feature = "approx")] +#[cfg_attr(docsrs, doc(cfg(feature = "approx")))] mod approx_methods { use crate::imp_prelude::*; @@ -10,8 +11,6 @@ mod approx_methods { /// A test for equality that uses the elementwise absolute difference to compute the /// approximate equality of two arrays. - /// - /// **Requires crate feature `"approx"`** pub fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool where A: ::approx::AbsDiffEq, @@ -23,8 +22,6 @@ mod approx_methods /// A test for equality that uses an elementwise relative comparison if the values are far /// apart; and the absolute difference otherwise. - /// - /// **Requires crate feature `"approx"`** pub fn relative_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool where A: ::approx::RelativeEq, @@ -192,4 +189,5 @@ macro_rules! impl_approx_traits { } #[cfg(feature = "approx")] +#[cfg_attr(docsrs, doc(cfg(feature = "approx")))] impl_approx_traits!(approx, "**Requires crate feature `\"approx\"`.**"); diff --git a/src/arraytraits.rs b/src/arraytraits.rs index d7a00fcfe..62f95df4a 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -316,6 +316,7 @@ where } #[cfg(feature = "serde")] +#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] // Use version number so we can add a packed format later. pub const ARRAY_FORMAT_VERSION: u8 = 1u8; diff --git a/src/error.rs b/src/error.rs index eb7395ad8..e19c32075 100644 --- a/src/error.rs +++ b/src/error.rs @@ -81,6 +81,7 @@ impl PartialEq for ShapeError } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl Error for ShapeError {} impl fmt::Display for ShapeError diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs index 260937a90..c1e5b1b8b 100644 --- a/src/impl_constructors.rs +++ b/src/impl_constructors.rs @@ -99,6 +99,7 @@ where S: DataOwned /// assert!(array == arr1(&[0.0, 0.25, 0.5, 0.75, 1.0])) /// ``` #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn linspace(start: A, end: A, n: usize) -> Self where A: Float { @@ -117,6 +118,7 @@ where S: DataOwned /// assert!(array == arr1(&[0., 1., 2., 3., 4.])) /// ``` #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn range(start: A, end: A, step: A) -> Self where A: Float { @@ -145,6 +147,7 @@ where S: DataOwned /// # } /// ``` #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn logspace(base: A, start: A, end: A, n: usize) -> Self where A: Float { @@ -179,6 +182,7 @@ where S: DataOwned /// # example().unwrap(); /// ``` #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn geomspace(start: A, end: A, n: usize) -> Option where A: Float { diff --git a/src/lib.rs b/src/lib.rs index b163f16a5..8b0c9c25e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,8 @@ #![doc(test(attr(allow(unused_variables))))] #![doc(test(attr(allow(deprecated))))] #![cfg_attr(not(feature = "std"), no_std)] +// Enable the doc_cfg nightly feature for including feature gate flags in the documentation +#![cfg_attr(docsrs, feature(doc_cfg))] //! The `ndarray` crate provides an *n*-dimensional container for general elements //! and for numerics. @@ -148,6 +150,7 @@ use crate::iterators::{ElementsBase, ElementsBaseMut, Iter, IterMut}; pub use crate::arraytraits::AsArray; pub use crate::linalg_traits::LinalgScalar; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::linalg_traits::NdFloat; pub use crate::stacking::{concatenate, stack}; @@ -189,9 +192,11 @@ mod layout; mod linalg_traits; mod linspace; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::linspace::{linspace, range, Linspace}; mod logspace; #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub use crate::logspace::{logspace, Logspace}; mod math_cell; mod numeric_util; @@ -1587,6 +1592,7 @@ where // parallel methods #[cfg(feature = "rayon")] +#[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] pub mod parallel; mod impl_1d; diff --git a/src/linalg_traits.rs b/src/linalg_traits.rs index 65d264c40..ec1aebbe7 100644 --- a/src/linalg_traits.rs +++ b/src/linalg_traits.rs @@ -39,7 +39,10 @@ impl LinalgScalar for T where T: 'static + Copy + Zero + One + Add { $($(#[$meta])* @@ -39,7 +37,6 @@ macro_rules! unary_ops { }; } -#[cfg(feature = "std")] macro_rules! binary_ops { ($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => { $($(#[$meta])* @@ -54,6 +51,7 @@ macro_rules! binary_ops { /// /// Element-wise math functions for any array type that contains float number. #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl ArrayBase where A: 'static + Float, diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs index 6c67b9135..a8a008395 100644 --- a/src/numeric/impl_numeric.rs +++ b/src/numeric/impl_numeric.rs @@ -140,6 +140,7 @@ where /// ``` #[track_caller] #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn var(&self, ddof: A) -> A where A: Float + FromPrimitive { @@ -205,6 +206,7 @@ where /// ``` #[track_caller] #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn std(&self, ddof: A) -> A where A: Float + FromPrimitive { @@ -361,6 +363,7 @@ where /// ``` #[track_caller] #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn var_axis(&self, axis: Axis, ddof: A) -> Array where A: Float + FromPrimitive, @@ -431,6 +434,7 @@ where /// ``` #[track_caller] #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn std_axis(&self, axis: Axis, ddof: A) -> Array where A: Float + FromPrimitive, diff --git a/src/parallel/impl_par_methods.rs b/src/parallel/impl_par_methods.rs index c6af4e8f3..7f01ea32f 100644 --- a/src/parallel/impl_par_methods.rs +++ b/src/parallel/impl_par_methods.rs @@ -8,8 +8,6 @@ use crate::parallel::prelude::*; use crate::partial::Partial; /// # Parallel methods -/// -/// These methods require crate feature `rayon`. impl ArrayBase where S: DataMut, diff --git a/src/partial.rs b/src/partial.rs index 99aba75a8..4509e77dc 100644 --- a/src/partial.rs +++ b/src/partial.rs @@ -37,6 +37,7 @@ impl Partial } #[cfg(feature = "rayon")] + #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] pub(crate) fn stub() -> Self { Self { @@ -46,6 +47,7 @@ impl Partial } #[cfg(feature = "rayon")] + #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] pub(crate) fn is_stub(&self) -> bool { self.ptr.is_null() @@ -60,6 +62,7 @@ impl Partial } #[cfg(feature = "rayon")] + #[cfg_attr(docsrs, doc(cfg(feature = "rayon")))] /// Merge if they are in order (left to right) and contiguous. /// Skips merge if T does not need drop. pub(crate) fn try_merge(mut left: Self, right: Self) -> Self From 804845b4808acde193a2a1b5556eaaaae51a610d Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Feb 2025 13:17:38 -0500 Subject: [PATCH 2/5] Adds appropriate arguments to CI/CD and removes serde-1, test, and docs features --- .github/workflows/ci.yaml | 8 ++++---- Cargo.toml | 10 +--------- src/lib.rs | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 27289853d..1a1ee6415 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,7 +10,7 @@ name: Continuous integration env: CARGO_TERM_COLOR: always HOST: x86_64-unknown-linux-gnu - FEATURES: "test docs" + FEATURES: "approx,serde,rayon" RUSTFLAGS: "-D warnings" MSRV: 1.64.0 BLAS_MSRV: 1.71.1 @@ -30,7 +30,7 @@ jobs: toolchain: ${{ matrix.rust }} components: clippy - uses: Swatinem/rust-cache@v2 - - run: cargo clippy --features docs + - run: cargo clippy --features approx,serde,rayon format: runs-on: ubuntu-latest @@ -139,7 +139,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Install cross run: cargo install cross - - run: ./scripts/cross-tests.sh "docs" ${{ matrix.rust }} ${{ matrix.target }} + - run: ./scripts/cross-tests.sh "approx,serde,rayon" ${{ matrix.rust }} ${{ matrix.target }} cargo-careful: #if: ${{ github.event_name == 'merge_group' }} @@ -164,7 +164,7 @@ jobs: - nightly # This is what docs.rs runs on, and is needed for the feature flags name: docs/${{ matrix.rust }} env: - RUSTDOCFLAGS: "-Dwarnings" + RUSTDOCFLAGS: "-Dwarnings --cfg docsrs" steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master diff --git a/Cargo.toml b/Cargo.toml index 0f487b3b8..3d1c1dde6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,14 +59,6 @@ default = ["std"] blas = ["dep:cblas-sys", "dep:libc"] serde = ["dep:serde"] -# Old name for the serde feature -serde-1 = ["dep:serde"] - -# These features are used for testing -test = [] - -# This feature is used for docs -docs = ["approx", "serde", "rayon"] std = ["num-traits/std", "matrixmultiply/std"] rayon = ["dep:rayon", "std"] @@ -123,6 +115,6 @@ tag-name = "{{version}}" # Config specific to docs.rs [package.metadata.docs.rs] -features = ["docs"] +features = ["approx", "serde", "rayon"] # Define the configuration attribute `docsrs` rustdoc-args = ["--cfg", "docsrs"] diff --git a/src/lib.rs b/src/lib.rs index 8b0c9c25e..f0b64028f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -122,7 +122,7 @@ extern crate std; #[cfg(feature = "blas")] extern crate cblas_sys; -#[cfg(feature = "docs")] +#[cfg(docsrs)] pub mod doc; #[cfg(target_has_atomic = "ptr")] From 790ab246d5e14efd46e58c92c3da80b7546cf513 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Feb 2025 15:45:57 -0500 Subject: [PATCH 3/5] Suppress warnings about unused macros when the std feature is not enabled --- src/numeric/impl_float_maths.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/numeric/impl_float_maths.rs b/src/numeric/impl_float_maths.rs index d3243eff4..7a88364e3 100644 --- a/src/numeric/impl_float_maths.rs +++ b/src/numeric/impl_float_maths.rs @@ -5,6 +5,7 @@ use num_traits::Float; use crate::imp_prelude::*; +#[cfg(feature = "std")] macro_rules! boolean_ops { ($(#[$meta1:meta])* fn $func:ident $(#[$meta2:meta])* fn $all:ident @@ -27,6 +28,7 @@ macro_rules! boolean_ops { }; } +#[cfg(feature = "std")] macro_rules! unary_ops { ($($(#[$meta:meta])* fn $id:ident)+) => { $($(#[$meta])* @@ -37,6 +39,7 @@ macro_rules! unary_ops { }; } +#[cfg(feature = "std")] macro_rules! binary_ops { ($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => { $($(#[$meta])* From e5a421143721d9e07f8634e10b8664aac896dd98 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Feb 2025 15:51:19 -0500 Subject: [PATCH 4/5] Fixes clippy complaining about `return None` instead of question marks --- src/impl_methods.rs | 5 +---- src/iterators/mod.rs | 6 +----- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/impl_methods.rs b/src/impl_methods.rs index 4a00ea000..3da63b936 100644 --- a/src/impl_methods.rs +++ b/src/impl_methods.rs @@ -2299,10 +2299,7 @@ where let dim = dim.into_dimension(); // Note: zero strides are safe precisely because we return an read-only view - let broadcast_strides = match upcast(&dim, &self.dim, &self.strides) { - Some(st) => st, - None => return None, - }; + let broadcast_strides = upcast(&dim, &self.dim, &self.strides)?; unsafe { Some(ArrayView::new(self.ptr, dim, broadcast_strides)) } } diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 01fff14f5..67a0fbea5 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -139,13 +139,9 @@ impl DoubleEndedIterator for Baseiter #[inline] fn next_back(&mut self) -> Option { - let index = match self.index { - None => return None, - Some(ix) => ix, - }; self.dim[0] -= 1; let offset = Ix1::stride_offset(&self.dim, &self.strides); - if index == self.dim { + if self.index? == self.dim { self.index = None; } From d8d3dc557c5e4b22addf09c933ebaaec6b272e06 Mon Sep 17 00:00:00 2001 From: Adam Kern Date: Sun, 2 Feb 2025 22:12:54 -0500 Subject: [PATCH 5/5] Option in next_back had to be unwrapped at the start --- src/iterators/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs index 67a0fbea5..e0da8f6c9 100644 --- a/src/iterators/mod.rs +++ b/src/iterators/mod.rs @@ -139,9 +139,10 @@ impl DoubleEndedIterator for Baseiter #[inline] fn next_back(&mut self) -> Option { + let index = self.index?; self.dim[0] -= 1; let offset = Ix1::stride_offset(&self.dim, &self.strides); - if self.index? == self.dim { + if index == self.dim { self.index = None; }