Auto merge of #12041 - jonas-schievink:prefer-core-cfg-attr-no-std, r=jonas-schievink
feat: prefer core/alloc over std in auto-imports if `#[no_std]` is conditional We already did this if `#![no_std]` was present, this PR makes it work with `#![cfg_attr(not(test), no_std)]` too, which is very common in libraries. Fixes https://github.com/rust-lang/rust-analyzer/issues/12035 cc https://github.com/rust-lang/rust-analyzer/issues/10718
This commit is contained in:
commit
3bda70a232
@ -18,7 +18,7 @@ use crate::{
|
|||||||
generics::GenericParams,
|
generics::GenericParams,
|
||||||
import_map::ImportMap,
|
import_map::ImportMap,
|
||||||
intern::Interned,
|
intern::Interned,
|
||||||
item_tree::ItemTree,
|
item_tree::{AttrOwner, ItemTree},
|
||||||
lang_item::{LangItemTarget, LangItems},
|
lang_item::{LangItemTarget, LangItems},
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
visibility::{self, Visibility},
|
visibility::{self, Visibility},
|
||||||
@ -184,6 +184,8 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
|
|||||||
|
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
|
fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
|
||||||
|
|
||||||
|
fn crate_supports_no_std(&self, crate_id: CrateId) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
|
fn crate_def_map_wait(db: &dyn DefDatabase, krate: CrateId) -> Arc<DefMap> {
|
||||||
@ -204,3 +206,38 @@ fn crate_limits(db: &dyn DefDatabase, crate_id: CrateId) -> CrateLimits {
|
|||||||
recursion_limit: def_map.recursion_limit().unwrap_or(128),
|
recursion_limit: def_map.recursion_limit().unwrap_or(128),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn crate_supports_no_std(db: &dyn DefDatabase, crate_id: CrateId) -> bool {
|
||||||
|
let file = db.crate_graph()[crate_id].root_file_id;
|
||||||
|
let item_tree = db.file_item_tree(file.into());
|
||||||
|
let attrs = item_tree.raw_attrs(AttrOwner::TopLevel);
|
||||||
|
for attr in &**attrs {
|
||||||
|
match attr.path().as_ident().and_then(|id| id.as_text()) {
|
||||||
|
Some(ident) if ident == "no_std" => return true,
|
||||||
|
Some(ident) if ident == "cfg_attr" => {}
|
||||||
|
_ => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a `cfg_attr`; check if it could possibly expand to `no_std`.
|
||||||
|
// Syntax is: `#[cfg_attr(condition(cfg, style), attr0, attr1, <...>)]`
|
||||||
|
let tt = match attr.token_tree_value() {
|
||||||
|
Some(tt) => &tt.token_trees,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
let segments = tt.split(|tt| match tt {
|
||||||
|
tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => true,
|
||||||
|
_ => false,
|
||||||
|
});
|
||||||
|
for output in segments.skip(1) {
|
||||||
|
match output {
|
||||||
|
[tt::TokenTree::Leaf(tt::Leaf::Ident(ident))] if ident.text == "no_std" => {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
@ -43,8 +43,7 @@ impl ModPathExt for ModPath {
|
|||||||
self.segments().first() == Some(&known::std)
|
self.segments().first() == Some(&known::std)
|
||||||
}
|
}
|
||||||
|
|
||||||
// When std library is present, paths starting with `std::`
|
// Can we replace the first segment with `std::` and still get a valid, identical path?
|
||||||
// should be preferred over paths starting with `core::` and `alloc::`
|
|
||||||
fn can_start_with_std(&self) -> bool {
|
fn can_start_with_std(&self) -> bool {
|
||||||
let first_segment = self.segments().first();
|
let first_segment = self.segments().first();
|
||||||
first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
|
first_segment == Some(&known::alloc) || first_segment == Some(&known::core)
|
||||||
@ -203,7 +202,7 @@ fn find_path_inner_(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// - otherwise, look for modules containing (reexporting) it and import it from one of those
|
// - otherwise, look for modules containing (reexporting) it and import it from one of those
|
||||||
let prefer_no_std = db.attrs(crate_root.into()).by_key("no_std").exists();
|
let prefer_no_std = db.crate_supports_no_std(crate_root.krate);
|
||||||
let mut best_path = None;
|
let mut best_path = None;
|
||||||
let mut best_path_len = max_len;
|
let mut best_path_len = max_len;
|
||||||
|
|
||||||
@ -830,6 +829,32 @@ pub mod fmt {
|
|||||||
|
|
||||||
//- /zzz.rs crate:core
|
//- /zzz.rs crate:core
|
||||||
|
|
||||||
|
pub mod fmt {
|
||||||
|
pub struct Error;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"core::fmt::Error",
|
||||||
|
"core::fmt::Error",
|
||||||
|
"core::fmt::Error",
|
||||||
|
"core::fmt::Error",
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should also work (on a best-effort basis) if `no_std` is conditional.
|
||||||
|
check_found_path(
|
||||||
|
r#"
|
||||||
|
//- /main.rs crate:main deps:core,std
|
||||||
|
#![cfg_attr(not(test), no_std)]
|
||||||
|
|
||||||
|
$0
|
||||||
|
|
||||||
|
//- /std.rs crate:std deps:core
|
||||||
|
|
||||||
|
pub mod fmt {
|
||||||
|
pub use core::fmt::Error;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- /zzz.rs crate:core
|
||||||
|
|
||||||
pub mod fmt {
|
pub mod fmt {
|
||||||
pub struct Error;
|
pub struct Error;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user