Skip to content

Commit bdbf099

Browse files
authored
Rollup merge of #95194 - kckeiks:update-algo-in-find-use-placement, r=pnkfelix
remove find_use_placement A more robust solution to finding where to place use suggestions was added in #94584. The algorithm uses the AST to find the span for the suggestion so we pass this span down to the HIR during lowering and use it instead of calling `find_use_placement` Fixes #94941
2 parents a32e0f3 + 8c2353b commit bdbf099

File tree

12 files changed

+57
-148
lines changed

12 files changed

+57
-148
lines changed

Diff for: compiler/rustc_ast_lowering/src/index.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ pub(super) fn index_hir<'hir>(
5252
};
5353

5454
match item {
55-
OwnerNode::Crate(citem) => collector.visit_mod(&citem, citem.inner, hir::CRATE_HIR_ID),
55+
OwnerNode::Crate(citem) => {
56+
collector.visit_mod(&citem, citem.spans.inner_span, hir::CRATE_HIR_ID)
57+
}
5658
OwnerNode::Item(item) => collector.visit_item(item),
5759
OwnerNode::TraitItem(item) => collector.visit_trait_item(item),
5860
OwnerNode::ImplItem(item) => collector.visit_impl_item(item),

Diff for: compiler/rustc_ast_lowering/src/item.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
124124
debug_assert_eq!(self.resolver.local_def_id(CRATE_NODE_ID), CRATE_DEF_ID);
125125

126126
self.with_lctx(CRATE_NODE_ID, |lctx| {
127-
let module = lctx.lower_mod(&c.items, c.spans.inner_span);
127+
let module = lctx.lower_mod(&c.items, &c.spans);
128128
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
129129
hir::OwnerNode::Crate(lctx.arena.alloc(module))
130130
})
@@ -186,9 +186,12 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
186186
}
187187

188188
impl<'hir> LoweringContext<'_, 'hir> {
189-
pub(super) fn lower_mod(&mut self, items: &[P<Item>], inner: Span) -> hir::Mod<'hir> {
189+
pub(super) fn lower_mod(&mut self, items: &[P<Item>], spans: &ModSpans) -> hir::Mod<'hir> {
190190
hir::Mod {
191-
inner: self.lower_span(inner),
191+
spans: hir::ModSpans {
192+
inner_span: self.lower_span(spans.inner_span),
193+
inject_use_span: self.lower_span(spans.inject_use_span),
194+
},
192195
item_ids: self.arena.alloc_from_iter(items.iter().flat_map(|x| self.lower_item_ref(x))),
193196
}
194197
}
@@ -308,8 +311,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
308311
})
309312
}
310313
ItemKind::Mod(_, ref mod_kind) => match mod_kind {
311-
ModKind::Loaded(items, _, ModSpans { inner_span, inject_use_span: _ }) => {
312-
hir::ItemKind::Mod(self.lower_mod(items, *inner_span))
314+
ModKind::Loaded(items, _, spans) => {
315+
hir::ItemKind::Mod(self.lower_mod(items, spans))
313316
}
314317
ModKind::Unloaded => panic!("`mod` items should have been loaded by now"),
315318
},

Diff for: compiler/rustc_hir/src/hir.rs

+10-4
Original file line numberDiff line numberDiff line change
@@ -2557,11 +2557,17 @@ impl FnRetTy<'_> {
25572557

25582558
#[derive(Encodable, Debug, HashStable_Generic)]
25592559
pub struct Mod<'hir> {
2560+
pub spans: ModSpans,
2561+
pub item_ids: &'hir [ItemId],
2562+
}
2563+
2564+
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable)]
2565+
pub struct ModSpans {
25602566
/// A span from the first token past `{` to the last token until `}`.
25612567
/// For `mod foo;`, the inner span ranges from the first token
25622568
/// to the last token in the external file.
2563-
pub inner: Span,
2564-
pub item_ids: &'hir [ItemId],
2569+
pub inner_span: Span,
2570+
pub inject_use_span: Span,
25652571
}
25662572

