Repository: rust-lang/rust-analyzer
Pull request #17656 (2024-07-21 11:26:41)

fix: Allow flyimport to import primitive shadowing modules
Fixes https://github.com/rust-lang/rust-analyzer/issues/16371


Patch:
diff --git a/crates/hir-def/src/find_path.rs b/crates/hir-def/src/find_path.rs
index fc08003052..a3a602c2c1 100644
--- a/crates/hir-def/src/find_path.rs
+++ b/crates/hir-def/src/find_path.rs
@@ -1,8 +1,7 @@
 //! An algorithm to find a path to refer to a certain item.
 
-use std::{cell::Cell, cmp::Ordering, iter, ops::BitOr};
+use std::{cell::Cell, cmp::Ordering, iter};
 
-use base_db::CrateId;
 use hir_expand::{
     name::{AsName, Name},
     Lookup,
@@ -38,8 +37,7 @@ pub fn find_path(
 
     // within block modules, forcing a `self` or `crate` prefix will not allow using inner items, so
     // default to plain paths.
-    let item_module = item.module(db)?;
-    if item_module.is_within_block() {
+    if item.module(db).is_some_and(ModuleId::is_within_block) {
         prefix_kind = PrefixKind::Plain;
     }
     cfg.prefer_no_std = cfg.prefer_no_std || db.crate_supports_no_std(from.krate());
@@ -52,7 +50,6 @@ 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,
@@ -107,7 +104,6 @@ struct FindPathCtx<'db> {
     ignore_local_imports: bool,
     from: ModuleId,
     from_def_map: &'db DefMap,
-    is_std_item: bool,
     fuel: Cell<usize>,
 }
 
@@ -377,12 +373,9 @@ 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.
 
-        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 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 };
             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
@@ -403,33 +396,8 @@ 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
 }
