Rewrite std_instead_of_core - #17364
Conversation
|
Lintcheck changes for 9342210
This comment will be updated if you push new changes |
eca13eb to
226f1c7
Compare
std_instead_of_core
std_instead_of_corestd_instead_of_core
|
r? @llogiq rustbot has assigned @llogiq. Use Why was this reviewer chosen?The reviewer was selected based on:
|
|
r? Jarcho Please feel free to reverse this! I'm assigning it to you since this is an alternative to #17252 and based on your feedback to me, so I feel you'd be most appropriate to review. Again, thanks for all your help so far! |
This comment has been minimized.
This comment has been minimized.
226f1c7 to
a834ba8
Compare
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
a834ba8 to
9ee6d98
Compare
This comment has been minimized.
This comment has been minimized.
9ee6d98 to
bad1275
Compare
bad1275 to
de99588
Compare
This comment has been minimized.
This comment has been minimized.
de99588 to
b37f793
Compare
|
You beat me to finishing my first review by a few hours (I had it mostly typed up yesterday). You ended up doing one of the major things I was pointing out so that's cool. |
There was a problem hiding this comment.
You might find it nicer to handle list stems specially and turn lint points into a flattened tree. This will get you something like:
newtype_index! {
pub struct NodeId {}
}
enum RootCrate {
Std,
Alloc,
}
enum NodeKind {
Stem {
did: DefId,
/// Span of the final segment
sp: Span,
/// Index of the final path segment
idx: u32,
},
Item(PerNs<Option<DefId>>),
Glob,
}
struct Node {
parent: NodeId,
sp: Span,
kind: NodeKind
}
struct Pass {
root: Option<(RootCrate, Span)>,
nodes: IndexVec<NodeId, Node>,
skip_paths: u32,
}
fn get_parent_for(nodes: &IndexSlice<NodeId, Node>, p: &[PathSegment<'_>]) -> NodeId {
let mut i = NodeId::from_usize(nodes.len() - 1);
while i != NodeId::ZERO {
let node = &nodes[i];
if let Node::Stem { sp, idx } = node.kind
&& let Some(seg) = p.get(idx as usize)
&& seg.ident.span == s.span
{
return i;
}
i = node.parent;
}
i
}
fn check_item(...) {
if let ItemKind::Use(path, kind) = item.kind
&& let (krate_seg, rest) = match path.segments {
[root, krate, rest @ ..] if root.ident.name == kw::PathRoot => (krate, rest),
[krate, rest @ ..] => (krate, rest),
_ => return,
}
&& let Some(krate) = RootCrate::from_name(krate_seg.ident.name)
{
// skip each path with a res only if there's a second path segment.
self.skip_paths = path.res.map(|x| u32::from(x.is_some())).iter().sum::<u32>()
& u32::from(rest.is_empty()).wrapping_sub(1);
if self.root == Some((krate, krate_seg.ident.span)) {
let last = path.segments.last().unwrap();
self.nodes.push(Node {
parent = get_parent_for(self.nodes, path.segments),
sp: path.span,
kind: match kind {
UseKind::ListStem => NodeKind::Seg {
did: last.res.def_id(),
sp: last.ident.span,
idx: path.segments.len() as u32 - 1
},
UseKind::Item(_) => NodeKind::Item(path.res.map(|x| x.and_then(|r| r.opt_def_id()))),
UseKind::Glob => NodeKind::Glob,
},
});
return;
}
self.try_emit_use_tree();
if let [.., last] = rest {
match kind {
UseKind::ListStem {
self.root = Some((krate, krate_seg.ident.span));
self.nodes.push(Node {
parent: NodeId::ZERO,
sp: path.span,
kind: NodeKind::ListStem {
did: last.res.def_id(),
sp: last.ident.span,
idx: path.segments.len() as u32 - 1,
},
});
},
UseKind::Item(_) | UseKind::Glob => {
// Lint like a single path, but with three potential namespaces
},
}
}
} else {
self.try_emit_use_tree();
}
}
fn check_path(...) {
let (krate, last) = match path.segments {
[root, krate, _, last] if root.ident.name == kw::PathRoot => (krate, last),
[krate, _, last] => (krate, last),
_ => return,
};
if let Some(krate) = RootCrate::from_name(krate_seg.ident.name) {
if let Some(n) = self.skip_paths.checked_sub(1) {
self.skip_paths = n;
return;
}
// Lint a path
}
}
I'm so sorry for that! I figured this was a better version of my PR anyway, but I won't do q big change like this again without a comment and a ping first to check in. |
This comment has been minimized.
This comment has been minimized.
b37f793 to
5726ad0
Compare
This comment has been minimized.
This comment has been minimized.
f670b91 to
f26808e
Compare
|
@rustbot ready |
|
The reason for keeping the tree structure was to catch all imports in a use statement with a single multi-span lint. If it's going to be notably more work to implement it's fine to work on it separately (or just not do it). I'll try to get to a review some time this week. |
Oh that makes sense! If you'd approve moving to a multi-span in this PR I'm happy to spend a little longer switching to the tree structure as you suggested. Sorry, I falsely assumed you weren't aware of that issue I opened (or were interested in acting on it!). |
|
I don't mind either way. If you're adding it to this PR I'll wait a bit before reviewing. |
|
Yeah I'll mark as waiting on author and switch back when I'm done. |
197e873 to
74025ff
Compare
|
@rustbot ready Ok I'm pretty happy with how I've adapted my rewrite to work with The way I emit the lint now is to emit on the root of a path (e.g., the |
This comment has been minimized.
This comment has been minimized.
74025ff to
9342210
Compare
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
View all comments
Objective
std_instead_of_coredoes not detect uses of non-fully qualified items #11159std_instead_of_corecreates an invalid fix on stacked imports #12468std_instead_of_core/etc. miss macros #17260std_instead_of_core/etc. should emitMultiLinehelp messages #17261Solution
This PR is a major rewrite of
std_instead_of_corewhich reduces allocations and hardens against possible changes to path segment resolution information, such as what is proposed in rust-lang/rust#159760. Instead of relying exclusively oncheck_pathto collect possible lint points, we primarily rely oncheck_itemforusestatements.check_pathis still used to catch instances such asstd::boxed::Box::new(123), but such cases will contain resolution information for the full path, as the path is not shared across multiple items.Lintcheck Interpretation
alloc_instead_of_corefmt::Resultwhen a crate doesuse std::fmt;. This is intended.std_instead_of_allocsync::{Arc, Mutex}and catching crate renames (extern crate std as alloc, etc.)std_instead_of_corehash::Hash.extern crate core as std, etc.)std::fmt::{self, Debug}(whileDebugis defined incore,std::fmt::selfisalloc::fmt, so this cannot be a merged suggestion.Please write a short comment explaining your change (or "none" for internal only changes)
changelog: [
std_instead_of_core], [std_instead_of_alloc], [alloc_instead_of_core]: fix false positive suggestions for multi-importschangelog: [
std_instead_of_core], [std_instead_of_alloc], [alloc_instead_of_core]: consider crate renameschangelog: [
std_instead_of_core], [std_instead_of_alloc], [alloc_instead_of_core]: include relative pathschangelog: [
std_instead_of_core], [std_instead_of_alloc], [alloc_instead_of_core]: include macros