25672573
#[derive(Debug, HashStable_Generic)]
@@ -3059,8 +3065,8 @@ impl<'hir> OwnerNode<'hir> {
30593065
OwnerNode::Item(Item { span, .. })
30603066
| OwnerNode::ForeignItem(ForeignItem { span, .. })
30613067
| OwnerNode::ImplItem(ImplItem { span, .. })
3062-
| OwnerNode::TraitItem(TraitItem { span, .. })
3063-
| OwnerNode::Crate(Mod { inner: span, .. }) => *span,
3068+
| OwnerNode::TraitItem(TraitItem { span, .. }) => *span,
3069+
OwnerNode::Crate(Mod { spans: ModSpans { inner_span, .. }, .. }) => *inner_span,
30643070
}
30653071
}
30663072

Diff for: compiler/rustc_middle/src/hir/map/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ impl<'hir> Map<'hir> {
584584
Some(OwnerNode::Item(&Item { span, kind: ItemKind::Mod(ref m), .. })) => {
585585
(m, span, hir_id)
586586
}
587-
Some(OwnerNode::Crate(item)) => (item, item.inner, hir_id),
587+
Some(OwnerNode::Crate(item)) => (item, item.spans.inner_span, hir_id),
588588
node => panic!("not a module: {:?}", node),
589589
}
590590
}
@@ -1012,7 +1012,7 @@ impl<'hir> Map<'hir> {
10121012
Node::Infer(i) => i.span,
10131013
Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
10141014
Node::Local(local) => local.span,
1015-
Node::Crate(item) => item.inner,
1015+
Node::Crate(item) => item.spans.inner_span,
10161016
};
10171017
Some(span)
10181018
}

Diff for: compiler/rustc_save_analysis/src/dump_visitor.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1095,11 +1095,11 @@ impl<'tcx> DumpVisitor<'tcx> {
10951095

10961096
let sm = self.tcx.sess.source_map();
10971097
let krate_mod = self.tcx.hir().root_module();
1098-
let filename = sm.span_to_filename(krate_mod.inner);
1098+
let filename = sm.span_to_filename(krate_mod.spans.inner_span);
10991099
let data_id = id_from_hir_id(id, &self.save_ctxt);
11001100
let children =
11011101
krate_mod.item_ids.iter().map(|i| id_from_def_id(i.def_id.to_def_id())).collect();
1102-
let span = self.span_from_span(krate_mod.inner);
1102+
let span = self.span_from_span(krate_mod.spans.inner_span);
11031103
let attrs = self.tcx.hir().attrs(id);
11041104

11051105
self.dumper.dump_def(

Diff for: compiler/rustc_save_analysis/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ impl<'tcx> SaveContext<'tcx> {
282282
let qualname = format!("::{}", self.tcx.def_path_str(def_id));
283283

284284
let sm = self.tcx.sess.source_map();
285-
let filename = sm.span_to_filename(m.inner);
285+
let filename = sm.span_to_filename(m.spans.inner_span);
286286

287287
filter!(self.span_utils, item.ident.span);
288288

Diff for: compiler/rustc_typeck/src/check/method/suggest.rs

+21-125
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_errors::{
88
MultiSpan,
99
};
1010
use rustc_hir as hir;
11-
use rustc_hir::def_id::{DefId, LocalDefId};
11+
use rustc_hir::def_id::DefId;
1212
use rustc_hir::lang_items::LangItem;
1313
use rustc_hir::{ExprKind, Node, QPath};
1414
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -1473,12 +1473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14731473
}
14741474
}
14751475

1476-
fn suggest_use_candidates(
1477-
&self,
1478-
err: &mut Diagnostic,
1479-
mut msg: String,
1480-
candidates: Vec<DefId>,
1481-
) {
1476+
fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) {
14821477
let parent_map = self.tcx.visible_parent_map(());
14831478

14841479
// Separate out candidates that must be imported with a glob, because they are named `_`
@@ -1502,80 +1497,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15021497
});
15031498

