-
Notifications
You must be signed in to change notification settings - Fork 11
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
API: Make item Visibility
visible in the API
#294
Conversation
9843a2f
to
a52ab04
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll take a closer look on weekends, didn't review everything yet.
marker_api/src/ast/item.rs
Outdated
/// Returns `true` if the item is declared as `pub(...)` with a path in brackets | ||
/// that defines the scope, where the item is visible. | ||
pub fn is_pub_with_path(&self) -> bool { | ||
matches!(self.kind, VisibilityKind::Path(_) | VisibilityKind::Crate(_)) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect that pub_with_path
wouldn't include pub(crate)
and only check for pub(in ...)
, so the name is a bit confusing. I'm not sure wether this method would be useful. Maybe if there was pub_in_path
that checked only for Path
visibility kind, that would be more obvious
marker_api/src/ast/item.rs
Outdated
/// | ||
/// 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> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This again looks weird due to the mixture of syntactic and semantic properties of the visibility.
I think if we split the representation into ast
and sem
there would be only a method on sem
type:
sem::Visibility::visibility_root() -> Option<ItemId>
: returns theItemId
of the root module this visibility level allows access in; returnsNone
if the visibility ispub
And there wouldn't be such a method for ast::Visibility
. I think it would be more straightforward to just expose the syntactic VisibilityKind
to them, and the users would then handle all possible syntactic flavors of the visibility they want to check for.
The VisibilityKind
in the ast
module would look like this:
enum VisibilityKind {
Pub,
PubIn(Path),
PubCrate,
/// There is no distinction between `Default/DefautPub` because in the synatctic representation
/// we only care if the visibility was explicitly specified or not
Default,
}
Btw. syn
uses the term "inherited" visibility instead of "default". In general, it would be cool if marker's AST representation followed syn's naming and concepts if possible, because it would make it easier to adapt to marker
for anyone who already wrote proc macros. But this is not strictly required (subjectively "default" visibility reads more intuitive to me than "inherited").
Maybe I'm dreaming here, and rustc doesn't provide the original syntax form of the visibility, in which case it would be a bummer.
UPD: I noticed the module_id
method only after writing this comment.
This is still WIP, so far I've only applied the easy changes and skimmed over your suggestions. I'll answer properly soon. Thank you for the review! |
536b532
to
7f1f58f
Compare
Okay, I've rebased. Only the last two commits are new. I've now split the visibility into a semantic and ast representation, as discussed previously. Sorry that it took so long, to get this fixed 😅 Hope you have a beautiful weekend :D I say as the CI fails... Oh what fun, let's hope it's fixed now :) |
7f1f58f
to
fd7f151
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider making the design a bit more minimalistic but obvious as suggested in my comment #294 (comment).
marker_api/src/sem/item.rs
Outdated
/// // Returns `false` since the visibility is not restricted | ||
/// fn example_in_root() {} | ||
/// ``` | ||
pub fn is_pub_crate(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think people will be confused if we name this method after the special pub(crate)
syntax when this method covers more than just that.
pub fn is_pub_crate(&self) -> bool { | |
pub fn is_crate_scoped(&self) -> bool { |
Given the amount of info we get from rustc (the lost of AST context) consider this number of methods instead:
is_crate_scoped() -> bool
(true ifscope
resolves to the current crate no matter what syntax was used)is_pub() -> bool { self.scope().is_none() }
is_default() -> bool
scope() -> Option<ItemId>
(renamedmodule_id
)
In other words I'd like to make it explicit that the semantic visibility no longer represents the original syntax that was used.
You can also notice that in the list of methods I suggested there isn't pub_with_path_module_id
. I think it's not needed because it's expressible with just vis.scope().filter(|_| !vis.is_default())
or similar.
If the user wants to check if the visibility was specified (non-default), they would use !is_default()
. If they want to exclude pub
they will use !is_default && !is_pub()
which would leave them with pub(crate),
pub(super)and
pub(in ...)` syntaxes.
Maybe to have checks for AST syntax like is_pub_crate, is_pub_super, is_pub_in_path
etc. (which would be available on ast::Visibility
we could just re-parse the text snippet from the span manually? However, I wouldn't do it in this PR to just land this chunk of work and have something good enough 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like the suggested names. I'll make the changes. We should be able to reparse at least part of the text snippet. I haven't worked with Rustc's lexer and parser yet, which is partially why I'm avoiding such solutions 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-parsing the text snippet is more of a hack. I wonder how clippy fights with this? Maybe other kinds of lint passes such as EarlyLintPass
have the necessary ast info?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clippy usually just checks if things are public, and that's it. In Clippy we don't really have lints that span over more than a module and those that do don't require the visibility AFAIK.
For the EarlyLintPass
, I've just checked and that one still has the path [1]. The big problem with this approach is that nodes from the AST, are hard to connect to the nodes from the HIR, as the AST doesn't have a unique ID for everything. HirId
s are also structured differently.
The current rustc interface has covered all of Clippy's use cases so far. So I hope that this is enough for Marker.
Okay, this should hopefully be it. Thank you for the excellent review. I think this very is way better, than my initial proposal :) |
marker_api/src/sem/item.rs
Outdated
/// // Returns `false` since the visibility is not restricted | ||
/// fn example_in_root() {} | ||
/// ``` | ||
pub fn is_pub_crate(&self) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re-parsing the text snippet is more of a hack. I wonder how clippy fights with this? Maybe other kinds of lint passes such as EarlyLintPass
have the necessary ast info?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Designing is hard, but things eventually settle 👍
Designing a sensible
Visibility
representation is surprisingly hard. I had to update the function names several times, and still feel like they can be improved. There is also still one util function missing, which requires the implementation of #242 first. I've left a corresponding comment in the PR.I think this implementation will cover 99% of the current use cases. This PR also updates the
not_using_has_span_trait
lint and enables it by default inside themarker_api
crateCloses: #26