Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API: Make item Visibility visible in the API #294

Merged
merged 11 commits into from
Nov 14, 2023
91 changes: 42 additions & 49 deletions marker_api/src/sem/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ pub struct Visibility<'ast> {
}

impl<'ast> Visibility<'ast> {
/// Returns `true` if the item is declared as public, without any restrictions.
/// Returns `true` if the item is public, meaning that it can be visible outside
/// the crate.
///
/// ```
/// // This returns `true`
Expand All @@ -41,35 +42,29 @@ impl<'ast> Visibility<'ast> {
///
/// // This returns `false`, since the visibility is not defined
/// fn dragon() {}
/// ```
///
/// See [`Visibility::is_pub_in_path`] to detect pub declarations with a
/// defined path.
/// pub trait MythicalCreature {
/// // This returns `true`, since the default visibility for trait items is public
/// fn name() -> &'static str;
/// }
/// ```
pub fn is_pub(&self) -> bool {
matches!(self.kind, VisibilityKind::Public | VisibilityKind::DefaultPub)
self.scope().is_none()
}

/// Returns `true` if the item is declared as `pub(in ..)` with a path in brackets
/// that defines the scope, where the item is visible.
pub fn is_pub_in_path(&self) -> bool {
matches!(self.kind, VisibilityKind::Path(_))
}

/// Returns `true` if the visibility is declared as `pub(crate)`. This is a
/// special case of the `pub(<path>)` visibility.
/// Returns `true` if the item is visible in the entire crate. This is
/// the case for items declared as `pub(crate)`. Items declared in the root
/// module of the crate or specify the path of the root module are also
/// scoped to the entire crate.
///
/// This function checks if the visibility is restricted and the defined path
/// belongs to the root module of the crate. Meaning, that this can also be `true`,
/// if the visibility uses `pub(super)` to travel up to the crate root.
// Ignore, since the `in crate::example_1` path doesn't work for doc tests
/// ```ignore
/// ```
/// // lib.rs
///
/// mod example_1 {
/// // Returns `false` since no visibility is declared
/// // Returns `false` since it's only visible in `crate::example_1`
/// fn foo() {}
///
/// // Returns `false` since the item is not visible from the root of the crate.
/// // Returns `false` since it's only visible in `crate::example_1`
/// pub(in crate::example_1) fn bar() {}
///
/// // Returns `true` as the visibility is restricted to the root of the crate.
Expand All @@ -80,57 +75,52 @@ impl<'ast> Visibility<'ast> {
/// pub(super) fn boo() {}
/// }
///
/// // Returns `false` since the visibility is not restricted
/// // Returns `true` since this item is at the root of the crate and the default
/// // visibility is therefore `pub(crate)`
/// fn example_in_root() {}
///
/// # fn main() {}
/// ```
pub fn is_pub_crate(&self) -> bool {
matches!(self.kind, VisibilityKind::Crate(_))
pub fn is_crate_scoped(&self) -> bool {
matches!(self.kind, VisibilityKind::Crate(_) | VisibilityKind::DefaultCrate(_))
}

/// Returns `true` if a visibility is the default visibility, meaning that it wasn't
/// declared.
pub fn is_default(&self) -> bool {
matches!(self.kind, VisibilityKind::Default(_) | VisibilityKind::DefaultPub)
}

/// Returns the [`ItemId`] of the module where this item is visible in, if the
/// visibility is defined to be public inside a specified path.
///
/// See [`Visibility::module_id`] to get the `ItemId`, even if the item or
/// field uses the default visibility.
pub fn pub_with_path_module_id(&self) -> Option<ItemId> {
match self.kind {
VisibilityKind::Path(id) | VisibilityKind::Crate(id) => Some(id),
_ => None,
}
matches!(
self.kind,
VisibilityKind::DefaultPub | VisibilityKind::DefaultCrate(_) | VisibilityKind::Default(_)
)
}

/// Returns the [`ItemId`] of the module that this item or field is visible in.
/// It will return `None`, if the item is public, as the visibility extends even past
/// It will return `None`, if the item is public, as the visibility extends past
/// the root module of the crate.
///
/// This function also works for items which have the default visibility, of the
/// module they are declared in.
///
/// ```
/// // lib.rs
///
/// mod scope {
/// // Returns `None` since this is even visible outside the current crate
/// pub fn turtle() {}
///
/// // Returns the `ItemId` of the root module of the crate
/// pub(crate) fn shark() {}
///
/// // Returns the `ItemId` of the module it is declared in
/// // Returns the `ItemId` of the module it is declared in, in this case `crate::scope`
/// fn dolphin() {}
/// }
/// ```
///
/// Note that this only returns the [`ItemId`] that this item is visible in
/// based on the declared visibility. The item might be reexported, which can
/// increase the visibility.
pub fn module_id(&self) -> Option<ItemId> {
pub fn scope(&self) -> Option<ItemId> {
match self.kind {
VisibilityKind::Path(id) | VisibilityKind::Crate(id) | VisibilityKind::Default(id) => Some(id),
VisibilityKind::Path(id)
| VisibilityKind::Crate(id)
| VisibilityKind::DefaultCrate(id)
| VisibilityKind::Default(id) => Some(id),
_ => None,
xFrednet marked this conversation as resolved.
Show resolved Hide resolved
}
}
Expand All @@ -145,16 +135,19 @@ impl<'ast> Visibility<'ast> {
enum VisibilityKind {
/// The item is declared as `pub` without any restrictions
Public,
/// For items which are `pub` by default, like trait functions or enum variants
DefaultPub,
/// The visibility is restricted to the root module of the crate. The [`ItemId`]
/// identifies the root module.
Crate(ItemId),
/// The items doesn't have a declared visibility. The default is visible in the
/// entire crate. The [`ItemId`] identifies the root module.
DefaultCrate(ItemId),
/// The visibility is restricted to a specific module using `pub(<path>)`.
/// The module, targeted by the path is identified by the [`ItemId`].
/// The `pub(crate)` has it's own variant in this struct.
Path(ItemId),
/// The visibility is restricted to the root module of the crate. The [`ItemId`]
/// identifies the root module.
Crate(ItemId),
/// The items doesn't have a declared visibility. The default is restricted to
/// a module, identified by the stored [`ItemId`]
Default(ItemId),
/// For items which are `pub` by default, like trait functions or enum variants
DefaultPub,
}
15 changes: 12 additions & 3 deletions marker_rustc_driver/src/conversion/marker/sem/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,20 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> {
let vis = self.rustc_cx.visibility(owner_id);
let kind = match vis {
mid::ty::Visibility::Public => sem::VisibilityKind::Public,
mid::ty::Visibility::Restricted(id) if !has_span => sem::VisibilityKind::Default(self.to_item_id(id)),
mid::ty::Visibility::Restricted(id) if id == hir::def_id::CRATE_DEF_ID.to_def_id() => {
sem::VisibilityKind::Crate(self.to_item_id(id))
if has_span {
sem::VisibilityKind::Crate(self.to_item_id(id))
} else {
sem::VisibilityKind::DefaultCrate(self.to_item_id(id))
}
},
mid::ty::Visibility::Restricted(id) => {
if has_span {
sem::VisibilityKind::Path(self.to_item_id(id))
} else {
sem::VisibilityKind::Default(self.to_item_id(id))
}
},
mid::ty::Visibility::Restricted(id) => sem::VisibilityKind::Path(self.to_item_id(id)),
};

sem::Visibility::builder().kind(kind).build()
Expand Down
13 changes: 4 additions & 9 deletions marker_uilints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,15 +160,10 @@ impl LintPass for TestLintPass {
let ast_vis = item.visibility();
let vis = ast_vis.semantics();
diag.span(item.ident().unwrap().span());
diag.note(format!("vis.is_pub() -> {}", vis.is_pub()));
diag.note(format!("vis.is_pub_in_path() -> {}", vis.is_pub_in_path()));
diag.note(format!("vis.is_pub_crate() -> {}", vis.is_pub_crate()));
diag.note(format!("vis.is_default() -> {}", vis.is_default()));
diag.note(format!(
"vis.pub_with_path_module_id() -> {:?}",
vis.pub_with_path_module_id()
));
diag.note(format!("vis.module_id() -> {:?}", vis.module_id()));
diag.note(format!("vis.is_default() -> {}", vis.is_default()));
diag.note(format!("vis.is_pub() -> {}", vis.is_pub()));
diag.note(format!("vis.is_crate_scoped() -> {}", vis.is_crate_scoped()));
diag.note(format!("vis.scope() -> {:?}", vis.scope()));
diag.note(format!("vis.span(): `{:?}`", ast_vis.span().map(|s| s.snippet_or(""))));
});
}
Expand Down
6 changes: 3 additions & 3 deletions marker_uilints/tests/ui/context/test_ast_map.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ warning: testing `AstMap::variant`
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down Expand Up @@ -84,7 +84,7 @@ warning: testing `AstMap::item`
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand All @@ -106,7 +106,7 @@ warning: testing `AstMap::item`
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down
6 changes: 3 additions & 3 deletions marker_uilints/tests/ui/item/print_async_fn.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ warning: printing item with body
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down Expand Up @@ -97,7 +97,7 @@ warning: printing item with body
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down Expand Up @@ -529,7 +529,7 @@ warning: printing item with body
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down
2 changes: 1 addition & 1 deletion marker_uilints/tests/ui/item/print_me_root_module.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ warning: printing item
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down
60 changes: 24 additions & 36 deletions marker_uilints/tests/ui/item/test_visibility.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,10 @@ warning: can you see this item?
1 | fn test_vis_private() {}
| ^^^^^^^^^^^^^^^^
|
= note: vis.is_pub() -> false
= note: vis.is_pub_in_path() -> false
= note: vis.is_pub_crate() -> false
= note: vis.is_default() -> true
= note: vis.pub_with_path_module_id() -> None
= note: vis.module_id() -> Some(ItemId(..))
= note: vis.is_default() -> true
= note: vis.is_pub() -> false
= note: vis.is_crate_scoped() -> true
= note: vis.scope() -> Some(ItemId(..))
= note: vis.span(): `None`
= note: `#[warn(marker::marker_uilints::test_item_visibility)]` on by default

Expand All @@ -19,12 +17,10 @@ warning: can you see this item?
3 | pub fn test_vis_public() {}
| ^^^^^^^^^^^^^^^
|
= note: vis.is_pub() -> true
= note: vis.is_pub_in_path() -> false
= note: vis.is_pub_crate() -> false
= note: vis.is_default() -> false
= note: vis.pub_with_path_module_id() -> None
= note: vis.module_id() -> None
= note: vis.is_default() -> false
= note: vis.is_pub() -> true
= note: vis.is_crate_scoped() -> false
= note: vis.scope() -> None
= note: vis.span(): `Some("pub")`

warning: can you see this item?
Expand All @@ -33,12 +29,10 @@ warning: can you see this item?
6 | pub(crate) fn test_vis_pub_crate() {}
| ^^^^^^^^^^^^^^^^^^
|
= note: vis.is_pub() -> false
= note: vis.is_pub_in_path() -> false
= note: vis.is_pub_crate() -> true
= note: vis.is_default() -> false
= note: vis.pub_with_path_module_id() -> Some(ItemId(..))
= note: vis.module_id() -> Some(ItemId(..))
= note: vis.is_default() -> false
= note: vis.is_pub() -> false
= note: vis.is_crate_scoped() -> true
= note: vis.scope() -> Some(ItemId(..))
= note: vis.span(): `Some("pub(crate)")`

warning: can you see this item?
Expand All @@ -47,12 +41,10 @@ warning: can you see this item?
8 | pub(super) fn test_vis_pub_super_crate_root() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: vis.is_pub() -> false
= note: vis.is_pub_in_path() -> false
= note: vis.is_pub_crate() -> true
= note: vis.is_default() -> false
= note: vis.pub_with_path_module_id() -> Some(ItemId(..))
= note: vis.module_id() -> Some(ItemId(..))
= note: vis.is_default() -> false
= note: vis.is_pub() -> false
= note: vis.is_crate_scoped() -> true
= note: vis.scope() -> Some(ItemId(..))
= note: vis.span(): `Some("pub(super)")`

warning: can you see this item?
Expand All @@ -61,12 +53,10 @@ warning: can you see this item?
11 | pub(super) fn test_vis_pub_super() {}
| ^^^^^^^^^^^^^^^^^^
|
= note: vis.is_pub() -> false
= note: vis.is_pub_in_path() -> true
= note: vis.is_pub_crate() -> false
= note: vis.is_default() -> false
= note: vis.pub_with_path_module_id() -> Some(ItemId(..))
= note: vis.module_id() -> Some(ItemId(..))
= note: vis.is_default() -> false
= note: vis.is_pub() -> false
= note: vis.is_crate_scoped() -> false
= note: vis.scope() -> Some(ItemId(..))
= note: vis.span(): `Some("pub(super)")`

warning: can you see this item?
Expand All @@ -75,12 +65,10 @@ warning: can you see this item?
13 | pub(in crate::module) fn test_vis_pub_in_path() {}
| ^^^^^^^^^^^^^^^^^^^^
|
= note: vis.is_pub() -> false
= note: vis.is_pub_in_path() -> true
= note: vis.is_pub_crate() -> false
= note: vis.is_default() -> false
= note: vis.pub_with_path_module_id() -> Some(ItemId(..))
= note: vis.module_id() -> Some(ItemId(..))
= note: vis.is_default() -> false
= note: vis.is_pub() -> false
= note: vis.is_crate_scoped() -> false
= note: vis.scope() -> Some(ItemId(..))
= note: vis.span(): `Some("pub(in crate::module)")`

warning: 6 warnings emitted
Expand Down
6 changes: 3 additions & 3 deletions marker_uilints/tests/ui/print_const_generics.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ warning: printing item
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down Expand Up @@ -53,7 +53,7 @@ warning: printing item
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down Expand Up @@ -132,7 +132,7 @@ warning: printing item
span: None,
sem: Visibility {
_lifetime: PhantomData<&()>,
kind: Default(
kind: DefaultCrate(
ItemId(..),
),
},
Expand Down
Loading