about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs51
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/test-fixture/src/lib.rs136
7 files changed, 166 insertions, 68 deletions
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index b8eadb608fe..dbf949c470c 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -30,6 +30,8 @@ use triomphe::Arc;
 pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
 
 pub type FxIndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>;
+pub type FxIndexMap<K, V> =
+    indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
 
 #[macro_export]
 macro_rules! impl_intern_key {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index a0be69cb2f9..600f2067700 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -12,7 +12,7 @@ use crate::{
     StaticId, StructId, TraitId, TypeAliasId, UnionId,
     db::DefDatabase,
     expr_store::path::Path,
-    nameres::{assoc::TraitItems, crate_def_map},
+    nameres::{assoc::TraitItems, crate_def_map, crate_local_def_map},
 };
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -170,7 +170,19 @@ pub fn lang_item(
     {
         return Some(target);
     }
-    start_crate.data(db).dependencies.iter().find_map(|dep| lang_item(db, dep.crate_id, item))
+
+    // Our `CrateGraph` eagerly inserts sysroot dependencies like `core` or `std` into dependencies
+    // even if the target crate has `#![no_std]`, `#![no_core]` or shadowed sysroot dependencies
+    // like `dependencies.std.path = ".."`. So we use `extern_prelude()` instead of
+    // `CrateData.dependencies` here, which has already come through such sysroot complexities
+    // while nameres.
+    //
+    // See https://github.com/rust-lang/rust-analyzer/pull/20475 for details.
+    crate_local_def_map(db, start_crate).local(db).extern_prelude().find_map(|(_, (krate, _))| {
+        // Some crates declares themselves as extern crate like `extern crate self as core`.
+        // Ignore these to prevent cycles.
+        if krate.krate == start_crate { None } else { lang_item(db, krate.krate, item) }
+    })
 }
 
 #[derive(Default, Debug, Clone, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 5030585147d..bf6fc15b7d1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -545,6 +545,10 @@ impl DefMap {
         self.data.no_std || self.data.no_core
     }
 
