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 3fd7d5cd4f..5a7cf9455b 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': u32 + 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': f32 + 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 "#]], ); } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 89efe71dab..f71b804fe9 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) => { @@ -725,7 +758,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() { @@ -735,7 +768,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/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index b9b3580223..730efff0de 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 { @@ -289,8 +294,14 @@ static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; 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 87d8c15568..79409fe816 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 {
@@ -51,8 +56,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;