Skip to content

Commit 12e28c3

Browse files
committed
Auto merge of #15611 - Veykril:stability-import, r=Veykril
Prefer stable paths over unstable ones in import path calculation Fixes rust-lang/rust-analyzer#15610
2 parents 47c51b7 + e63e323 commit 12e28c3

File tree

2 files changed

+128
-33
lines changed

2 files changed

+128
-33
lines changed

crates/hir-def/src/find_path.rs

+82-21
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ pub fn find_path_prefixed(
3737
find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
3838
}
3939

40+
#[derive(Copy, Clone, Debug)]
41+
enum Stability {
42+
Unstable,
43+
Stable,
44+
}
45+
use Stability::*;
46+
47+
fn zip_stability(a: Stability, b: Stability) -> Stability {
48+
match (a, b) {
49+
(Stable, Stable) => Stable,
50+
_ => Unstable,
51+
}
52+
}
53+
4054
const MAX_PATH_LEN: usize = 15;
4155

4256
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -95,7 +109,8 @@ fn find_path_inner(
95109
MAX_PATH_LEN,
96110
prefixed,
97111
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
98-
);
112+
)
113+
.map(|(item, _)| item);
99114
}
100115

101116
// - if the item is already in scope, return the name under which it is
@@ -143,6 +158,7 @@ fn find_path_inner(
143158
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
144159
scope_name,
145160
)
161+
.map(|(item, _)| item)
146162
}
147163

148164
fn find_path_for_module(
@@ -155,7 +171,7 @@ fn find_path_for_module(
155171
max_len: usize,
156172
prefixed: Option<PrefixKind>,
157173
prefer_no_std: bool,
158-
) -> Option<ModPath> {
174+
) -> Option<(ModPath, Stability)> {
159175
if max_len == 0 {
160176
return None;
161177
}
@@ -165,19 +181,19 @@ fn find_path_for_module(
165181
let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into()));
166182
if prefixed.is_none() {
167183
if let Some(scope_name) = scope_name {
168-
return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
184+
return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable));
169185
}
170186
}
171187

172188
// - if the item is the crate root, return `crate`
173189
if module_id == crate_root {
174-
return Some(ModPath::from_segments(PathKind::Crate, None));
190+
return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
175191
}
176192

177193
// - if relative paths are fine, check if we are searching for a parent
178194
if prefixed.filter(PrefixKind::is_absolute).is_none() {
179195
if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
180-
return modpath;
196+
return modpath.zip(Some(Stable));
181197
}
182198
}
183199

@@ -201,14 +217,14 @@ fn find_path_for_module(
201217
} else {
202218
PathKind::Plain
203219
};
204-
return Some(ModPath::from_segments(kind, Some(name)));
220+
return Some((ModPath::from_segments(kind, Some(name)), Stable));
205221
}
206222
}
207223

