From 263f9a7f231a474dd56d02adbcd7c57d079e88fd Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Wed, 3 Jun 2020 23:38:25 -0400 Subject: [PATCH 01/16] Add tracking of packed repr, use it to highlight unsafe refs Taking a reference to a misaligned field on a packed struct is an unsafe operation. Highlight that behavior. Currently, the misaligned part isn't tracked, so this highlight is a bit too aggressive. --- crates/ra_hir/src/code_model.rs | 18 ++++++ crates/ra_hir_def/src/adt.rs | 56 +++++++++++++++++-- crates/ra_ide/src/syntax_highlighting.rs | 24 ++++++++ .../ra_ide/src/syntax_highlighting/tests.rs | 11 ++++ 4 files changed, 105 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 44456e49e2..6f9c56d294 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -4,6 +4,7 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; use either::Either; use hir_def::{ + adt::ReprKind, adt::StructKind, adt::VariantData, builtin_type::BuiltinType, @@ -431,6 +432,10 @@ impl Struct { Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) } + pub fn is_packed(self, db: &dyn HirDatabase) -> bool { + matches!(db.struct_data(self.id).repr, Some(ReprKind::Packed)) + } + fn variant_data(self, db: &dyn HirDatabase) -> Arc { db.struct_data(self.id).variant_data.clone() } @@ -1253,6 +1258,19 @@ impl Type { ) } + pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { + let adt_id = match self.ty.value { + Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id, + _ => return false, + }; + + let adt = adt_id.into(); + match adt { + Adt::Struct(s) => s.is_packed(db), + _ => false, + } + } + pub fn is_raw_ptr(&self) -> bool { matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) } diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 6cb56a1cd0..6d59c86428 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -9,11 +9,13 @@ use hir_expand::{ }; use ra_arena::{map::ArenaMap, Arena}; use ra_syntax::ast::{self, NameOwner, VisibilityOwner}; +use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; use crate::{ + attr::AttrInput, body::{CfgExpander, LowerCtx}, db::DefDatabase, - item_tree::{Field, Fields, ItemTree}, + item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, src::HasChildSource, src::HasSource, trace::Trace, @@ -29,6 +31,7 @@ use ra_cfg::CfgOptions; pub struct StructData { pub name: Name, pub variant_data: Arc, + pub repr: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -58,26 +61,71 @@ pub struct FieldData { pub visibility: RawVisibility, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ReprKind { + Packed, + Other, +} + +fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option { + item_tree.attrs(of).iter().find_map(|a| { + if a.path.segments[0].to_string() == "repr" { + if let Some(AttrInput::TokenTree(subtree)) = &a.input { + parse_repr_tt(subtree) + } else { + None + } + } else { + None + } + }) +} + +fn parse_repr_tt(tt: &Subtree) -> Option { + match tt.delimiter { + Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {} + _ => return None, + } + + let mut it = tt.token_trees.iter(); + match it.next() { + None => None, + Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => { + Some(ReprKind::Packed) + } + _ => Some(ReprKind::Other), + } +} + impl StructData { pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc { let loc = id.lookup(db); let item_tree = db.item_tree(loc.id.file_id); + let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); let strukt = &item_tree[loc.id.value]; let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); - - Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) }) + Arc::new(StructData { + name: strukt.name.clone(), + variant_data: Arc::new(variant_data), + repr, + }) } pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc { let loc = id.lookup(db); let item_tree = db.item_tree(loc.id.file_id); + let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); let union = &item_tree[loc.id.value]; let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); - Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) }) + Arc::new(StructData { + name: union.name.clone(), + variant_data: Arc::new(variant_data), + repr, + }) } } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 6b7874460a..0cab684eb4 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -565,6 +565,30 @@ fn highlight_element( _ => h, } } + REF_EXPR => { + let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?; + let expr = ref_expr.expr()?; + let field_expr = match expr { + ast::Expr::FieldExpr(fe) => fe, + _ => return None, + }; + + let expr = field_expr.expr()?; + let ty = match sema.type_of_expr(&expr) { + Some(ty) => ty, + None => { + println!("No type :("); + return None; + } + }; + if !ty.is_packed(db) { + return None; + } + + // FIXME account for alignment... somehow + + Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe + } p if p.is_punct() => match p { T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => { HighlightTag::Operator.into() diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 09062c38e7..f2c078d347 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -292,6 +292,13 @@ struct TypeForStaticMut { static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; +#[repr(packed)] +struct Packed { + a: u16, + b: u8, + c: u32, +} + fn main() { let x = &5 as *const usize; let u = Union { b: 0 }; @@ -306,6 +313,10 @@ fn main() { let y = *(x); let z = -x; let a = global_mut.a; + let packed = Packed { a: 0, b: 0, c: 0 }; + let a = &packed.a; + let b = &packed.b; + let c = &packed.c; } } "# From fd30134cf84b134259fe8140e513b152e37f3f88 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Mon, 15 Jun 2020 07:19:45 -0400 Subject: [PATCH 02/16] Remove token tree from ReprKind::Other variant, expose ReprKind higher, remove debug println. --- crates/ra_hir/src/code_model.rs | 6 +++--- crates/ra_ide/src/syntax_highlighting.rs | 8 +------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 6f9c56d294..0007d7fa88 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -432,8 +432,8 @@ impl Struct { Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) } - pub fn is_packed(self, db: &dyn HirDatabase) -> bool { - matches!(db.struct_data(self.id).repr, Some(ReprKind::Packed)) + pub fn repr(self, db: &dyn HirDatabase) -> Option { + db.struct_data(self.id).repr.clone() } fn variant_data(self, db: &dyn HirDatabase) -> Arc { @@ -1266,7 +1266,7 @@ impl Type { let adt = adt_id.into(); match adt { - Adt::Struct(s) => s.is_packed(db), + Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), _ => false, } } diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 0cab684eb4..b82b51efda 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -574,13 +574,7 @@ fn highlight_element( }; let expr = field_expr.expr()?; - let ty = match sema.type_of_expr(&expr) { - Some(ty) => ty, - None => { - println!("No type :("); - return None; - } - }; + let ty = sema.type_of_expr(&expr)?; if !ty.is_packed(db) { return None; } From 4a4b1f48efeff4ebe578eb92b7bb8338d0181a83 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Mon, 15 Jun 2020 07:41:13 -0400 Subject: [PATCH 03/16] Limit scope of unsafe to & instead of all ref exprs, add test showing missing support for autoref behavior --- crates/ra_ide/src/syntax_highlighting.rs | 2 +- .../ra_ide/src/syntax_highlighting/tests.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index b82b51efda..c5098189b3 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -565,7 +565,7 @@ fn highlight_element( _ => h, } } - REF_EXPR => { + T![&] => { let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?; let expr = ref_expr.expr()?; let field_expr = match expr { diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index f2c078d347..c408058501 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -299,6 +299,23 @@ struct Packed { c: u32, } +trait DoTheAutoref { + fn calls_autoref(&self); +} + +struct NeedsAlign { + a: u16 +} + +#[repr(packed)] +struct HasAligned { + a: NeedsAlign +} + +impl DoTheAutoref for NeedsAlign { + fn calls_autored(&self) {} +} + fn main() { let x = &5 as *const usize; let u = Union { b: 0 }; @@ -317,6 +334,8 @@ fn main() { let a = &packed.a; let b = &packed.b; let c = &packed.c; + let h = HasAligned{ a: NeedsAlign { a: 1 } }; + h.a.calls_autoref(); } } "# From c9e670b8754b8262b5071a96c32cbcd22ff968f4 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Mon, 15 Jun 2020 08:21:32 -0400 Subject: [PATCH 04/16] Update FIXME comment to be more useful --- crates/ra_ide/src/syntax_highlighting.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index c5098189b3..9e8419c5f8 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -579,7 +579,8 @@ fn highlight_element( return None; } - // FIXME account for alignment... somehow + // FIXME This needs layout computation to be correct. It will highlight + // more than it should with the current implementation. Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe } From 38440d53d8329ac9f3f2013c6e32b3f69b069c72 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sat, 27 Jun 2020 14:42:42 -0400 Subject: [PATCH 05/16] Cleanup repr check, fix packed repr check and test --- crates/ra_hir_def/src/adt.rs | 11 ++++++++--- crates/ra_ide/src/syntax_highlighting.rs | 4 ++-- crates/ra_ide/src/syntax_highlighting/tests.rs | 14 +++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 6d59c86428..4ba6944805 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -12,10 +12,11 @@ use ra_syntax::ast::{self, NameOwner, VisibilityOwner}; use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; use crate::{ - attr::AttrInput, + attr::{Attr, AttrInput}, body::{CfgExpander, LowerCtx}, db::DefDatabase, item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, + path::{ModPath, PathKind}, src::HasChildSource, src::HasSource, trace::Trace, @@ -69,8 +70,12 @@ pub enum ReprKind { fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option { item_tree.attrs(of).iter().find_map(|a| { - if a.path.segments[0].to_string() == "repr" { - if let Some(AttrInput::TokenTree(subtree)) = &a.input { + if let Attr { + path: ModPath { kind: PathKind::Plain, segments }, + input: Some(AttrInput::TokenTree(subtree)), + } = a + { + if segments.len() == 1 && segments[0].to_string() == "repr" { parse_repr_tt(subtree) } else { None diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 9e8419c5f8..a4a7aa2280 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -565,7 +565,7 @@ fn highlight_element( _ => h, } } - T![&] => { + REF_EXPR => { let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?; let expr = ref_expr.expr()?; let field_expr = match expr { @@ -582,7 +582,7 @@ fn highlight_element( // FIXME This needs layout computation to be correct. It will highlight // more than it should with the current implementation. - Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe + HighlightTag::Operator | HighlightModifier::Unsafe } p if p.is_punct() => match p { T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => { diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index c408058501..a7f5ad8622 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -295,8 +295,6 @@ static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; #[repr(packed)] struct Packed { a: u16, - b: u8, - c: u32, } trait DoTheAutoref { @@ -313,11 +311,11 @@ struct HasAligned { } impl DoTheAutoref for NeedsAlign { - fn calls_autored(&self) {} + fn calls_autoref(&self) {} } fn main() { - let x = &5 as *const usize; + let x = &5 as *const _ as *const usize; let u = Union { b: 0 }; unsafe { unsafe_fn(); @@ -327,13 +325,11 @@ fn main() { Union { a } => (), } HasUnsafeFn.unsafe_method(); - let y = *(x); + let _y = *(x); let z = -x; let a = global_mut.a; - let packed = Packed { a: 0, b: 0, c: 0 }; - let a = &packed.a; - let b = &packed.b; - let c = &packed.c; + let packed = Packed { a: 0 }; + let _a = &packed.a; let h = HasAligned{ a: NeedsAlign { a: 1 } }; h.a.calls_autoref(); } From d5f11e530dbf6edbdd0ca32d6cd5fafe634c8c4a Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sat, 27 Jun 2020 17:11:43 -0400 Subject: [PATCH 06/16] Unsafe borrow of packed fields: account for borrow through ref binding, auto ref function calls --- crates/ra_hir/src/code_model.rs | 5 +- crates/ra_hir_def/src/data.rs | 6 +- crates/ra_hir_def/src/item_tree.rs | 8 +- crates/ra_hir_def/src/item_tree/lower.rs | 9 +- crates/ra_hir_ty/src/method_resolution.rs | 2 +- crates/ra_ide/src/completion/complete_dot.rs | 2 +- .../src/completion/complete_trait_impl.rs | 2 +- crates/ra_ide/src/completion/presentation.rs | 2 +- crates/ra_ide/src/syntax_highlighting.rs | 137 +++++++++++++++--- .../ra_ide/src/syntax_highlighting/tests.rs | 31 ++-- 10 files changed, 156 insertions(+), 48 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 0007d7fa88..a880fa6713 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -11,6 +11,7 @@ use hir_def::{ docs::Documentation, expr::{BindingAnnotation, Pat, PatId}, import_map, + item_tree::SelfParam, per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, @@ -670,8 +671,8 @@ impl Function { db.function_data(self.id).name.clone() } - pub fn has_self_param(self, db: &dyn HirDatabase) -> bool { - db.function_data(self.id).has_self_param + pub fn self_param(self, db: &dyn HirDatabase) -> Option { + db.function_data(self.id).self_param } pub fn params(self, db: &dyn HirDatabase) -> Vec { diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 88a8ef9bff..2a26b0183e 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -10,7 +10,7 @@ use crate::{ attr::Attrs, body::Expander, db::DefDatabase, - item_tree::{AssocItem, ItemTreeId, ModItem}, + item_tree::{AssocItem, ItemTreeId, ModItem, SelfParam}, type_ref::{TypeBound, TypeRef}, visibility::RawVisibility, AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, @@ -25,7 +25,7 @@ pub struct FunctionData { pub attrs: Attrs, /// True if the first param is `self`. This is relevant to decide whether this /// can be called as a method. - pub has_self_param: bool, + pub self_param: Option, pub is_unsafe: bool, pub is_varargs: bool, pub visibility: RawVisibility, @@ -42,7 +42,7 @@ impl FunctionData { params: func.params.to_vec(), ret_type: func.ret_type.clone(), attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), - has_self_param: func.has_self_param, + self_param: func.self_param, is_unsafe: func.is_unsafe, is_varargs: func.is_varargs, visibility: item_tree[func.visibility].clone(), diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs index a67e75dac0..1eaea66e4a 100644 --- a/crates/ra_hir_def/src/item_tree.rs +++ b/crates/ra_hir_def/src/item_tree.rs @@ -500,7 +500,7 @@ pub struct Function { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: GenericParamsId, - pub has_self_param: bool, + pub self_param: Option, pub is_unsafe: bool, pub params: Box<[TypeRef]>, pub is_varargs: bool, @@ -508,6 +508,12 @@ pub struct Function { pub ast_id: FileAstId, } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct SelfParam { + pub is_ref: bool, + pub is_mut: bool, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub struct Struct { pub name: Name, diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs index 450ef87981..89ad91d376 100644 --- a/crates/ra_hir_def/src/item_tree/lower.rs +++ b/crates/ra_hir_def/src/item_tree/lower.rs @@ -283,7 +283,7 @@ impl Ctx { let name = func.name()?.as_name(); let mut params = Vec::new(); - let mut has_self_param = false; + let mut func_self_param = None; if let Some(param_list) = func.param_list() { if let Some(self_param) = param_list.self_param() { let self_type = match self_param.ty() { @@ -302,7 +302,10 @@ impl Ctx { } }; params.push(self_type); - has_self_param = true; + func_self_param = Some(SelfParam { + is_ref: self_param.amp_token().is_some(), + is_mut: self_param.mut_token().is_some(), + }); } for param in param_list.params() { let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); @@ -335,7 +338,7 @@ impl Ctx { name, visibility, generic_params: GenericParamsId::EMPTY, - has_self_param, + self_param: func_self_param, is_unsafe: func.unsafe_token().is_some(), params: params.into_boxed_slice(), is_varargs, diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index fb4b30a131..79c5adf0f1 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -640,7 +640,7 @@ fn is_valid_candidate( } } if let Some(receiver_ty) = receiver_ty { - if !data.has_self_param { + if data.self_param.is_none() { return false; } let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 5326652852..5488db43f7 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -48,7 +48,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T let mut seen_methods = FxHashSet::default(); let traits_in_scope = ctx.scope.traits_in_scope(); receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { - if func.has_self_param(ctx.db) + if func.self_param(ctx.db).is_some() && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) && seen_methods.insert(func.name(ctx.db)) { diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index d9a0ef167d..e3ba7ebc47 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -136,7 +136,7 @@ fn add_function_impl( .lookup_by(fn_name) .set_documentation(func.docs(ctx.db)); - let completion_kind = if func.has_self_param(ctx.db) { + let completion_kind = if func.self_param(ctx.db).is_some() { CompletionItemKind::Method } else { CompletionItemKind::Function diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 9a94ff4767..fc3d1a4bda 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -191,7 +191,7 @@ impl Completions { func: hir::Function, local_name: Option, ) { - let has_self_param = func.has_self_param(ctx.db); + let has_self_param = func.self_param(ctx.db).is_some(); let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); let ast_node = func.source(ctx.db).value; diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index a4a7aa2280..454fef39c6 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -497,9 +497,9 @@ fn highlight_element( match name_kind { Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(), Some(NameClass::Definition(def)) => { - highlight_name(db, def, false) | HighlightModifier::Definition + highlight_name(sema, db, def, None, false) | HighlightModifier::Definition } - Some(NameClass::ConstReference(def)) => highlight_name(db, def, false), + Some(NameClass::ConstReference(def)) => highlight_name(sema, db, def, None, false), Some(NameClass::FieldShorthand { field, .. }) => { let mut h = HighlightTag::Field.into(); if let Definition::Field(field) = field { @@ -532,7 +532,7 @@ fn highlight_element( binding_hash = Some(calc_binding_hash(&name, *shadow_count)) } }; - highlight_name(db, def, possibly_unsafe) + highlight_name(sema, db, def, Some(name_ref), possibly_unsafe) } NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), }, @@ -565,8 +565,8 @@ fn highlight_element( _ => h, } } - REF_EXPR => { - let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?; + T![&] => { + let ref_expr = element.parent().and_then(ast::RefExpr::cast)?; let expr = ref_expr.expr()?; let field_expr = match expr { ast::Expr::FieldExpr(fe) => fe, @@ -668,6 +668,52 @@ fn highlight_element( HighlightTag::SelfKeyword.into() } } + T![ref] => { + let modifier: Option = (|| { + let bind_pat = element.parent().and_then(ast::BindPat::cast)?; + let parent = bind_pat.syntax().parent()?; + + let ty = if let Some(pat_list) = + ast::RecordFieldPatList::cast(parent.clone()) + { + let record_pat = + pat_list.syntax().parent().and_then(ast::RecordPat::cast)?; + sema.type_of_pat(&ast::Pat::RecordPat(record_pat)) + } else if let Some(let_stmt) = ast::LetStmt::cast(parent.clone()) { + let field_expr = + if let ast::Expr::FieldExpr(field_expr) = let_stmt.initializer()? { + field_expr + } else { + return None; + }; + + sema.type_of_expr(&field_expr.expr()?) + } else if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent) { + let record_pat = record_field_pat + .syntax() + .parent() + .and_then(ast::RecordFieldPatList::cast)? + .syntax() + .parent() + .and_then(ast::RecordPat::cast)?; + sema.type_of_pat(&ast::Pat::RecordPat(record_pat)) + } else { + None + }?; + + if !ty.is_packed(db) { + return None; + } + + Some(HighlightModifier::Unsafe) + })(); + + if let Some(modifier) = modifier { + h | modifier + } else { + h + } + } _ => h, } } @@ -697,7 +743,13 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool { } } -fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> Highlight { +fn highlight_name( + sema: &Semantics, + db: &RootDatabase, + def: Definition, + name_ref: Option, + possibly_unsafe: bool, +) -> Highlight { match def { Definition::Macro(_) => HighlightTag::Macro, Definition::Field(field) => { @@ -716,6 +768,29 @@ fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> let mut h = HighlightTag::Function.into(); if func.is_unsafe(db) { h |= HighlightModifier::Unsafe; + } else { + (|| { + let method_call_expr = + name_ref?.syntax().parent().and_then(ast::MethodCallExpr::cast)?; + let expr = method_call_expr.expr()?; + let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { + Some(field_expr) + } else { + None + }?; + let ty = sema.type_of_expr(&field_expr.expr()?)?; + if !ty.is_packed(db) { + return None; + } + + let func = sema.resolve_method_call(&method_call_expr)?; + if func.self_param(db)?.is_ref { + Some(HighlightModifier::Unsafe) + } else { + None + } + })() + .map(|modifier| h |= modifier); } return h; } @@ -787,8 +862,33 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics return default.into(), }; - let tag = match parent.kind() { - METHOD_CALL_EXPR => HighlightTag::Function, + match parent.kind() { + METHOD_CALL_EXPR => { + let mut h = Highlight::new(HighlightTag::Function); + let modifier: Option = (|| { + let method_call_expr = ast::MethodCallExpr::cast(parent)?; + let expr = method_call_expr.expr()?; + let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { + field_expr + } else { + return None; + }; + + let expr = field_expr.expr()?; + let ty = sema.type_of_expr(&expr)?; + if ty.is_packed(sema.db) { + Some(HighlightModifier::Unsafe) + } else { + None + } + })(); + + if let Some(modifier) = modifier { + h |= modifier; + } + + h + } FIELD_EXPR => { let h = HighlightTag::Field; let is_union = ast::FieldExpr::cast(parent) @@ -801,7 +901,7 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics { let path = match parent.parent().and_then(ast::Path::cast) { @@ -826,18 +926,15 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics HighlightTag::Function, - _ => { - if name.text().chars().next().unwrap_or_default().is_uppercase() { - HighlightTag::Struct - } else { - HighlightTag::Constant - } + CALL_EXPR => HighlightTag::Function.into(), + _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { + HighlightTag::Struct.into() + } else { + HighlightTag::Constant } + .into(), } } - _ => default, - }; - - tag.into() + _ => default.into(), + } } diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index a7f5ad8622..a8087635a8 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -301,16 +301,7 @@ trait DoTheAutoref { fn calls_autoref(&self); } -struct NeedsAlign { - a: u16 -} - -#[repr(packed)] -struct HasAligned { - a: NeedsAlign -} - -impl DoTheAutoref for NeedsAlign { +impl DoTheAutoref for u16 { fn calls_autoref(&self) {} } @@ -318,6 +309,7 @@ fn main() { let x = &5 as *const _ as *const usize; let u = Union { b: 0 }; unsafe { + // unsafe fn and method calls unsafe_fn(); let b = u.b; match u { @@ -325,13 +317,22 @@ fn main() { Union { a } => (), } HasUnsafeFn.unsafe_method(); - let _y = *(x); - let z = -x; + + // unsafe deref + let y = *x; + + // unsafe access to a static mut let a = global_mut.a; + + // unsafe ref of packed fields let packed = Packed { a: 0 }; - let _a = &packed.a; - let h = HasAligned{ a: NeedsAlign { a: 1 } }; - h.a.calls_autoref(); + let a = &packed.a; + let ref a = packed.a; + let Packed { ref a } = packed; + let Packed { a: ref _a } = packed; + + // unsafe auto ref of packed field + packed.a.calls_autoref(); } } "# From aca3d6c57ec2c668cdb51eca34d6f7bc8fa7412b Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sat, 27 Jun 2020 17:28:07 -0400 Subject: [PATCH 07/16] Deduplicate unsafe method call into a single function --- crates/ra_ide/src/syntax_highlighting.rs | 72 ++++++++++-------------- 1 file changed, 31 insertions(+), 41 deletions(-) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 454fef39c6..02b16b13c0 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -743,6 +743,26 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool { } } +fn is_method_call_unsafe( + sema: &Semantics, + method_call_expr: ast::MethodCallExpr, +) -> Option<()> { + let expr = method_call_expr.expr()?; + let field_expr = + if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None }; + let ty = sema.type_of_expr(&field_expr.expr()?)?; + if !ty.is_packed(sema.db) { + return None; + } + + let func = sema.resolve_method_call(&method_call_expr)?; + if func.self_param(sema.db)?.is_ref { + Some(()) + } else { + None + } +} + fn highlight_name( sema: &Semantics, db: &RootDatabase, @@ -769,28 +789,13 @@ fn highlight_name( if func.is_unsafe(db) { h |= HighlightModifier::Unsafe; } else { - (|| { - let method_call_expr = - name_ref?.syntax().parent().and_then(ast::MethodCallExpr::cast)?; - let expr = method_call_expr.expr()?; - let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { - Some(field_expr) - } else { - None - }?; - let ty = sema.type_of_expr(&field_expr.expr()?)?; - if !ty.is_packed(db) { - return None; - } - - let func = sema.resolve_method_call(&method_call_expr)?; - if func.self_param(db)?.is_ref { - Some(HighlightModifier::Unsafe) - } else { - None - } - })() - .map(|modifier| h |= modifier); + let is_unsafe = name_ref + .and_then(|name_ref| name_ref.syntax().parent()) + .and_then(ast::MethodCallExpr::cast) + .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr)); + if is_unsafe.is_some() { + h |= HighlightModifier::Unsafe; + } } return h; } @@ -865,26 +870,11 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics { let mut h = Highlight::new(HighlightTag::Function); - let modifier: Option = (|| { - let method_call_expr = ast::MethodCallExpr::cast(parent)?; - let expr = method_call_expr.expr()?; - let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { - field_expr - } else { - return None; - }; + let is_unsafe = ast::MethodCallExpr::cast(parent) + .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr)); - let expr = field_expr.expr()?; - let ty = sema.type_of_expr(&expr)?; - if ty.is_packed(sema.db) { - Some(HighlightModifier::Unsafe) - } else { - None - } - })(); - - if let Some(modifier) = modifier { - h |= modifier; + if is_unsafe.is_some() { + h |= HighlightModifier::Unsafe; } h From c5cc24cb312c70159e63315ea49769b575e8cb65 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sun, 28 Jun 2020 16:04:00 -0400 Subject: [PATCH 08/16] Revert function structs back to using bool to track self param, use first param for self information in syntax highlighting instead --- crates/ra_hir/src/code_model.rs | 5 ++--- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir_def/src/data.rs | 6 +++--- crates/ra_hir_def/src/item_tree.rs | 8 +------- crates/ra_hir_def/src/item_tree/lower.rs | 9 +++------ crates/ra_hir_ty/src/method_resolution.rs | 2 +- crates/ra_ide/src/completion/complete_dot.rs | 2 +- crates/ra_ide/src/completion/complete_trait_impl.rs | 2 +- crates/ra_ide/src/completion/presentation.rs | 2 +- crates/ra_ide/src/syntax_highlighting.rs | 11 ++++++++--- 10 files changed, 22 insertions(+), 27 deletions(-) diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index a880fa6713..0007d7fa88 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -11,7 +11,6 @@ use hir_def::{ docs::Documentation, expr::{BindingAnnotation, Pat, PatId}, import_map, - item_tree::SelfParam, per_ns::PerNs, resolver::{HasResolver, Resolver}, src::HasSource as _, @@ -671,8 +670,8 @@ impl Function { db.function_data(self.id).name.clone() } - pub fn self_param(self, db: &dyn HirDatabase) -> Option { - db.function_data(self.id).self_param + pub fn has_self_param(self, db: &dyn HirDatabase) -> bool { + db.function_data(self.id).has_self_param } pub fn params(self, db: &dyn HirDatabase) -> Vec { diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 31f3241c9e..34b02c5365 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -49,7 +49,7 @@ pub use hir_def::{ docs::Documentation, nameres::ModuleSource, path::{ModPath, Path, PathKind}, - type_ref::Mutability, + type_ref::{Mutability, TypeRef}, }; pub use hir_expand::{ hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs index 2a26b0183e..88a8ef9bff 100644 --- a/crates/ra_hir_def/src/data.rs +++ b/crates/ra_hir_def/src/data.rs @@ -10,7 +10,7 @@ use crate::{ attr::Attrs, body::Expander, db::DefDatabase, - item_tree::{AssocItem, ItemTreeId, ModItem, SelfParam}, + item_tree::{AssocItem, ItemTreeId, ModItem}, type_ref::{TypeBound, TypeRef}, visibility::RawVisibility, AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, @@ -25,7 +25,7 @@ pub struct FunctionData { pub attrs: Attrs, /// True if the first param is `self`. This is relevant to decide whether this /// can be called as a method. - pub self_param: Option, + pub has_self_param: bool, pub is_unsafe: bool, pub is_varargs: bool, pub visibility: RawVisibility, @@ -42,7 +42,7 @@ impl FunctionData { params: func.params.to_vec(), ret_type: func.ret_type.clone(), attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), - self_param: func.self_param, + has_self_param: func.has_self_param, is_unsafe: func.is_unsafe, is_varargs: func.is_varargs, visibility: item_tree[func.visibility].clone(), diff --git a/crates/ra_hir_def/src/item_tree.rs b/crates/ra_hir_def/src/item_tree.rs index 1eaea66e4a..a67e75dac0 100644 --- a/crates/ra_hir_def/src/item_tree.rs +++ b/crates/ra_hir_def/src/item_tree.rs @@ -500,7 +500,7 @@ pub struct Function { pub name: Name, pub visibility: RawVisibilityId, pub generic_params: GenericParamsId, - pub self_param: Option, + pub has_self_param: bool, pub is_unsafe: bool, pub params: Box<[TypeRef]>, pub is_varargs: bool, @@ -508,12 +508,6 @@ pub struct Function { pub ast_id: FileAstId, } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct SelfParam { - pub is_ref: bool, - pub is_mut: bool, -} - #[derive(Debug, Clone, Eq, PartialEq)] pub struct Struct { pub name: Name, diff --git a/crates/ra_hir_def/src/item_tree/lower.rs b/crates/ra_hir_def/src/item_tree/lower.rs index 89ad91d376..450ef87981 100644 --- a/crates/ra_hir_def/src/item_tree/lower.rs +++ b/crates/ra_hir_def/src/item_tree/lower.rs @@ -283,7 +283,7 @@ impl Ctx { let name = func.name()?.as_name(); let mut params = Vec::new(); - let mut func_self_param = None; + let mut has_self_param = false; if let Some(param_list) = func.param_list() { if let Some(self_param) = param_list.self_param() { let self_type = match self_param.ty() { @@ -302,10 +302,7 @@ impl Ctx { } }; params.push(self_type); - func_self_param = Some(SelfParam { - is_ref: self_param.amp_token().is_some(), - is_mut: self_param.mut_token().is_some(), - }); + has_self_param = true; } for param in param_list.params() { let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); @@ -338,7 +335,7 @@ impl Ctx { name, visibility, generic_params: GenericParamsId::EMPTY, - self_param: func_self_param, + has_self_param, is_unsafe: func.unsafe_token().is_some(), params: params.into_boxed_slice(), is_varargs, diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 79c5adf0f1..fb4b30a131 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs @@ -640,7 +640,7 @@ fn is_valid_candidate( } } if let Some(receiver_ty) = receiver_ty { - if data.self_param.is_none() { + if !data.has_self_param { return false; } let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 5488db43f7..5326652852 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -48,7 +48,7 @@ fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &T let mut seen_methods = FxHashSet::default(); let traits_in_scope = ctx.scope.traits_in_scope(); receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { - if func.self_param(ctx.db).is_some() + if func.has_self_param(ctx.db) && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) && seen_methods.insert(func.name(ctx.db)) { diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index e3ba7ebc47..d9a0ef167d 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -136,7 +136,7 @@ fn add_function_impl( .lookup_by(fn_name) .set_documentation(func.docs(ctx.db)); - let completion_kind = if func.self_param(ctx.db).is_some() { + let completion_kind = if func.has_self_param(ctx.db) { CompletionItemKind::Method } else { CompletionItemKind::Function diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index fc3d1a4bda..9a94ff4767 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -191,7 +191,7 @@ impl Completions { func: hir::Function, local_name: Option, ) { - let has_self_param = func.self_param(ctx.db).is_some(); + let has_self_param = func.has_self_param(ctx.db); let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); let ast_node = func.source(ctx.db).value; diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 02b16b13c0..d5a5f69cca 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, VariantDef}; +use hir::{Name, Semantics, TypeRef, VariantDef}; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, RootDatabase, @@ -756,8 +756,13 @@ fn is_method_call_unsafe( } let func = sema.resolve_method_call(&method_call_expr)?; - if func.self_param(sema.db)?.is_ref { - Some(()) + if func.has_self_param(sema.db) { + let params = func.params(sema.db); + if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) { + Some(()) + } else { + None + } } else { None } From 08182aa9fad4021e60cdc80ee0a578929507e115 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sun, 19 Jul 2020 11:45:46 -0400 Subject: [PATCH 09/16] Move unsafe packed ref logic to Semantics, use `Attrs::by_key` to simplify repr attr lookup --- crates/ra_hir/src/semantics.rs | 41 ++ crates/ra_hir_def/src/adt.rs | 25 +- crates/ra_ide/src/call_info.rs.orig | 769 +++++++++++++++++++++++ crates/ra_ide/src/syntax_highlighting.rs | 34 +- 4 files changed, 815 insertions(+), 54 deletions(-) create mode 100644 crates/ra_ide/src/call_info.rs.orig diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index e392130ab6..1072b39718 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -279,6 +279,47 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn assert_contains_node(&self, node: &SyntaxNode) { self.imp.assert_contains_node(node) } + + pub fn is_unsafe_pat(&self, pat: &ast::Pat) -> bool { + let ty = (|| { + let parent = match pat { + ast::Pat::BindPat(bind_pat) => bind_pat.syntax().parent()?, + _ => return None, + }; + + // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or + // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`, + // so this tries to lookup the `BindPat` anywhere along that structure to the + // `RecordPat` so we can get the containing type. + let record_pat = ast::RecordFieldPat::cast(parent.clone()) + .and_then(|record_pat| record_pat.syntax().parent()) + .or_else(|| Some(parent.clone())) + .and_then(|parent| { + ast::RecordFieldPatList::cast(parent)? + .syntax() + .parent() + .and_then(ast::RecordPat::cast) + }); + + // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if + // this is initialized from a `FieldExpr`. + if let Some(record_pat) = record_pat { + self.type_of_pat(&ast::Pat::RecordPat(record_pat)) + } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { + let field_expr = match let_stmt.initializer()? { + ast::Expr::FieldExpr(field_expr) => field_expr, + _ => return None, + }; + + self.type_of_expr(&field_expr.expr()?) + } else { + None + } + })(); + + // Binding a reference to a packed type is possibly unsafe. + ty.map(|ty| ty.is_packed(self.db)).unwrap_or(false) + } } impl<'db> SemanticsImpl<'db> { diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 4ba6944805..35c3a91402 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -12,11 +12,9 @@ use ra_syntax::ast::{self, NameOwner, VisibilityOwner}; use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; use crate::{ - attr::{Attr, AttrInput}, body::{CfgExpander, LowerCtx}, db::DefDatabase, item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, - path::{ModPath, PathKind}, src::HasChildSource, src::HasSource, trace::Trace, @@ -69,21 +67,7 @@ pub enum ReprKind { } fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option { - item_tree.attrs(of).iter().find_map(|a| { - if let Attr { - path: ModPath { kind: PathKind::Plain, segments }, - input: Some(AttrInput::TokenTree(subtree)), - } = a - { - if segments.len() == 1 && segments[0].to_string() == "repr" { - parse_repr_tt(subtree) - } else { - None - } - } else { - None - } - }) + item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt) } fn parse_repr_tt(tt: &Subtree) -> Option { @@ -93,11 +77,8 @@ fn parse_repr_tt(tt: &Subtree) -> Option { } let mut it = tt.token_trees.iter(); - match it.next() { - None => None, - Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => { - Some(ReprKind::Packed) - } + match it.next()? { + TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed), _ => Some(ReprKind::Other), } } diff --git a/crates/ra_ide/src/call_info.rs.orig b/crates/ra_ide/src/call_info.rs.orig new file mode 100644 index 0000000000..0e04c0b607 --- /dev/null +++ b/crates/ra_ide/src/call_info.rs.orig @@ -0,0 +1,769 @@ +//! FIXME: write short doc here +use either::Either; +use hir::{Docs, HirDisplay, Semantics, Type}; +use ra_ide_db::RootDatabase; +use ra_syntax::{ + ast::{self, ArgListOwner}, + match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, +}; +use stdx::format_to; +use test_utils::mark; + +use crate::FilePosition; + +/// Contains information about a call site. Specifically the +/// `FunctionSignature`and current parameter. +#[derive(Debug)] +pub struct CallInfo { + pub doc: Option, + pub signature: String, + pub active_parameter: Option, + parameters: Vec, +} + +impl CallInfo { + pub fn parameter_labels(&self) -> impl Iterator + '_ { + self.parameters.iter().map(move |&it| &self.signature[it]) + } + pub fn parameter_ranges(&self) -> &[TextRange] { + &self.parameters + } + fn push_param(&mut self, param: &str) { + if !self.signature.ends_with('(') { + self.signature.push_str(", "); + } + let start = TextSize::of(&self.signature); + self.signature.push_str(param); + let end = TextSize::of(&self.signature); + self.parameters.push(TextRange::new(start, end)) + } +} + +/// Computes parameter information for the given call expression. +pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option { + let sema = Semantics::new(db); + let file = sema.parse(position.file_id); + let file = file.syntax(); + let token = file.token_at_offset(position.offset).next()?; + let token = sema.descend_into_macros(token); + + let (callable, active_parameter) = call_info_impl(&sema, token)?; + + let mut res = + CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter }; + + match callable.kind() { + hir::CallableKind::Function(func) => { + res.doc = func.docs(db).map(|it| it.as_str().to_string()); + format_to!(res.signature, "fn {}", func.name(db)); + } + hir::CallableKind::TupleStruct(strukt) => { + res.doc = strukt.docs(db).map(|it| it.as_str().to_string()); + format_to!(res.signature, "struct {}", strukt.name(db)); + } + hir::CallableKind::TupleEnumVariant(variant) => { + res.doc = variant.docs(db).map(|it| it.as_str().to_string()); + format_to!( + res.signature, + "enum {}::{}", + variant.parent_enum(db).name(db), + variant.name(db) + ); + } + hir::CallableKind::Closure => (), + } + + res.signature.push('('); + { + if let Some(self_param) = callable.receiver_param(db) { + format_to!(res.signature, "{}", self_param) + } + let mut buf = String::new(); + for (pat, ty) in callable.params(db) { + buf.clear(); + if let Some(pat) = pat { + match pat { + Either::Left(_self) => format_to!(buf, "self: "), + Either::Right(pat) => format_to!(buf, "{}: ", pat), + } + } + format_to!(buf, "{}", ty.display(db)); + res.push_param(&buf); + } + } + res.signature.push(')'); + + match callable.kind() { + hir::CallableKind::Function(_) | hir::CallableKind::Closure => { + let ret_type = callable.return_type(); + if !ret_type.is_unit() { + format_to!(res.signature, " -> {}", ret_type.display(db)); + } + } + hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} + } + Some(res) +} + +fn call_info_impl( + sema: &Semantics, + token: SyntaxToken, +) -> Option<(hir::Callable, Option)> { + // Find the calling expression and it's NameRef + let calling_node = FnCallNode::with_node(&token.parent())?; + +<<<<<<< HEAD + let callable = match &calling_node { + FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?, + FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?, + }; + let active_param = if let Some(arg_list) = calling_node.arg_list() { + // Number of arguments specified at the call site + let num_args_at_callsite = arg_list.args().count(); + + let arg_list_range = arg_list.syntax().text_range(); + if !arg_list_range.contains_inclusive(token.text_range().start()) { + mark::hit!(call_info_bad_offset); + return None; +======= + let (mut call_info, has_self) = match &calling_node { + FnCallNode::CallExpr(call) => { + //FIXME: Type::as_callable is broken + let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?; + match callable_def { + hir::CallableDef::FunctionId(it) => { + let fn_def = it.into(); + (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db)) + } + hir::CallableDef::StructId(it) => { + (CallInfo::with_struct(sema.db, it.into())?, false) + } + hir::CallableDef::EnumVariantId(it) => { + (CallInfo::with_enum_variant(sema.db, it.into())?, false) + } + } + } + FnCallNode::MethodCallExpr(method_call) => { + let function = sema.resolve_method_call(&method_call)?; + (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db)) + } + FnCallNode::MacroCallExpr(macro_call) => { + let macro_def = sema.resolve_macro_call(¯o_call)?; + (CallInfo::with_macro(sema.db, macro_def)?, false) +>>>>>>> Revert function structs back to using bool to track self param, use first param for self information in syntax highlighting instead + } + let param = std::cmp::min( + num_args_at_callsite, + arg_list + .args() + .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start()) + .count(), + ); + + Some(param) + } else { + None + }; + Some((callable, active_param)) +} + +#[derive(Debug)] +pub(crate) struct ActiveParameter { + pub(crate) ty: Type, + pub(crate) name: String, +} + +impl ActiveParameter { + pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option { + let sema = Semantics::new(db); + let file = sema.parse(position.file_id); + let file = file.syntax(); + let token = file.token_at_offset(position.offset).next()?; + let token = sema.descend_into_macros(token); + Self::at_token(&sema, token) + } + + pub(crate) fn at_token(sema: &Semantics, token: SyntaxToken) -> Option { + let (signature, active_parameter) = call_info_impl(&sema, token)?; + + let idx = active_parameter?; + let mut params = signature.params(sema.db); + if !(idx < params.len()) { + mark::hit!(too_many_arguments); + return None; + } + let (pat, ty) = params.swap_remove(idx); + let name = pat?.to_string(); + Some(ActiveParameter { ty, name }) + } +} + +#[derive(Debug)] +pub(crate) enum FnCallNode { + CallExpr(ast::CallExpr), + MethodCallExpr(ast::MethodCallExpr), +} + +impl FnCallNode { + fn with_node(syntax: &SyntaxNode) -> Option { + syntax.ancestors().find_map(|node| { + match_ast! { + match node { + ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)), + ast::MethodCallExpr(it) => { + let arg_list = it.arg_list()?; + if !arg_list.syntax().text_range().contains_range(syntax.text_range()) { + return None; + } + Some(FnCallNode::MethodCallExpr(it)) + }, + _ => None, + } + } + }) + } + + pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option { + match_ast! { + match node { + ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)), + ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)), + _ => None, + } + } + } + + pub(crate) fn name_ref(&self) -> Option { + match self { + FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? { + ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?, + _ => return None, + }), + + FnCallNode::MethodCallExpr(call_expr) => { + call_expr.syntax().children().filter_map(ast::NameRef::cast).next() + } + } + } + + fn arg_list(&self) -> Option { + match self { + FnCallNode::CallExpr(expr) => expr.arg_list(), + FnCallNode::MethodCallExpr(expr) => expr.arg_list(), + } + } +} + +#[cfg(test)] +mod tests { + use expect::{expect, Expect}; + use test_utils::mark; + + use crate::mock_analysis::analysis_and_position; + + fn check(ra_fixture: &str, expect: Expect) { + let (analysis, position) = analysis_and_position(ra_fixture); + let call_info = analysis.call_info(position).unwrap(); + let actual = match call_info { + Some(call_info) => { + let docs = match &call_info.doc { + None => "".to_string(), + Some(docs) => format!("{}\n------\n", docs.as_str()), + }; + let params = call_info + .parameter_labels() + .enumerate() + .map(|(i, param)| { + if Some(i) == call_info.active_parameter { + format!("<{}>", param) + } else { + param.to_string() + } + }) + .collect::>() + .join(", "); + format!("{}{}\n({})\n", docs, call_info.signature, params) + } + None => String::new(), + }; + expect.assert_eq(&actual); + } + + #[test] + fn test_fn_signature_two_args() { + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(<|>3, ); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (, y: u32) + "#]], + ); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(3<|>, ); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (, y: u32) + "#]], + ); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(3,<|> ); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (x: u32, ) + "#]], + ); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(3, <|>); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (x: u32, ) + "#]], + ); + } + + #[test] + fn test_fn_signature_two_args_empty() { + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo(<|>); } +"#, + expect![[r#" + fn foo(x: u32, y: u32) -> u32 + (, y: u32) + "#]], + ); + } + + #[test] + fn test_fn_signature_two_args_first_generics() { + check( + r#" +fn foo(x: T, y: U) -> u32 + where T: Copy + Display, U: Debug +{ x + y } + +fn bar() { foo(<|>3, ); } +"#, + expect![[r#" + fn foo(x: i32, y: {unknown}) -> u32 + (, y: {unknown}) + "#]], + ); + } + + #[test] + fn test_fn_signature_no_params() { + check( + r#" +fn foo() -> T where T: Copy + Display {} +fn bar() { foo(<|>); } +"#, + expect![[r#" + fn foo() -> {unknown} + () + "#]], + ); + } + + #[test] + fn test_fn_signature_for_impl() { + check( + r#" +struct F; +impl F { pub fn new() { } } +fn bar() { + let _ : F = F::new(<|>); +} +"#, + expect![[r#" + fn new() + () + "#]], + ); + } + + #[test] + fn test_fn_signature_for_method_self() { + check( + r#" +struct S; +impl S { pub fn do_it(&self) {} } + +fn bar() { + let s: S = S; + s.do_it(<|>); +} +"#, + expect![[r#" + fn do_it(&self) + () + "#]], + ); + } + + #[test] + fn test_fn_signature_for_method_with_arg() { + check( + r#" +struct S; +impl S { + fn foo(&self, x: i32) {} +} + +fn main() { S.foo(<|>); } +"#, + expect![[r#" + fn foo(&self, x: i32) + () + "#]], + ); + } + + #[test] + fn test_fn_signature_for_method_with_arg_as_assoc_fn() { + check( + r#" +struct S; +impl S { + fn foo(&self, x: i32) {} +} + +fn main() { S::foo(<|>); } +"#, + expect![[r#" + fn foo(self: &S, x: i32) + (, x: i32) + "#]], + ); + } + + #[test] + fn test_fn_signature_with_docs_simple() { + check( + r#" +/// test +// non-doc-comment +fn foo(j: u32) -> u32 { + j +} + +fn bar() { + let _ = foo(<|>); +} +"#, + expect![[r#" + test + ------ + fn foo(j: u32) -> u32 + () + "#]], + ); + } + + #[test] + fn test_fn_signature_with_docs() { + check( + r#" +/// Adds one to the number given. +/// +/// # Examples +/// +/// ``` +/// let five = 5; +/// +/// assert_eq!(6, my_crate::add_one(5)); +/// ``` +pub fn add_one(x: i32) -> i32 { + x + 1 +} + +pub fn do() { + add_one(<|> +}"#, + expect![[r##" + Adds one to the number given. + + # Examples + + ``` + let five = 5; + + assert_eq!(6, my_crate::add_one(5)); + ``` + ------ + fn add_one(x: i32) -> i32 + () + "##]], + ); + } + + #[test] + fn test_fn_signature_with_docs_impl() { + check( + r#" +struct addr; +impl addr { + /// Adds one to the number given. + /// + /// # Examples + /// + /// ``` + /// let five = 5; + /// + /// assert_eq!(6, my_crate::add_one(5)); + /// ``` + pub fn add_one(x: i32) -> i32 { + x + 1 + } +} + +pub fn do_it() { + addr {}; + addr::add_one(<|>); +} +"#, + expect![[r##" + Adds one to the number given. + + # Examples + + ``` + let five = 5; + + assert_eq!(6, my_crate::add_one(5)); + ``` + ------ + fn add_one(x: i32) -> i32 + () + "##]], + ); + } + + #[test] + fn test_fn_signature_with_docs_from_actix() { + check( + r#" +struct WriteHandler; + +impl WriteHandler { + /// Method is called when writer emits error. + /// + /// If this method returns `ErrorAction::Continue` writer processing + /// continues otherwise stream processing stops. + fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running { + Running::Stop + } + + /// Method is called when writer finishes. + /// + /// By default this method stops actor's `Context`. + fn finished(&mut self, ctx: &mut Self::Context) { + ctx.stop() + } +} + +pub fn foo(mut r: WriteHandler<()>) { + r.finished(<|>); +} +"#, + expect![[r#" + Method is called when writer finishes. + + By default this method stops actor's `Context`. + ------ + fn finished(&mut self, ctx: &mut {unknown}) + () + "#]], + ); + } + + #[test] + fn call_info_bad_offset() { + mark::check!(call_info_bad_offset); + check( + r#" +fn foo(x: u32, y: u32) -> u32 {x + y} +fn bar() { foo <|> (3, ); } +"#, + expect![[""]], + ); + } + + #[test] + fn test_nested_method_in_lambda() { + check( + r#" +struct Foo; +impl Foo { fn bar(&self, _: u32) { } } + +fn bar(_: u32) { } + +fn main() { + let foo = Foo; + std::thread::spawn(move || foo.bar(<|>)); +} +"#, + expect![[r#" + fn bar(&self, _: u32) + (<_: u32>) + "#]], + ); + } + + #[test] + fn works_for_tuple_structs() { + check( + r#" +/// A cool tuple struct +struct S(u32, i32); +fn main() { + let s = S(0, <|>); +} +"#, + expect![[r#" + A cool tuple struct + ------ + struct S(u32, i32) + (u32, ) + "#]], + ); + } + + #[test] + fn generic_struct() { + check( + r#" +struct S(T); +fn main() { + let s = S(<|>); +} +"#, + expect![[r#" + struct S({unknown}) + (<{unknown}>) + "#]], + ); + } + + #[test] + fn works_for_enum_variants() { + check( + r#" +enum E { + /// A Variant + A(i32), + /// Another + B, + /// And C + C { a: i32, b: i32 } +} + +fn main() { + let a = E::A(<|>); +} +"#, + expect![[r#" + A Variant + ------ + enum E::A(i32) + () + "#]], + ); + } + + #[test] + fn cant_call_struct_record() { + check( + r#" +struct S { x: u32, y: i32 } +fn main() { + let s = S(<|>); +} +"#, + expect![[""]], + ); + } + + #[test] + fn cant_call_enum_record() { + check( + r#" +enum E { + /// A Variant + A(i32), + /// Another + B, + /// And C + C { a: i32, b: i32 } +} + +fn main() { + let a = E::C(<|>); +} +"#, + expect![[""]], + ); + } + + #[test] + fn fn_signature_for_call_in_macro() { + check( + r#" +macro_rules! id { ($($tt:tt)*) => { $($tt)* } } +fn foo() { } +id! { + fn bar() { foo(<|>); } +} +"#, + expect![[r#" + fn foo() + () + "#]], + ); + } + + #[test] + fn call_info_for_lambdas() { + check( + r#" +struct S; +fn foo(s: S) -> i32 { 92 } +fn main() { + (|s| foo(s))(<|>) +} + "#, + expect![[r#" + (S) -> i32 + () + "#]], + ) + } + + #[test] + fn call_info_for_fn_ptr() { + check( + r#" +fn main(f: fn(i32, f64) -> char) { + f(0, <|>) +} + "#, + expect![[r#" + (i32, f64) -> char + (i32, ) + "#]], + ) + } +} diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index d5a5f69cca..cf93205b6e 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -671,41 +671,11 @@ fn highlight_element( T![ref] => { let modifier: Option = (|| { let bind_pat = element.parent().and_then(ast::BindPat::cast)?; - let parent = bind_pat.syntax().parent()?; - - let ty = if let Some(pat_list) = - ast::RecordFieldPatList::cast(parent.clone()) - { - let record_pat = - pat_list.syntax().parent().and_then(ast::RecordPat::cast)?; - sema.type_of_pat(&ast::Pat::RecordPat(record_pat)) - } else if let Some(let_stmt) = ast::LetStmt::cast(parent.clone()) { - let field_expr = - if let ast::Expr::FieldExpr(field_expr) = let_stmt.initializer()? { - field_expr - } else { - return None; - }; - - sema.type_of_expr(&field_expr.expr()?) - } else if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent) { - let record_pat = record_field_pat - .syntax() - .parent() - .and_then(ast::RecordFieldPatList::cast)? - .syntax() - .parent() - .and_then(ast::RecordPat::cast)?; - sema.type_of_pat(&ast::Pat::RecordPat(record_pat)) + if sema.is_unsafe_pat(&ast::Pat::BindPat(bind_pat)) { + Some(HighlightModifier::Unsafe) } else { None - }?; - - if !ty.is_packed(db) { - return None; } - - Some(HighlightModifier::Unsafe) })(); if let Some(modifier) = modifier { From 55633f34048434de18d54b4300bca186db052cf5 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Thu, 23 Jul 2020 09:31:07 -0400 Subject: [PATCH 10/16] Fix rebase errors --- crates/ra_ide/test_data/highlight_doctest.html | 2 +- crates/ra_ide/test_data/highlight_injection.html | 2 +- crates/ra_ide/test_data/highlight_unsafe.html | 5 +++-- crates/ra_ide/test_data/highlighting.html | 10 +++++----- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/ra_ide/test_data/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html index 6322d404fb..46c1e0a11f 100644 --- a/crates/ra_ide/test_data/highlight_doctest.html +++ b/crates/ra_ide/test_data/highlight_doctest.html @@ -87,7 +87,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd /// ```sh /// echo 1 /// ``` - pub fn foo(&self) -> bool { + pub fn foo(&self) -> bool { true } } diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html index 18addd00d2..60c3943994 100644 --- a/crates/ra_ide/test_data/highlight_injection.html +++ b/crates/ra_ide/test_data/highlight_injection.html @@ -35,7 +35,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
fn fixture(ra_fixture: &str) {}
+
fn fixture(ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 79409fe816..454ff6d5f8 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
-    unsafe fn unsafe_method(&self) {}
+    unsafe fn unsafe_method(&self) {}
 }
 
 struct TypeForStaticMut {
@@ -55,9 +55,10 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
 fn main() {
-    let x = &5 as *const usize;
+    let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
     unsafe {
+        // unsafe fn and method calls
         unsafe_fn();
         let b = u.b;
         match u {
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 8e0160eee5..678cf9bd33 100644
--- a/crates/ra_ide/test_data/highlighting.html
+++ b/crates/ra_ide/test_data/highlighting.html
@@ -45,11 +45,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 trait Bar {
-    fn bar(&self) -> i32;
+    fn bar(&self) -> i32;
 }
 
 impl Bar for Foo {
-    fn bar(&self) -> i32 {
+    fn bar(&self) -> i32 {
         self.x
     }
 }
@@ -59,7 +59,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         self.x
     }
 
-    fn qux(&mut self) {
+    fn qux(&mut self) {
         self.x = 0;
     }
 }
@@ -107,8 +107,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     noop!(noop!(1));
 
     let mut x = 42;
-    let y = &mut x;
-    let z = &y;
+    let y = &mut x;
+    let z = &y;
 
     let Foo { x: z, y } = Foo { x: z, y };
 

From 87cb09365cf841b559e76951eedb826f2d4d3dfd Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Thu, 23 Jul 2020 09:39:53 -0400
Subject: [PATCH 11/16] Remove merge backup

---
 crates/ra_ide/src/call_info.rs.orig | 769 ----------------------------
 1 file changed, 769 deletions(-)
 delete mode 100644 crates/ra_ide/src/call_info.rs.orig

diff --git a/crates/ra_ide/src/call_info.rs.orig b/crates/ra_ide/src/call_info.rs.orig
deleted file mode 100644
index 0e04c0b607..0000000000
--- a/crates/ra_ide/src/call_info.rs.orig
+++ /dev/null
@@ -1,769 +0,0 @@
-//! FIXME: write short doc here
-use either::Either;
-use hir::{Docs, HirDisplay, Semantics, Type};
-use ra_ide_db::RootDatabase;
-use ra_syntax::{
-    ast::{self, ArgListOwner},
-    match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize,
-};
-use stdx::format_to;
-use test_utils::mark;
-
-use crate::FilePosition;
-
-/// Contains information about a call site. Specifically the
-/// `FunctionSignature`and current parameter.
-#[derive(Debug)]
-pub struct CallInfo {
-    pub doc: Option,
-    pub signature: String,
-    pub active_parameter: Option,
-    parameters: Vec,
-}
-
-impl CallInfo {
-    pub fn parameter_labels(&self) -> impl Iterator + '_ {
-        self.parameters.iter().map(move |&it| &self.signature[it])
-    }
-    pub fn parameter_ranges(&self) -> &[TextRange] {
-        &self.parameters
-    }
-    fn push_param(&mut self, param: &str) {
-        if !self.signature.ends_with('(') {
-            self.signature.push_str(", ");
-        }
-        let start = TextSize::of(&self.signature);
-        self.signature.push_str(param);
-        let end = TextSize::of(&self.signature);
-        self.parameters.push(TextRange::new(start, end))
-    }
-}
-
-/// Computes parameter information for the given call expression.
-pub(crate) fn call_info(db: &RootDatabase, position: FilePosition) -> Option {
-    let sema = Semantics::new(db);
-    let file = sema.parse(position.file_id);
-    let file = file.syntax();
-    let token = file.token_at_offset(position.offset).next()?;
-    let token = sema.descend_into_macros(token);
-
-    let (callable, active_parameter) = call_info_impl(&sema, token)?;
-
-    let mut res =
-        CallInfo { doc: None, signature: String::new(), parameters: vec![], active_parameter };
-
-    match callable.kind() {
-        hir::CallableKind::Function(func) => {
-            res.doc = func.docs(db).map(|it| it.as_str().to_string());
-            format_to!(res.signature, "fn {}", func.name(db));
-        }
-        hir::CallableKind::TupleStruct(strukt) => {
-            res.doc = strukt.docs(db).map(|it| it.as_str().to_string());
-            format_to!(res.signature, "struct {}", strukt.name(db));
-        }
-        hir::CallableKind::TupleEnumVariant(variant) => {
-            res.doc = variant.docs(db).map(|it| it.as_str().to_string());
-            format_to!(
-                res.signature,
-                "enum {}::{}",
-                variant.parent_enum(db).name(db),
-                variant.name(db)
-            );
-        }
-        hir::CallableKind::Closure => (),
-    }
-
-    res.signature.push('(');
-    {
-        if let Some(self_param) = callable.receiver_param(db) {
-            format_to!(res.signature, "{}", self_param)
-        }
-        let mut buf = String::new();
-        for (pat, ty) in callable.params(db) {
-            buf.clear();
-            if let Some(pat) = pat {
-                match pat {
-                    Either::Left(_self) => format_to!(buf, "self: "),
-                    Either::Right(pat) => format_to!(buf, "{}: ", pat),
-                }
-            }
-            format_to!(buf, "{}", ty.display(db));
-            res.push_param(&buf);
-        }
-    }
-    res.signature.push(')');
-
-    match callable.kind() {
-        hir::CallableKind::Function(_) | hir::CallableKind::Closure => {
-            let ret_type = callable.return_type();
-            if !ret_type.is_unit() {
-                format_to!(res.signature, " -> {}", ret_type.display(db));
-            }
-        }
-        hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {}
-    }
-    Some(res)
-}
-
-fn call_info_impl(
-    sema: &Semantics,
-    token: SyntaxToken,
-) -> Option<(hir::Callable, Option)> {
-    // Find the calling expression and it's NameRef
-    let calling_node = FnCallNode::with_node(&token.parent())?;
-
-<<<<<<< HEAD
-    let callable = match &calling_node {
-        FnCallNode::CallExpr(call) => sema.type_of_expr(&call.expr()?)?.as_callable(sema.db)?,
-        FnCallNode::MethodCallExpr(call) => sema.resolve_method_call_as_callable(call)?,
-    };
-    let active_param = if let Some(arg_list) = calling_node.arg_list() {
-        // Number of arguments specified at the call site
-        let num_args_at_callsite = arg_list.args().count();
-
-        let arg_list_range = arg_list.syntax().text_range();
-        if !arg_list_range.contains_inclusive(token.text_range().start()) {
-            mark::hit!(call_info_bad_offset);
-            return None;
-=======
-    let (mut call_info, has_self) = match &calling_node {
-        FnCallNode::CallExpr(call) => {
-            //FIXME: Type::as_callable is broken
-            let callable_def = sema.type_of_expr(&call.expr()?)?.as_callable()?;
-            match callable_def {
-                hir::CallableDef::FunctionId(it) => {
-                    let fn_def = it.into();
-                    (CallInfo::with_fn(sema.db, fn_def), fn_def.has_self_param(sema.db))
-                }
-                hir::CallableDef::StructId(it) => {
-                    (CallInfo::with_struct(sema.db, it.into())?, false)
-                }
-                hir::CallableDef::EnumVariantId(it) => {
-                    (CallInfo::with_enum_variant(sema.db, it.into())?, false)
-                }
-            }
-        }
-        FnCallNode::MethodCallExpr(method_call) => {
-            let function = sema.resolve_method_call(&method_call)?;
-            (CallInfo::with_fn(sema.db, function), function.has_self_param(sema.db))
-        }
-        FnCallNode::MacroCallExpr(macro_call) => {
-            let macro_def = sema.resolve_macro_call(¯o_call)?;
-            (CallInfo::with_macro(sema.db, macro_def)?, false)
->>>>>>> Revert function structs back to using bool to track self param, use first param for self information in syntax highlighting instead
-        }
-        let param = std::cmp::min(
-            num_args_at_callsite,
-            arg_list
-                .args()
-                .take_while(|arg| arg.syntax().text_range().end() <= token.text_range().start())
-                .count(),
-        );
-
-        Some(param)
-    } else {
-        None
-    };
-    Some((callable, active_param))
-}
-
-#[derive(Debug)]
-pub(crate) struct ActiveParameter {
-    pub(crate) ty: Type,
-    pub(crate) name: String,
-}
-
-impl ActiveParameter {
-    pub(crate) fn at(db: &RootDatabase, position: FilePosition) -> Option {
-        let sema = Semantics::new(db);
-        let file = sema.parse(position.file_id);
-        let file = file.syntax();
-        let token = file.token_at_offset(position.offset).next()?;
-        let token = sema.descend_into_macros(token);
-        Self::at_token(&sema, token)
-    }
-
-    pub(crate) fn at_token(sema: &Semantics, token: SyntaxToken) -> Option {
-        let (signature, active_parameter) = call_info_impl(&sema, token)?;
-
-        let idx = active_parameter?;
-        let mut params = signature.params(sema.db);
-        if !(idx < params.len()) {
-            mark::hit!(too_many_arguments);
-            return None;
-        }
-        let (pat, ty) = params.swap_remove(idx);
-        let name = pat?.to_string();
-        Some(ActiveParameter { ty, name })
-    }
-}
-
-#[derive(Debug)]
-pub(crate) enum FnCallNode {
-    CallExpr(ast::CallExpr),
-    MethodCallExpr(ast::MethodCallExpr),
-}
-
-impl FnCallNode {
-    fn with_node(syntax: &SyntaxNode) -> Option {
-        syntax.ancestors().find_map(|node| {
-            match_ast! {
-                match node {
-                    ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
-                    ast::MethodCallExpr(it) => {
-                        let arg_list = it.arg_list()?;
-                        if !arg_list.syntax().text_range().contains_range(syntax.text_range()) {
-                            return None;
-                        }
-                        Some(FnCallNode::MethodCallExpr(it))
-                    },
-                    _ => None,
-                }
-            }
-        })
-    }
-
-    pub(crate) fn with_node_exact(node: &SyntaxNode) -> Option {
-        match_ast! {
-            match node {
-                ast::CallExpr(it) => Some(FnCallNode::CallExpr(it)),
-                ast::MethodCallExpr(it) => Some(FnCallNode::MethodCallExpr(it)),
-                _ => None,
-            }
-        }
-    }
-
-    pub(crate) fn name_ref(&self) -> Option {
-        match self {
-            FnCallNode::CallExpr(call_expr) => Some(match call_expr.expr()? {
-                ast::Expr::PathExpr(path_expr) => path_expr.path()?.segment()?.name_ref()?,
-                _ => return None,
-            }),
-
-            FnCallNode::MethodCallExpr(call_expr) => {
-                call_expr.syntax().children().filter_map(ast::NameRef::cast).next()
-            }
-        }
-    }
-
-    fn arg_list(&self) -> Option {
-        match self {
-            FnCallNode::CallExpr(expr) => expr.arg_list(),
-            FnCallNode::MethodCallExpr(expr) => expr.arg_list(),
-        }
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use expect::{expect, Expect};
-    use test_utils::mark;
-
-    use crate::mock_analysis::analysis_and_position;
-
-    fn check(ra_fixture: &str, expect: Expect) {
-        let (analysis, position) = analysis_and_position(ra_fixture);
-        let call_info = analysis.call_info(position).unwrap();
-        let actual = match call_info {
-            Some(call_info) => {
-                let docs = match &call_info.doc {
-                    None => "".to_string(),
-                    Some(docs) => format!("{}\n------\n", docs.as_str()),
-                };
-                let params = call_info
-                    .parameter_labels()
-                    .enumerate()
-                    .map(|(i, param)| {
-                        if Some(i) == call_info.active_parameter {
-                            format!("<{}>", param)
-                        } else {
-                            param.to_string()
-                        }
-                    })
-                    .collect::>()
-                    .join(", ");
-                format!("{}{}\n({})\n", docs, call_info.signature, params)
-            }
-            None => String::new(),
-        };
-        expect.assert_eq(&actual);
-    }
-
-    #[test]
-    fn test_fn_signature_two_args() {
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(<|>3, ); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (, y: u32)
-            "#]],
-        );
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3<|>, ); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (, y: u32)
-            "#]],
-        );
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3,<|> ); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (x: u32, )
-            "#]],
-        );
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(3, <|>); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (x: u32, )
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_two_args_empty() {
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo(x: u32, y: u32) -> u32
-                (, y: u32)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_two_args_first_generics() {
-        check(
-            r#"
-fn foo(x: T, y: U) -> u32
-    where T: Copy + Display, U: Debug
-{ x + y }
-
-fn bar() { foo(<|>3, ); }
-"#,
-            expect![[r#"
-                fn foo(x: i32, y: {unknown}) -> u32
-                (, y: {unknown})
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_no_params() {
-        check(
-            r#"
-fn foo() -> T where T: Copy + Display {}
-fn bar() { foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo() -> {unknown}
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_impl() {
-        check(
-            r#"
-struct F;
-impl F { pub fn new() { } }
-fn bar() {
-    let _ : F = F::new(<|>);
-}
-"#,
-            expect![[r#"
-                fn new()
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_method_self() {
-        check(
-            r#"
-struct S;
-impl S { pub fn do_it(&self) {} }
-
-fn bar() {
-    let s: S = S;
-    s.do_it(<|>);
-}
-"#,
-            expect![[r#"
-                fn do_it(&self)
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_method_with_arg() {
-        check(
-            r#"
-struct S;
-impl S {
-    fn foo(&self, x: i32) {}
-}
-
-fn main() { S.foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo(&self, x: i32)
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_for_method_with_arg_as_assoc_fn() {
-        check(
-            r#"
-struct S;
-impl S {
-    fn foo(&self, x: i32) {}
-}
-
-fn main() { S::foo(<|>); }
-"#,
-            expect![[r#"
-                fn foo(self: &S, x: i32)
-                (, x: i32)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_simple() {
-        check(
-            r#"
-/// test
-// non-doc-comment
-fn foo(j: u32) -> u32 {
-    j
-}
-
-fn bar() {
-    let _ = foo(<|>);
-}
-"#,
-            expect![[r#"
-                test
-                ------
-                fn foo(j: u32) -> u32
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs() {
-        check(
-            r#"
-/// Adds one to the number given.
-///
-/// # Examples
-///
-/// ```
-/// let five = 5;
-///
-/// assert_eq!(6, my_crate::add_one(5));
-/// ```
-pub fn add_one(x: i32) -> i32 {
-    x + 1
-}
-
-pub fn do() {
-    add_one(<|>
-}"#,
-            expect![[r##"
-                Adds one to the number given.
-
-                # Examples
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-                ------
-                fn add_one(x: i32) -> i32
-                ()
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_impl() {
-        check(
-            r#"
-struct addr;
-impl addr {
-    /// Adds one to the number given.
-    ///
-    /// # Examples
-    ///
-    /// ```
-    /// let five = 5;
-    ///
-    /// assert_eq!(6, my_crate::add_one(5));
-    /// ```
-    pub fn add_one(x: i32) -> i32 {
-        x + 1
-    }
-}
-
-pub fn do_it() {
-    addr {};
-    addr::add_one(<|>);
-}
-"#,
-            expect![[r##"
-                Adds one to the number given.
-
-                # Examples
-
-                ```
-                let five = 5;
-
-                assert_eq!(6, my_crate::add_one(5));
-                ```
-                ------
-                fn add_one(x: i32) -> i32
-                ()
-            "##]],
-        );
-    }
-
-    #[test]
-    fn test_fn_signature_with_docs_from_actix() {
-        check(
-            r#"
-struct WriteHandler;
-
-impl WriteHandler {
-    /// Method is called when writer emits error.
-    ///
-    /// If this method returns `ErrorAction::Continue` writer processing
-    /// continues otherwise stream processing stops.
-    fn error(&mut self, err: E, ctx: &mut Self::Context) -> Running {
-        Running::Stop
-    }
-
-    /// Method is called when writer finishes.
-    ///
-    /// By default this method stops actor's `Context`.
-    fn finished(&mut self, ctx: &mut Self::Context) {
-        ctx.stop()
-    }
-}
-
-pub fn foo(mut r: WriteHandler<()>) {
-    r.finished(<|>);
-}
-"#,
-            expect![[r#"
-                Method is called when writer finishes.
-
-                By default this method stops actor's `Context`.
-                ------
-                fn finished(&mut self, ctx: &mut {unknown})
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn call_info_bad_offset() {
-        mark::check!(call_info_bad_offset);
-        check(
-            r#"
-fn foo(x: u32, y: u32) -> u32 {x + y}
-fn bar() { foo <|> (3, ); }
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
-    fn test_nested_method_in_lambda() {
-        check(
-            r#"
-struct Foo;
-impl Foo { fn bar(&self, _: u32) { } }
-
-fn bar(_: u32) { }
-
-fn main() {
-    let foo = Foo;
-    std::thread::spawn(move || foo.bar(<|>));
-}
-"#,
-            expect![[r#"
-                fn bar(&self, _: u32)
-                (<_: u32>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn works_for_tuple_structs() {
-        check(
-            r#"
-/// A cool tuple struct
-struct S(u32, i32);
-fn main() {
-    let s = S(0, <|>);
-}
-"#,
-            expect![[r#"
-                A cool tuple struct
-                ------
-                struct S(u32, i32)
-                (u32, )
-            "#]],
-        );
-    }
-
-    #[test]
-    fn generic_struct() {
-        check(
-            r#"
-struct S(T);
-fn main() {
-    let s = S(<|>);
-}
-"#,
-            expect![[r#"
-                struct S({unknown})
-                (<{unknown}>)
-            "#]],
-        );
-    }
-
-    #[test]
-    fn works_for_enum_variants() {
-        check(
-            r#"
-enum E {
-    /// A Variant
-    A(i32),
-    /// Another
-    B,
-    /// And C
-    C { a: i32, b: i32 }
-}
-
-fn main() {
-    let a = E::A(<|>);
-}
-"#,
-            expect![[r#"
-                A Variant
-                ------
-                enum E::A(i32)
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn cant_call_struct_record() {
-        check(
-            r#"
-struct S { x: u32, y: i32 }
-fn main() {
-    let s = S(<|>);
-}
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
-    fn cant_call_enum_record() {
-        check(
-            r#"
-enum E {
-    /// A Variant
-    A(i32),
-    /// Another
-    B,
-    /// And C
-    C { a: i32, b: i32 }
-}
-
-fn main() {
-    let a = E::C(<|>);
-}
-"#,
-            expect![[""]],
-        );
-    }
-
-    #[test]
-    fn fn_signature_for_call_in_macro() {
-        check(
-            r#"
-macro_rules! id { ($($tt:tt)*) => { $($tt)* } }
-fn foo() { }
-id! {
-    fn bar() { foo(<|>); }
-}
-"#,
-            expect![[r#"
-                fn foo()
-                ()
-            "#]],
-        );
-    }
-
-    #[test]
-    fn call_info_for_lambdas() {
-        check(
-            r#"
-struct S;
-fn foo(s: S) -> i32 { 92 }
-fn main() {
-    (|s| foo(s))(<|>)
-}
-        "#,
-            expect![[r#"
-                (S) -> i32
-                ()
-            "#]],
-        )
-    }
-
-    #[test]
-    fn call_info_for_fn_ptr() {
-        check(
-            r#"
-fn main(f: fn(i32, f64) -> char) {
-    f(0, <|>)
-}
-        "#,
-            expect![[r#"
-                (i32, f64) -> char
-                (i32, )
-            "#]],
-        )
-    }
-}

From a6af0272f7bf129a3063cdd7096f685fc58438e6 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Thu, 23 Jul 2020 10:11:37 -0400
Subject: [PATCH 12/16] Move semantic logic into Semantics, fix missing tag for
 safe amp operator, using functional methods rather than clunky inline closure

---
 crates/ra_hir/src/semantics.rs                | 110 ++++++++++++------
 crates/ra_ide/src/syntax_highlighting.rs      |  89 +++++---------
 .../ra_ide/test_data/highlight_doctest.html   |   2 +-
 .../ra_ide/test_data/highlight_injection.html |   2 +-
 crates/ra_ide/test_data/highlight_unsafe.html |   4 +-
 crates/ra_ide/test_data/highlighting.html     |  10 +-
 6 files changed, 112 insertions(+), 105 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 1072b39718..f706a186e7 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -25,7 +25,8 @@ use crate::{
     semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
     source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
     AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
-    Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
+    Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef,
+    VariantDef,
 };
 use resolver::TypeNs;
 
@@ -280,45 +281,84 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.assert_contains_node(node)
     }
 
-    pub fn is_unsafe_pat(&self, pat: &ast::Pat) -> bool {
-        let ty = (|| {
-            let parent = match pat {
-                ast::Pat::BindPat(bind_pat) => bind_pat.syntax().parent()?,
-                _ => return None,
-            };
+    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> Option<()> {
+        let expr = method_call_expr.expr()?;
+        let field_expr =
+            if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
+        let ty = self.type_of_expr(&field_expr.expr()?)?;
+        if !ty.is_packed(self.db) {
+            return None;
+        }
 
-            // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
-            // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
-            // so this tries to lookup the `BindPat` anywhere along that structure to the
-            // `RecordPat` so we can get the containing type.
-            let record_pat = ast::RecordFieldPat::cast(parent.clone())
-                .and_then(|record_pat| record_pat.syntax().parent())
-                .or_else(|| Some(parent.clone()))
-                .and_then(|parent| {
-                    ast::RecordFieldPatList::cast(parent)?
-                        .syntax()
-                        .parent()
-                        .and_then(ast::RecordPat::cast)
-                });
-
-            // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
-            // this is initialized from a `FieldExpr`.
-            if let Some(record_pat) = record_pat {
-                self.type_of_pat(&ast::Pat::RecordPat(record_pat))
-            } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
-                let field_expr = match let_stmt.initializer()? {
-                    ast::Expr::FieldExpr(field_expr) => field_expr,
-                    _ => return None,
-                };
-
-                self.type_of_expr(&field_expr.expr()?)
+        let func = self.resolve_method_call(&method_call_expr)?;
+        if func.has_self_param(self.db) {
+            let params = func.params(self.db);
+            if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
+                Some(())
             } else {
                 None
             }
-        })();
+        } else {
+            None
+        }
+    }
 
-        // Binding a reference to a packed type is possibly unsafe.
-        ty.map(|ty| ty.is_packed(self.db)).unwrap_or(false)
+    pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
+        ref_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = match expr {
+                    ast::Expr::FieldExpr(field_expr) => field_expr,
+                    _ => return None,
+                };
+                let expr = field_expr.expr()?;
+                self.type_of_expr(&expr)
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
+
+        // FIXME This needs layout computation to be correct. It will highlight
+        // more than it should with the current implementation.
+    }
+
+    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
+        bind_pat
+            .syntax()
+            .parent()
+            .and_then(|parent| {
+                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
+                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
+                // so this tries to lookup the `BindPat` anywhere along that structure to the
+                // `RecordPat` so we can get the containing type.
+                let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                    .and_then(|record_pat| record_pat.syntax().parent())
+                    .or_else(|| Some(parent.clone()))
+                    .and_then(|parent| {
+                        ast::RecordFieldPatList::cast(parent)?
+                            .syntax()
+                            .parent()
+                            .and_then(ast::RecordPat::cast)
+                    });
+
+                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
+                // this is initialized from a `FieldExpr`.
+                if let Some(record_pat) = record_pat {
+                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
+                    let field_expr = match let_stmt.initializer()? {
+                        ast::Expr::FieldExpr(field_expr) => field_expr,
+                        _ => return None,
+                    };
+
+                    self.type_of_expr(&field_expr.expr()?)
+                } else {
+                    None
+                }
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
     }
 }
 
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index cf93205b6e..e29f65a784 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -565,29 +565,21 @@ fn highlight_element(
                 _ => h,
             }
         }
-        T![&] => {
-            let ref_expr = element.parent().and_then(ast::RefExpr::cast)?;
-            let expr = ref_expr.expr()?;
-            let field_expr = match expr {
-                ast::Expr::FieldExpr(fe) => fe,
-                _ => return None,
-            };
-
-            let expr = field_expr.expr()?;
-            let ty = sema.type_of_expr(&expr)?;
-            if !ty.is_packed(db) {
-                return None;
-            }
-
-            // FIXME This needs layout computation to be correct. It will highlight
-            // more than it should with the current implementation.
-
-            HighlightTag::Operator | HighlightModifier::Unsafe
-        }
         p if p.is_punct() => match p {
-            T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => {
-                HighlightTag::Operator.into()
+            T![&] => {
+                let h = HighlightTag::Operator.into();
+                let is_unsafe = element
+                    .parent()
+                    .and_then(ast::RefExpr::cast)
+                    .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr))
+                    .unwrap_or(false);
+                if is_unsafe {
+                    h | HighlightModifier::Unsafe
+                } else {
+                    h
+                }
             }
+            T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] => HighlightTag::Operator.into(),
             T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
                 HighlightTag::Macro.into()
             }
@@ -668,22 +660,18 @@ fn highlight_element(
                         HighlightTag::SelfKeyword.into()
                     }
                 }
-                T![ref] => {
-                    let modifier: Option = (|| {
-                        let bind_pat = element.parent().and_then(ast::BindPat::cast)?;
-                        if sema.is_unsafe_pat(&ast::Pat::BindPat(bind_pat)) {
+                T![ref] => element
+                    .parent()
+                    .and_then(ast::BindPat::cast)
+                    .and_then(|bind_pat| {
+                        if sema.is_unsafe_bind_pat(&bind_pat) {
                             Some(HighlightModifier::Unsafe)
                         } else {
                             None
                         }
-                    })();
-
-                    if let Some(modifier) = modifier {
-                        h | modifier
-                    } else {
-                        h
-                    }
-                }
+                    })
+                    .map(|modifier| h | modifier)
+                    .unwrap_or(h),
                 _ => h,
             }
         }
@@ -713,31 +701,6 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
     }
 }
 
-fn is_method_call_unsafe(
-    sema: &Semantics,
-    method_call_expr: ast::MethodCallExpr,
-) -> Option<()> {
-    let expr = method_call_expr.expr()?;
-    let field_expr =
-        if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
-    let ty = sema.type_of_expr(&field_expr.expr()?)?;
-    if !ty.is_packed(sema.db) {
-        return None;
-    }
-
-    let func = sema.resolve_method_call(&method_call_expr)?;
-    if func.has_self_param(sema.db) {
-        let params = func.params(sema.db);
-        if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
-            Some(())
-        } else {
-            None
-        }
-    } else {
-        None
-    }
-}
-
 fn highlight_name(
     sema: &Semantics,
     db: &RootDatabase,
@@ -767,7 +730,7 @@ fn highlight_name(
                     let is_unsafe = name_ref
                         .and_then(|name_ref| name_ref.syntax().parent())
                         .and_then(ast::MethodCallExpr::cast)
-                        .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr));
+                        .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
                     if is_unsafe.is_some() {
                         h |= HighlightModifier::Unsafe;
                     }
@@ -846,7 +809,7 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics {
             let mut h = Highlight::new(HighlightTag::Function);
             let is_unsafe = ast::MethodCallExpr::cast(parent)
-                .and_then(|method_call_expr| is_method_call_unsafe(sema, method_call_expr));
+                .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
 
             if is_unsafe.is_some() {
                 h |= HighlightModifier::Unsafe;
@@ -866,7 +829,11 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics {
             let path = match parent.parent().and_then(ast::Path::cast) {
diff --git a/crates/ra_ide/test_data/highlight_doctest.html b/crates/ra_ide/test_data/highlight_doctest.html
index 46c1e0a11f..6322d404fb 100644
--- a/crates/ra_ide/test_data/highlight_doctest.html
+++ b/crates/ra_ide/test_data/highlight_doctest.html
@@ -87,7 +87,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     /// ```sh
     /// echo 1
     /// ```
-    pub fn foo(&self) -> bool {
+    pub fn foo(&self) -> bool {
         true
     }
 }
diff --git a/crates/ra_ide/test_data/highlight_injection.html b/crates/ra_ide/test_data/highlight_injection.html
index 60c3943994..18addd00d2 100644
--- a/crates/ra_ide/test_data/highlight_injection.html
+++ b/crates/ra_ide/test_data/highlight_injection.html
@@ -35,7 +35,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 .unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
 
-
fn fixture(ra_fixture: &str) {}
+
fn fixture(ra_fixture: &str) {}
 
 fn main() {
     fixture(r#"
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index 454ff6d5f8..a2df2c27e6 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -45,7 +45,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
-    unsafe fn unsafe_method(&self) {}
+    unsafe fn unsafe_method(&self) {}
 }
 
 struct TypeForStaticMut {
@@ -55,7 +55,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
 fn main() {
-    let x = &5 as *const _ as *const usize;
+    let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
     unsafe {
         // unsafe fn and method calls
diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html
index 678cf9bd33..8e0160eee5 100644
--- a/crates/ra_ide/test_data/highlighting.html
+++ b/crates/ra_ide/test_data/highlighting.html
@@ -45,11 +45,11 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 }
 
 trait Bar {
-    fn bar(&self) -> i32;
+    fn bar(&self) -> i32;
 }
 
 impl Bar for Foo {
-    fn bar(&self) -> i32 {
+    fn bar(&self) -> i32 {
         self.x
     }
 }
@@ -59,7 +59,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         self.x
     }
 
-    fn qux(&mut self) {
+    fn qux(&mut self) {
         self.x = 0;
     }
 }
@@ -107,8 +107,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     noop!(noop!(1));
 
     let mut x = 42;
-    let y = &mut x;
-    let z = &y;
+    let y = &mut x;
+    let z = &y;
 
     let Foo { x: z, y } = Foo { x: z, y };
 

From 39fdd41df4052cef5da4876067ae28615012476b Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Thu, 23 Jul 2020 18:31:28 -0400
Subject: [PATCH 13/16] Return bool from is_unsafe_method_call and cleanup
 usages

---
 crates/ra_hir/src/semantics.rs           | 38 ++++++++++++------------
 crates/ra_ide/src/syntax_highlighting.rs | 11 +++----
 2 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index f706a186e7..9697c7082b 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -281,26 +281,26 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.assert_contains_node(node)
     }
 
-    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> Option<()> {
-        let expr = method_call_expr.expr()?;
-        let field_expr =
-            if let ast::Expr::FieldExpr(field_expr) = expr { field_expr } else { return None };
-        let ty = self.type_of_expr(&field_expr.expr()?)?;
-        if !ty.is_packed(self.db) {
-            return None;
-        }
+    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
+        method_call_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
+                    field_expr
+                } else {
+                    return None;
+                };
+                let ty = self.type_of_expr(&field_expr.expr()?)?;
+                if !ty.is_packed(self.db) {
+                    return None;
+                }
 
-        let func = self.resolve_method_call(&method_call_expr)?;
-        if func.has_self_param(self.db) {
-            let params = func.params(self.db);
-            if matches!(params.into_iter().next(), Some(TypeRef::Reference(..))) {
-                Some(())
-            } else {
-                None
-            }
-        } else {
-            None
-        }
+                let func = self.resolve_method_call(&method_call_expr)?;
+                let is_unsafe = func.has_self_param(self.db)
+                    && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
+                Some(is_unsafe)
+            })
+            .unwrap_or(false)
     }
 
     pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index e29f65a784..4527885e93 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -730,8 +730,9 @@ fn highlight_name(
                     let is_unsafe = name_ref
                         .and_then(|name_ref| name_ref.syntax().parent())
                         .and_then(ast::MethodCallExpr::cast)
-                        .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
-                    if is_unsafe.is_some() {
+                        .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr))
+                        .unwrap_or(false);
+                    if is_unsafe {
                         h |= HighlightModifier::Unsafe;
                     }
                 }
@@ -809,9 +810,9 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics {
             let mut h = Highlight::new(HighlightTag::Function);
             let is_unsafe = ast::MethodCallExpr::cast(parent)
-                .and_then(|method_call_expr| sema.is_unsafe_method_call(method_call_expr));
-
-            if is_unsafe.is_some() {
+                .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr))
+                .unwrap_or(false);
+            if is_unsafe {
                 h |= HighlightModifier::Unsafe;
             }
 

From 61dff939f909e0c53bcd3be4c3e672c794022cde Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Thu, 30 Jul 2020 09:26:40 -0400
Subject: [PATCH 14/16] Move unsafe semantics methods into `SemanticsImpl` and
 reference them in `Semantics`

---
 crates/ra_hir/src/semantics.rs | 154 ++++++++++++++++++---------------
 1 file changed, 83 insertions(+), 71 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 9697c7082b..758d004098 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -282,83 +282,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
     }
 
     pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
-        method_call_expr
-            .expr()
-            .and_then(|expr| {
-                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
-                    field_expr
-                } else {
-                    return None;
-                };
-                let ty = self.type_of_expr(&field_expr.expr()?)?;
-                if !ty.is_packed(self.db) {
-                    return None;
-                }
-
-                let func = self.resolve_method_call(&method_call_expr)?;
-                let is_unsafe = func.has_self_param(self.db)
-                    && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
-                Some(is_unsafe)
-            })
-            .unwrap_or(false)
+        self.imp.is_unsafe_method_call(method_call_expr)
     }
 
     pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
-        ref_expr
-            .expr()
-            .and_then(|expr| {
-                let field_expr = match expr {
-                    ast::Expr::FieldExpr(field_expr) => field_expr,
-                    _ => return None,
-                };
-                let expr = field_expr.expr()?;
-                self.type_of_expr(&expr)
-            })
-            // Binding a reference to a packed type is possibly unsafe.
-            .map(|ty| ty.is_packed(self.db))
-            .unwrap_or(false)
-
-        // FIXME This needs layout computation to be correct. It will highlight
-        // more than it should with the current implementation.
+        self.imp.is_unsafe_ref_expr(ref_expr)
     }
 
     pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
-        bind_pat
-            .syntax()
-            .parent()
-            .and_then(|parent| {
-                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
-                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
-                // so this tries to lookup the `BindPat` anywhere along that structure to the
-                // `RecordPat` so we can get the containing type.
-                let record_pat = ast::RecordFieldPat::cast(parent.clone())
-                    .and_then(|record_pat| record_pat.syntax().parent())
-                    .or_else(|| Some(parent.clone()))
-                    .and_then(|parent| {
-                        ast::RecordFieldPatList::cast(parent)?
-                            .syntax()
-                            .parent()
-                            .and_then(ast::RecordPat::cast)
-                    });
-
-                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
-                // this is initialized from a `FieldExpr`.
-                if let Some(record_pat) = record_pat {
-                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
-                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
-                    let field_expr = match let_stmt.initializer()? {
-                        ast::Expr::FieldExpr(field_expr) => field_expr,
-                        _ => return None,
-                    };
-
-                    self.type_of_expr(&field_expr.expr()?)
-                } else {
-                    None
-                }
-            })
-            // Binding a reference to a packed type is possibly unsafe.
-            .map(|ty| ty.is_packed(self.db))
-            .unwrap_or(false)
+        self.imp.is_unsafe_bind_pat(bind_pat)
     }
 }
 
@@ -655,6 +587,86 @@ impl<'db> SemanticsImpl<'db> {
         });
         InFile::new(file_id, node)
     }
+
+    pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
+        method_call_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
+                    field_expr
+                } else {
+                    return None;
+                };
+                let ty = self.type_of_expr(&field_expr.expr()?)?;
+                if !ty.is_packed(self.db) {
+                    return None;
+                }
+
+                let func = self.resolve_method_call(&method_call_expr).map(Function::from)?;
+                let is_unsafe = func.has_self_param(self.db)
+                    && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
+                Some(is_unsafe)
+            })
+            .unwrap_or(false)
+    }
+
+    pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
+        ref_expr
+            .expr()
+            .and_then(|expr| {
+                let field_expr = match expr {
+                    ast::Expr::FieldExpr(field_expr) => field_expr,
+                    _ => return None,
+                };
+                let expr = field_expr.expr()?;
+                self.type_of_expr(&expr)
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
+
+        // FIXME This needs layout computation to be correct. It will highlight
+        // more than it should with the current implementation.
+    }
+
+    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
+        bind_pat
+            .syntax()
+            .parent()
+            .and_then(|parent| {
+                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
+                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
+                // so this tries to lookup the `BindPat` anywhere along that structure to the
+                // `RecordPat` so we can get the containing type.
+                let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                    .and_then(|record_pat| record_pat.syntax().parent())
+                    .or_else(|| Some(parent.clone()))
+                    .and_then(|parent| {
+                        ast::RecordFieldPatList::cast(parent)?
+                            .syntax()
+                            .parent()
+                            .and_then(ast::RecordPat::cast)
+                    });
+
+                // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
+                // this is initialized from a `FieldExpr`.
+                if let Some(record_pat) = record_pat {
+                    self.type_of_pat(&ast::Pat::RecordPat(record_pat))
+                } else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
+                    let field_expr = match let_stmt.initializer()? {
+                        ast::Expr::FieldExpr(field_expr) => field_expr,
+                        _ => return None,
+                    };
+
+                    self.type_of_expr(&field_expr.expr()?)
+                } else {
+                    None
+                }
+            })
+            // Binding a reference to a packed type is possibly unsafe.
+            .map(|ty| ty.is_packed(self.db))
+            .unwrap_or(false)
+    }
 }
 
 pub trait ToDef: AstNode + Clone {

From 2199d0cda9c745ecb460dd987b8da982d02bc130 Mon Sep 17 00:00:00 2001
From: Paul Daniel Faria 
Date: Fri, 7 Aug 2020 10:40:09 -0400
Subject: [PATCH 15/16] Fix type names broken by rebase, redo expected test
 because of rebase

---
 crates/ra_hir/src/semantics.rs                | 22 ++++++++------
 crates/ra_ide/src/syntax_highlighting.rs      |  6 ++--
 crates/ra_ide/test_data/highlight_unsafe.html | 30 +++++++++++++++++--
 3 files changed, 44 insertions(+), 14 deletions(-)

diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 758d004098..872f5fa4c7 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -289,8 +289,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.is_unsafe_ref_expr(ref_expr)
     }
 
-    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
-        self.imp.is_unsafe_bind_pat(bind_pat)
+    pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
+        self.imp.is_unsafe_ident_pat(ident_pat)
     }
 }
 
@@ -629,20 +629,24 @@ impl<'db> SemanticsImpl<'db> {
         // more than it should with the current implementation.
     }
 
-    pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool {
-        bind_pat
+    pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
+        if !ident_pat.ref_token().is_some() {
+            return false;
+        }
+
+        ident_pat
             .syntax()
             .parent()
             .and_then(|parent| {
-                // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or
-                // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`,
-                // so this tries to lookup the `BindPat` anywhere along that structure to the
+                // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
+                // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
+                // so this tries to lookup the `IdentPat` anywhere along that structure to the
                 // `RecordPat` so we can get the containing type.
-                let record_pat = ast::RecordFieldPat::cast(parent.clone())
+                let record_pat = ast::RecordPatField::cast(parent.clone())
                     .and_then(|record_pat| record_pat.syntax().parent())
                     .or_else(|| Some(parent.clone()))
                     .and_then(|parent| {
-                        ast::RecordFieldPatList::cast(parent)?
+                        ast::RecordPatFieldList::cast(parent)?
                             .syntax()
                             .parent()
                             .and_then(ast::RecordPat::cast)
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index 4527885e93..c62bb3f1ab 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -662,9 +662,9 @@ fn highlight_element(
                 }
                 T![ref] => element
                     .parent()
-                    .and_then(ast::BindPat::cast)
-                    .and_then(|bind_pat| {
-                        if sema.is_unsafe_bind_pat(&bind_pat) {
+                    .and_then(ast::IdentPat::cast)
+                    .and_then(|ident_pat| {
+                        if sema.is_unsafe_ident_pat(&ident_pat) {
                             Some(HighlightModifier::Unsafe)
                         } else {
                             None
diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html
index a2df2c27e6..552fea6689 100644
--- a/crates/ra_ide/test_data/highlight_unsafe.html
+++ b/crates/ra_ide/test_data/highlight_unsafe.html
@@ -54,6 +54,19 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
 
+#[repr(packed)]
+struct Packed {
+    a: u16,
+}
+
+trait DoTheAutoref {
+    fn calls_autoref(&self);
+}
+
+impl DoTheAutoref for u16 {
+    fn calls_autoref(&self) {}
+}
+
 fn main() {
     let x = &5 as *const _ as *const usize;
     let u = Union { b: 0 };
@@ -66,8 +79,21 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
             Union { a } => (),
         }
         HasUnsafeFn.unsafe_method();
-        let y = *(x);
-        let z = -x;
+
+        // unsafe deref
+        let y = *x;
+
+        // unsafe access to a static mut
         let a = global_mut.a;
+
+        // unsafe ref of packed fields
+        let packed = Packed { a: 0 };
+        let a = &packed.a;
+        let ref a = packed.a;
+        let Packed { ref a } = packed;
+        let Packed { a: ref _a } = packed;
+
+        // unsafe auto ref of packed field
+        packed.a.calls_autoref();
     }
 }
\ No newline at end of file From 72baf1acdd544c645fd69c16967b91be9e75371b Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Sun, 9 Aug 2020 18:54:04 -0400 Subject: [PATCH 16/16] Remove unused import left behind after rebasing --- crates/ra_ide/src/syntax_highlighting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index c62bb3f1ab..c10e15db8b 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, TypeRef, VariantDef}; +use hir::{Name, Semantics, VariantDef}; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, RootDatabase,