@@ -1950,34 +1918,4 @@ 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-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs
index 3bb5ac1b10..1c4c15f255 100644
--- a/crates/ide-db/src/imports/import_assets.rs
+++ b/crates/ide-db/src/imports/import_assets.rs
@@ -703,7 +703,7 @@ fn path_import_candidate(
 ) -> Option<ImportCandidate> {
     Some(match qualifier {
         Some(qualifier) => match sema.resolve_path(&qualifier) {
-            None => {
+            Some(PathResolution::Def(ModuleDef::BuiltinType(_))) | None => {
                 if qualifier.first_qualifier().map_or(true, |it| sema.resolve_path(&it).is_none()) {
                     let qualifier = qualifier
                         .segments()
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})
-}
 "#,
         );
     }
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs
index 568906a098..9245818584 100644
--- a/crates/ide/src/file_structure.rs
+++ b/crates/ide/src/file_structure.rs
@@ -197,7 +197,9 @@ fn structure_token(token: SyntaxToken) -> Option<StructureNode> {
     if let Some(comment) = ast::Comment::cast(token) {
         let text = comment.text().trim();
 
-        if let Some(region_name) = text.strip_prefix("// region:").map(str::trim) {
+        if let Some(region_name) =
+            text.strip_prefix("// region:").map(str::trim).filter(|it| !it.is_empty())
+        {
             return Some(StructureNode {
                 parent: None,
                 label: region_name.to_owned(),
diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs
index 01b8f9e918..86e19fbe5f 100644
--- a/crates/parser/src/grammar/paths.rs
+++ b/crates/parser/src/grammar/paths.rs
@@ -2,6 +2,8 @@ use super::*;
 
 pub(super) const PATH_FIRST: TokenSet =
     TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self], T![:], T![<]]);
+pub(super) const WEAK_DYN_PATH_FIRST: TokenSet =
+    TokenSet::new(&[IDENT, T![self], T![super], T![crate], T![Self]]);
 
 pub(super) fn is_path_start(p: &Parser<'_>) -> bool {
     is_use_path_start(p) || p.at(T![<]) || p.at(T![Self])
diff --git a/crates/parser/src/grammar/types.rs b/crates/parser/src/grammar/types.rs
index f4e57d3d6f..9a1c6dfdf7 100644
--- a/crates/parser/src/grammar/types.rs
+++ b/crates/parser/src/grammar/types.rs
@@ -1,3 +1,5 @@
+use crate::grammar::paths::WEAK_DYN_PATH_FIRST;
+
 use super::*;
 
 pub(super) const TYPE_FIRST: TokenSet = paths::PATH_FIRST.union(TokenSet::new(&[
@@ -49,7 +51,13 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
         T![dyn] => dyn_trait_type(p),
         // Some path types are not allowed to have bounds (no plus)
         T![<] => path_type_bounds(p, allow_bounds),
-        T![ident] if !p.edition().at_least_2018() && is_dyn_weak(p) => dyn_trait_type_weak(p),
+        T![ident]
+            if !p.edition().at_least_2018()
+                && p.at_contextual_kw(T![dyn])
+                && WEAK_DYN_PATH_FIRST.contains(p.nth(1)) =>
+        {
+            dyn_trait_type_weak(p)
+        }
         _ if paths::is_path_start(p) => path_or_macro_type_(p, allow_bounds),
         LIFETIME_IDENT if p.nth_at(1, T![+]) => bare_dyn_trait_type(p),
         _ => {
@@ -58,25 +66,6 @@ fn type_with_bounds_cond(p: &mut Parser<'_>, allow_bounds: bool) {
     }
 }
 
-fn is_dyn_weak(p: &Parser<'_>) -> bool {
-    const WEAK_DYN_PATH_FIRST: TokenSet = TokenSet::new(&[
-        IDENT,
-        T![self],
-        T![super],
-        T![crate],
-        T![Self],
-        T![lifetime_ident],
-        T![?],
-        T![for],
-        T!['('],
-    ]);
-
-    p.at_contextual_kw(T![dyn]) && {
-        let la = p.nth(1);
-        WEAK_DYN_PATH_FIRST.contains(la) && (la != T![:] || la != T![<])
-    }
-}
-
 pub(super) fn ascription(p: &mut Parser<'_>) {
     assert!(p.at(T![:]));
     p.bump(T![:]);
@@ -300,14 +289,9 @@ fn dyn_trait_type(p: &mut Parser<'_>) {
 }
 
 // test dyn_trait_type_weak 2015
-// type DynPlain = dyn Path;
-// type DynRef = &dyn Path;
-// type DynLt = dyn 'a + Path;
-// type DynQuestion = dyn ?Path;
-// type DynFor = dyn for<'a> Path;
-// type DynParen = dyn(Path);
-// type Path = dyn::Path;
-// type Generic = dyn<Path>;
+// type A = dyn Iterator<Item=Foo<'a>> + 'a;
+// type A = &dyn Iterator<Item=Foo<'a>> + 'a;
+// type A = dyn::Path;
 fn dyn_trait_type_weak(p: &mut Parser<'_>) {
     assert!(p.at_contextual_kw(T![dyn]));
     let m = p.start();


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/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs
index eacec018c7..158dbaf1b1 100644
--- a/crates/ide-completion/src/tests/flyimport.rs
+++ b/crates/ide-completion/src/tests/flyimport.rs
@@ -1618,3 +1618,18 @@ pub struct FooStruct;
         "#]],
     );
 }
+
+#[test]
+fn primitive_mod() {
+    check(
+        r#"
+//- minicore: str
+fn main() {
+    str::from$0
+}
+"#,
+        expect![[r#"
+            fn from_utf8_unchecked(…) (use core::str) const unsafe fn(&[u8]) -> &str
+        "#]],
+    );
+}
diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
index dcc66dc1e2..00ca92402c 100644
--- a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
+++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rast
@@ -5,7 +5,7 @@ SOURCE_FILE
     TYPE_KW "type"
     WHITESPACE " "
     NAME
-      IDENT "DynPlain"
+      IDENT "A"
     WHITESPACE " "
     EQ "="
     WHITESPACE " "
@@ -18,14 +18,38 @@ SOURCE_FILE
             PATH
               PATH_SEGMENT
                 NAME_REF
-                  IDENT "Path"
+                  IDENT "Iterator"
+                GENERIC_ARG_LIST
+                  L_ANGLE "<"
+                  ASSOC_TYPE_ARG
+                    NAME_REF
+                      IDENT "Item"
+                    EQ "="
+                    PATH_TYPE
+                      PATH
+                        PATH_SEGMENT
+                          NAME_REF
+                            IDENT "Foo"
+                          GENERIC_ARG_LIST
+                            L_ANGLE "<"
+                            LIFETIME_ARG
+                              LIFETIME
+                                LIFETIME_IDENT "'a"
+                            R_ANGLE ">"
+                  R_ANGLE ">"
+        WHITESPACE " "
+        PLUS "+"
+        WHITESPACE " "
+        TYPE_BOUND
+          LIFETIME
+            LIFETIME_IDENT "'a"
     SEMICOLON ";"
   WHITESPACE "\n"
   TYPE_ALIAS
     TYPE_KW "type"
     WHITESPACE " "
     NAME
-      IDENT "DynRef"
+      IDENT "A"
     WHITESPACE " "
     EQ "="
     WHITESPACE " "
@@ -40,111 +64,38 @@ SOURCE_FILE
               PATH
                 PATH_SEGMENT
                   NAME_REF
-                    IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynLt"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      WHITESPACE " "
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          LIFETIME
-            LIFETIME_IDENT "'a"
-        WHITESPACE " "
-        PLUS "+"
-        WHITESPACE " "
-        TYPE_BOUND
-          PATH_TYPE
-            PATH
-              PATH_SEGMENT
-                NAME_REF
-                  IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynQuestion"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      WHITESPACE " "
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          QUESTION "?"
-          PATH_TYPE
-            PATH
-              PATH_SEGMENT
-                NAME_REF
-                  IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynFor"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      WHITESPACE " "
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          FOR_TYPE
-            FOR_KW "for"
-            GENERIC_PARAM_LIST
-              L_ANGLE "<"
-              LIFETIME_PARAM
-                LIFETIME
-                  LIFETIME_IDENT "'a"
-              R_ANGLE ">"
-            WHITESPACE " "
-            PATH_TYPE
-              PATH
-                PATH_SEGMENT
-                  NAME_REF
-                    IDENT "Path"
-    SEMICOLON ";"
-  WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "DynParen"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    DYN_TRAIT_TYPE
-      DYN_KW "dyn"
-      TYPE_BOUND_LIST
-        TYPE_BOUND
-          L_PAREN "("
-          PATH_TYPE
-            PATH
-              PATH_SEGMENT
-                NAME_REF
-                  IDENT "Path"
-          R_PAREN ")"
+                    IDENT "Iterator"
+                  GENERIC_ARG_LIST
+                    L_ANGLE "<"
+                    ASSOC_TYPE_ARG
+                      NAME_REF
+                        IDENT "Item"
+                      EQ "="
+                      PATH_TYPE
+                        PATH
+                          PATH_SEGMENT
+                            NAME_REF
+                              IDENT "Foo"
+                            GENERIC_ARG_LIST
+                              L_ANGLE "<"
+                              LIFETIME_ARG
+                                LIFETIME
+                                  LIFETIME_IDENT "'a"
+                              R_ANGLE ">"
+                    R_ANGLE ">"
+          WHITESPACE " "
+          PLUS "+"
+          WHITESPACE " "
+          TYPE_BOUND
+            LIFETIME
+              LIFETIME_IDENT "'a"
     SEMICOLON ";"
   WHITESPACE "\n"
   TYPE_ALIAS
     TYPE_KW "type"
     WHITESPACE " "
     NAME
-      IDENT "Path"
+      IDENT "A"
     WHITESPACE " "
     EQ "="
     WHITESPACE " "
@@ -160,27 +111,3 @@ SOURCE_FILE
             IDENT "Path"
     SEMICOLON ";"
   WHITESPACE "\n"
-  TYPE_ALIAS
-    TYPE_KW "type"
-    WHITESPACE " "
-    NAME
-      IDENT "Generic"
-    WHITESPACE " "
-    EQ "="
-    WHITESPACE " "
-    PATH_TYPE
-      PATH
-        PATH_SEGMENT
-          NAME_REF
-            IDENT "dyn"
-          GENERIC_ARG_LIST
-            L_ANGLE "<"
-            TYPE_ARG
-              PATH_TYPE
-                PATH
-                  PATH_SEGMENT
-                    NAME_REF
-                      IDENT "Path"
-            R_ANGLE ">"
-    SEMICOLON ";"
-  WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
index c4ef1f2b7a..c4941e65bf 100644
--- a/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
+++ b/crates/parser/test_data/parser/inline/ok/dyn_trait_type_weak.rs
@@ -1,9 +1,4 @@
 // 2015
-type DynPlain = dyn Path;
-type DynRef = &dyn Path;
-type DynLt = dyn 'a + Path;
-type DynQuestion = dyn ?Path;
-type DynFor = dyn for<'a> Path;
-type DynParen = dyn(Path);
-type Path = dyn::Path;
-type Generic = dyn<Path>;
+type A = dyn Iterator<Item=Foo<'a>> + 'a;
+type A = &dyn Iterator<Item=Foo<'a>> + 'a;
+type A = dyn::Path;
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index c10ff4aede..19c81a7c5f 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -246,7 +246,6 @@ 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,
@@ -254,7 +253,7 @@ impl ChangeFixture {
                             CrateName::new(&to).unwrap(),
                             to_id,
                             prelude,
-                            sysroot,
+                            false,
                         ),
                     )
                     .unwrap();
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index d1862f7d73..2d615c34a3 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -55,6 +55,7 @@
 //!     size_of: sized
 //!     sized:
 //!     slice:
+//!     str:
 //!     sync: sized
 //!     transmute:
 //!     try: infallible
@@ -1368,6 +1369,14 @@ pub mod iter {
 }
 // endregion:iterator
 
+// region:str
+pub mod str {
+    pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str {
+        ""
+    }
+}
+// endregion:str
+
 // region:panic
 mod panic {
     pub macro panic_2021 {