208224
if let value @ Some(_) =
209225
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
210226
{
211-
return value;
227+
return value.zip(Some(Stable));
212228
}
213229
calculate_best_path(
214230
db,
@@ -301,11 +317,19 @@ fn calculate_best_path(
301317
mut prefixed: Option<PrefixKind>,
302318
prefer_no_std: bool,
303319
scope_name: Option<Name>,
304-
) -> Option<ModPath> {
320+
) -> Option<(ModPath, Stability)> {
305321
if max_len <= 1 {
306322
return None;
307323
}
308324
let mut best_path = None;
325+
let update_best_path =
326+
|best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path {
327+
Some((old_path, old_stability)) => {
328+
*old_path = new_path.0;
329+
*old_stability = zip_stability(*old_stability, new_path.1);
330+
}
331+
None => *best_path = Some(new_path),
332+
};
309333
// Recursive case:
310334
// - otherwise, look for modules containing (reexporting) it and import it from one of those
311335
if item.krate(db) == Some(from.krate) {
@@ -328,14 +352,14 @@ fn calculate_best_path(
328352
prefixed,
329353
prefer_no_std,
330354
) {
331-
path.push_segment(name);
355+
path.0.push_segment(name);
332356

333-
let new_path = match best_path {
357+
let new_path = match best_path.take() {
334358
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
335359
None => path,
336360
};
337-
best_path_len = new_path.len();
338-
best_path = Some(new_path);
361+
best_path_len = new_path.0.len();
362+
update_best_path(&mut best_path, new_path);
339363
}
340364
}
341365
} else {
@@ -354,7 +378,7 @@ fn calculate_best_path(
354378

355379
// Determine best path for containing module and append last segment from `info`.
356380
// FIXME: we should guide this to look up the path locally, or from the same crate again?
357-
let mut path = find_path_for_module(
381+
let (mut path, path_stability) = find_path_for_module(
358382
db,
359383
def_map,
360384
visited_modules,
@@ -367,16 +391,19 @@ fn calculate_best_path(
367391
)?;
368392
cov_mark::hit!(partially_imported);
369393
path.push_segment(info.name.clone());
370-
Some(path)
394+
Some((
395+
path,
396+
zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
397+
))
371398
})
372399
});
373400

374401
for path in extern_paths {
375-
let new_path = match best_path {
402+
let new_path = match best_path.take() {
376403
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
377404
None => path,
378405
};
379-
best_path = Some(new_path);
406+
update_best_path(&mut best_path, new_path);
380407
}
381408
}
382409
if let Some(module) = item.module(db) {
@@ -387,15 +414,24 @@ fn calculate_best_path(
387414
}
388415
match prefixed.map(PrefixKind::prefix) {
389416
Some(prefix) => best_path.or_else(|| {
390-
scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
417+
scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable))
391418
}),
392419
None => best_path,
393420
}
394421
}
395422

