Prefer stable paths over unstable ones in import path calculation

This commit is contained in:
Lukas Wirth 2023-09-14 11:03:41 +02:00
parent 712e67cf11
commit e63e323823
2 changed files with 128 additions and 33 deletions

View file

@ -37,6 +37,20 @@ pub fn find_path_prefixed(
find_path_inner(db, item, from, Some(prefix_kind), prefer_no_std)
}
#[derive(Copy, Clone, Debug)]
enum Stability {
Unstable,
Stable,
}
use Stability::*;
fn zip_stability(a: Stability, b: Stability) -> Stability {
match (a, b) {
(Stable, Stable) => Stable,
_ => Unstable,
}
}
const MAX_PATH_LEN: usize = 15;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
@ -95,7 +109,8 @@ fn find_path_inner(
MAX_PATH_LEN,
prefixed,
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
);
)
.map(|(item, _)| item);
}
// - if the item is already in scope, return the name under which it is
@ -143,6 +158,7 @@ fn find_path_inner(
prefer_no_std || db.crate_supports_no_std(crate_root.krate),
scope_name,
)
.map(|(item, _)| item)
}
fn find_path_for_module(
@ -155,7 +171,7 @@ fn find_path_for_module(
max_len: usize,
prefixed: Option<PrefixKind>,
prefer_no_std: bool,
) -> Option<ModPath> {
) -> Option<(ModPath, Stability)> {
if max_len == 0 {
return None;
}
@ -165,19 +181,19 @@ fn find_path_for_module(
let scope_name = find_in_scope(db, def_map, from, ItemInNs::Types(module_id.into()));
if prefixed.is_none() {
if let Some(scope_name) = scope_name {
return Some(ModPath::from_segments(PathKind::Plain, Some(scope_name)));
return Some((ModPath::from_segments(PathKind::Plain, Some(scope_name)), Stable));
}
}
// - if the item is the crate root, return `crate`
if module_id == crate_root {
return Some(ModPath::from_segments(PathKind::Crate, None));
return Some((ModPath::from_segments(PathKind::Crate, None), Stable));
}
// - if relative paths are fine, check if we are searching for a parent
if prefixed.filter(PrefixKind::is_absolute).is_none() {
if let modpath @ Some(_) = find_self_super(def_map, module_id, from) {
return modpath;
return modpath.zip(Some(Stable));
}
}
@ -201,14 +217,14 @@ fn find_path_for_module(
} else {
PathKind::Plain
};
return Some(ModPath::from_segments(kind, Some(name)));
return Some((ModPath::from_segments(kind, Some(name)), Stable));
}
}
if let value @ Some(_) =
find_in_prelude(db, &root_def_map, &def_map, ItemInNs::Types(module_id.into()), from)
{
return value;
return value.zip(Some(Stable));
}
calculate_best_path(
db,
@ -301,11 +317,19 @@ fn calculate_best_path(
mut prefixed: Option<PrefixKind>,
prefer_no_std: bool,
scope_name: Option<Name>,
) -> Option<ModPath> {
) -> Option<(ModPath, Stability)> {
if max_len <= 1 {
return None;
}
let mut best_path = None;
let update_best_path =
|best_path: &mut Option<_>, new_path: (ModPath, Stability)| match best_path {
Some((old_path, old_stability)) => {
*old_path = new_path.0;
*old_stability = zip_stability(*old_stability, new_path.1);
}
None => *best_path = Some(new_path),
};
// Recursive case:
// - otherwise, look for modules containing (reexporting) it and import it from one of those
if item.krate(db) == Some(from.krate) {
@ -328,14 +352,14 @@ fn calculate_best_path(
prefixed,
prefer_no_std,
) {
path.push_segment(name);
path.0.push_segment(name);
let new_path = match best_path {
let new_path = match best_path.take() {
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
None => path,
};
best_path_len = new_path.len();
best_path = Some(new_path);
best_path_len = new_path.0.len();
update_best_path(&mut best_path, new_path);
}
}
} else {
@ -354,7 +378,7 @@ fn calculate_best_path(
// Determine best path for containing module and append last segment from `info`.
// FIXME: we should guide this to look up the path locally, or from the same crate again?
let mut path = find_path_for_module(
let (mut path, path_stability) = find_path_for_module(
db,
def_map,
visited_modules,
@ -367,16 +391,19 @@ fn calculate_best_path(
)?;
cov_mark::hit!(partially_imported);
path.push_segment(info.name.clone());
Some(path)
Some((
path,
zip_stability(path_stability, if info.is_unstable { Unstable } else { Stable }),
))
})
});
for path in extern_paths {
let new_path = match best_path {
let new_path = match best_path.take() {
Some(best_path) => select_best_path(best_path, path, prefer_no_std),
None => path,
};
best_path = Some(new_path);
update_best_path(&mut best_path, new_path);
}
}
if let Some(module) = item.module(db) {
@ -387,15 +414,24 @@ fn calculate_best_path(
}
match prefixed.map(PrefixKind::prefix) {
Some(prefix) => best_path.or_else(|| {
scope_name.map(|scope_name| ModPath::from_segments(prefix, Some(scope_name)))
scope_name.map(|scope_name| (ModPath::from_segments(prefix, Some(scope_name)), Stable))
}),
None => best_path,
}
}
fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -> ModPath {
fn select_best_path(
old_path: (ModPath, Stability),
new_path: (ModPath, Stability),
prefer_no_std: bool,
) -> (ModPath, Stability) {
match (old_path.1, new_path.1) {
(Stable, Unstable) => return old_path,
(Unstable, Stable) => return new_path,
_ => {}
}
const STD_CRATES: [Name; 3] = [known::std, known::core, known::alloc];
match (old_path.segments().first(), new_path.segments().first()) {
match (old_path.0.segments().first(), new_path.0.segments().first()) {
(Some(old), Some(new)) if STD_CRATES.contains(old) && STD_CRATES.contains(new) => {
let rank = match prefer_no_std {
false => |name: &Name| match name {
@ -416,7 +452,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
match nrank.cmp(&orank) {
Ordering::Less => old_path,
Ordering::Equal => {
if new_path.len() < old_path.len() {
if new_path.0.len() < old_path.0.len() {
new_path
} else {
old_path
@ -426,7 +462,7 @@ fn select_best_path(old_path: ModPath, new_path: ModPath, prefer_no_std: bool) -
}
}
_ => {
if new_path.len() < old_path.len() {
if new_path.0.len() < old_path.0.len() {
new_path
} else {
old_path
@ -1360,4 +1396,29 @@ pub mod ops {
"std::ops::Deref",
);
}
#[test]
fn respect_unstable_modules() {
check_found_path(
r#"
//- /main.rs crate:main deps:std,core
#![no_std]
extern crate std;
$0
//- /longer.rs crate:std deps:core
pub mod error {
pub use core::error::Error;
}
//- /core.rs crate:core
pub mod error {
#![unstable(feature = "error_in_core", issue = "103765")]
pub trait Error {}
}
"#,
"std::error::Error",
"std::error::Error",
"std::error::Error",
"std::error::Error",
);
}
}