about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/utils.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs23
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs16
5 files changed, 69 insertions, 11 deletions
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index d6d669258cc..64c4cdeaddf 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -15,8 +15,9 @@ use hir_def::{
 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 @@ struct UnsafeVisitor<'db> {
     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 @@ impl<'db> UnsafeVisitor<'db> {
             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 @@ impl<'db> UnsafeVisitor<'db> {
             callback: unsafe_expr_cb,
             def_target_features,
             edition,
+            target_feature_is_safe,
         }
     }
 
@@ -184,7 +194,13 @@ impl<'db> UnsafeVisitor<'db> {
     }
 
     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/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index c16bbb7b992..ee352619487 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -129,7 +129,10 @@ pub use mapping::{
 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/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs b/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
index 881b1c1a9db..07679d2a119 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/utils.rs
+++ b/src/tools/rust-analyzer/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 @@ pub enum Unsafety {
     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/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 6eb8a8bf60f..3f78a04df1d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -2542,11 +2542,26 @@ impl Function {
         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/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index 17caf630182..4bb64747f5b 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -998,4 +998,20 @@ extern "C" fn naked() {
         "#,
         );
     }
+
+    #[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();
+}
+            "#,
+        );
+    }
 }