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)]