Merge pull request #20475 from ShoyuVanilla/analysis-std-panic

fix: Make lang items query properly filter out overwritten/excluded sysroots
This commit is contained in:
Chayim Refael Friedman 2025-08-17 13:46:37 +00:00 committed by GitHub
commit 484db3a517
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 167 additions and 69 deletions

View File

@ -30,6 +30,8 @@ use triomphe::Arc;
pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet}; pub use vfs::{AnchoredPath, AnchoredPathBuf, FileId, VfsPath, file_set::FileSet};
pub type FxIndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>; 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_export]
macro_rules! impl_intern_key { macro_rules! impl_intern_key {

View File

@ -12,7 +12,7 @@ use crate::{
StaticId, StructId, TraitId, TypeAliasId, UnionId, StaticId, StructId, TraitId, TypeAliasId, UnionId,
db::DefDatabase, db::DefDatabase,
expr_store::path::Path, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -170,7 +170,19 @@ pub fn lang_item(
{ {
return Some(target); 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)] #[derive(Default, Debug, Clone, PartialEq, Eq)]

View File

@ -545,6 +545,10 @@ impl DefMap {
self.data.no_std || self.data.no_core 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> { pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option<ProcMacroId> {
self.data.fn_proc_macro_mapping.get(&id).copied() self.data.fn_proc_macro_mapping.get(&id).copied()
} }

View File

@ -27,10 +27,11 @@ use triomphe::Arc;
use crate::{ use crate::{
AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc,
ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, FxIndexMap, ImplLoc, Intern,
LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, ItemContainerId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId,
MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, MacroRulesId, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId,
StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId, UseLoc, ProcMacroLoc, StaticLoc, StructLoc, TraitLoc, TypeAliasLoc, UnionLoc, UnresolvedMacro, UseId,
UseLoc,
attr::Attrs, attr::Attrs,
db::DefDatabase, db::DefDatabase,
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
@ -69,7 +70,7 @@ pub(super) fn collect_defs(
// populate external prelude and dependency list // populate external prelude and dependency list
let mut deps = 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 { for dep in &krate.dependencies {
tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id); tracing::debug!("crate dep {:?} -> {:?}", dep.name, dep.crate_id);
@ -220,7 +221,7 @@ struct DefCollector<'db> {
/// Set only in case of blocks. /// Set only in case of blocks.
crate_local_def_map: Option<&'db LocalDefMap>, crate_local_def_map: Option<&'db LocalDefMap>,
// The dependencies of the current crate, including optional deps like `test`. // 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)>>, glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
unresolved_imports: Vec<ImportDirective>, unresolved_imports: Vec<ImportDirective>,
indeterminate_imports: Vec<(ImportDirective, PerNs)>, indeterminate_imports: Vec<(ImportDirective, PerNs)>,
@ -332,7 +333,9 @@ impl<'db> DefCollector<'db> {
let skip = dep.is_sysroot() let skip = dep.is_sysroot()
&& match dep.crate_id.data(self.db).origin { && match dep.crate_id.data(self.db).origin {
CrateOrigin::Lang(LangCrateOrigin::Core) => crate_data.no_core, 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, _ => false,
}; };
if skip { if skip {
@ -2550,7 +2553,7 @@ mod tests {
def_map, def_map,
local_def_map: LocalDefMap::default(), local_def_map: LocalDefMap::default(),
crate_local_def_map: None, crate_local_def_map: None,
deps: FxHashMap::default(), deps: FxIndexMap::default(),
glob_imports: FxHashMap::default(), glob_imports: FxHashMap::default(),
unresolved_imports: Vec::new(), unresolved_imports: Vec::new(),
indeterminate_imports: Vec::new(), indeterminate_imports: Vec::new(),

View File

@ -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 {}
}
"#,
);
}

View File

@ -66,13 +66,9 @@ pub use rustc_hash::{FxHashMap, FxHashSet, FxHasher};
pub use ::line_index; pub use ::line_index;
/// `base_db` is normally also needed in places where `ide_db` is used, so this re-export is for convenience. /// `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 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 FilePosition = FilePositionWrapper<FileId>;
pub type FileRange = FileRangeWrapper<FileId>; pub type FileRange = FileRangeWrapper<FileId>;

View File

@ -3,8 +3,8 @@ use std::{any::TypeId, mem, str::FromStr, sync};
use base_db::{ use base_db::{
Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData, Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
DependencyBuilder, Env, FileChange, FileSet, LangCrateOrigin, SourceDatabase, SourceRoot, DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
Version, VfsPath, salsa, SourceRoot, Version, VfsPath, salsa,
}; };
use cfg::CfgOptions; use cfg::CfgOptions;
use hir_expand::{ use hir_expand::{
@ -20,7 +20,6 @@ use hir_expand::{
}; };
use intern::{Symbol, sym}; use intern::{Symbol, sym};
use paths::AbsPathBuf; use paths::AbsPathBuf;
use rustc_hash::FxHashMap;
use span::{Edition, FileId, Span}; use span::{Edition, FileId, Span};
use stdx::itertools::Itertools; use stdx::itertools::Itertools;
use test_utils::{ use test_utils::{
@ -147,7 +146,7 @@ impl ChangeFixture {
let mut files = Vec::new(); let mut files = Vec::new();
let mut crate_graph = CrateGraphBuilder::default(); let mut crate_graph = CrateGraphBuilder::default();
let mut crates = FxHashMap::default(); let mut crates = FxIndexMap::default();
let mut crate_deps = Vec::new(); let mut crate_deps = Vec::new();
let mut default_crate_root: Option<FileId> = None; let mut default_crate_root: Option<FileId> = None;
let mut default_edition = Edition::CURRENT; let mut default_edition = Edition::CURRENT;
@ -249,37 +248,7 @@ impl ChangeFixture {
file_id = FileId::from_raw(file_id.index() + 1); file_id = FileId::from_raw(file_id.index() + 1);
} }
if crates.is_empty() { let mini_core = mini_core.map(|mini_core| {
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 core_file = file_id; let core_file = file_id;
file_id = FileId::from_raw(file_id.index() + 1); 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())); 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( let core_crate = crate_graph.add_crate_root(
core_file, core_file,
Edition::CURRENT, Edition::CURRENT,
@ -308,16 +275,58 @@ impl ChangeFixture {
crate_ws_data.clone(), 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 crate_graph
.add_dep( .add_dep(
krate, from_id,
DependencyBuilder::with_prelude( DependencyBuilder::with_prelude(to.clone(), to_id, prelude, sysroot),
CrateName::new("core").unwrap(),
core_crate,
true,
true,
),
) )
.unwrap(); .unwrap();
} }
@ -627,11 +636,23 @@ impl FileMeta {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ForceNoneLangOrigin {
Yes,
No,
}
fn parse_crate( fn parse_crate(
crate_str: String, crate_str: String,
current_source_root_kind: SourceRootKind, current_source_root_kind: SourceRootKind,
explicit_non_workspace_member: bool, explicit_non_workspace_member: bool,
) -> (String, CrateOrigin, Option<String>) { ) -> (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: // syntax:
// "my_awesome_crate" // "my_awesome_crate"
// "my_awesome_crate@0.0.1,http://example.com" // "my_awesome_crate@0.0.1,http://example.com"
@ -646,16 +667,25 @@ fn parse_crate(
let non_workspace_member = explicit_non_workspace_member let non_workspace_member = explicit_non_workspace_member
|| matches!(current_source_root_kind, SourceRootKind::Library); || matches!(current_source_root_kind, SourceRootKind::Library);
let origin = match LangCrateOrigin::from(&*name) { let origin = if force_non_lang_origin == ForceNoneLangOrigin::Yes {
LangCrateOrigin::Other => { let name = Symbol::intern(&name);
let name = Symbol::intern(&name); if non_workspace_member {
if non_workspace_member { CrateOrigin::Library { repo, name }
CrateOrigin::Library { repo, name } } else {
} else { CrateOrigin::Local { repo, name: Some(name) }
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) (name, origin, version)