15041499
let module_did = self.tcx.parent_module(self.body_id);
1505-
let (span, found_use) = find_use_placement(self.tcx, module_did);
1506-
if let Some(span) = span {
1507-
let path_strings = candidates.iter().map(|trait_did| {
1508-
// Produce an additional newline to separate the new use statement
1509-
// from the directly following item.
1510-
let additional_newline = if found_use { "" } else { "\n" };
1511-
format!(
1512-
"use {};\n{}",
1513-
with_crate_prefix!(self.tcx.def_path_str(*trait_did)),
1514-
additional_newline
1515-
)
1516-
});
1500+
let (module, _, _) = self.tcx.hir().get_module(module_did);
1501+
let span = module.spans.inject_use_span;
15171502

1518-
let glob_path_strings = globs.iter().map(|trait_did| {
1519-
let parent_did = parent_map.get(trait_did).unwrap();
1503+
let path_strings = candidates.iter().map(|trait_did| {
1504+
format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),)
1505+
});
15201506

1521-
// Produce an additional newline to separate the new use statement
1522-
// from the directly following item.
1523-
let additional_newline = if found_use { "" } else { "\n" };
1524-
format!(
1525-
"use {}::*; // trait {}\n{}",
1526-
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
1527-
self.tcx.item_name(*trait_did),
1528-
additional_newline
1529-
)
1530-
});
1507+
let glob_path_strings = globs.iter().map(|trait_did| {
1508+
let parent_did = parent_map.get(trait_did).unwrap();
1509+
format!(
1510+
"use {}::*; // trait {}\n",
1511+
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
1512+
self.tcx.item_name(*trait_did),
1513+
)
1514+
});
15311515

1532-
err.span_suggestions(
1533-
span,
1534-
&msg,
1535-
path_strings.chain(glob_path_strings),
1536-
Applicability::MaybeIncorrect,
1537-
);
1538-
} else {
1539-
let limit = if candidates.len() + globs.len() == 5 { 5 } else { 4 };
1540-
for (i, trait_did) in candidates.iter().take(limit).enumerate() {
1541-
if candidates.len() + globs.len() > 1 {
1542-
msg.push_str(&format!(
1543-
"\ncandidate #{}: `use {};`",
1544-
i + 1,
1545-
with_crate_prefix!(self.tcx.def_path_str(*trait_did))
1546-
));
1547-
} else {
1548-
msg.push_str(&format!(
1549-
"\n`use {};`",
1550-
with_crate_prefix!(self.tcx.def_path_str(*trait_did))
1551-
));
1552-
}
1553-
}
1554-
for (i, trait_did) in
1555-
globs.iter().take(limit.saturating_sub(candidates.len())).enumerate()
1556-
{
1557-
let parent_did = parent_map.get(trait_did).unwrap();
1558-
1559-
if candidates.len() + globs.len() > 1 {
1560-
msg.push_str(&format!(
1561-
"\ncandidate #{}: `use {}::*; // trait {}`",
1562-
candidates.len() + i + 1,
1563-
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
1564-
self.tcx.item_name(*trait_did),
1565-
));
1566-
} else {
1567-
msg.push_str(&format!(
1568-
"\n`use {}::*; // trait {}`",
1569-
with_crate_prefix!(self.tcx.def_path_str(*parent_did)),
1570-
self.tcx.item_name(*trait_did),
1571-
));
1572-
}
1573-
}
1574-
if candidates.len() > limit {
1575-
msg.push_str(&format!("\nand {} others", candidates.len() + globs.len() - limit));
1576-
}
1577-
err.note(&msg);
1578-
}
1516+
err.span_suggestions(
1517+
span,
1518+
&msg,
1519+
path_strings.chain(glob_path_strings),
1520+
Applicability::MaybeIncorrect,
1521+
);
15791522
}
15801523

15811524
fn suggest_valid_traits(
@@ -2106,53 +2049,6 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
21062049
tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect()
21072050
}
21082051

2109-
fn find_use_placement<'tcx>(tcx: TyCtxt<'tcx>, target_module: LocalDefId) -> (Option<Span>, bool) {
2110-
// FIXME(#94854): this code uses an out-of-date method for inferring a span
2111-
// to suggest. It would be better to thread the ModSpans from the AST into
2112-
// the HIR, and then use that to drive the suggestion here.
2113-
2114-
let mut span = None;
2115-
let mut found_use = false;
2116-
let (module, _, _) = tcx.hir().get_module(target_module);
2117-
2118-
// Find a `use` statement.
2119-
for &item_id in module.item_ids {
2120-
let item = tcx.hir().item(item_id);
2121-
match item.kind {
2122-
hir::ItemKind::Use(..) => {
2123-
// Don't suggest placing a `use` before the prelude
2124-
// import or other generated ones.
2125-
if !item.span.from_expansion() {
2126-
span = Some(item.span.shrink_to_lo());
2127-
found_use = true;
2128-
break;
2129-
}
2130-
}
2131-
// Don't place `use` before `extern crate`...
2132-
hir::ItemKind::ExternCrate(_) => {}
2133-
// ...but do place them before the first other item.
2134-
_ => {
2135-
if span.map_or(true, |span| item.span < span) {
2136-
if !item.span.from_expansion() {
2137-
span = Some(item.span.shrink_to_lo());
2138-
// Don't insert between attributes and an item.
2139-
let attrs = tcx.hir().attrs(item.hir_id());
2140-
// Find the first attribute on the item.
2141-
// FIXME: This is broken for active attributes.
2142-
for attr in attrs {
2143-
if !attr.span.is_dummy() && span.map_or(true, |span| attr.span < span) {
2144-
span = Some(attr.span.shrink_to_lo());
2145-
}
2146-
}
2147-
}
2148-
}
2149-
}
2150-
}
2151-
}
2152-
2153-
(span, found_use)
2154-
}
2155-
21562052
fn print_disambiguation_help<'tcx>(
21572053
item_name: Ident,
21582054
args: Option<&'tcx [hir::Expr<'tcx>]>,

