Repository: rust-lang/rust-analyzer
Pull request #17653 (2024-07-21 07:14:57)

Prefer standard library paths over shorter extern deps re-exports
This should generally speed up path finding for std items as we no longer bother looking through all external dependencies. It also makes more sense to prefer importing std items from the std dependencies directly.

Fixes https://github.com/rust-lang/rust-analyzer/issues/17540


Patch:
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index a3a602c2c1..fc08003052 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/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/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs
index ab4ffbb2c1..19fdd8e33d 100644
--- a/crates/hir-def/src/nameres/mod_resolution.rs
+++ b/crates/hir-def/src/nameres/mod_resolution.rs
@@ -1,7 +1,7 @@
 //! This module resolves `mod foo;` declaration to file.
 use arrayvec::ArrayVec;
 use base_db::AnchoredPath;
-use hir_expand::{name::Name, HirFileIdExt};
+use hir_expand::{name::Name, HirFileIdExt, MacroFileIdExt};
 use limit::Limit;
 use span::EditionedFileId;
 use syntax::ToSmolStr as _;
@@ -73,6 +73,10 @@ impl ModDir {
             Some(attr_path) => {
                 candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
             }
+            None if file_id.macro_file().map_or(false, |it| it.is_include_macro(db.upcast())) => {
+                candidate_files.push(format!("{}.rs", name.display(db.upcast())));
+                candidate_files.push(format!("{}/mod.rs", name.display(db.upcast())));
+            }
             None => {
                 candidate_files.push(format!(
                     "{}{}.rs",
diff --git a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index a12e201cf3..7bf8af4cab 100644
--- a/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -86,15 +86,6 @@ impl<'db> MatchCheckCtx<'db> {
         arms: &[MatchArm<'db>],
         scrut_ty: Ty,
     ) -> Result<UsefulnessReport<'db, Self>, ()> {
-        if scrut_ty.contains_unknown() {
-            return Err(());
-        }
-        for arm in arms {
-            if arm.pat.ty().contains_unknown() {
-                return Err(());
-            }
-        }
-
         // FIXME: Determine place validity correctly. For now, err on the safe side.
         let place_validity = PlaceValidity::MaybeInvalid;
         // Measured to take ~100ms on modern hardware.
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
index 4e52d28051..4c25532228 100644
--- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs
+++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -745,18 +745,6 @@ fn f() {
         0
     }
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn regression_17585() {
-        check_diagnostics(
-            r#"
-fn f() {
-    let (_, _, _, ..) = (true, 42);
-     // ^^^^^^^^^^^^^ error: expected (bool, i32), found (bool, i32, {unknown})
-}
 "#,
         );
     }


Test patch:
diff --git a/crates/hir-def/src/nameres/tests/macros.rs b/crates/hir-def/src/nameres/tests/macros.rs
index 9f67d6fa8b..d278b75e81 100644
--- a/crates/hir-def/src/nameres/tests/macros.rs
+++ b/crates/hir-def/src/nameres/tests/macros.rs
@@ -1309,52 +1309,6 @@ pub mod ip_address {
     );
 }
 
-#[test]
-fn include_with_submod_file() {
-    check(
-        r#"
-//- minicore: include
-//- /lib.rs
-include!("out_dir/includes.rs");
-
-//- /out_dir/includes.rs
-pub mod company_name {
-    pub mod network {
-        pub mod v1;
-    }
-}
-//- /out_dir/company_name/network/v1.rs
-pub struct IpAddress {
-    pub ip_type: &'static str,
-}
-/// Nested message and enum types in `IpAddress`.
-pub mod ip_address {
-    pub enum IpType {
-        IpV4(u32),
-    }
-}
-
-"#,
-        expect![[r#"
-            crate
-            company_name: t
-
-            crate::company_name
-            network: t
-
-            crate::company_name::network
-            v1: t
-
-            crate::company_name::network::v1
-            IpAddress: t
-            ip_address: t
-
-            crate::company_name::network::v1::ip_address
-            IpType: t
-        "#]],
-    );
-}
-
 #[test]
 fn macro_use_imports_all_macro_types() {
     let db = TestDB::with_files(
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 19c81a7c5f..c10ff4aede 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/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();