Prefer standard library paths over shorter extern deps re-exports

This commit is contained in:
Lukas Wirth 2024-07-21 09:14:17 +02:00
parent 41603ab14e
commit 40bbc684ad
2 changed files with 69 additions and 6 deletions

View file

@ -1,7 +1,8 @@
//! An algorithm to find a path to refer to a certain item. //! An algorithm to find a path to refer to a certain item.
use std::{cell::Cell, cmp::Ordering, iter}; use std::{cell::Cell, cmp::Ordering, iter, ops::BitOr};
use base_db::CrateId;
use hir_expand::{ use hir_expand::{
name::{AsName, Name}, name::{AsName, Name},
Lookup, Lookup,
@ -37,7 +38,8 @@ pub fn find_path(
// within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so // within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so
// default to plain paths. // default to plain paths.
if item.module(db).is_some_and(ModuleId::is_within_block) { let item_module = item.module(db)?;
if item_module.is_within_block() {
prefix_kind = PrefixKind::Plain; prefix_kind = PrefixKind::Plain;
} }
cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate()); cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate());
@ -50,6 +52,7 @@ pub fn find_path(
ignore_local_imports, ignore_local_imports,
from, from,
from_def_map: &from.def_map(db), from_def_map: &from.def_map(db),
is_std_item: db.crate_graph()[item_module.krate()].origin.is_lang(),
fuel: Cell::new(FIND_PATH_FUEL), fuel: Cell::new(FIND_PATH_FUEL),
}, },
item, item,
@ -104,6 +107,7 @@ struct FindPathCtx<'db> {
ignore_local_imports: bool, ignore_local_imports: bool,
from: ModuleId, from: ModuleId,
from_def_map: &'db DefMap, from_def_map: &'db DefMap,
is_std_item: bool,
fuel: Cell<usize>, fuel: Cell<usize>,
} }
@ -373,9 +377,12 @@ fn calculate_best_path(
// too (unless we can't name it at all). It could *also* be (re)exported by the same crate // too (unless we can't name it at all). It could *also* be (re)exported by the same crate
// that wants to import it here, but we always prefer to use the external path here. // that wants to import it here, but we always prefer to use the external path here.
for dep in &db.crate_graph()[ctx.from.krate].dependencies { let mut process_dep = |dep: CrateId| {
let import_map = db.import_map(dep.crate_id); let import_map = db.import_map(dep);
let Some(import_info_for) = import_map.import_info_for(item) else { continue }; let Some(import_info_for) = import_map.import_info_for(item) else {
return false;
};
let mut processed_something = false;
for info in import_info_for { for info in import_info_for {
if info.is_doc_hidden { if info.is_doc_hidden {
// the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate // the item or import is `#[doc(hidden)]`, so skip it as it is in an external crate
@ -396,8 +403,33 @@ fn calculate_best_path(
); );
process(path, info.name.clone(), &mut best_path_len); process(path, info.name.clone(), &mut best_path_len);
processed_something = true;
}
processed_something
};
let dependencies = &db.crate_graph()[ctx.from.krate].dependencies;
if ctx.is_std_item {
// The item we are searching for comes from the sysroot libraries, so skip prefer looking in
// the sysroot libraries directly.
// We do need to fallback as the item in question could be re-exported by another crate
// while not being a transitive dependency of the current crate.
let processed = dependencies
.iter()
.filter(|it| it.is_sysroot())
.map(|dep| process_dep(dep.crate_id))
.reduce(BitOr::bitor)
.unwrap_or(false);
if processed {
// Found a path in a sysroot crate, so return it.
return best_path;
} }
} }
dependencies
.iter()
.filter(|it| !ctx.is_std_item || !it.is_sysroot())
.for_each(|dep| _ = process_dep(dep.crate_id));
} }
best_path best_path
} }
@ -1918,4 +1950,34 @@ pub fn c() {}
"#]], "#]],
); );
} }
#[test]
fn prefer_long_std_over_short_extern() {
check_found_path(
r#"
//- /lib.rs crate:main deps:futures_lite,std,core
$0
//- /futures_lite.rs crate:futures_lite deps:std,core
pub use crate::future::Future;
pub mod future {
pub use core::future::Future;
}
//- /std.rs crate:std deps:core
pub use core::future;
//- /core.rs crate:core
pub mod future {
pub trait Future {}
}
"#,
"core::future::Future",
expect![[r#"
Plain (imports ): std::future::Future
Plain (imports ): std::future::Future
ByCrate(imports ): std::future::Future
ByCrate(imports ): std::future::Future
BySelf (imports ): std::future::Future
BySelf (imports ): std::future::Future
"#]],
);
}
} }

View file

@ -246,6 +246,7 @@ impl ChangeFixture {
for (from, to, prelude) in crate_deps { for (from, to, prelude) in crate_deps {
let from_id = crates[&from]; let from_id = crates[&from];
let to_id = crates[&to]; let to_id = crates[&to];
let sysroot = crate_graph[to_id].origin.is_lang();
crate_graph crate_graph
.add_dep( .add_dep(
from_id, from_id,
@ -253,7 +254,7 @@ impl ChangeFixture {
CrateName::new(&to).unwrap(), CrateName::new(&to).unwrap(),
to_id, to_id,
prelude, prelude,
false, sysroot,
), ),
) )
.unwrap(); .unwrap();