diff --git a/CHANGELOG.md b/CHANGELOG.md index d1475e98..3f020a1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,10 +28,12 @@ The [v0.4.0 milestone] contains a list of planned changes. [v0.4.0 milestone]: https://github.com/rust-marker/marker/milestone/4 [#278]: https://github.com/rust-marker/marker/pull/278 [#288]: https://github.com/rust-marker/marker/pull/288 +[#294]: https://github.com/rust-marker/marker/pull/294 [#306]: https://github.com/rust-marker/marker/pull/306 ### Added - [#306]: The `LintPass` trait now as a new `check_crate` method. +- [#294]: Items and fields now provide information about their visibility ### Fixed - [#306]: Rustc's driver no longer ICEs on spans from compiler generated code. diff --git a/README.md b/README.md index cd087797..7940f0a1 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ Marker is still growing up, and that's a good thing. We can still shape the API * Higher order types * Attributes [#51](https://github.com/rust-marker/marker/issues/51) * Macros [rust-marker/design#47](https://github.com/rust-marker/design/issues/47) - * Item visibility [#26](https://github.com/rust-marker/marker/issues/26) + * Item visibility [#26](https://github.com/rust-marker/marker/issues/26) (Will be added in v0.4) * **Utility**: The API is currently lacking a lot of utility functions, to handle edge cases and make linting more pleasant. * **Documentation**: Marker still requires a lot of documentation, in the form of doc comments and a book, which explains the basic concept and works as a guide for end-users, lint- and marker-devs alike. diff --git a/marker_api/src/ast/expr.rs b/marker_api/src/ast/expr.rs index 825e12d8..c8de9cbf 100644 --- a/marker_api/src/ast/expr.rs +++ b/marker_api/src/ast/expr.rs @@ -372,14 +372,10 @@ impl<'ast> ConstExpr<'ast> { #[cfg(all(test, target_arch = "x86_64", target_pointer_width = "64"))] mod test { - use super::*; - use expect_test::{expect, Expect}; + use crate::test::assert_size_of; - #[track_caller] - fn assert_size_of(expected: &Expect) { - let actual = std::mem::size_of::(); - expected.assert_eq(&actual.to_string()); - } + use super::*; + use expect_test::expect; #[test] fn expr_struct_size() { diff --git a/marker_api/src/ast/item.rs b/marker_api/src/ast/item.rs index ec343daf..263ba798 100644 --- a/marker_api/src/ast/item.rs +++ b/marker_api/src/ast/item.rs @@ -1,8 +1,10 @@ -use std::{fmt::Debug, marker::PhantomData}; +use std::fmt::Debug; use crate::{ common::{HasNodeId, ItemId, SpanId}, + context::with_cx, diagnostic::EmissionNode, + ffi::FfiOption, private::Sealed, span::{HasSpan, Ident, Span}, CtorBlocker, @@ -188,6 +190,7 @@ use impl_item_type_fn; #[repr(C)] #[derive(Debug)] #[cfg_attr(feature = "driver-api", visibility::make(pub))] +#[cfg_attr(feature = "driver-api", derive(typed_builder::TypedBuilder))] struct CommonItemData<'ast> { id: ItemId, span: SpanId, @@ -236,43 +239,47 @@ macro_rules! impl_item_data { use impl_item_data; -#[cfg(feature = "driver-api")] -impl<'ast> CommonItemData<'ast> { - pub fn new(id: ItemId, span: SpanId, ident: Ident<'ast>) -> Self { - Self { - id, - span, - vis: Visibility::new(id), - ident, - } - } -} - -/// This struct represents the visibility of an item. +/// The declared visibility of an item or field. +/// +/// Note that this is only the syntactic visibility. The item or field might be +/// reexported with a higher visibility, or have a high default visibility. +/// +/// ``` +/// // An item without a visibility +/// fn moon() {} +/// +/// // A public item +/// pub fn sun() {} /// -/// Currently, it's only a placeholder until a proper representation is implemented. -/// rust-marker/marker#26 tracks the task of implementing this. You're welcome to -/// leave any comments in that issue. +/// // An item with a restricted scope +/// pub(crate) fn star() {} +/// +/// pub trait Planet { +/// // An item without a visibility. But it still has the semantic visibility +/// // of `pub` as this is inside a trait declaration. +/// fn mass(); +/// } +/// ``` #[repr(C)] +#[derive(Debug)] +#[cfg_attr(feature = "driver-api", derive(typed_builder::TypedBuilder))] pub struct Visibility<'ast> { - _lifetime: PhantomData<&'ast ()>, - _item_id: ItemId, + #[cfg_attr(feature = "driver-api", builder(setter(into), default))] + span: FfiOption, + sem: crate::sem::Visibility<'ast>, } -impl<'ast> Debug for Visibility<'ast> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Visibility {{ /* WIP: See rust-marker/marker#26 */}}") - .finish() +impl<'ast> Visibility<'ast> { + /// The [`Span`] of the visibility, if it has been declared. + pub fn span(&self) -> Option<&Span<'ast>> { + self.span.copy().map(|span| with_cx(self, |cx| cx.span(span))) } -} -#[cfg(feature = "driver-api")] -impl<'ast> Visibility<'ast> { - pub fn new(item_id: ItemId) -> Self { - Self { - _lifetime: PhantomData, - _item_id: item_id, - } + /// This function returns the semantic representation for the [`Visibility`] + /// of this item. That visibility can be used to check if the item is public + /// or restricted to specific modules. + pub fn semantics(&self) -> &crate::sem::Visibility<'ast> { + &self.sem } } @@ -311,26 +318,28 @@ impl<'ast> Body<'ast> { #[cfg(all(test, target_arch = "x86_64", target_pointer_width = "64"))] mod test { + use crate::test::assert_size_of; + use super::*; - use std::mem::size_of; + use expect_test::expect; #[test] fn test_item_struct_size() { // These sizes are allowed to change, this is just a check to have a // general overview and to prevent accidental changes - assert_eq!(56, size_of::>(), "ModItem"); - assert_eq!(48, size_of::>(), "ExternCrateItem"); - assert_eq!(64, size_of::>(), "UseItem"); - assert_eq!(80, size_of::>(), "StaticItem"); - assert_eq!(72, size_of::>(), "ConstItem"); - assert_eq!(144, size_of::>(), "FnItem"); - assert_eq!(112, size_of::>(), "TyAliasItem"); - assert_eq!(96, size_of::>(), "StructItem"); - assert_eq!(88, size_of::>(), "EnumItem"); - assert_eq!(88, size_of::>(), "UnionItem"); - assert_eq!(112, size_of::>(), "TraitItem"); - assert_eq!(144, size_of::>(), "ImplItem"); - assert_eq!(64, size_of::>(), "ExternBlockItem"); - assert_eq!(48, size_of::>(), "UnstableItem"); + assert_size_of::>(&expect!["80"]); + assert_size_of::>(&expect!["72"]); + assert_size_of::>(&expect!["88"]); + assert_size_of::>(&expect!["104"]); + assert_size_of::>(&expect!["96"]); + assert_size_of::>(&expect!["168"]); + assert_size_of::>(&expect!["136"]); + assert_size_of::>(&expect!["120"]); + assert_size_of::>(&expect!["112"]); + assert_size_of::>(&expect!["112"]); + assert_size_of::>(&expect!["136"]); + assert_size_of::>(&expect!["168"]); + assert_size_of::>(&expect!["88"]); + assert_size_of::>(&expect!["72"]); } } diff --git a/marker_api/src/ffi.rs b/marker_api/src/ffi.rs index 0cada531..2ff18c1d 100644 --- a/marker_api/src/ffi.rs +++ b/marker_api/src/ffi.rs @@ -84,9 +84,10 @@ impl std::hash::Hash for FfiStr<'_> { /// This is an FFI safe option. In most cases it's better to pass a pointer and /// then use `as_ref()` but this doesn't work for owned return values. #[repr(C)] -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Default)] pub enum FfiOption { Some(T), + #[default] None, } diff --git a/marker_api/src/lib.rs b/marker_api/src/lib.rs index c2af0973..2d59ebde 100644 --- a/marker_api/src/lib.rs +++ b/marker_api/src/lib.rs @@ -7,12 +7,7 @@ #![allow(clippy::missing_panics_doc)] // Temporary allow for `todo!`s #![allow(clippy::new_without_default)] // Not very helpful as `new` is almost always cfged #![cfg_attr(not(feature = "driver-api"), allow(dead_code))] -// FIXME(#26): this is commented out because it is the lint that we want to enable -// here makes sense only on a public items, but it triggers of private/pub(crate) -// methods today. There isn't a way to inspect the item visibility in this lint's -// impl yet. Once #26 is done and lint impl ignores private functions we may enable -// this lint. -// #![cfg_attr(marker, warn(marker::not_using_has_span_trait))] +#![cfg_attr(marker, warn(marker::marker_lints::not_using_has_span_trait))] pub static MARKER_API_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -22,6 +17,9 @@ pub use interface::*; mod lint; pub use lint::*; +#[cfg(test)] +pub(crate) mod test; + pub mod ast; pub mod common; pub mod context; diff --git a/marker_api/src/sem.rs b/marker_api/src/sem.rs index 4ad0920d..2c0f6a52 100644 --- a/marker_api/src/sem.rs +++ b/marker_api/src/sem.rs @@ -3,8 +3,10 @@ mod common; mod generic; +mod item; mod ty; pub use common::*; pub use generic::*; +pub use item::*; pub use ty::*; diff --git a/marker_api/src/sem/item.rs b/marker_api/src/sem/item.rs new file mode 100644 index 00000000..27ad016a --- /dev/null +++ b/marker_api/src/sem/item.rs @@ -0,0 +1,153 @@ +use std::marker::PhantomData; + +use crate::common::ItemId; + +/// The declared visibility of an item or field. +/// +/// ``` +/// // An item without a visibility +/// fn moon() {} +/// +/// // A public item +/// pub fn sun() {} +/// +/// // An item with a restricted scope +/// pub(crate) fn star() {} +/// +/// pub trait Planet { +/// // An item without a visibility. But it still has the semantic visibility +/// // of `pub` as this is inside a trait declaration. +/// fn mass(); +/// } +/// ``` +#[repr(C)] +#[derive(Debug)] +#[cfg_attr(feature = "driver-api", derive(typed_builder::TypedBuilder))] +pub struct Visibility<'ast> { + #[cfg_attr(feature = "driver-api", builder(setter(skip), default))] + _lifetime: PhantomData<&'ast ()>, + kind: VisibilityKind, +} + +impl<'ast> Visibility<'ast> { + /// Returns `true` if the item is public, meaning that it can be visible outside + /// the crate. + /// + /// ``` + /// // This returns `true` + /// pub fn unicorn() {} + /// + /// // This returns `false`, since the visibility is restricted to a specified path. + /// pub(crate) fn giraffe() {} + /// + /// // This returns `false`, since the visibility is not defined + /// fn dragon() {} + /// + /// 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 { + self.scope().is_none() + } + + /// 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. + /// + /// ``` + /// // lib.rs + /// + /// mod example_1 { + /// // Returns `false` since it's only visible in `crate::example_1` + /// fn foo() {} + /// + /// // 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. + /// pub(crate) fn baz() {} + /// + /// // Returns `true` as the visibility is restricted to `super` which is the + /// // root of the crate. + /// pub(super) fn boo() {} + /// } + /// + /// // 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_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 { + match self.kind { + VisibilityKind::DefaultPub | VisibilityKind::DefaultCrate(_) | VisibilityKind::Default(_) => true, + VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Path(_) => false, + } + } + + /// 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 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, in this case `crate::scope` + /// fn dolphin() {} + /// } + /// ``` + pub fn scope(&self) -> Option { + match self.kind { + VisibilityKind::Path(id) + | VisibilityKind::Crate(id) + | VisibilityKind::DefaultCrate(id) + | VisibilityKind::Default(id) => Some(id), + VisibilityKind::Public | VisibilityKind::DefaultPub => None, + } + } + + // FIXME(xFrednet): Implement functions to check if an item is visible from a + // given `ItemId`. This can be done once rust-marker/marker#242 is implemented. +} + +#[derive(Debug)] +#[allow(clippy::exhaustive_enums)] +#[cfg_attr(feature = "driver-api", visibility::make(pub))] +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()`. + /// 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 items doesn't have a declared visibility. The default is restricted to + /// a module, identified by the stored [`ItemId`] + Default(ItemId), +} diff --git a/marker_api/src/test.rs b/marker_api/src/test.rs new file mode 100644 index 00000000..09452231 --- /dev/null +++ b/marker_api/src/test.rs @@ -0,0 +1,7 @@ +use expect_test::Expect; + +#[track_caller] +pub(crate) fn assert_size_of(expected: &Expect) { + let actual = std::mem::size_of::(); + expected.assert_eq(&actual.to_string()); +} diff --git a/marker_lints/src/not_using_has_span_trait.rs b/marker_lints/src/not_using_has_span_trait.rs index 76760410..38a3ae32 100644 --- a/marker_lints/src/not_using_has_span_trait.rs +++ b/marker_lints/src/not_using_has_span_trait.rs @@ -4,15 +4,18 @@ marker_api::declare_lint! { /// ### What it does /// Suggests using `impl HasSpan` for functions that need to take `Span` as /// a parameter to make them more ergonomic. + /// + /// This function only triggers on public items, as it's targeted to towards + /// the public interface of crates. NOT_USING_HAS_SPAN_TRAIT, - // FIXME(#26): This allow by default until we have the item visibility info - // in the AST. Once that is in place the lint should be made `warn` by default, - // and it should ignore non `pub` functions. Allow, } pub(crate) fn check_item<'ast>(cx: &'ast MarkerContext<'ast>, item: ItemKind<'ast>) { let ItemKind::Fn(func) = item else { return }; + if !func.visibility().semantics().is_pub() { + return; + } for param in func.params() { let ast::TyKind::Path(path) = param.ty().peel_refs() else { diff --git a/marker_rustc_driver/src/conversion/marker.rs b/marker_rustc_driver/src/conversion/marker.rs index e2b10318..7ce49a62 100644 --- a/marker_rustc_driver/src/conversion/marker.rs +++ b/marker_rustc_driver/src/conversion/marker.rs @@ -12,9 +12,10 @@ use std::cell::{OnceCell, RefCell}; use crate::context::storage::Storage; use marker_api::{ - ast::{Body, CommonItemData, Crate, EnumVariant, ItemField, ModItem}, + ast::{Body, CommonItemData, Crate, EnumVariant, ItemField, ModItem, Visibility as AstVisibility}, common::{Level, SymbolId}, prelude::*, + sem::{Visibility as SemVisibility, VisibilityKind}, span::{ExpnInfo, FilePos, Span, SpanSource}, }; use rustc_hash::FxHashMap; @@ -329,7 +330,17 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { self.to_symbol_id(self.rustc_cx.crate_name(hir::def_id::LOCAL_CRATE)), self.to_span_id(rustc_span::DUMMY_SP), ); - let data = CommonItemData::new(id, self.to_span_id(krate_mod.spans.inner_span), ident); + let data = CommonItemData::builder() + .id(id) + .span(self.to_span_id(krate_mod.spans.inner_span)) + .vis( + AstVisibility::builder() + .span(None) + .sem(SemVisibility::builder().kind(VisibilityKind::DefaultPub).build()) + .build(), + ) + .ident(ident) + .build(); ModItem::builder() .data(data) .items(self.to_items(krate_mod.item_ids)) diff --git a/marker_rustc_driver/src/conversion/marker/ast/item.rs b/marker_rustc_driver/src/conversion/marker/ast/item.rs index 7b9160b8..b18d27f1 100644 --- a/marker_rustc_driver/src/conversion/marker/ast/item.rs +++ b/marker_rustc_driver/src/conversion/marker/ast/item.rs @@ -6,6 +6,7 @@ use marker_api::{ Visibility, }, common::{Abi, Constness, Mutability, Safety, Syncness}, + prelude::*, CtorBlocker, }; use rustc_hir as hir; @@ -46,7 +47,12 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { } let ident = self.to_ident(rustc_item.ident); - let data = CommonItemData::new(id, self.to_span_id(rustc_item.span), ident); + let data = CommonItemData::builder() + .id(id) + .span(self.to_span_id(rustc_item.span)) + .vis(self.to_visibility(rustc_item.owner_id.def_id, rustc_item.vis_span)) + .ident(ident) + .build(); let item = match &rustc_item.kind { hir::ItemKind::ExternCrate(original_name) => ItemKind::ExternCrate(self.alloc({ @@ -174,6 +180,15 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { !ctxt.is_root() && matches!(ctxt.outer_expn_data().kind, rustc_span::ExpnKind::AstPass(_)) } + fn to_visibility(&self, owner_id: hir::def_id::LocalDefId, vis_span: rustc_span::Span) -> Visibility<'ast> { + let span = (!vis_span.is_empty()).then(|| self.to_span_id(vis_span)); + + Visibility::builder() + .span(span) + .sem(self.to_sem_visibility(owner_id, span.is_some())) + .build() + } + fn to_fn_item( &self, data: CommonItemData<'ast>, @@ -260,11 +275,9 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { fn to_fields(&self, fields: &'tcx [hir::FieldDef]) -> &'ast [ItemField<'ast>] { let fields = self.alloc_slice(fields.iter().map(|field| { - // FIXME update Visibility creation to use the stored local def id inside the - // field after the next sync. See #55 ItemField::new( self.to_field_id(field.hir_id), - Visibility::new(self.to_item_id(field.def_id)), + self.to_visibility(field.def_id, field.vis_span), self.to_symbol_id(field.ident.name), self.to_syn_ty(field.ty), self.to_span_id(field.span), @@ -294,7 +307,12 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { } let foreign_item = self.rustc_cx.hir().foreign_item(rustc_item.id); - let data = CommonItemData::new(id, self.to_span_id(rustc_item.span), self.to_ident(rustc_item.ident)); + let data = CommonItemData::builder() + .id(id) + .span(self.to_span_id(rustc_item.span)) + .vis(self.to_visibility(foreign_item.owner_id.def_id, foreign_item.vis_span)) + .ident(self.to_ident(rustc_item.ident)) + .build(); let item = match &foreign_item.kind { hir::ForeignItemKind::Fn(decl, idents, generics) => { let return_ty = if let hir::FnRetTy::Return(rust_ty) = decl.output { @@ -354,7 +372,16 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { } let trait_item = self.rustc_cx.hir().trait_item(rustc_item.id); - let data = CommonItemData::new(id, self.to_span_id(rustc_item.span), self.to_ident(rustc_item.ident)); + let data = CommonItemData::builder() + .id(id) + .span(self.to_span_id(rustc_item.span)) + .vis( + Visibility::builder() + .sem(sem::Visibility::builder().kind(sem::VisibilityKind::DefaultPub).build()) + .build(), + ) + .ident(self.to_ident(rustc_item.ident)) + .build(); let item = match &trait_item.kind { hir::TraitItemKind::Const(ty, body_id) => AssocItemKind::Const( @@ -403,7 +430,12 @@ impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { } let impl_item = self.rustc_cx.hir().impl_item(rustc_item.id); - let data = CommonItemData::new(id, self.to_span_id(rustc_item.span), self.to_ident(rustc_item.ident)); + let data = CommonItemData::builder() + .id(id) + .span(self.to_span_id(rustc_item.span)) + .vis(self.to_visibility(rustc_item.id.owner_id.def_id, impl_item.vis_span)) + .ident(self.to_ident(rustc_item.ident)) + .build(); let item = match &impl_item.kind { hir::ImplItemKind::Const(ty, body_id) => AssocItemKind::Const( diff --git a/marker_rustc_driver/src/conversion/marker/sem.rs b/marker_rustc_driver/src/conversion/marker/sem.rs index 1584887a..5ed9a356 100644 --- a/marker_rustc_driver/src/conversion/marker/sem.rs +++ b/marker_rustc_driver/src/conversion/marker/sem.rs @@ -1,2 +1,3 @@ mod generic; +mod item; mod ty; diff --git a/marker_rustc_driver/src/conversion/marker/sem/item.rs b/marker_rustc_driver/src/conversion/marker/sem/item.rs new file mode 100644 index 00000000..409901d2 --- /dev/null +++ b/marker_rustc_driver/src/conversion/marker/sem/item.rs @@ -0,0 +1,30 @@ +use marker_api::prelude::*; +use rustc_hir as hir; +use rustc_middle as mid; + +use crate::conversion::marker::MarkerConverterInner; + +impl<'ast, 'tcx> MarkerConverterInner<'ast, 'tcx> { + pub fn to_sem_visibility(&self, owner_id: hir::def_id::LocalDefId, has_span: bool) -> sem::Visibility<'ast> { + 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 id == hir::def_id::CRATE_DEF_ID.to_def_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)) + } + }, + }; + + sem::Visibility::builder().kind(kind).build() + } +} diff --git a/marker_uilints/src/lib.rs b/marker_uilints/src/lib.rs index 62d75e0d..eafbddf7 100644 --- a/marker_uilints/src/lib.rs +++ b/marker_uilints/src/lib.rs @@ -56,13 +56,18 @@ marker_api::declare_lint! { marker_api::declare_lint! { /// # What it does - /// A lint used for marker's uitests. - /// /// A lint to test [`marker_api::AstMap`]. TEST_AST_MAP, Warn, } +marker_api::declare_lint! { + /// # What it does + /// A lint to test Marker's `Visibility` representation. + TEST_ITEM_VISIBILITY, + Warn, +} + fn emit_item_with_test_name_lint<'ast>( cx: &'ast MarkerContext<'ast>, node: impl EmissionNode<'ast>, @@ -133,10 +138,11 @@ impl LintPass for TestLintPass { } if let ItemKind::Fn(func) = item { - if matches!( - item.ident().map(marker_api::span::Ident::name), - Some(name) if name.starts_with("print_with_body") - ) { + if item + .ident() + .map(|ident| ident.name().starts_with("print_with_body")) + .unwrap_or_default() + { cx.emit_lint(TEST_LINT, item, "printing item with body") .decorate(|diag| { diag.span(item.ident().unwrap().span()); @@ -144,6 +150,23 @@ impl LintPass for TestLintPass { diag.note(format!("Body: {:#?}", cx.ast().body(func.body_id().unwrap()))); }); } + if item + .ident() + .map(|name| name.name().starts_with("test_vis")) + .unwrap_or_default() + { + cx.emit_lint(TEST_ITEM_VISIBILITY, item, "can you see this item?") + .decorate(|diag| { + let ast_vis = item.visibility(); + let vis = ast_vis.semantics(); + diag.span(item.ident().unwrap().span()); + 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("")))); + }); + } } } diff --git a/marker_uilints/tests/ui/context/test_ast_map.stderr b/marker_uilints/tests/ui/context/test_ast_map.stderr index 3b5bfa64..f6405914 100644 --- a/marker_uilints/tests/ui/context/test_ast_map.stderr +++ b/marker_uilints/tests/ui/context/test_ast_map.stderr @@ -22,7 +22,15 @@ warning: testing `AstMap::variant` [ ItemField { id: FieldId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: SymbolId(..), ty: Path( PathTy { @@ -72,7 +80,15 @@ warning: testing `AstMap::item` data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "LocalStruct", span: $DIR/test_ast_map.rs:6:8 - 6:19, @@ -86,7 +102,15 @@ warning: testing `AstMap::item` [ ItemField { id: FieldId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: SymbolId(..), ty: Num( NumTy { diff --git a/marker_uilints/tests/ui/item/print_async_fn.stderr b/marker_uilints/tests/ui/item/print_async_fn.stderr index 2c2ee660..07704c64 100644 --- a/marker_uilints/tests/ui/item/print_async_fn.stderr +++ b/marker_uilints/tests/ui/item/print_async_fn.stderr @@ -9,7 +9,15 @@ warning: printing item with body data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "print_with_body_foo", span: $DIR/print_async_fn.rs:1:10 - 1:29, @@ -85,7 +93,15 @@ warning: printing item with body data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "print_with_body_bar", span: $DIR/print_async_fn.rs:9:10 - 9:29, @@ -509,7 +525,15 @@ warning: printing item with body data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "print_with_body_with_lifetime", span: $DIR/print_async_fn.rs:16:10 - 16:39, diff --git a/marker_uilints/tests/ui/item/print_fn_item.stderr b/marker_uilints/tests/ui/item/print_fn_item.stderr index b4c2dd39..67eee0a5 100644 --- a/marker_uilints/tests/ui/item/print_fn_item.stderr +++ b/marker_uilints/tests/ui/item/print_fn_item.stderr @@ -9,7 +9,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: Some( + SpanId(..), + ), + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: Public, + }, + }, ident: Ident { name: "print_me_simple", span: $DIR/print_fn_item.rs:1:8 - 1:23, @@ -45,7 +53,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: Some( + SpanId(..), + ), + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: Public, + }, + }, ident: Ident { name: "print_me_special", span: $DIR/print_fn_item.rs:3:21 - 3:37, @@ -80,7 +96,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: Some( + SpanId(..), + ), + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: Public, + }, + }, ident: Ident { name: "print_me_params", span: $DIR/print_fn_item.rs:5:8 - 5:23, @@ -237,7 +261,13 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultPub, + }, + }, ident: Ident { name: "print_me_trait_with_body", span: $DIR/print_fn_item.rs:10:8 - 10:32, @@ -394,7 +424,13 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultPub, + }, + }, ident: Ident { name: "print_me_trait_no_body", span: $DIR/print_fn_item.rs:14:8 - 14:30, diff --git a/marker_uilints/tests/ui/item/print_me_root_module.stderr b/marker_uilints/tests/ui/item/print_me_root_module.stderr index 4cae9a39..71b0e532 100644 --- a/marker_uilints/tests/ui/item/print_me_root_module.stderr +++ b/marker_uilints/tests/ui/item/print_me_root_module.stderr @@ -5,7 +5,13 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultPub, + }, + }, ident: Ident { name: "print_me_root_module", span: $DIR/print_me_root_module.rs:1:1 - 1:1, @@ -17,7 +23,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "main", span: $DIR/print_me_root_module.rs:7:4 - 7:8, diff --git a/marker_uilints/tests/ui/item/test_visibility.rs b/marker_uilints/tests/ui/item/test_visibility.rs new file mode 100644 index 00000000..03d8754c --- /dev/null +++ b/marker_uilints/tests/ui/item/test_visibility.rs @@ -0,0 +1,17 @@ +fn test_vis_private() {} + +pub fn test_vis_public() {} + +mod module { + pub(crate) fn test_vis_pub_crate() {} + + pub(super) fn test_vis_pub_super_crate_root() {} + + mod nested { + pub(super) fn test_vis_pub_super() {} + + pub(in crate::module) fn test_vis_pub_in_path() {} + } +} + +fn main() {} diff --git a/marker_uilints/tests/ui/item/test_visibility.stderr b/marker_uilints/tests/ui/item/test_visibility.stderr new file mode 100644 index 00000000..be0db849 --- /dev/null +++ b/marker_uilints/tests/ui/item/test_visibility.stderr @@ -0,0 +1,75 @@ +warning: can you see this item? + --> $DIR/test_visibility.rs:1:4 + | +1 | fn test_vis_private() {} + | ^^^^^^^^^^^^^^^^ + | + = 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 + +warning: can you see this item? + --> $DIR/test_visibility.rs:3:8 + | +3 | pub fn test_vis_public() {} + | ^^^^^^^^^^^^^^^ + | + = 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? + --> $DIR/test_visibility.rs:6:19 + | +6 | pub(crate) fn test_vis_pub_crate() {} + | ^^^^^^^^^^^^^^^^^^ + | + = 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? + --> $DIR/test_visibility.rs:8:19 + | +8 | pub(super) fn test_vis_pub_super_crate_root() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = 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? + --> $DIR/test_visibility.rs:11:23 + | +11 | pub(super) fn test_vis_pub_super() {} + | ^^^^^^^^^^^^^^^^^^ + | + = 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? + --> $DIR/test_visibility.rs:13:34 + | +13 | pub(in crate::module) fn test_vis_pub_in_path() {} + | ^^^^^^^^^^^^^^^^^^^^ + | + = 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 + diff --git a/marker_uilints/tests/ui/print_adt_item.stderr b/marker_uilints/tests/ui/print_adt_item.stderr index cbcbac40..d213e841 100644 --- a/marker_uilints/tests/ui/print_adt_item.stderr +++ b/marker_uilints/tests/ui/print_adt_item.stderr @@ -9,7 +9,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: Some( + SpanId(..), + ), + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: Public, + }, + }, ident: Ident { name: "PrintMeEnum", span: $DIR/print_adt_item.rs:1:10 - 1:21, diff --git a/marker_uilints/tests/ui/print_const_generics.stderr b/marker_uilints/tests/ui/print_const_generics.stderr index 77f3bce9..5044e375 100644 --- a/marker_uilints/tests/ui/print_const_generics.stderr +++ b/marker_uilints/tests/ui/print_const_generics.stderr @@ -9,7 +9,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "PrintMeConstGenerics", span: $DIR/print_const_generics.rs:1:8 - 1:28, @@ -41,7 +49,15 @@ warning: printing item [ ItemField { id: FieldId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: SymbolId(..), ty: Array( ArrayTy { @@ -112,7 +128,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "print_me", span: $DIR/print_const_generics.rs:5:4 - 5:12, diff --git a/marker_uilints/tests/ui/print_use.stderr b/marker_uilints/tests/ui/print_use.stderr index e93f6c12..aafc57d9 100644 --- a/marker_uilints/tests/ui/print_use.stderr +++ b/marker_uilints/tests/ui/print_use.stderr @@ -9,7 +9,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "PrintMeBTreeMap", span: $DIR/print_use.rs:1:36 - 1:51, @@ -62,7 +70,15 @@ warning: printing item data: CommonItemData { id: ItemId(..), span: SpanId(..), - vis: Visibility {{ /* WIP: See rust-marker/marker#26 */}}, + vis: Visibility { + span: None, + sem: Visibility { + _lifetime: PhantomData<&()>, + kind: DefaultCrate( + ItemId(..), + ), + }, + }, ident: Ident { name: "PrintMeHashMap", span: $DIR/print_use.rs:1:64 - 1:78,