From a6532905a983c4a3f035fde95ec288dc751f833a Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 6 Aug 2020 21:15:31 -0400 Subject: [PATCH 1/4] Add test for unsafe union field access highlighting --- crates/ra_ide/src/syntax_highlighting/tests.rs | 11 +++++++++++ crates/ra_ide/test_data/highlight_unsafe.html | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 2deee404cc..a09422da35 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -275,6 +275,11 @@ fn test_unsafe_highlighting() { r#" unsafe fn unsafe_fn() {} +union Union { + a: u32, + b: f32, +} + struct HasUnsafeFn; impl HasUnsafeFn { @@ -283,8 +288,14 @@ impl HasUnsafeFn { fn main() { let x = &5 as *const usize; + let u = Union { b: 0 }; unsafe { unsafe_fn(); + let b = u.b; + match u { + Union { b: 0 } => (), + Union { a } => (), + } HasUnsafeFn.unsafe_method(); let y = *(x); let z = -x; diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html index b81b6f1c3b..39582b5bb4 100644 --- a/crates/ra_ide/test_data/highlight_unsafe.html +++ b/crates/ra_ide/test_data/highlight_unsafe.html @@ -37,6 +37,11 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
unsafe fn unsafe_fn() {}
 
+union Union {
+    a: u32,
+    b: f32,
+}
+
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
@@ -45,8 +50,14 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 fn main() {
     let x = &5 as *const usize;
+    let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
+        let b = u.b;
+        match u {
+            Union { b: 0 } => (),
+            Union { a } => (),
+        }
         HasUnsafeFn.unsafe_method();
         let y = *(x);
         let z = -x;

From a39d503ef3ba26ec324639e22e46e1a8173b397e Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Sat, 8 Aug 2020 11:26:01 -0400
Subject: [PATCH 2/4] Add additional checks for union inference tests

---
 crates/ra_hir_ty/src/tests/simple.rs | 38 ++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 5 deletions(-)

diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 3fd7d5cd4f..a2db6944df 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -334,16 +334,44 @@ fn infer_union() {
             bar: f32,
         }
 
+        fn test() {
+            let u = MyUnion { foo: 0 };
+            unsafe { baz(u); }
+            let u = MyUnion { bar: 0.0 };
+            unsafe { baz(u); }
+        }
+
         unsafe fn baz(u: MyUnion) {
             let inner = u.foo;
+            let inner = u.bar;
         }
         "#,
         expect![[r#"
-            61..62 'u': MyUnion
-            73..99 '{     ...foo; }': ()
-            83..88 'inner': u32
-            91..92 'u': MyUnion
-            91..96 'u.foo': u32
+            57..172 '{     ...); } }': ()
+            67..68 'u': MyUnion
+            71..89 'MyUnio...o: 0 }': MyUnion
+            86..87 '0': i32
+            95..113 'unsafe...(u); }': ()
+            102..113 '{ baz(u); }': ()
+            104..107 'baz': fn baz(MyUnion)
+            104..110 'baz(u)': ()
+            108..109 'u': MyUnion
+            122..123 'u': MyUnion
+            126..146 'MyUnio... 0.0 }': MyUnion
+            141..144 '0.0': f64
+            152..170 'unsafe...(u); }': ()
+            159..170 '{ baz(u); }': ()
+            161..164 'baz': fn baz(MyUnion)
+            161..167 'baz(u)': ()
+            165..166 'u': MyUnion
+            188..189 'u': MyUnion
+            200..249 '{     ...bar; }': ()
+            210..215 'inner': u32
+            218..219 'u': MyUnion
+            218..223 'u.foo': u32
+            233..238 'inner': f32
+            241..242 'u': MyUnion
+            241..246 'u.bar': f32
         "#]],
     );
 }

From 3bf033e54814919f2214ca4e9b73cebc5ba7d86d Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Fri, 7 Aug 2020 09:24:20 -0400
Subject: [PATCH 3/4] Add support for unions in inference and lowering

---
 crates/ra_hir_ty/src/infer.rs                 | 11 +++++++----
 crates/ra_hir_ty/src/lower.rs                 |  5 ++++-
 crates/ra_hir_ty/src/tests/simple.rs          |  4 ++--
 crates/ra_ide/test_data/highlight_unsafe.html |  8 ++++----
 4 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index 28f32a0a4d..3d12039a6d 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -440,6 +440,12 @@ impl<'a> InferenceContext<'a> {
                 let ty = self.insert_type_vars(ty.subst(&substs));
                 forbid_unresolved_segments((ty, Some(strukt.into())), unresolved)
             }
+            TypeNs::AdtId(AdtId::UnionId(u)) => {
+                let substs = Ty::substs_from_path(&ctx, path, u.into(), true);
+                let ty = self.db.ty(u.into());
+                let ty = self.insert_type_vars(ty.subst(&substs));
+                forbid_unresolved_segments((ty, Some(u.into())), unresolved)
+            }
             TypeNs::EnumVariantId(var) => {
                 let substs = Ty::substs_from_path(&ctx, path, var.into(), true);
                 let ty = self.db.ty(var.parent.into());
@@ -490,10 +496,7 @@ impl<'a> InferenceContext<'a> {
                 // FIXME potentially resolve assoc type
                 (Ty::Unknown, None)
             }
-            TypeNs::AdtId(AdtId::EnumId(_))
-            | TypeNs::AdtId(AdtId::UnionId(_))
-            | TypeNs::BuiltinType(_)
-            | TypeNs::TraitId(_) => {
+            TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => {
                 // FIXME diagnostic
                 (Ty::Unknown, None)
             }
diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs
index 1eacc6f95e..7638f167b5 100644
--- a/crates/ra_hir_ty/src/lower.rs
+++ b/crates/ra_hir_ty/src/lower.rs
@@ -518,6 +518,7 @@ impl Ty {
         let (segment, generic_def) = match resolved {
             ValueTyDefId::FunctionId(it) => (last, Some(it.into())),
             ValueTyDefId::StructId(it) => (last, Some(it.into())),
+            ValueTyDefId::UnionId(it) => (last, Some(it.into())),
             ValueTyDefId::ConstId(it) => (last, Some(it.into())),
             ValueTyDefId::StaticId(_) => (last, None),
             ValueTyDefId::EnumVariantId(var) => {
@@ -1148,11 +1149,12 @@ impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefI
 pub enum ValueTyDefId {
     FunctionId(FunctionId),
     StructId(StructId),
+    UnionId(UnionId),
     EnumVariantId(EnumVariantId),
     ConstId(ConstId),
     StaticId(StaticId),
 }
-impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
+impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId);
 
 /// Build the declared type of an item. This depends on the namespace; e.g. for
 /// `struct Foo(usize)`, we have two types: The type of the struct itself, and
@@ -1179,6 +1181,7 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders
     match def {
         ValueTyDefId::FunctionId(it) => type_for_fn(db, it),
         ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it),
+        ValueTyDefId::UnionId(it) => type_for_adt(db, it.into()),
         ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it),
         ValueTyDefId::ConstId(it) => type_for_const(db, it),
         ValueTyDefId::StaticId(it) => type_for_static(db, it),
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index a2db6944df..5a7cf9455b 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -350,7 +350,7 @@ fn infer_union() {
             57..172 '{     ...); } }': ()
             67..68 'u': MyUnion
             71..89 'MyUnio...o: 0 }': MyUnion
-            86..87 '0': i32
+            86..87 '0': u32
             95..113 'unsafe...(u); }': ()
             102..113 '{ baz(u); }': ()
             104..107 'baz': fn baz(MyUnion)
@@ -358,7 +358,7 @@ fn infer_union() {
             108..109 'u': MyUnion
             122..123 'u': MyUnion
             126..146 'MyUnio... 0.0 }': MyUnion
-            141..144 '0.0': f64
+            141..144 '0.0': f32
             152..170 'unsafe...(u); }': ()
             159..170 '{ baz(u); }': ()
             161..164 'baz': fn baz(MyUnion)
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 39582b5bb4..de70363da2 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -50,13 +50,13 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 fn main() {
     let x = &5 as *const usize;
-    let u = Union { b: 0 };
+    let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
-        let b = u.b;
+        let b = u.b;
         match u {
-            Union { b: 0 } => (),
-            Union { a } => (),
+            Union { b: 0 } => (),
+            Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
         let y = *(x);

From be935b2b56dcbda5a5918d8c600552b0adbb3a96 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Fri, 7 Aug 2020 09:33:40 -0400
Subject: [PATCH 4/4] Apply unsafe semantic highlighting to union field access

---
 crates/ra_ide/src/syntax_highlighting.rs      | 66 ++++++++++++++++---
 crates/ra_ide/test_data/highlight_unsafe.html |  6 +-
 2 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index a32ae0165e..bfe6143ca5 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -4,7 +4,7 @@ mod injection;
 #[cfg(test)]
 mod tests;
 
-use hir::{Name, Semantics};
+use hir::{Name, Semantics, VariantDef};
 use ra_ide_db::{
     defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass},
     RootDatabase,
@@ -455,6 +455,18 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option {
     Some(TextRange::new(range_start, range_end))
 }
 
+fn is_possibly_unsafe(name_ref: &ast::NameRef) -> bool {
+    name_ref
+        .syntax()
+        .parent()
+        .and_then(|parent| {
+            ast::FieldExpr::cast(parent.clone())
+                .map(|_| true)
+                .or_else(|| ast::RecordPatField::cast(parent).map(|_| true))
+        })
+        .unwrap_or(false)
+}
+
 fn highlight_element(
     sema: &Semantics,
     bindings_shadow_count: &mut FxHashMap,
@@ -484,10 +496,19 @@ fn highlight_element(
 
             match name_kind {
                 Some(NameClass::Definition(def)) => {
-                    highlight_name(db, def) | HighlightModifier::Definition
+                    highlight_name(db, def, false) | HighlightModifier::Definition
+                }
+                Some(NameClass::ConstReference(def)) => highlight_name(db, def, false),
+                Some(NameClass::FieldShorthand { field, .. }) => {
+                    let mut h = HighlightTag::Field.into();
+                    if let Definition::Field(field) = field {
+                        if let VariantDef::Union(_) = field.parent_def(db) {
+                            h |= HighlightModifier::Unsafe;
+                        }
+                    }
+
+                    h
                 }
-                Some(NameClass::ConstReference(def)) => highlight_name(db, def),
-                Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(),
                 None => highlight_name_by_syntax(name) | HighlightModifier::Definition,
             }
         }
@@ -498,6 +519,7 @@ fn highlight_element(
         }
         NAME_REF => {
             let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap();
+            let possibly_unsafe = is_possibly_unsafe(&name_ref);
             match classify_name_ref(sema, &name_ref) {
                 Some(name_kind) => match name_kind {
                     NameRefClass::Definition(def) => {
@@ -508,11 +530,13 @@ fn highlight_element(
                                 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
                             }
                         };
-                        highlight_name(db, def)
+                        highlight_name(db, def, possibly_unsafe)
                     }
                     NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
                 },
-                None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref),
+                None if syntactic_name_ref_highlighting => {
+                    highlight_name_ref_by_syntax(name_ref, sema)
+                }
                 None => HighlightTag::UnresolvedReference.into(),
             }
         }
@@ -652,10 +676,19 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
     }
 }
 
-fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight {
+fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> Highlight {
     match def {
         Definition::Macro(_) => HighlightTag::Macro,
-        Definition::Field(_) => HighlightTag::Field,
+        Definition::Field(field) => {
+            let mut h = HighlightTag::Field.into();
+            if possibly_unsafe {
+                if let VariantDef::Union(_) = field.parent_def(db) {
+                    h |= HighlightModifier::Unsafe;
+                }
+            }
+
+            return h;
+        }
         Definition::ModuleDef(def) => match def {
             hir::ModuleDef::Module(_) => HighlightTag::Module,
             hir::ModuleDef::Function(func) => {
@@ -724,7 +757,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
     tag.into()
 }
 
-fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
+fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics) -> Highlight {
     let default = HighlightTag::UnresolvedReference;
 
     let parent = match name.syntax().parent() {
@@ -734,7 +767,20 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
 
     let tag = match parent.kind() {
         METHOD_CALL_EXPR => HighlightTag::Function,
-        FIELD_EXPR => HighlightTag::Field,
+        FIELD_EXPR => {
+            let h = HighlightTag::Field;
+            let is_union = ast::FieldExpr::cast(parent)
+                .and_then(|field_expr| {
+                    let field = sema.resolve_field(&field_expr)?;
+                    Some(if let VariantDef::Union(_) = field.parent_def(sema.db) {
+                        true
+                    } else {
+                        false
+                    })
+                })
+                .unwrap_or(false);
+            return if is_union { h | HighlightModifier::Unsafe } else { h.into() };
+        }
         PATH_SEGMENT => {
             let path = match parent.parent().and_then(ast::Path::cast) {
                 Some(it) => it,
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index de70363da2..cfc872832a 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -53,10 +53,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     let u = Union { b: 0 };
     unsafe {
         unsafe_fn();
-        let b = u.b;
+        let b = u.b;
         match u {
-            Union { b: 0 } => (),
-            Union { a } => (),
+            Union { b: 0 } => (),
+            Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
         let y = *(x);