Skip to content

Commit

Permalink
Apply visibility restriction to struct fields (#10)
Browse files Browse the repository at this point in the history
When changing the visibility of a struct field from `pub` to
`pub(crate)`, also apply the same change to any struct fields that are
also `pub`.

Fixes #7.
  • Loading branch information
sagebind authored Apr 2, 2024
1 parent 3b2a340 commit 2777981
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 6 deletions.
1 change: 1 addition & 0 deletions example/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ publish = false

[features]
unstable-risky-function = []
unstable-risky-struct = []

[dependencies.stability]
path = "../"
10 changes: 10 additions & 0 deletions example/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![forbid(unreachable_pub)]

//! This is an example library demonstrating various attributes from the
//! stability crate.

Expand All @@ -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,
}
10 changes: 10 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
///
Expand Down
36 changes: 30 additions & 6 deletions src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand All @@ -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(_))
Expand All @@ -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;
}
}
)*
Expand All @@ -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;
}
}

0 comments on commit 2777981

Please sign in to comment.