Diff for: src/librustdoc/html/render/span_map.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -119,11 +119,14 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
119119
fn visit_mod(&mut self, m: &'tcx Mod<'tcx>, span: Span, id: HirId) {
120120
// To make the difference between "mod foo {}" and "mod foo;". In case we "import" another
121121
// file, we want to link to it. Otherwise no need to create a link.
122-
if !span.overlaps(m.inner) {
122+
if !span.overlaps(m.spans.inner_span) {
123123
// Now that we confirmed it's a file import, we want to get the span for the module
124124
// name only and not all the "mod foo;".
125125
if let Some(Node::Item(item)) = self.tcx.hir().find(id) {
126-
self.matches.insert(item.ident.span, LinkFromSrc::Local(clean::Span::new(m.inner)));
126+
self.matches.insert(
127+
item.ident.span,
128+
LinkFromSrc::Local(clean::Span::new(m.spans.inner_span)),
129+
);
127130
}
128131
}
129132
intravisit::walk_mod(self, m, id);

Diff for: src/librustdoc/visit_ast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
154154
m: &'tcx hir::Mod<'tcx>,
155155
name: Symbol,
156156
) -> Module<'tcx> {
157-
let mut om = Module::new(name, id, m.inner);
157+
let mut om = Module::new(name, id, m.spans.inner_span);
158158
let def_id = self.cx.tcx.hir().local_def_id(id).to_def_id();
159159
// Keep track of if there were any private modules in the path.
160160
let orig_inside_public_path = self.inside_public_path;

Diff for: src/test/ui/imports/overlapping_pub_trait.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
* This crate declares two public paths, `m::Tr` and `prelude::_`. Make sure we prefer the former.
55
*/
66
extern crate overlapping_pub_trait_source;
7+
//~^ HELP the following trait is implemented but not in scope; perhaps add a `use` for it:
8+
//~| SUGGESTION overlapping_pub_trait_source::m::Tr
79

810
fn main() {
9-
//~^ HELP the following trait is implemented but not in scope; perhaps add a `use` for it:
10-
//~| SUGGESTION overlapping_pub_trait_source::m::Tr
1111
use overlapping_pub_trait_source::S;
1212
S.method();
1313
//~^ ERROR no method named `method` found for struct `S` in the current scope [E0599]

Diff for: src/test/ui/imports/unnamed_pub_trait.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
* importing it by name, and instead we suggest importing it by glob.
66
*/
77
extern crate unnamed_pub_trait_source;
8+
//~^ HELP the following trait is implemented but not in scope; perhaps add a `use` for it:
9+
//~| SUGGESTION unnamed_pub_trait_source::prelude::*; // trait Tr
810

911
fn main() {
10-
//~^ HELP the following trait is implemented but not in scope; perhaps add a `use` for it:
11-
//~| SUGGESTION unnamed_pub_trait_source::prelude::*; // trait Tr
1212
use unnamed_pub_trait_source::S;
1313
S.method();
1414
//~^ ERROR no method named `method` found for struct `S` in the current scope [E0599]

Diff for: src/test/ui/suggestions/use-placement-typeck.fixed

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#![allow(unused)]
88

99
use m::Foo;
10-
1110
fn main() {
1211
let s = m::S;
1312
s.abc(); //~ ERROR no method named `abc`

0 commit comments

Comments
 (0)