about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-07-21 09:14:17 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-07-21 09:14:17 +0200
commitcfdcaa7450a916710e4c5f5a902d0f4f81cf9a1b (patch)
tree998263cd0adf659a2a483eff0a85b464596c7276
parent38814ece185fc215ecac4bc64ad4cbdad2fd7b24 (diff)
downloadrust-cfdcaa7450a916710e4c5f5a902d0f4f81cf9a1b.tar.gz
rust-cfdcaa7450a916710e4c5f5a902d0f4f81cf9a1b.zip
Prefer standard library paths over shorter extern deps re-exports
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/find_path.rs72
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/src/lib.rs3
2 files changed, 69 insertions, 6 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
index a3a602c2c11..fc080030523 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs
@@ -1,7 +1,8 @@
 //! 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::{
     name::{AsName, Name},
     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
     // 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;
     }
     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,
             from,
             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),
         },
         item,
@@ -104,6 +107,7 @@ struct FindPathCtx<'db> {
     ignore_local_imports: bool,
     from: ModuleId,
     from_def_map: &'db DefMap,
+    is_std_item: bool,
     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
         // 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 import_map = db.import_map(dep.crate_id);
-            let Some(import_info_for) = import_map.import_info_for(item) else { continue };
+        let mut process_dep = |dep: CrateId| {
+            let import_map = db.import_map(dep);
+            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 {
                 if info.is_doc_hidden {
                     // 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);
+                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
 }
@@ -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
+            "#]],
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
index 19c81a7c5f7..c10ff4aede5 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -246,6 +246,7 @@ impl ChangeFixture {
             for (from, to, prelude) in crate_deps {
                 let from_id = crates[&from];
                 let to_id = crates[&to];
+                let sysroot = crate_graph[to_id].origin.is_lang();
                 crate_graph
                     .add_dep(
                         from_id,
@@ -253,7 +254,7 @@ impl ChangeFixture {
                             CrateName::new(&to).unwrap(),
                             to_id,
                             prelude,
-                            false,
+                            sysroot,
                         ),
                     )
                     .unwrap();