diff --git a/example/Cargo.toml b/example/Cargo.toml index cce0705..48d02f7 100644 --- a/example/Cargo.toml +++ b/example/Cargo.toml @@ -8,6 +8,7 @@ publish = false [features] unstable-risky-function = [] +unstable-risky-struct = [] [dependencies.stability] path = "../" diff --git a/example/src/lib.rs b/example/src/lib.rs index 19fa211..6314783 100644 --- a/example/src/lib.rs +++ b/example/src/lib.rs @@ -1,3 +1,5 @@ +#![forbid(unreachable_pub)] + //! This is an example library demonstrating various attributes from the //! stability crate. @@ -8,3 +10,11 @@ pub fn risky_function() { unimplemented!() } + +/// This struct does something really risky! +/// +/// Don't use it yet! +#[stability::unstable(feature = "risky-struct", issue = "#102")] +pub struct RiskyStruct { + pub x: u8, +} diff --git a/src/lib.rs b/src/lib.rs index 0fd02b4..f8dcd59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,6 +38,14 @@ mod unstable; /// certain crate feature is enabled. This ensures that internal code within /// the crate can always use the item, but downstream consumers cannot access /// it unless they opt-in to the unstable API. +/// - Visibility of certain child items of the annotated item will also be +/// changed to match the new item visibility, such as struct fields. Children +/// that are not public will not be affected. +/// - Child items of annotated modules will *not* have their visibility changed, +/// as it might be desirable to be able to re-export them even if the module +/// visibility is restricted. You should apply the attribute to each item +/// within the module with the same feature name if you want to restrict the +/// module's contents itself and not just the module namespace. /// - Appends an "Availability" section to the item's documentation that notes /// that the item is unstable, and indicates the name of the crate feature to /// enable it. @@ -57,6 +65,8 @@ mod unstable; /// this item's availability. The crate feature will have the string /// `unstable-` prepended to it. If not specified, it will be guarded by a /// catch-all `unstable` feature. +/// - `issue`: Provide a link or reference to a tracking issue for the unstable +/// feature. This will be included in the item's documentation. /// /// # Examples /// diff --git a/src/unstable.rs b/src/unstable.rs index 601bc1d..c79a760 100644 --- a/src/unstable.rs +++ b/src/unstable.rs @@ -72,9 +72,9 @@ The tracking issue is: `{}` } let mut hidden_item = item.clone(); - *hidden_item.visibility_mut() = parse_quote! { + hidden_item.set_visibility(parse_quote! { pub(crate) - }; + }); TokenStream::from(quote! { #[cfg(feature = #feature_name)] @@ -97,7 +97,7 @@ pub(crate) trait ItemLike { fn visibility(&self) -> &Visibility; - fn visibility_mut(&mut self) -> &mut Visibility; + fn set_visibility(&mut self, visibility: Visibility); fn is_public(&self) -> bool { matches!(self.visibility(), Visibility::Public(_)) @@ -120,8 +120,8 @@ macro_rules! impl_has_visibility { &self.vis } - fn visibility_mut(&mut self) -> &mut Visibility { - &mut self.vis + fn set_visibility(&mut self, visibility: Visibility) { + self.vis = visibility; } } )* @@ -131,10 +131,34 @@ macro_rules! impl_has_visibility { impl_has_visibility!( syn::ItemType, syn::ItemEnum, - syn::ItemStruct, syn::ItemFn, syn::ItemMod, syn::ItemTrait, syn::ItemConst, syn::ItemStatic, ); + +impl ItemLike for syn::ItemStruct { + fn attrs(&self) -> &[syn::Attribute] { + &self.attrs + } + + fn push_attr(&mut self, attr: syn::Attribute) { + self.attrs.push(attr); + } + + fn visibility(&self) -> &Visibility { + &self.vis + } + + fn set_visibility(&mut self, visibility: Visibility) { + // Also constrain visibility of all fields to be at most the given + // item visibility. + self.fields + .iter_mut() + .filter(|field| matches!(&field.vis, Visibility::Public(_))) + .for_each(|field| field.vis = visibility.clone()); + + self.vis = visibility; + } +}