396-
fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
423+
fn select_best_path(
424+
old_path: (ModPath, Stability),
425+
new_path: (ModPath, Stability),
426+
prefer_no_std: bool,
427+
) -> (ModPath, Stability) {
428+
match (old_path.1, new_path.1) {
429+
(Stable, Unstable) => return old_path,
430+
(Unstable, Stable) => return new_path,
431+
_ => {}
432+
}
397433
const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
398-
match (old_path.segments().first(), new_path.segments().first()) {
434+
match (old_path.0.segments().first(), new_path.0.segments().first()) {
399435
(Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => {
400436
let rank = match prefer_no_std {
401437
false => |name: &Name| match name {
@@ -416,7 +452,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
416452
match nrank.cmp(&orank) {
417453
Ordering::Less => old_path,
418454
Ordering::Equal => {
419-
if new_path.len() < old_path.len() {
455+
if new_path.0.len() < old_path.0.len() {
420456
new_path
421457
} else {
422458
old_path
@@ -426,7 +462,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
426462
}
427463
}
428464
_ => {
429-
if new_path.len() < old_path.len() {
465+
if new_path.0.len() < old_path.0.len() {
430466
new_path
431467
} else {
432468
old_path
@@ -1360,4 +1396,29 @@ pub mod ops {
13601396
"std::ops::Deref",
13611397
);
13621398
}
1399+
1400+
#[test]
1401+
fn respect_unstable_modules() {
1402+
check_found_path(
1403+
r#"
1404+
//- /main.rs crate:main deps:std,core
1405+
#![no_std]
1406+
extern crate std;
1407+
$0
1408+
//- /longer.rs crate:std deps:core
1409+
pub mod error {
1410+
pub use core::error::Error;
1411+
}
1412+
//- /core.rs crate:core
1413+
pub mod error {
1414+
#![unstable(feature = "error_in_core", issue = "103765")]
1415+
pub trait Error {}
1416+
}
1417+
"#,
1418+
"std::error::Error",
1419+
"std::error::Error",
1420+
"std::error::Error",
1421+
"std::error::Error",
1422+
);
1423+
}
13631424
}

crates/hir-def/src/import_map.rs

+46-12
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub struct ImportInfo {
3232
pub is_trait_assoc_item: bool,
3333
/// Whether this item is annotated with `#[doc(hidden)]`.
3434
pub is_doc_hidden: bool,
35+
/// Whether this item is annotated with `#[unstable(..)]`.
36+
pub is_unstable: bool,
3537
}
3638

3739
/// A map from publicly exported items to its name.
@@ -117,7 +119,6 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
117119

118120
for (name, per_ns) in visible_items {
119121
for (item, import) in per_ns.iter_items() {
120-
// FIXME: Not yet used, but will be once we handle doc(hidden) import sources
121122
let attr_id = if let Some(import) = import {
122123
match import {
123124
ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
@@ -129,28 +130,59 @@ fn collect_import_map(db: &dyn DefDatabase, krate: CrateId) -> FxIndexMap<ItemIn
129130
ItemInNs::Macros(id) => Some(id.into()),
130131
}
131132
};
132-
let is_doc_hidden =
133-
attr_id.map_or(false, |attr_id| db.attrs(attr_id).has_doc_hidden());
133+
let status @ (is_doc_hidden, is_unstable) =
134+
attr_id.map_or((false, false), |attr_id| {
135+
let attrs = db.attrs(attr_id);
136+
(attrs.has_doc_hidden(), attrs.is_unstable())
137+
});
134138

135139
let import_info = ImportInfo {
136140
name: name.clone(),
137141
container: module,
138142
is_trait_assoc_item: false,
139143
is_doc_hidden,
144+
is_unstable,
140145
};
141146

142147
match depth_map.entry(item) {
143-
Entry::Vacant(entry) => _ = entry.insert((depth, is_doc_hidden)),
148+
Entry::Vacant(entry) => _ = entry.insert((depth, status)),
144149
Entry::Occupied(mut entry) => {
145-
let &(occ_depth, occ_is_doc_hidden) = entry.get();
146-
// Prefer the one that is not doc(hidden),
147-
// Otherwise, if both have the same doc(hidden)-ness and the new path is shorter, prefer that one.
148-
let overwrite_entry = occ_is_doc_hidden && !is_doc_hidden
149-
|| occ_is_doc_hidden == is_doc_hidden && depth < occ_depth;
150-
if !overwrite_entry {
150+
let &(occ_depth, (occ_is_doc_hidden, occ_is_unstable)) = entry.get();
151+
(depth, occ_depth);
152+
let overwrite = match (
153+
is_doc_hidden,
154+
occ_is_doc_hidden,
155+
is_unstable,
156+
occ_is_unstable,
157+
) {
158+
// no change of hiddeness or unstableness
159+
(true, true, true, true)
160+
| (true, true, false, false)
161+
| (false, false, true, true)
162+
| (false, false, false, false) => depth < occ_depth,
163+
164+
// either less hidden or less unstable, accept
165+
(true, true, false, true)
166+
| (false, true, true, true)
167+
| (false, true, false, true)
168+
| (false, true, false, false)
169+
| (false, false, false, true) => true,
170+
// more hidden or unstable, discard
171+
(true, true, true, false)
172+
| (true, false, true, true)
173+
| (true, false, true, false)
174+
| (true, false, false, false)
175+
| (false, false, true, false) => false,
176+
177+
// exchanges doc(hidden) for unstable (and vice-versa),
178+
(true, false, false, true) | (false, true, true, false) => {
179+
depth < occ_depth
180+
}
181+
};
182+
if !overwrite {
151183
continue;
152184
}
153-
entry.insert((depth, is_doc_hidden));
185+
entry.insert((depth, status));
154186
}
155187
}
156188

@@ -204,11 +236,13 @@ fn collect_trait_assoc_items(
204236
ItemInNs::Values(module_def_id)
205237
};
206238

239+
let attrs = &db.attrs(item.into());
207240
let assoc_item_info = ImportInfo {
208241
container: trait_import_info.container,
209242
name: assoc_item_name.clone(),
210243
is_trait_assoc_item: true,
211-
is_doc_hidden: db.attrs(item.into()).has_doc_hidden(),
244+
is_doc_hidden: attrs.has_doc_hidden(),
245+
is_unstable: attrs.is_unstable(),
212246
};
213247
map.insert(assoc_item, assoc_item_info);
214248
}

0 commit comments

Comments
 (0)