+    pub fn is_no_core(&self) -> bool {
+        self.data.no_core
+    }
+
     pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
         self.data.fn_proc_macro_mapping.get(&id).copied()
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index 07e61718222..0f84728dcb4 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -27,10 +27,11 @@ use triomphe::Arc;
 
 use crate::{
     AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc,
-    ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
-    LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId,
-    MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc,
-    StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc,
+    ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern,
+    ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
+    MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
+    ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
+    UseLoc,
     attr::Attrs,
     db::DefDatabase,
     item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
@@ -69,7 +70,7 @@ pub(super) fn collect_defs(
 
     // populate external prelude and dependency list
     let mut deps =
-        FxHashMap::with_capacity_and_hasher(krate.dependencies.len(), Default::default());
+        FxIndexMap::with_capacity_and_hasher(krate.dependencies.len(), Default::default());
     for dep in &krate.dependencies {
         tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
 
@@ -220,7 +221,7 @@ struct DefCollector<'db> {
     /// Set only in case of blocks.
     crate_local_def_map: Option<&'db LocalDefMap>,
     // The dependencies of the current crate, including optional deps like `test`.
-    deps: FxHashMap<Name, BuiltDependency>,
+    deps: FxIndexMap<Name, BuiltDependency>,
     glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
     unresolved_imports: Vec<ImportDirective>,
     indeterminate_imports: Vec<(ImportDirective, PerNs)>,
@@ -332,7 +333,9 @@ impl<'db> DefCollector<'db> {
                 let skip = dep.is_sysroot()
                     && match dep.crate_id.data(self.db).origin {
                         CrateOrigin::Lang(LangCrateOrigin::Core) => crate_data.no_core,
-                        CrateOrigin::Lang(LangCrateOrigin::Std) => crate_data.no_std,
+                        CrateOrigin::Lang(LangCrateOrigin::Std) => {
+                            crate_data.no_core || crate_data.no_std
+                        }
                         _ => false,
                     };
                 if skip {
@@ -2550,7 +2553,7 @@ mod tests {
             def_map,
             local_def_map: LocalDefMap::default(),
             crate_local_def_map: None,
-            deps: FxHashMap::default(),
+            deps: FxIndexMap::default(),
             glob_imports: FxHashMap::default(),
             unresolved_imports: Vec::new(),
             indeterminate_imports: Vec::new(),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
index 25061e1dbdb..212fec4a4e4 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs
@@ -2388,3 +2388,54 @@ pub trait Destruct {}
 "#,
     );
 }
+
+#[test]
+fn no_duplicated_lang_item_metadata() {
+    check_types(
+        r#"
+//- minicore: pointee
+//- /main.rs crate:main deps:std,core
+use std::AtomicPtr;
+use std::null_mut;
+
+fn main() {
+    let x: AtomicPtr<()> = AtomicPtr::new(null_mut());
+      //^ AtomicPtr<()>
+}
+
+//- /lib.rs crate:r#std deps:core
+#![no_std]
+pub use core::*;
+
+//- /lib.rs crate:r#core
+#![no_core]
+
+#[lang = "pointee_trait"]
+pub trait Pointee {
+    #[lang = "metadata_type"]
+    type Metadata;
+}
+
+pub struct AtomicPtr<T>(T);
+
+impl<T> AtomicPtr<T> {
+    pub fn new(p: *mut T) -> AtomicPtr<T> {
+        loop {}
+    }
+}
+
+#[lang = "pointee_sized"]
+pub trait PointeeSized {}
+#[lang = "meta_sized"]
+pub trait MetaSized: PointeeSized {}
+#[lang = "sized"]
+pub trait Sized: MetaSized {}
+
+pub trait Thin = Pointee<Metadata = ()> + PointeeSized;
+
+pub fn null_mut<T: PointeeSized + Thin>() -> *mut T {
+    loop {}
+}
+"#,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index 1f074de4cd7..9d2474d91da 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -66,13 +66,9 @@ pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
 pub use ::line_index;
 
 /// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience.
-pub use base_db;
+pub use base_db::{self, FxIndexMap, FxIndexSet};
 pub use span::{self, FileId};
 
-pub type FxIndexSet<T> = indexmap::IndexSet<T, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
-pub type FxIndexMap<K, V> =
-    indexmap::IndexMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
-
 pub type FilePosition = FilePositionWrapper<FileId>;
 pub type FileRange = FileRangeWrapper<FileId>;
 
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 4413d2f222c..57fca70547b 100644
--- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs
@@ -3,8 +3,8 @@ use std::{any::TypeId, mem, str::FromStr, sync};
 
 use base_db::{
     Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
-    DependencyBuilder, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabase, SourceRoot,
-    Version, VfsPath, salsa,
+    DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
+    SourceRoot, Version, VfsPath, salsa,
 };
 use cfg::CfgOptions;
 use hir_expand::{
@@ -20,7 +20,6 @@ use hir_expand::{
 };
 use intern::{Symbol, sym};
 use paths::AbsPathBuf;
-use rustc_hash::FxHashMap;
 use span::{Edition, FileId, Span};
 use stdx::itertools::Itertools;
 use test_utils::{
@@ -147,7 +146,7 @@ impl ChangeFixture {
 
         let mut files = Vec::new();
         let mut crate_graph = CrateGraphBuilder::default();
-        let mut crates = FxHashMap::default();
+        let mut crates = FxIndexMap::default();
         let mut crate_deps = Vec::new();
         let mut default_crate_root: Option<FileId> = None;
         let mut default_edition = Edition::CURRENT;
@@ -249,37 +248,7 @@ impl ChangeFixture {
             file_id = FileId::from_raw(file_id.index() + 1);
         }
 
-        if crates.is_empty() {
-            let crate_root = default_crate_root
-                .expect("missing default crate root, specify a main.rs or lib.rs");
-            crate_graph.add_crate_root(
-                crate_root,
-                default_edition,
-                Some(CrateName::new("ra_test_fixture").unwrap().into()),
-                None,
-                default_cfg.clone(),
-                Some(default_cfg),
-                default_env,
-                CrateOrigin::Local { repo: None, name: None },
-                false,
-                proc_macro_cwd.clone(),
-                crate_ws_data.clone(),
-            );
-        } else {
-            for (from, to, prelude) in crate_deps {
-                let from_id = crates[&from];
-                let to_id = crates[&to];
-                let sysroot = crate_graph[to_id].basic.origin.is_lang();
-                crate_graph
-                    .add_dep(
-                        from_id,
-                        DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot),
-                    )
-                    .unwrap();
-            }
-        }
-
-        if let Some(mini_core) = mini_core {
+        let mini_core = mini_core.map(|mini_core| {
             let core_file = file_id;
             file_id = FileId::from_raw(file_id.index() + 1);
 
@@ -289,8 +258,6 @@ impl ChangeFixture {
 
             source_change.change_file(core_file, Some(mini_core.source_code()));
 
-            let all_crates = crate_graph.iter().collect::<Vec<_>>();
-
             let core_crate = crate_graph.add_crate_root(
                 core_file,
                 Edition::CURRENT,
@@ -308,16 +275,58 @@ impl ChangeFixture {
                 crate_ws_data.clone(),
             );
 
-            for krate in all_crates {
+            (
+                move || {
+                    DependencyBuilder::with_prelude(
+                        CrateName::new("core").unwrap(),
+                        core_crate,
+                        true,
+                        true,
+                    )
+                },
+                core_crate,
+            )
+        });
+
+        if crates.is_empty() {
+            let crate_root = default_crate_root
+                .expect("missing default crate root, specify a main.rs or lib.rs");
+            let root = crate_graph.add_crate_root(
+                crate_root,
+                default_edition,
+                Some(CrateName::new("ra_test_fixture").unwrap().into()),
+                None,
+                default_cfg.clone(),
+                Some(default_cfg),
+                default_env,
+                CrateOrigin::Local { repo: None, name: None },
+                false,
+                proc_macro_cwd.clone(),
+                crate_ws_data.clone(),
+            );
+            if let Some((mini_core, _)) = mini_core {
+                crate_graph.add_dep(root, mini_core()).unwrap();
+            }
+        } else {
+            // Insert minicore first to match with `project-model::workspace`
+            if let Some((mini_core, core_crate)) = mini_core {
+                let all_crates = crate_graph.iter().collect::<Vec<_>>();
+                for krate in all_crates {
+                    if krate == core_crate {
+                        continue;
+                    }
+                    crate_graph.add_dep(krate, mini_core()).unwrap();
+                }
+            }
+
+            for (from, to, prelude) in crate_deps {
+                let from_id = crates[&from];
+                let to_id = crates[&to];
+                let sysroot = crate_graph[to_id].basic.origin.is_lang();
                 crate_graph
                     .add_dep(
-                        krate,
-                        DependencyBuilder::with_prelude(
-                            CrateName::new("core").unwrap(),
-                            core_crate,
-                            true,
-                            true,
-                        ),
+                        from_id,
+                        DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot),
                     )
                     .unwrap();
             }
@@ -627,11 +636,23 @@ impl FileMeta {
     }
 }
 
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ForceNoneLangOrigin {
+    Yes,
+    No,
+}
+
 fn parse_crate(
     crate_str: String,
     current_source_root_kind: SourceRootKind,
     explicit_non_workspace_member: bool,
 ) -> (String, CrateOrigin, Option<String>) {
+    let (crate_str, force_non_lang_origin) = if let Some(s) = crate_str.strip_prefix("r#") {
+        (s.to_owned(), ForceNoneLangOrigin::Yes)
+    } else {
+        (crate_str, ForceNoneLangOrigin::No)
+    };
+
     // syntax:
     //   "my_awesome_crate"
     //   "my_awesome_crate@0.0.1,http://example.com"
@@ -646,16 +667,25 @@ fn parse_crate(
     let non_workspace_member = explicit_non_workspace_member
         || matches!(current_source_root_kind, SourceRootKind::Library);
 
-    let origin = match LangCrateOrigin::from(&*name) {
-        LangCrateOrigin::Other => {
-            let name = Symbol::intern(&name);
-            if non_workspace_member {
-                CrateOrigin::Library { repo, name }
-            } else {
-                CrateOrigin::Local { repo, name: Some(name) }
+    let origin = if force_non_lang_origin == ForceNoneLangOrigin::Yes {
+        let name = Symbol::intern(&name);
+        if non_workspace_member {
+            CrateOrigin::Library { repo, name }
+        } else {
+            CrateOrigin::Local { repo, name: Some(name) }
+        }
+    } else {
+        match LangCrateOrigin::from(&*name) {
+            LangCrateOrigin::Other => {
+                let name = Symbol::intern(&name);
+                if non_workspace_member {
+                    CrateOrigin::Library { repo, name }
+                } else {
+                    CrateOrigin::Local { repo, name: Some(name) }
+                }
             }
+            origin => CrateOrigin::Lang(origin),
         }
-        origin => CrateOrigin::Lang(origin),
     };
 
     (name, origin, version)