Merge pull request #20652 from ChayimFriedman2/cli-diags
internal: Improve `rust-analyzer diagnostics`
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 0bf4fbd..cac7477 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -295,8 +295,6 @@
}
}
-pub type TargetLayoutLoadResult = Result<Arc<str>, Arc<str>>;
-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ReleaseChannel {
Stable,
@@ -929,7 +927,7 @@
use super::{CrateGraphBuilder, CrateName, CrateOrigin, Edition::Edition2018, Env, FileId};
fn empty_ws_data() -> Arc<CrateWorkspaceData> {
- Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None })
+ Arc::new(CrateWorkspaceData { target: Err("".into()), toolchain: None })
}
#[test]
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 14544ac..0e411bc 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -6,6 +6,7 @@
// FIXME: Rename this crate, base db is non descriptive
mod change;
mod input;
+pub mod target;
use std::{
cell::RefCell,
@@ -20,8 +21,7 @@
BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder,
CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap,
DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroLoadingError,
- ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
- UniqueCrateData,
+ ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, UniqueCrateData,
},
};
use dashmap::{DashMap, mapref::entry::Entry};
@@ -359,8 +359,7 @@
/// Crate related data shared by the whole workspace.
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct CrateWorkspaceData {
- // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query
- pub data_layout: TargetLayoutLoadResult,
+ pub target: Result<target::TargetData, target::TargetLoadError>,
/// Toolchain version used to compile the crate.
pub toolchain: Option<Version>,
}
diff --git a/crates/base-db/src/target.rs b/crates/base-db/src/target.rs
new file mode 100644
index 0000000..19d3407
--- /dev/null
+++ b/crates/base-db/src/target.rs
@@ -0,0 +1,50 @@
+//! Information about the target.
+
+use std::fmt;
+
+use triomphe::Arc;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum Arch {
+ // Only what we need is present here.
+ Wasm32,
+ Wasm64,
+ Other,
+}
+
+#[derive(Debug, PartialEq, Eq, Hash, Clone)]
+pub struct TargetData {
+ pub data_layout: Box<str>,
+ pub arch: Arch,
+}
+
+#[derive(Clone, PartialEq, Eq, Hash)]
+pub struct TargetLoadError(Arc<str>);
+
+impl fmt::Debug for TargetLoadError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Debug::fmt(&self.0, f)
+ }
+}
+
+impl fmt::Display for TargetLoadError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+impl std::error::Error for TargetLoadError {}
+
+impl From<String> for TargetLoadError {
+ fn from(value: String) -> Self {
+ Self(value.into())
+ }
+}
+
+impl From<&str> for TargetLoadError {
+ fn from(value: &str) -> Self {
+ Self(value.into())
+ }
+}
+
+pub type TargetLoadResult = Result<TargetData, TargetLoadError>;
diff --git a/crates/hir-def/src/nameres/tests/incremental.rs b/crates/hir-def/src/nameres/tests/incremental.rs
index 338851b..6afa04b 100644
--- a/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/crates/hir-def/src/nameres/tests/incremental.rs
@@ -84,7 +84,7 @@
)
.unwrap(),
),
- Arc::new(CrateWorkspaceData { data_layout: Err("".into()), toolchain: None }),
+ Arc::new(CrateWorkspaceData { target: Err("".into()), toolchain: None }),
)
};
let a = add_crate("a", 0);
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs
index 0b7213d..0aec2b9 100644
--- a/crates/hir-ty/src/db.rs
+++ b/crates/hir-ty/src/db.rs
@@ -2,6 +2,7 @@
//! type inference-related queries.
use base_db::Crate;
+use base_db::target::TargetLoadError;
use hir_def::{
AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId,
GeneralConstId, GenericDefId, ImplId, LifetimeParamId, LocalFieldId, StaticId, TraitId,
@@ -108,7 +109,7 @@
) -> Result<Arc<Layout>, LayoutError>;
#[salsa::invoke(crate::layout::target_data_layout_query)]
- fn target_data_layout(&self, krate: Crate) -> Result<Arc<TargetDataLayout>, Arc<str>>;
+ fn target_data_layout(&self, krate: Crate) -> Result<Arc<TargetDataLayout>, TargetLoadError>;
#[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
diff --git a/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d6d6692..64c4cde 100644
--- a/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -15,8 +15,9 @@
use span::Edition;
use crate::{
- InferenceResult, Interner, TargetFeatures, TyExt, TyKind, db::HirDatabase,
- utils::is_fn_unsafe_to_call,
+ InferenceResult, Interner, TargetFeatures, TyExt, TyKind,
+ db::HirDatabase,
+ utils::{is_fn_unsafe_to_call, target_feature_is_safe_in_target},
};
#[derive(Debug, Default)]
@@ -144,6 +145,9 @@
def_target_features: TargetFeatures,
// FIXME: This needs to be the edition of the span of each call.
edition: Edition,
+ /// On some targets (WASM), calling safe functions with `#[target_feature]` is always safe, even when
+ /// the target feature is not enabled. This flag encodes that.
+ target_feature_is_safe: bool,
}
impl<'db> UnsafeVisitor<'db> {
@@ -159,7 +163,12 @@
DefWithBodyId::FunctionId(func) => TargetFeatures::from_attrs(&db.attrs(func.into())),
_ => TargetFeatures::default(),
};
- let edition = resolver.module().krate().data(db).edition;
+ let krate = resolver.module().krate();
+ let edition = krate.data(db).edition;
+ let target_feature_is_safe = match &krate.workspace_data(db).target {
+ Ok(target) => target_feature_is_safe_in_target(target),
+ Err(_) => false,
+ };
Self {
db,
infer,
@@ -172,6 +181,7 @@
callback: unsafe_expr_cb,
def_target_features,
edition,
+ target_feature_is_safe,
}
}
@@ -184,7 +194,13 @@
}
fn check_call(&mut self, node: ExprId, func: FunctionId) {
- let unsafety = is_fn_unsafe_to_call(self.db, func, &self.def_target_features, self.edition);
+ let unsafety = is_fn_unsafe_to_call(
+ self.db,
+ func,
+ &self.def_target_features,
+ self.edition,
+ self.target_feature_is_safe,
+ );
match unsafety {
crate::utils::Unsafety::Safe => {}
crate::utils::Unsafety::Unsafe => {
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs
index d4b3751..b87c998 100644
--- a/crates/hir-ty/src/dyn_compatibility.rs
+++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -138,6 +138,9 @@
let interner = DbInterner::new_with(db, Some(krate), None);
let predicates = db.generic_predicates_ns(def);
+ // FIXME: We should use `explicit_predicates_of` here, which hasn't been implemented to
+ // rust-analyzer yet
+ // https://github.com/rust-lang/rust/blob/ddaf12390d3ffb7d5ba74491a48f3cd528e5d777/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L490
elaborate::elaborate(interner, predicates.iter().copied()).any(|pred| {
match pred.kind().skip_binder() {
ClauseKind::Trait(trait_pred) => {
diff --git a/crates/hir-ty/src/dyn_compatibility/tests.rs b/crates/hir-ty/src/dyn_compatibility/tests.rs
index 4ffa455..04a9ba7 100644
--- a/crates/hir-ty/src/dyn_compatibility/tests.rs
+++ b/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -253,7 +253,8 @@
trait Baz : Bar<Self> {
}
"#,
- [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])],
+ // FIXME: We should also report `SizedSelf` here
+ [("Bar", vec![]), ("Baz", vec![SelfReferential])],
);
}
diff --git a/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs
index 82d0ed4..8a7d93d 100644
--- a/crates/hir-ty/src/layout/target.rs
+++ b/crates/hir-ty/src/layout/target.rs
@@ -1,6 +1,6 @@
//! Target dependent parameters needed for layouts
-use base_db::Crate;
+use base_db::{Crate, target::TargetLoadError};
use hir_def::layout::TargetDataLayout;
use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors};
use triomphe::Arc;
@@ -10,9 +10,9 @@
pub fn target_data_layout_query(
db: &dyn HirDatabase,
krate: Crate,
-) -> Result<Arc<TargetDataLayout>, Arc<str>> {
- match &krate.workspace_data(db).data_layout {
- Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it, AddressSpace::ZERO) {
+) -> Result<Arc<TargetDataLayout>, TargetLoadError> {
+ match &krate.workspace_data(db).target {
+ Ok(target) => match TargetDataLayout::parse_from_llvm_datalayout_string(&target.data_layout, AddressSpace::ZERO) {
Ok(it) => Ok(Arc::new(it)),
Err(e) => {
Err(match e {
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs
index 90de7e5..523ddad 100644
--- a/crates/hir-ty/src/layout/tests.rs
+++ b/crates/hir-ty/src/layout/tests.rs
@@ -1,3 +1,4 @@
+use base_db::target::TargetData;
use chalk_ir::{AdtId, TyKind};
use either::Either;
use hir_def::db::DefDatabase;
@@ -18,8 +19,8 @@
mod closure;
-fn current_machine_data_layout() -> String {
- project_model::toolchain_info::target_data_layout::get(
+fn current_machine_target_data() -> TargetData {
+ project_model::toolchain_info::target_data::get(
QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()),
None,
&FxHashMap::default(),
@@ -32,7 +33,8 @@
minicore: &str,
) -> Result<Arc<Layout>, LayoutError> {
let _tracing = setup_tracing();
- let target_data_layout = current_machine_data_layout();
+ let target_data = current_machine_target_data();
+ let target_data_layout = target_data.data_layout;
let ra_fixture = format!(
"//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\n{ra_fixture}",
);
@@ -104,7 +106,8 @@
minicore: &str,
) -> Result<Arc<Layout>, LayoutError> {
let _tracing = setup_tracing();
- let target_data_layout = current_machine_data_layout();
+ let target_data = current_machine_target_data();
+ let target_data_layout = target_data.data_layout;
let ra_fixture = format!(
"//- target_data_layout: {target_data_layout}\n{minicore}//- /main.rs crate:test\nfn main(){{let goal = {{{ra_fixture}}};}}",
);
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs
index 659bc5a..02fdbf0 100644
--- a/crates/hir-ty/src/lib.rs
+++ b/crates/hir-ty/src/lib.rs
@@ -132,7 +132,10 @@
pub use method_resolution::check_orphan_rules;
pub use target_feature::TargetFeatures;
pub use traits::TraitEnvironment;
-pub use utils::{Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call};
+pub use utils::{
+ Unsafety, all_super_traits, direct_super_traits, is_fn_unsafe_to_call,
+ target_feature_is_safe_in_target,
+};
pub use variance::Variance;
pub use chalk_ir::{
diff --git a/crates/hir-ty/src/lower_nextsolver.rs b/crates/hir-ty/src/lower_nextsolver.rs
index c6a8fa8..5c29bef 100644
--- a/crates/hir-ty/src/lower_nextsolver.rs
+++ b/crates/hir-ty/src/lower_nextsolver.rs
@@ -1358,6 +1358,9 @@
}
}
+ // FIXME: rustc gathers more predicates by recursing through resulting trait predicates.
+ // See https://github.com/rust-lang/rust/blob/76c5ed2847cdb26ef2822a3a165d710f6b772217/compiler/rustc_hir_analysis/src/collect/predicates_of.rs#L689-L715
+
(
GenericPredicates(predicates.is_empty().not().then(|| predicates.into())),
create_diagnostics(ctx.diagnostics),
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index ca7467d..cddd1fb 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -3,6 +3,7 @@
use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range};
use base_db::Crate;
+use base_db::target::TargetLoadError;
use chalk_ir::{Mutability, cast::Cast};
use either::Either;
use hir_def::{
@@ -337,7 +338,7 @@
pub enum MirEvalError {
ConstEvalError(String, Box<ConstEvalError>),
LayoutError(LayoutError, Ty),
- TargetDataLayoutNotAvailable(Arc<str>),
+ TargetDataLayoutNotAvailable(TargetLoadError),
/// Means that code had undefined behavior. We don't try to actively detect UB, but if it was detected
/// then use this type of error.
UndefinedBehavior(String),
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs
index 0f512fd..7b6e8a1 100644
--- a/crates/hir-ty/src/next_solver/interner.rs
+++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -5,7 +5,7 @@
use chalk_ir::{ProgramClauseImplication, SeparatorTraitRef, Variances};
use hir_def::lang_item::LangItem;
use hir_def::signatures::{FieldData, FnFlags, ImplFlags, StructFlags, TraitFlags};
-use hir_def::{AdtId, BlockId, TypeAliasId, VariantId};
+use hir_def::{AdtId, BlockId, GenericDefId, TypeAliasId, VariantId};
use hir_def::{AttrDefId, Lookup};
use hir_def::{CallableDefId, EnumVariantId, ItemContainerId, StructId, UnionId};
use intern::sym::non_exhaustive;
@@ -1334,6 +1334,13 @@
.db()
.generic_predicates_ns(def_id.0.into())
.iter()
+ .filter(|p| match p.kind().skip_binder() {
+ rustc_type_ir::ClauseKind::Trait(tr) => match tr.self_ty().kind() {
+ rustc_type_ir::TyKind::Param(param) => param.index == 0,
+ _ => false,
+ },
+ _ => true,
+ })
.cloned()
.map(|p| (p, Span::dummy()))
.collect();
@@ -1345,10 +1352,24 @@
self,
def_id: Self::DefId,
) -> EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>> {
+ fn is_self_or_assoc(ty: Ty<'_>) -> bool {
+ match ty.kind() {
+ rustc_type_ir::TyKind::Param(param) => param.index == 0,
+ rustc_type_ir::TyKind::Alias(rustc_type_ir::AliasTyKind::Projection, alias) => {
+ is_self_or_assoc(alias.self_ty())
+ }
+ _ => false,
+ }
+ }
+
let predicates: Vec<(Clause<'db>, Span)> = self
.db()
.generic_predicates_ns(def_id.try_into().unwrap())
.iter()
+ .filter(|p| match p.kind().skip_binder() {
+ rustc_type_ir::ClauseKind::Trait(tr) => is_self_or_assoc(tr.self_ty()),
+ _ => true,
+ })
.cloned()
.map(|p| (p, Span::dummy()))
.collect();
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs
index 4df7886..f559230 100644
--- a/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -3,6 +3,132 @@
use crate::tests::{check_infer, check_no_mismatches};
#[test]
+fn regression_20365() {
+ check_infer(
+ r#"
+//- minicore: iterator
+struct Vec<T>(T);
+struct IntoIter<T>(T);
+impl<T> IntoIterator for Vec<T> {
+ type IntoIter = IntoIter<T>;
+ type Item = T;
+}
+impl<T> Iterator for IntoIter<T> {
+ type Item = T;
+}
+
+fn f<T: Space>(a: Vec<u8>) {
+ let iter = a.into_iter();
+}
+
+pub trait Space: IntoIterator {
+ type Ty: Space;
+}
+impl Space for [u8; 1] {
+ type Ty = Self;
+}
+ "#,
+ expect![[r#"
+ 201..202 'a': Vec<u8>
+ 213..246 '{ ...r(); }': ()
+ 223..227 'iter': IntoIter<u8>
+ 230..231 'a': Vec<u8>
+ 230..243 'a.into_iter()': IntoIter<u8>
+ "#]],
+ );
+}
+
+#[test]
+fn regression_19971() {
+ check_infer(
+ r#"
+//- minicore: pointee
+fn make<T>(_thin: *const (), _meta: core::ptr::DynMetadata<T>) -> *const T
+where
+ T: core::ptr::Pointee<Metadata = core::ptr::DynMetadata<T>> + ?Sized,
+{
+ loop {}
+}
+trait Foo {
+ fn foo(&self) -> i32 {
+ loop {}
+ }
+}
+
+fn test() -> i32 {
+ struct F {}
+ impl Foo for F {}
+ let meta = core::ptr::metadata(0 as *const F as *const dyn Foo);
+
+ let f = F {};
+ let fat_ptr = make(&f as *const F as *const (), meta); // <-- infers type as `*const {unknown}`
+
+ let fat_ref = unsafe { &*fat_ptr }; // <-- infers type as `&{unknown}`
+ fat_ref.foo() // cannot 'go to definition' on `foo`
+}
+
+ "#,
+ expect![[r#"
+ 11..16 '_thin': *const ()
+ 29..34 '_meta': DynMetadata<T>
+ 155..170 '{ loop {} }': *const T
+ 161..168 'loop {}': !
+ 166..168 '{}': ()
+ 195..199 'self': &'? Self
+ 208..231 '{ ... }': i32
+ 218..225 'loop {}': !
+ 223..225 '{}': ()
+ 252..613 '{ ...foo` }': i32
+ 300..304 'meta': DynMetadata<dyn Foo + '?>
+ 307..326 'core::...tadata': fn metadata<dyn Foo + '?>(*const (dyn Foo + '?)) -> <dyn Foo + '? as Pointee>::Metadata
+ 307..359 'core::...n Foo)': DynMetadata<dyn Foo + '?>
+ 327..328 '0': usize
+ 327..340 '0 as *const F': *const F
+ 327..358 '0 as *...yn Foo': *const (dyn Foo + '?)
+ 370..371 'f': F
+ 374..378 'F {}': F
+ 388..395 'fat_ptr': *const (dyn Foo + '?)
+ 398..402 'make': fn make<dyn Foo + '?>(*const (), DynMetadata<dyn Foo + '?>) -> *const (dyn Foo + '?)
+ 398..437 'make(&... meta)': *const (dyn Foo + '?)
+ 403..405 '&f': &'? F
+ 403..417 '&f as *const F': *const F
+ 403..430 '&f as ...nst ()': *const ()
+ 404..405 'f': F
+ 432..436 'meta': DynMetadata<dyn Foo + '?>
+ 489..496 'fat_ref': &'? (dyn Foo + '?)
+ 499..519 'unsafe..._ptr }': &'? (dyn Foo + '?)
+ 508..517 '&*fat_ptr': &'? (dyn Foo + '?)
+ 509..517 '*fat_ptr': dyn Foo + '?
+ 510..517 'fat_ptr': *const (dyn Foo + '?)
+ 560..567 'fat_ref': &'? (dyn Foo + '?)
+ 560..573 'fat_ref.foo()': i32
+ "#]],
+ );
+}
+
+#[test]
+fn regression_19752() {
+ check_no_mismatches(
+ r#"
+//- minicore: sized, copy
+trait T1<T: T2>: Sized + Copy {
+ fn a(self, other: Self) -> Self {
+ other
+ }
+
+ fn b(&mut self, other: Self) {
+ *self = self.a(other);
+ }
+}
+
+trait T2: Sized {
+ type T1: T1<Self>;
+}
+ "#,
+ );
+}
+
+#[test]
fn opaque_generics() {
check_infer(
r#"
@@ -116,3 +242,34 @@
"#]],
);
}
+
+#[test]
+fn no_infinite_loop_on_super_predicates_elaboration() {
+ check_infer(
+ r#"
+//- minicore: sized
+trait DimMax<Other: Dimension> {
+ type Output: Dimension;
+}
+
+trait Dimension: DimMax<<Self as Dimension>:: Smaller, Output = Self> {
+ type Smaller: Dimension;
+}
+
+fn test<T, U>(t: T)
+where
+ T: DimMax<U>,
+ U: Dimension,
+{
+ let t: <T as DimMax<U>>::Output = loop {};
+}
+"#,
+ expect![[r#"
+ 182..183 't': T
+ 230..280 '{ ... {}; }': ()
+ 240..241 't': <T as DimMax<U>>::Output
+ 270..277 'loop {}': !
+ 275..277 '{}': ()
+ "#]],
+ )
+}
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 881b1c1..07679d2 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -3,7 +3,10 @@
use std::{cell::LazyCell, iter};
-use base_db::Crate;
+use base_db::{
+ Crate,
+ target::{self, TargetData},
+};
use chalk_ir::{DebruijnIndex, fold::FallibleTypeFolder};
use hir_def::{
EnumId, EnumVariantId, FunctionId, Lookup, TraitId, TypeAliasId, TypeOrConstParamId,
@@ -275,18 +278,23 @@
DeprecatedSafe2024,
}
+pub fn target_feature_is_safe_in_target(target: &TargetData) -> bool {
+ matches!(target.arch, target::Arch::Wasm32 | target::Arch::Wasm64)
+}
+
pub fn is_fn_unsafe_to_call(
db: &dyn HirDatabase,
func: FunctionId,
caller_target_features: &TargetFeatures,
call_edition: Edition,
+ target_feature_is_safe: bool,
) -> Unsafety {
let data = db.function_signature(func);
if data.is_unsafe() {
return Unsafety::Unsafe;
}
- if data.has_target_feature() {
+ if data.has_target_feature() && !target_feature_is_safe {
// RFC 2396 <https://rust-lang.github.io/rfcs/2396-target-feature-1.1.html>.
let callee_target_features =
TargetFeatures::from_attrs_no_implications(&db.attrs(func.into()));
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 888392b..bd9360f 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2542,11 +2542,26 @@
caller: Option<Function>,
call_edition: Edition,
) -> bool {
- let target_features = caller
- .map(|caller| hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into())))
- .unwrap_or_default();
+ let (target_features, target_feature_is_safe_in_target) = caller
+ .map(|caller| {
+ let target_features =
+ hir_ty::TargetFeatures::from_attrs(&db.attrs(caller.id.into()));
+ let target_feature_is_safe_in_target =
+ match &caller.krate(db).id.workspace_data(db).target {
+ Ok(target) => hir_ty::target_feature_is_safe_in_target(target),
+ Err(_) => false,
+ };
+ (target_features, target_feature_is_safe_in_target)
+ })
+ .unwrap_or_else(|| (hir_ty::TargetFeatures::default(), false));
matches!(
- hir_ty::is_fn_unsafe_to_call(db, self.id, &target_features, call_edition),
+ hir_ty::is_fn_unsafe_to_call(
+ db,
+ self.id,
+ &target_features,
+ call_edition,
+ target_feature_is_safe_in_target
+ ),
hir_ty::Unsafety::Unsafe
)
}
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index 88ed6f9..a9cf2c1 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -364,11 +364,13 @@
Visibility::Crate => Some(make::visibility_pub_crate()),
Visibility::Pub => Some(make::visibility_pub()),
};
+ let type_params =
+ self.generic_param_list.filter(|list| list.generic_params().next().is_some());
let fn_def = make::fn_(
None,
visibility,
self.fn_name,
- self.generic_param_list,
+ type_params,
self.where_clause,
self.params,
self.fn_body,
@@ -2416,6 +2418,33 @@
}
#[test]
+ fn create_method_with_unused_generics() {
+ check_assist(
+ generate_function,
+ r#"
+struct Foo<S>(S);
+impl<S> Foo<S> {
+ fn foo(&self) {
+ self.bar()$0;
+ }
+}
+"#,
+ r#"
+struct Foo<S>(S);
+impl<S> Foo<S> {
+ fn foo(&self) {
+ self.bar();
+ }
+
+ fn bar(&self) ${0:-> _} {
+ todo!()
+ }
+}
+"#,
+ )
+ }
+
+ #[test]
fn create_function_with_async() {
check_assist(
generate_function,
diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs
index b3d7709..6162d98 100644
--- a/crates/ide-completion/src/completions/keyword.rs
+++ b/crates/ide-completion/src/completions/keyword.rs
@@ -270,6 +270,46 @@
);
check_edit(
+ "else if",
+ r#"
+fn main() {
+ let x = if true {
+ ()
+ } $0 else {};
+}
+"#,
+ r#"
+fn main() {
+ let x = if true {
+ ()
+ } else if $1 {
+ $0
+} else {};
+}
+"#,
+ );
+
+ check_edit(
+ "else if",
+ r#"
+fn main() {
+ let x = if true {
+ ()
+ } $0 else if true {};
+}
+"#,
+ r#"
+fn main() {
+ let x = if true {
+ ()
+ } else if $1 {
+ $0
+} else if true {};
+}
+"#,
+ );
+
+ check_edit(
"else",
r#"
fn main() {
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 33b98a3..b33a547 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -970,6 +970,16 @@
let after_incomplete_let = |node: SyntaxNode| {
prev_expr(node).and_then(|it| it.syntax().parent()).and_then(ast::LetStmt::cast)
};
+ let before_else_kw = |node: &SyntaxNode| {
+ node.parent()
+ .and_then(ast::ExprStmt::cast)
+ .filter(|stmt| stmt.semicolon_token().is_none())
+ .and_then(|stmt| non_trivia_sibling(stmt.syntax().clone().into(), Direction::Next))
+ .and_then(NodeOrToken::into_node)
+ .filter(|next| next.kind() == SyntaxKind::ERROR)
+ .and_then(|next| next.first_token())
+ .is_some_and(|token| token.kind() == SyntaxKind::ELSE_KW)
+ };
// We do not want to generate path completions when we are sandwiched between an item decl signature and its body.
// ex. trait Foo $0 {}
@@ -1276,7 +1286,7 @@
.parent()
.and_then(ast::LetStmt::cast)
.is_some_and(|it| it.semicolon_token().is_none())
- || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true);
+ || after_incomplete_let && incomplete_expr_stmt.unwrap_or(true) && !before_else_kw(it);
let in_value = it.parent().and_then(Either::<ast::LetStmt, ast::ArgList>::cast).is_some();
let impl_ = fetch_immediate_impl(sema, original_file, expr.syntax());
diff --git a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 17caf63..4bb6474 100644
--- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -998,4 +998,20 @@
"#,
);
}
+
+ #[test]
+ fn target_feature_safe_on_wasm() {
+ check_diagnostics(
+ r#"
+//- target_arch: wasm32
+
+#[target_feature(enable = "simd128")]
+fn requires_target_feature() {}
+
+fn main() {
+ requires_target_feature();
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 3f52303..03b9b36 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -483,6 +483,12 @@
db: &'db RootDatabase,
ty: &hir::Type<'db>,
) -> Vec<(hir::Trait, Vec<(Option<hir::Type<'db>>, hir::Name)>)> {
+ if ty.is_unknown() {
+ // The trait solver returns "yes" to the question whether the error type
+ // impls any trait, and we don't want to show it as having any notable trait.
+ return Vec::new();
+ }
+
db.notable_traits_in_deps(ty.krate(db).into())
.iter()
.flat_map(|it| &**it)
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs
index 58d8a7e..a88c0c9 100644
--- a/crates/ide/src/hover/tests.rs
+++ b/crates/ide/src/hover/tests.rs
@@ -11097,3 +11097,26 @@
"#]],
);
}
+
+#[test]
+fn unknown_should_not_implement_notable_traits() {
+ check(
+ r#"
+//- minicore: future, iterator
+fn foo() {
+ let x$0;
+}
+ "#,
+ expect![[r#"
+ *x*
+
+ ```rust
+ let x: {unknown}
+ ```
+
+ ---
+
+ no Drop
+ "#]],
+ );
+}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 3a6b324..50cbef5 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -263,7 +263,7 @@
false,
proc_macro_cwd,
Arc::new(CrateWorkspaceData {
- data_layout: Err("fixture has no layout".into()),
+ target: Err("fixture has no layout".into()),
toolchain: None,
}),
);
diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs
index d39781b..e36b904 100644
--- a/crates/project-model/src/lib.rs
+++ b/crates/project-model/src/lib.rs
@@ -18,7 +18,7 @@
pub mod project_json;
pub mod toolchain_info {
pub mod rustc_cfg;
- pub mod target_data_layout;
+ pub mod target_data;
pub mod target_tuple;
pub mod version;
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index ed72520..987d381 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -9,7 +9,6 @@
use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;
use span::FileId;
-use triomphe::Arc;
use crate::{
CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace,
@@ -47,7 +46,7 @@
sysroot: Sysroot::empty(),
rustc_cfg: Vec::new(),
toolchain: None,
- target_layout: Err("target_data_layout not loaded".into()),
+ target: Err("target_data_layout not loaded".into()),
extra_includes: Vec::new(),
set_test: true,
}
@@ -62,7 +61,7 @@
sysroot,
rustc_cfg: Vec::new(),
toolchain: None,
- target_layout: Err(Arc::from("test has no data layout")),
+ target: Err("test has no target data".into()),
cfg_overrides: Default::default(),
extra_includes: Vec::new(),
set_test: true,
@@ -265,7 +264,7 @@
rustc_cfg: Vec::new(),
cfg_overrides: Default::default(),
toolchain: None,
- target_layout: Err("target_data_layout not loaded".into()),
+ target: Err("target_data_layout not loaded".into()),
extra_includes: Vec::new(),
set_test: true,
};
diff --git a/crates/project-model/src/toolchain_info/target_data_layout.rs b/crates/project-model/src/toolchain_info/target_data.rs
similarity index 72%
rename from crates/project-model/src/toolchain_info/target_data_layout.rs
rename to crates/project-model/src/toolchain_info/target_data.rs
index f1d99cb..b815c0b 100644
--- a/crates/project-model/src/toolchain_info/target_data_layout.rs
+++ b/crates/project-model/src/toolchain_info/target_data.rs
@@ -1,23 +1,54 @@
//! Runs `rustc --print target-spec-json` to get the target_data_layout.
use anyhow::Context;
+use base_db::target;
use rustc_hash::FxHashMap;
+use serde_derive::Deserialize;
use toolchain::Tool;
use crate::{Sysroot, toolchain_info::QueryConfig, utf8_stdout};
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum Arch {
+ Wasm32,
+ Wasm64,
+ #[serde(other)]
+ Other,
+}
+
+impl From<Arch> for target::Arch {
+ fn from(value: Arch) -> Self {
+ match value {
+ Arch::Wasm32 => target::Arch::Wasm32,
+ Arch::Wasm64 => target::Arch::Wasm64,
+ Arch::Other => target::Arch::Other,
+ }
+ }
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct TargetSpec {
+ pub data_layout: String,
+ pub arch: Arch,
+}
+
/// Uses `rustc --print target-spec-json`.
pub fn get(
config: QueryConfig<'_>,
target: Option<&str>,
extra_env: &FxHashMap<String, Option<String>>,
-) -> anyhow::Result<String> {
+) -> anyhow::Result<target::TargetData> {
const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"];
let process = |output: String| {
- (|| Some(output.split_once(r#""data-layout": ""#)?.1.split_once('"')?.0.to_owned()))()
- .ok_or_else(|| {
- anyhow::format_err!("could not parse target-spec-json from command output")
- })
+ let target_spec = serde_json::from_str::<TargetSpec>(&output).map_err(|_| {
+ anyhow::format_err!("could not parse target-spec-json from command output")
+ })?;
+ Ok(target::TargetData {
+ arch: target_spec.arch.into(),
+ data_layout: target_spec.data_layout.into_boxed_str(),
+ })
};
let (sysroot, current_dir) = match config {
QueryConfig::Cargo(sysroot, cargo_toml, _) => {
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 00032bc..e0d2105 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -8,7 +8,7 @@
use base_db::{
CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin,
CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError,
- ProcMacroPaths, TargetLayoutLoadResult,
+ ProcMacroPaths, target::TargetLoadResult,
};
use cfg::{CfgAtom, CfgDiff, CfgOptions};
use intern::{Symbol, sym};
@@ -30,7 +30,7 @@
env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
project_json::{Crate, CrateArrayIdx},
sysroot::RustLibSrcWorkspace,
- toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
+ toolchain_info::{QueryConfig, rustc_cfg, target_data, target_tuple, version},
utf8_stdout,
};
use tracing::{debug, error, info};
@@ -63,7 +63,7 @@
/// The toolchain version used by this workspace.
pub toolchain: Option<Version>,
/// The target data layout queried for workspace.
- pub target_layout: TargetLayoutLoadResult,
+ pub target: TargetLoadResult,
/// A set of cfg overrides for this workspace.
pub cfg_overrides: CfgOverrides,
/// Additional includes to add for the VFS.
@@ -115,7 +115,7 @@
sysroot,
rustc_cfg,
toolchain,
- target_layout,
+ target: target_layout,
cfg_overrides,
extra_includes,
set_test,
@@ -309,8 +309,8 @@
let rustc_cfg = s.spawn(|| {
rustc_cfg::get(toolchain_config, targets.first().map(Deref::deref), extra_env)
});
- let data_layout = s.spawn(|| {
- target_data_layout::get(
+ let target_data = s.spawn(|| {
+ target_data::get(
toolchain_config,
targets.first().map(Deref::deref),
extra_env,
@@ -392,7 +392,7 @@
s.spawn(move || cargo_config_env(cargo_toml, &config_file));
thread::Result::Ok((
rustc_cfg.join()?,
- data_layout.join()?,
+ target_data.join()?,
rustc_dir.join()?,
loaded_sysroot.join()?,
cargo_metadata.join()?,
@@ -442,7 +442,7 @@
rustc_cfg,
cfg_overrides: cfg_overrides.clone(),
toolchain,
- target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+ target: data_layout.map_err(|it| it.to_string().into()),
extra_includes: extra_includes.clone(),
set_test: *set_test,
})
@@ -480,11 +480,7 @@
rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
});
let data_layout = s.spawn(|| {
- target_data_layout::get(
- query_config,
- targets.first().map(Deref::deref),
- &config.extra_env,
- )
+ target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env)
});
let loaded_sysroot = s.spawn(|| {
if let Some(sysroot_project) = sysroot_project {
@@ -513,7 +509,7 @@
thread::Result::Ok((rustc_cfg.join()?, data_layout.join()?, loaded_sysroot.join()?))
});
- let (rustc_cfg, target_layout, loaded_sysroot) = match join {
+ let (rustc_cfg, target_data, loaded_sysroot) = match join {
Ok(it) => it,
Err(e) => std::panic::resume_unwind(e),
};
@@ -527,7 +523,7 @@
sysroot,
rustc_cfg,
toolchain,
- target_layout: target_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+ target: target_data.map_err(|it| it.to_string().into()),
cfg_overrides: config.cfg_overrides.clone(),
extra_includes: config.extra_includes.clone(),
set_test: config.set_test,
@@ -551,7 +547,7 @@
let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
.unwrap_or_default();
let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env);
- let data_layout = target_data_layout::get(query_config, None, &config.extra_env);
+ let target_data = target_data::get(query_config, None, &config.extra_env);
let target_dir = config
.target_dir
.clone()
@@ -610,7 +606,7 @@
sysroot,
rustc_cfg,
toolchain,
- target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+ target: target_data.map_err(|it| it.to_string().into()),
cfg_overrides: config.cfg_overrides.clone(),
extra_includes: config.extra_includes.clone(),
set_test: config.set_test,
@@ -942,7 +938,7 @@
let Self { kind, sysroot, cfg_overrides, rustc_cfg, .. } = self;
let crate_ws_data = Arc::new(CrateWorkspaceData {
toolchain: self.toolchain.clone(),
- data_layout: self.target_layout.clone(),
+ target: self.target.clone(),
});
let (crate_graph, proc_macros) = match kind {
ProjectWorkspaceKind::Json(project) => project_json_to_crate_graph(
@@ -1000,13 +996,15 @@
}
pub fn eq_ignore_build_data(&self, other: &Self) -> bool {
- let Self { kind, sysroot, rustc_cfg, toolchain, target_layout, cfg_overrides, .. } = self;
+ let Self {
+ kind, sysroot, rustc_cfg, toolchain, target: target_layout, cfg_overrides, ..
+ } = self;
let Self {
kind: o_kind,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
toolchain: o_toolchain,
- target_layout: o_target_layout,
+ target: o_target_layout,
cfg_overrides: o_cfg_overrides,
..
} = other;
diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
index 3722e2c..4f6ce4d 100644
--- a/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
+++ b/crates/project-model/test_data/output/cargo_hello_world_project_model.txt
@@ -70,7 +70,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -155,7 +155,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -240,7 +240,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -325,7 +325,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -406,7 +406,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
index 3722e2c..4f6ce4d 100644
--- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
+++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_selective_overrides.txt
@@ -70,7 +70,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -155,7 +155,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -240,7 +240,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -325,7 +325,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -406,7 +406,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
diff --git a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
index 7b156ea..6862918 100644
--- a/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
+++ b/crates/project-model/test_data/output/cargo_hello_world_project_model_with_wildcard_overrides.txt
@@ -69,7 +69,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -153,7 +153,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -237,7 +237,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -321,7 +321,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
@@ -402,7 +402,7 @@
},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
+ target: Err(
"target_data_layout not loaded",
),
toolchain: None,
diff --git a/crates/project-model/test_data/output/rust_project_cfg_groups.txt b/crates/project-model/test_data/output/rust_project_cfg_groups.txt
index 98fe598..28ad323 100644
--- a/crates/project-model/test_data/output/rust_project_cfg_groups.txt
+++ b/crates/project-model/test_data/output/rust_project_cfg_groups.txt
@@ -43,8 +43,8 @@
entries: {},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
- "test has no data layout",
+ target: Err(
+ "test has no target data",
),
toolchain: None,
},
@@ -93,8 +93,8 @@
entries: {},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
- "test has no data layout",
+ target: Err(
+ "test has no target data",
),
toolchain: None,
},
diff --git a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
index 0dc373b..dabb3aa 100644
--- a/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
+++ b/crates/project-model/test_data/output/rust_project_hello_world_project_model.txt
@@ -40,8 +40,8 @@
entries: {},
},
ws_data: CrateWorkspaceData {
- data_layout: Err(
- "test has no data layout",
+ target: Err(
+ "test has no target data",
),
toolchain: None,
},
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index 36ae98b..609ebf2 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -11,7 +11,7 @@
use itertools::Either;
use paths::Utf8PathBuf;
use profile::StopWatch;
-use project_model::toolchain_info::{QueryConfig, target_data_layout};
+use project_model::toolchain_info::{QueryConfig, target_data};
use project_model::{
CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource,
RustSourceWorkspaceConfig, Sysroot,
@@ -19,7 +19,6 @@
use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace};
use rustc_hash::FxHashMap;
-use triomphe::Arc;
use vfs::{AbsPathBuf, FileId};
use walkdir::WalkDir;
@@ -87,7 +86,7 @@
sysroot.set_workspace(loaded_sysroot);
}
- let data_layout = target_data_layout::get(
+ let target_data = target_data::get(
QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()),
None,
&cargo_config.extra_env,
@@ -101,7 +100,7 @@
sysroot,
rustc_cfg: vec![],
toolchain: None,
- target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
+ target: target_data.map_err(|it| it.to_string().into()),
cfg_overrides: Default::default(),
extra_includes: vec![],
set_test: true,
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index 01ac75c..315c45d 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -104,11 +104,11 @@
}
impl FlycheckConfig {
- pub(crate) fn invocation_strategy_once(&self) -> bool {
+ pub(crate) fn invocation_strategy(&self) -> InvocationStrategy {
match self {
- FlycheckConfig::CargoCommand { .. } => false,
+ FlycheckConfig::CargoCommand { .. } => InvocationStrategy::PerWorkspace,
FlycheckConfig::CustomCommand { invocation_strategy, .. } => {
- *invocation_strategy == InvocationStrategy::Once
+ invocation_strategy.clone()
}
}
}
@@ -529,7 +529,7 @@
if let Some(command_handle) = self.command_handle.take() {
tracing::debug!(
command = ?command_handle,
- "did cancel flycheck"
+ "did cancel flycheck"
);
command_handle.cancel();
self.command_receiver.take();
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index e193ff7..68c91a6 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -1,9 +1,11 @@
//! This module is responsible for implementing handlers for Language Server
//! Protocol. This module specifically handles notifications.
-use std::ops::{Deref, Not as _};
+use std::{
+ ops::{Deref, Not as _},
+ panic::UnwindSafe,
+};
-use ide_db::base_db::salsa::Cancelled;
use itertools::Itertools;
use lsp_types::{
CancelParams, DidChangeConfigurationParams, DidChangeTextDocumentParams,
@@ -16,7 +18,7 @@
use crate::{
config::{Config, ConfigChange},
- flycheck::Target,
+ flycheck::{InvocationStrategy, Target},
global_state::{FetchWorkspaceRequest, GlobalState},
lsp::{from_proto, utils::apply_document_changes},
lsp_ext::{self, RunFlycheckParams},
@@ -301,124 +303,165 @@
let file_id = state.vfs.read().0.file_id(&vfs_path);
if let Some((file_id, vfs::FileExcluded::No)) = file_id {
let world = state.snapshot();
- let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once();
+ let invocation_strategy = state.config.flycheck(None).invocation_strategy();
let may_flycheck_workspace = state.config.flycheck_workspace(None);
- let mut updated = false;
- let task = move || -> std::result::Result<(), Cancelled> {
- if invocation_strategy_once {
- let saved_file = vfs_path.as_path().map(|p| p.to_owned());
- world.flycheck[0].restart_workspace(saved_file);
- }
- let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
- let tgt_kind = it.target_kind();
- let (tgt_name, root, package) = match it {
- TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package),
- _ => return None,
- };
-
- let tgt = match tgt_kind {
- project_model::TargetKind::Bin => Target::Bin(tgt_name),
- project_model::TargetKind::Example => Target::Example(tgt_name),
- project_model::TargetKind::Test => Target::Test(tgt_name),
- project_model::TargetKind::Bench => Target::Benchmark(tgt_name),
- _ => return Some((None, root, package)),
- };
-
- Some((Some(tgt), root, package))
- });
- tracing::debug!(?target, "flycheck target");
- // we have a specific non-library target, attempt to only check that target, nothing
- // else will be affected
- if let Some((target, root, package)) = target {
- // trigger a package check if we have a non-library target as that can't affect
- // anything else in the workspace OR if we're not allowed to check the workspace as
- // the user opted into package checks then
- let package_check_allowed = target.is_some() || !may_flycheck_workspace;
- if package_check_allowed {
- let workspace = world.workspaces.iter().position(|ws| match &ws.kind {
- project_model::ProjectWorkspaceKind::Cargo { cargo, .. }
- | project_model::ProjectWorkspaceKind::DetachedFile {
- cargo: Some((cargo, _, _)),
- ..
- } => *cargo.workspace_root() == root,
- _ => false,
- });
- if let Some(idx) = workspace {
- world.flycheck[idx].restart_for_package(package, target);
- }
+ let task: Box<dyn FnOnce() -> ide::Cancellable<()> + Send + UnwindSafe> =
+ match invocation_strategy {
+ InvocationStrategy::Once => {
+ Box::new(move || {
+ // FIXME: Because triomphe::Arc's auto UnwindSafe impl requires that the inner type
+ // be UnwindSafe, and FlycheckHandle is not UnwindSafe, `word.flycheck` cannot
+ // be captured directly. std::sync::Arc has an UnwindSafe impl that only requires
+ // that the inner type be RefUnwindSafe, so if we were using that one we wouldn't
+ // have this problem. Remove the line below when triomphe::Arc has an UnwindSafe impl
+ // like std::sync::Arc's.
+ let world = world;
+ stdx::always!(
+ world.flycheck.len() == 1,
+ "should have exactly one flycheck handle when invocation strategy is once"
+ );
+ let saved_file = vfs_path.as_path().map(ToOwned::to_owned);
+ world.flycheck[0].restart_workspace(saved_file);
+ Ok(())
+ })
}
- }
+ InvocationStrategy::PerWorkspace => {
+ Box::new(move || {
+ let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| {
+ let tgt_kind = it.target_kind();
+ let (tgt_name, root, package) = match it {
+ TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package),
+ _ => return None,
+ };
- if !may_flycheck_workspace {
- return Ok(());
- }
+ let tgt = match tgt_kind {
+ project_model::TargetKind::Bin => Target::Bin(tgt_name),
+ project_model::TargetKind::Example => Target::Example(tgt_name),
+ project_model::TargetKind::Test => Target::Test(tgt_name),
+ project_model::TargetKind::Bench => Target::Benchmark(tgt_name),
+ _ => return Some((None, root, package)),
+ };
- // Trigger flychecks for all workspaces that depend on the saved file
- // Crates containing or depending on the saved file
- let crate_ids = world
- .analysis
- .crates_for(file_id)?
- .into_iter()
- .flat_map(|id| world.analysis.transitive_rev_deps(id))
- .flatten()
- .unique()
- .collect::<Vec<_>>();
- tracing::debug!(?crate_ids, "flycheck crate ids");
- let crate_root_paths: Vec<_> = crate_ids
- .iter()
- .filter_map(|&crate_id| {
- world
- .analysis
- .crate_root(crate_id)
- .map(|file_id| {
- world.file_id_to_file_path(file_id).as_path().map(ToOwned::to_owned)
- })
- .transpose()
- })
- .collect::<ide::Cancellable<_>>()?;
- let crate_root_paths: Vec<_> = crate_root_paths.iter().map(Deref::deref).collect();
- tracing::debug!(?crate_root_paths, "flycheck crate roots");
+ Some((Some(tgt), root, package))
+ });
+ tracing::debug!(?target, "flycheck target");
+ // we have a specific non-library target, attempt to only check that target, nothing
+ // else will be affected
+ let mut package_workspace_idx = None;
+ if let Some((target, root, package)) = target {
+ // trigger a package check if we have a non-library target as that can't affect
+ // anything else in the workspace OR if we're not allowed to check the workspace as
+ // the user opted into package checks then
+ let package_check_allowed = target.is_some() || !may_flycheck_workspace;
+ if package_check_allowed {
+ package_workspace_idx =
+ world.workspaces.iter().position(|ws| match &ws.kind {
+ project_model::ProjectWorkspaceKind::Cargo {
+ cargo,
+ ..
+ }
+ | project_model::ProjectWorkspaceKind::DetachedFile {
+ cargo: Some((cargo, _, _)),
+ ..
+ } => *cargo.workspace_root() == root,
+ _ => false,
+ });
+ if let Some(idx) = package_workspace_idx {
+ world.flycheck[idx].restart_for_package(package, target);
+ }
+ }
+ }
- // Find all workspaces that have at least one target containing the saved file
- let workspace_ids =
- world.workspaces.iter().enumerate().filter(|(_, ws)| match &ws.kind {
- project_model::ProjectWorkspaceKind::Cargo { cargo, .. }
- | project_model::ProjectWorkspaceKind::DetachedFile {
- cargo: Some((cargo, _, _)),
- ..
- } => cargo.packages().any(|pkg| {
- cargo[pkg]
- .targets
+ if !may_flycheck_workspace {
+ return Ok(());
+ }
+
+ // Trigger flychecks for all workspaces that depend on the saved file
+ // Crates containing or depending on the saved file
+ let crate_ids: Vec<_> = world
+ .analysis
+ .crates_for(file_id)?
+ .into_iter()
+ .flat_map(|id| world.analysis.transitive_rev_deps(id))
+ .flatten()
+ .unique()
+ .collect();
+ tracing::debug!(?crate_ids, "flycheck crate ids");
+ let crate_root_paths: Vec<_> = crate_ids
.iter()
- .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path()))
- }),
- project_model::ProjectWorkspaceKind::Json(project) => project
- .crates()
- .any(|(_, krate)| crate_root_paths.contains(&krate.root_module.as_path())),
- project_model::ProjectWorkspaceKind::DetachedFile { .. } => false,
- });
+ .filter_map(|&crate_id| {
+ world
+ .analysis
+ .crate_root(crate_id)
+ .map(|file_id| {
+ world
+ .file_id_to_file_path(file_id)
+ .as_path()
+ .map(ToOwned::to_owned)
+ })
+ .transpose()
+ })
+ .collect::<ide::Cancellable<_>>()?;
+ let crate_root_paths: Vec<_> =
+ crate_root_paths.iter().map(Deref::deref).collect();
+ tracing::debug!(?crate_root_paths, "flycheck crate roots");
- let saved_file = vfs_path.as_path().map(|p| p.to_owned());
+ // Find all workspaces that have at least one target containing the saved file
+ let workspace_ids =
+ world.workspaces.iter().enumerate().filter(|&(idx, ws)| {
+ let ws_contains_file = match &ws.kind {
+ project_model::ProjectWorkspaceKind::Cargo {
+ cargo, ..
+ }
+ | project_model::ProjectWorkspaceKind::DetachedFile {
+ cargo: Some((cargo, _, _)),
+ ..
+ } => cargo.packages().any(|pkg| {
+ cargo[pkg].targets.iter().any(|&it| {
+ crate_root_paths.contains(&cargo[it].root.as_path())
+ })
+ }),
+ project_model::ProjectWorkspaceKind::Json(project) => {
+ project.crates().any(|(_, krate)| {
+ crate_root_paths.contains(&krate.root_module.as_path())
+ })
+ }
+ project_model::ProjectWorkspaceKind::DetachedFile {
+ ..
+ } => false,
+ };
+ let is_pkg_ws = match package_workspace_idx {
+ Some(pkg_idx) => pkg_idx == idx,
+ None => false,
+ };
+ ws_contains_file && !is_pkg_ws
+ });
- // Find and trigger corresponding flychecks
- 'flychecks: for flycheck in world.flycheck.iter() {
- for (id, _) in workspace_ids.clone() {
- if id == flycheck.id() {
- updated = true;
- flycheck.restart_workspace(saved_file.clone());
- continue 'flychecks;
- }
+ let saved_file = vfs_path.as_path().map(ToOwned::to_owned);
+ let mut workspace_check_triggered = false;
+ // Find and trigger corresponding flychecks
+ 'flychecks: for flycheck in world.flycheck.iter() {
+ for (id, _) in workspace_ids.clone() {
+ if id == flycheck.id() {
+ workspace_check_triggered = true;
+ flycheck.restart_workspace(saved_file.clone());
+ continue 'flychecks;
+ }
+ }
+ }
+
+ // No specific flycheck was triggered, so let's trigger all of them.
+ if !workspace_check_triggered && package_workspace_idx.is_none() {
+ for flycheck in world.flycheck.iter() {
+ flycheck.restart_workspace(saved_file.clone());
+ }
+ }
+ Ok(())
+ })
}
- }
- // No specific flycheck was triggered, so let's trigger all of them.
- if !updated {
- for flycheck in world.flycheck.iter() {
- flycheck.restart_workspace(saved_file.clone());
- }
- }
- Ok(())
- };
+ };
+
state.task_pool.handle.spawn_with_sender(stdx::thread::ThreadIntent::Worker, move |_| {
if let Err(e) = std::panic::catch_unwind(task) {
tracing::error!("flycheck task panicked: {e:?}")
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index a8a5493..27738fe 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -318,7 +318,7 @@
}
}
- let mut workspaces = linked_projects
+ let mut workspaces: Vec<_> = linked_projects
.iter()
.map(|project| match project {
LinkedProject::ProjectManifest(manifest) => {
@@ -339,7 +339,7 @@
Ok(workspace)
}
})
- .collect::<Vec<_>>();
+ .collect();
let mut i = 0;
while i < workspaces.len() {
@@ -848,23 +848,17 @@
fn reload_flycheck(&mut self) {
let _p = tracing::info_span!("GlobalState::reload_flycheck").entered();
let config = self.config.flycheck(None);
- let sender = self.flycheck_sender.clone();
- let invocation_strategy = match config {
- FlycheckConfig::CargoCommand { .. } => {
- crate::flycheck::InvocationStrategy::PerWorkspace
- }
- FlycheckConfig::CustomCommand { ref invocation_strategy, .. } => {
- invocation_strategy.clone()
- }
- };
- let next_gen = self.flycheck.iter().map(|f| f.generation() + 1).max().unwrap_or_default();
+ let sender = &self.flycheck_sender;
+ let invocation_strategy = config.invocation_strategy();
+ let next_gen =
+ self.flycheck.iter().map(FlycheckHandle::generation).max().unwrap_or_default() + 1;
self.flycheck = match invocation_strategy {
crate::flycheck::InvocationStrategy::Once => {
vec![FlycheckHandle::spawn(
0,
next_gen,
- sender,
+ sender.clone(),
config,
None,
self.config.root_path().clone(),
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index 2bebb0c..3464a96 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -97,6 +97,7 @@
proc_macro_names,
toolchain,
target_data_layout: _,
+ target_arch: _,
} = FixtureWithProjectMeta::parse(self.fixture);
assert!(proc_macro_names.is_empty());
assert!(mini_core.is_none());
@@ -177,6 +178,7 @@
proc_macro_names,
toolchain,
target_data_layout: _,
+ target_arch: _,
} = FixtureWithProjectMeta::parse(self.fixture);
assert!(proc_macro_names.is_empty());
assert!(mini_core.is_none());
diff --git a/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs
index 57fca70..7574d12 100644
--- a/crates/test-fixture/src/lib.rs
+++ b/crates/test-fixture/src/lib.rs
@@ -1,6 +1,7 @@
//! A set of high-level utility fixture methods to use in tests.
use std::{any::TypeId, mem, str::FromStr, sync};
+use base_db::target::TargetData;
use base_db::{
Crate, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CrateWorkspaceData,
DependencyBuilder, Env, FileChange, FileSet, FxIndexMap, LangCrateOrigin, SourceDatabase,
@@ -136,8 +137,11 @@
proc_macro_names,
toolchain,
target_data_layout,
+ target_arch,
} = FixtureWithProjectMeta::parse(ra_fixture);
- let target_data_layout = Ok(target_data_layout.into());
+ let target_data_layout = target_data_layout.into();
+ let target_arch = parse_target_arch(&target_arch);
+ let target = Ok(TargetData { arch: target_arch, data_layout: target_data_layout });
let toolchain = Some({
let channel = toolchain.as_deref().unwrap_or("stable");
Version::parse(&format!("1.76.0-{channel}")).unwrap()
@@ -163,8 +167,7 @@
let mut file_position = None;
- let crate_ws_data =
- Arc::new(CrateWorkspaceData { data_layout: target_data_layout, toolchain });
+ let crate_ws_data = Arc::new(CrateWorkspaceData { target, toolchain });
// FIXME: This is less than ideal
let proc_macro_cwd = Arc::new(AbsPathBuf::assert_utf8(std::env::current_dir().unwrap()));
@@ -395,6 +398,15 @@
}
}
+fn parse_target_arch(arch: &str) -> base_db::target::Arch {
+ use base_db::target::Arch::*;
+ match arch {
+ "wasm32" => Wasm32,
+ "wasm64" => Wasm64,
+ _ => Other,
+ }
+}
+
fn default_test_proc_macros() -> Box<[(String, ProcMacro)]> {
Box::new([
(
diff --git a/crates/test-utils/src/fixture.rs b/crates/test-utils/src/fixture.rs
index e830c6a..c024089 100644
--- a/crates/test-utils/src/fixture.rs
+++ b/crates/test-utils/src/fixture.rs
@@ -149,6 +149,8 @@
/// You probably don't want to manually specify this. See LLVM manual for the
/// syntax, if you must: <https://llvm.org/docs/LangRef.html#data-layout>
pub target_data_layout: String,
+ /// Specifies the target architecture.
+ pub target_arch: String,
}
impl FixtureWithProjectMeta {
@@ -178,6 +180,7 @@
let mut toolchain = None;
let mut target_data_layout =
"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128".to_owned();
+ let mut target_arch = "x86_64".to_owned();
let mut mini_core = None;
let mut res: Vec<Fixture> = Vec::new();
let mut proc_macro_names = vec![];
@@ -194,6 +197,12 @@
fixture = remain;
}
+ if let Some(meta) = fixture.strip_prefix("//- target_arch:") {
+ let (meta, remain) = meta.split_once('\n').unwrap();
+ meta.trim().clone_into(&mut target_arch);
+ fixture = remain;
+ }
+
if let Some(meta) = fixture.strip_prefix("//- proc_macros:") {
let (meta, remain) = meta.split_once('\n').unwrap();
proc_macro_names = meta.split(',').map(|it| it.trim().to_owned()).collect();
@@ -232,7 +241,14 @@
}
}
- Self { fixture: res, mini_core, proc_macro_names, toolchain, target_data_layout }
+ Self {
+ fixture: res,
+ mini_core,
+ proc_macro_names,
+ toolchain,
+ target_data_layout,
+ target_arch,
+ }
}
//- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
@@ -511,6 +527,7 @@
proc_macro_names,
toolchain,
target_data_layout: _,
+ target_arch: _,
} = FixtureWithProjectMeta::parse(
r#"
//- toolchain: nightly
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 26e0f46..7c3e7fe 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -55,7 +55,7 @@
//! panic: fmt
//! phantom_data:
//! pin:
-//! pointee: copy, send, sync, ord, hash, unpin
+//! pointee: copy, send, sync, ord, hash, unpin, phantom_data
//! range:
//! receiver: deref
//! result:
@@ -504,6 +504,16 @@
#[lang = "metadata_type"]
type Metadata: Copy + Send + Sync + Ord + Hash + Unpin;
}
+
+ #[lang = "dyn_metadata"]
+ pub struct DynMetadata<Dyn: PointeeSized> {
+ _phantom: crate::marker::PhantomData<Dyn>,
+ }
+
+ pub const fn metadata<T: PointeeSized>(ptr: *const T) -> <T as Pointee>::Metadata {
+ loop {}
+ }
+
// endregion:pointee
// region:non_null
#[rustc_layout_scalar_valid_range_start(1)]