Skip to content

Commit 8c2353b

Browse files
committed
remove find_use_placement
A more robust solution to finding where to place use suggestions was added. 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. Signed-off-by: Miguel Guarniz <[email protected]>
1 parent 0677edc commit 8c2353b

File tree

12 files changed

+57
-148
lines changed

12 files changed

+57
-148
lines changed

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),

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
},

compiler/rustc_hir/src/hir.rs

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

25232523
#[derive(Encodable, Debug, HashStable_Generic)]
25242524
pub struct Mod<'hir> {
2525+
pub spans: ModSpans,
2526+
pub item_ids: &'hir [ItemId],
2527+
}
2528+
2529+
#[derive(Copy, Clone, Debug, HashStable_Generic, Encodable)]
2530+
pub struct ModSpans {
25252531
/// A span from the first token past `{` to the last token until `}`.
25262532
/// For `mod foo;`, the inner span ranges from the first token
25272533
/// to the last token in the external file.
2528-
pub inner: Span,
2529-
pub item_ids: &'hir [ItemId],
2534+
pub inner_span: Span,
2535+
pub inject_use_span: Span,
25302536
}
25312537

25322538
#[derive(Debug, HashStable_Generic)]
@@ -3024,8 +3030,8 @@ impl<'hir> OwnerNode<'hir> {
30243030
OwnerNode::Item(Item { span, .. })
30253031
| OwnerNode::ForeignItem(ForeignItem { span, .. })
30263032
| OwnerNode::ImplItem(ImplItem { span, .. })
3027-
| OwnerNode::TraitItem(TraitItem { span, .. })
3028-
| OwnerNode::Crate(Mod { inner: span, .. }) => *span,
3033+
| OwnerNode::TraitItem(TraitItem { span, .. }) => *span,
3034+
OwnerNode::Crate(Mod { spans: ModSpans { inner_span, .. }, .. }) => *inner_span,
30293035
}
30303036
}
30313037

compiler/rustc_middle/src/hir/map/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,7 @@ impl<'hir> Map<'hir> {
586586
Some(OwnerNode::Item(&Item { span, kind: ItemKind::Mod(ref m), .. })) => {
587587
(m, span, hir_id)
588588
}
589-
Some(OwnerNode::Crate(item)) => (item, item.inner, hir_id),
589+
Some(OwnerNode::Crate(item)) => (item, item.spans.inner_span, hir_id),
590590
node => panic!("not a module: {:?}", node),
591591
}
592592
}
@@ -1014,7 +1014,7 @@ impl<'hir> Map<'hir> {
10141014
Node::Infer(i) => i.span,
10151015
Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
10161016
Node::Local(local) => local.span,
1017-
Node::Crate(item) => item.inner,
1017+
Node::Crate(item) => item.spans.inner_span,
10181018
};
10191019
Some(span)
10201020
}

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(

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

compiler/rustc_typeck/src/check/method/suggest.rs

+21-125
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rustc_errors::{
77
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
88
};
99
use rustc_hir as hir;
10-
use rustc_hir::def_id::{DefId, LocalDefId};
10+
use rustc_hir::def_id::DefId;
1111
use rustc_hir::lang_items::LangItem;
1212
use rustc_hir::{ExprKind, Node, QPath};
1313
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(
@@ -2100,53 +2043,6 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
21002043
tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect()
21012044
}
21022045

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

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);

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;

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]

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]

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)