aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorPaul Daniel Faria <[email protected]>2020-06-27 22:11:43 +0100
committerPaul Daniel Faria <[email protected]>2020-08-10 13:44:54 +0100
commitd5f11e530dbf6edbdd0ca32d6cd5fafe634c8c4a (patch)
treeb348ea6d5552be08913ac3a451836cad5ac75c1a /crates
parent38440d53d8329ac9f3f2013c6e32b3f69b069c72 (diff)
Unsafe borrow of packed fields: account for borrow through ref binding, auto ref function calls
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir/src/code_model.rs5
-rw-r--r--crates/ra_hir_def/src/data.rs6
-rw-r--r--crates/ra_hir_def/src/item_tree.rs8
-rw-r--r--crates/ra_hir_def/src/item_tree/lower.rs9
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_dot.rs2
-rw-r--r--crates/ra_ide/src/completion/complete_trait_impl.rs2
-rw-r--r--crates/ra_ide/src/completion/presentation.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs137
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs31
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 0007d7fa8..a880fa671 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -11,6 +11,7 @@ use hir_def::{
11 docs::Documentation, 11 docs::Documentation,
12 expr::{BindingAnnotation, Pat, PatId}, 12 expr::{BindingAnnotation, Pat, PatId},
13 import_map, 13 import_map,
14 item_tree::SelfParam,
14 per_ns::PerNs, 15 per_ns::PerNs,
15 resolver::{HasResolver, Resolver}, 16 resolver::{HasResolver, Resolver},
16 src::HasSource as _, 17 src::HasSource as _,
@@ -670,8 +671,8 @@ impl Function {
670 db.function_data(self.id).name.clone() 671 db.function_data(self.id).name.clone()
671 } 672 }
672 673
673 pub fn has_self_param(self, db: &dyn HirDatabase) -> bool { 674 pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
674 db.function_data(self.id).has_self_param 675 db.function_data(self.id).self_param
675 } 676 }
676 677
677 pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> { 678 pub fn params(self, db: &dyn HirDatabase) -> Vec<TypeRef> {
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 88a8ef9bf..2a26b0183 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -10,7 +10,7 @@ use crate::{
10 attr::Attrs, 10 attr::Attrs,
11 body::Expander, 11 body::Expander,
12 db::DefDatabase, 12 db::DefDatabase,
13 item_tree::{AssocItem, ItemTreeId, ModItem}, 13 item_tree::{AssocItem, ItemTreeId, ModItem, SelfParam},
14 type_ref::{TypeBound, TypeRef}, 14 type_ref::{TypeBound, TypeRef},
15 visibility::RawVisibility, 15 visibility::RawVisibility,
16 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, 16 AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
@@ -25,7 +25,7 @@ pub struct FunctionData {
25 pub attrs: Attrs, 25 pub attrs: Attrs,
26 /// True if the first param is `self`. This is relevant to decide whether this 26 /// True if the first param is `self`. This is relevant to decide whether this
27 /// can be called as a method. 27 /// can be called as a method.
28 pub has_self_param: bool, 28 pub self_param: Option<SelfParam>,
29 pub is_unsafe: bool, 29 pub is_unsafe: bool,
30 pub is_varargs: bool, 30 pub is_varargs: bool,
31 pub visibility: RawVisibility, 31 pub visibility: RawVisibility,
@@ -42,7 +42,7 @@ impl FunctionData {
42 params: func.params.to_vec(), 42 params: func.params.to_vec(),
43 ret_type: func.ret_type.clone(), 43 ret_type: func.ret_type.clone(),
44 attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(), 44 attrs: item_tree.attrs(ModItem::from(loc.id.value).into()).clone(),
45 has_self_param: func.has_self_param, 45 self_param: func.self_param,
46 is_unsafe: func.is_unsafe, 46 is_unsafe: func.is_unsafe,
47 is_varargs: func.is_varargs, 47 is_varargs: func.is_varargs,
48 visibility: item_tree[func.visibility].clone(), 48 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 a67e75dac..1eaea66e4 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 {
500 pub name: Name, 500 pub name: Name,
501 pub visibility: RawVisibilityId, 501 pub visibility: RawVisibilityId,
502 pub generic_params: GenericParamsId, 502 pub generic_params: GenericParamsId,
503 pub has_self_param: bool, 503 pub self_param: Option<SelfParam>,
504 pub is_unsafe: bool, 504 pub is_unsafe: bool,
505 pub params: Box<[TypeRef]>, 505 pub params: Box<[TypeRef]>,
506 pub is_varargs: bool, 506 pub is_varargs: bool,
@@ -508,6 +508,12 @@ pub struct Function {
508 pub ast_id: FileAstId<ast::Fn>, 508 pub ast_id: FileAstId<ast::Fn>,
509} 509}
510 510
511#[derive(Debug, Copy, Clone, Eq, PartialEq)]
512pub struct SelfParam {
513 pub is_ref: bool,
514 pub is_mut: bool,
515}
516
511#[derive(Debug, Clone, Eq, PartialEq)] 517#[derive(Debug, Clone, Eq, PartialEq)]
512pub struct Struct { 518pub struct Struct {
513 pub name: Name, 519 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 450ef8798..89ad91d37 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 {
283 let name = func.name()?.as_name(); 283 let name = func.name()?.as_name();
284 284
285 let mut params = Vec::new(); 285 let mut params = Vec::new();
286 let mut has_self_param = false; 286 let mut func_self_param = None;
287 if let Some(param_list) = func.param_list() { 287 if let Some(param_list) = func.param_list() {
288 if let Some(self_param) = param_list.self_param() { 288 if let Some(self_param) = param_list.self_param() {
289 let self_type = match self_param.ty() { 289 let self_type = match self_param.ty() {
@@ -302,7 +302,10 @@ impl Ctx {
302 } 302 }
303 }; 303 };
304 params.push(self_type); 304 params.push(self_type);
305 has_self_param = true; 305 func_self_param = Some(SelfParam {
306 is_ref: self_param.amp_token().is_some(),
307 is_mut: self_param.mut_token().is_some(),
308 });
306 } 309 }
307 for param in param_list.params() { 310 for param in param_list.params() {
308 let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty()); 311 let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
@@ -335,7 +338,7 @@ impl Ctx {
335 name, 338 name,
336 visibility, 339 visibility,
337 generic_params: GenericParamsId::EMPTY, 340 generic_params: GenericParamsId::EMPTY,
338 has_self_param, 341 self_param: func_self_param,
339 is_unsafe: func.unsafe_token().is_some(), 342 is_unsafe: func.unsafe_token().is_some(),
340 params: params.into_boxed_slice(), 343 params: params.into_boxed_slice(),
341 is_varargs, 344 is_varargs,
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
index fb4b30a13..79c5adf0f 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(
640 } 640 }
641 } 641 }
642 if let Some(receiver_ty) = receiver_ty { 642 if let Some(receiver_ty) = receiver_ty {
643 if !data.has_self_param { 643 if data.self_param.is_none() {
644 return false; 644 return false;
645 } 645 }
646 let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { 646 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 532665285..5488db43f 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
48 let mut seen_methods = FxHashSet::default(); 48 let mut seen_methods = FxHashSet::default();
49 let traits_in_scope = ctx.scope.traits_in_scope(); 49 let traits_in_scope = ctx.scope.traits_in_scope();
50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { 50 receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
51 if func.has_self_param(ctx.db) 51 if func.self_param(ctx.db).is_some()
52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m)) 52 && ctx.scope.module().map_or(true, |m| func.is_visible_from(ctx.db, m))
53 && seen_methods.insert(func.name(ctx.db)) 53 && seen_methods.insert(func.name(ctx.db))
54 { 54 {
diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs
index d9a0ef167..e3ba7ebc4 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(
136 .lookup_by(fn_name) 136 .lookup_by(fn_name)
137 .set_documentation(func.docs(ctx.db)); 137 .set_documentation(func.docs(ctx.db));
138 138
139 let completion_kind = if func.has_self_param(ctx.db) { 139 let completion_kind = if func.self_param(ctx.db).is_some() {
140 CompletionItemKind::Method 140 CompletionItemKind::Method
141 } else { 141 } else {
142 CompletionItemKind::Function 142 CompletionItemKind::Function
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 9a94ff476..fc3d1a4bd 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -191,7 +191,7 @@ impl Completions {
191 func: hir::Function, 191 func: hir::Function,
192 local_name: Option<String>, 192 local_name: Option<String>,
193 ) { 193 ) {
194 let has_self_param = func.has_self_param(ctx.db); 194 let has_self_param = func.self_param(ctx.db).is_some();
195 195
196 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string()); 196 let name = local_name.unwrap_or_else(|| func.name(ctx.db).to_string());
197 let ast_node = func.source(ctx.db).value; 197 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 a4a7aa228..454fef39c 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -497,9 +497,9 @@ fn highlight_element(
497 match name_kind { 497 match name_kind {
498 Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(), 498 Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(),
499 Some(NameClass::Definition(def)) => { 499 Some(NameClass::Definition(def)) => {
500 highlight_name(db, def, false) | HighlightModifier::Definition 500 highlight_name(sema, db, def, None, false) | HighlightModifier::Definition
501 } 501 }
502 Some(NameClass::ConstReference(def)) => highlight_name(db, def, false), 502 Some(NameClass::ConstReference(def)) => highlight_name(sema, db, def, None, false),
503 Some(NameClass::FieldShorthand { field, .. }) => { 503 Some(NameClass::FieldShorthand { field, .. }) => {
504 let mut h = HighlightTag::Field.into(); 504 let mut h = HighlightTag::Field.into();
505 if let Definition::Field(field) = field { 505 if let Definition::Field(field) = field {
@@ -532,7 +532,7 @@ fn highlight_element(
532 binding_hash = Some(calc_binding_hash(&name, *shadow_count)) 532 binding_hash = Some(calc_binding_hash(&name, *shadow_count))
533 } 533 }
534 }; 534 };
535 highlight_name(db, def, possibly_unsafe) 535 highlight_name(sema, db, def, Some(name_ref), possibly_unsafe)
536 } 536 }
537 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), 537 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
538 }, 538 },
@@ -565,8 +565,8 @@ fn highlight_element(
565 _ => h, 565 _ => h,
566 } 566 }
567 } 567 }
568 REF_EXPR => { 568 T![&] => {
569 let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?; 569 let ref_expr = element.parent().and_then(ast::RefExpr::cast)?;
570 let expr = ref_expr.expr()?; 570 let expr = ref_expr.expr()?;
571 let field_expr = match expr { 571 let field_expr = match expr {
572 ast::Expr::FieldExpr(fe) => fe, 572 ast::Expr::FieldExpr(fe) => fe,
@@ -668,6 +668,52 @@ fn highlight_element(
668 HighlightTag::SelfKeyword.into() 668 HighlightTag::SelfKeyword.into()
669 } 669 }
670 } 670 }
671 T![ref] => {
672 let modifier: Option<HighlightModifier> = (|| {
673 let bind_pat = element.parent().and_then(ast::BindPat::cast)?;
674 let parent = bind_pat.syntax().parent()?;
675
676 let ty = if let Some(pat_list) =
677 ast::RecordFieldPatList::cast(parent.clone())
678 {
679 let record_pat =
680 pat_list.syntax().parent().and_then(ast::RecordPat::cast)?;
681 sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
682 } else if let Some(let_stmt) = ast::LetStmt::cast(parent.clone()) {
683 let field_expr =
684 if let ast::Expr::FieldExpr(field_expr) = let_stmt.initializer()? {
685 field_expr
686 } else {
687 return None;
688 };
689
690 sema.type_of_expr(&field_expr.expr()?)
691 } else if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent) {
692 let record_pat = record_field_pat
693 .syntax()
694 .parent()
695 .and_then(ast::RecordFieldPatList::cast)?
696 .syntax()
697 .parent()
698 .and_then(ast::RecordPat::cast)?;
699 sema.type_of_pat(&ast::Pat::RecordPat(record_pat))
700 } else {
701 None
702 }?;
703
704 if !ty.is_packed(db) {
705 return None;
706 }
707
708 Some(HighlightModifier::Unsafe)
709 })();
710
711 if let Some(modifier) = modifier {
712 h | modifier
713 } else {
714 h
715 }
716 }
671 _ => h, 717 _ => h,
672 } 718 }
673 } 719 }
@@ -697,7 +743,13 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool {
697 } 743 }
698} 744}
699 745
700fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) -> Highlight { 746fn highlight_name(
747 sema: &Semantics<RootDatabase>,
748 db: &RootDatabase,
749 def: Definition,
750 name_ref: Option<ast::NameRef>,
751 possibly_unsafe: bool,
752) -> Highlight {
701 match def { 753 match def {
702 Definition::Macro(_) => HighlightTag::Macro, 754 Definition::Macro(_) => HighlightTag::Macro,
703 Definition::Field(field) => { 755 Definition::Field(field) => {
@@ -716,6 +768,29 @@ fn highlight_name(db: &RootDatabase, def: Definition, possibly_unsafe: bool) ->
716 let mut h = HighlightTag::Function.into(); 768 let mut h = HighlightTag::Function.into();
717 if func.is_unsafe(db) { 769 if func.is_unsafe(db) {
718 h |= HighlightModifier::Unsafe; 770 h |= HighlightModifier::Unsafe;
771 } else {
772 (|| {
773 let method_call_expr =
774 name_ref?.syntax().parent().and_then(ast::MethodCallExpr::cast)?;
775 let expr = method_call_expr.expr()?;
776 let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
777 Some(field_expr)
778 } else {
779 None
780 }?;
781 let ty = sema.type_of_expr(&field_expr.expr()?)?;
782 if !ty.is_packed(db) {
783 return None;
784 }
785
786 let func = sema.resolve_method_call(&method_call_expr)?;
787 if func.self_param(db)?.is_ref {
788 Some(HighlightModifier::Unsafe)
789 } else {
790 None
791 }
792 })()
793 .map(|modifier| h |= modifier);
719 } 794 }
720 return h; 795 return h;
721 } 796 }
@@ -787,8 +862,33 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
787 _ => return default.into(), 862 _ => return default.into(),
788 }; 863 };
789 864
790 let tag = match parent.kind() { 865 match parent.kind() {
791 METHOD_CALL_EXPR => HighlightTag::Function, 866 METHOD_CALL_EXPR => {
867 let mut h = Highlight::new(HighlightTag::Function);
868 let modifier: Option<HighlightModifier> = (|| {
869 let method_call_expr = ast::MethodCallExpr::cast(parent)?;
870 let expr = method_call_expr.expr()?;
871 let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
872 field_expr
873 } else {
874 return None;
875 };
876
877 let expr = field_expr.expr()?;
878 let ty = sema.type_of_expr(&expr)?;
879 if ty.is_packed(sema.db) {
880 Some(HighlightModifier::Unsafe)
881 } else {
882 None
883 }
884 })();
885
886 if let Some(modifier) = modifier {
887 h |= modifier;
888 }
889
890 h
891 }
792 FIELD_EXPR => { 892 FIELD_EXPR => {
793 let h = HighlightTag::Field; 893 let h = HighlightTag::Field;
794 let is_union = ast::FieldExpr::cast(parent) 894 let is_union = ast::FieldExpr::cast(parent)
@@ -801,7 +901,7 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics<RootDatabas
801 }) 901 })
802 }) 902 })
803 .unwrap_or(false); 903 .unwrap_or(false);
804 return if is_union { h | HighlightModifier::Unsafe } else { h.into() }; 904 if is_union { h | HighlightModifier::Unsafe } else { h.into() }
805 } 905 }
806 PATH_SEGMENT => { 906 PATH_SEGMENT => {
807 let path = match parent.parent().and_then(ast::Path::cast) { 907 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<RootDatabas
826 }; 926 };
827 927
828 match parent.kind() { 928 match parent.kind() {
829 CALL_EXPR => HighlightTag::Function, 929 CALL_EXPR => HighlightTag::Function.into(),
830 _ => { 930 _ => if name.text().chars().next().unwrap_or_default().is_uppercase() {
831 if name.text().chars().next().unwrap_or_default().is_uppercase() { 931 HighlightTag::Struct.into()
832 HighlightTag::Struct 932 } else {
833 } else { 933 HighlightTag::Constant
834 HighlightTag::Constant
835 }
836 } 934 }
935 .into(),
837 } 936 }
838 } 937 }
839 _ => default, 938 _ => default.into(),
840 }; 939 }
841
842 tag.into()
843} 940}
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index a7f5ad862..a8087635a 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 {
301 fn calls_autoref(&self); 301 fn calls_autoref(&self);
302} 302}
303 303
304struct NeedsAlign { 304impl DoTheAutoref for u16 {
305 a: u16
306}
307
308#[repr(packed)]
309struct HasAligned {
310 a: NeedsAlign
311}
312
313impl DoTheAutoref for NeedsAlign {
314 fn calls_autoref(&self) {} 305 fn calls_autoref(&self) {}
315} 306}
316 307
@@ -318,6 +309,7 @@ fn main() {
318 let x = &5 as *const _ as *const usize; 309 let x = &5 as *const _ as *const usize;
319 let u = Union { b: 0 }; 310 let u = Union { b: 0 };
320 unsafe { 311 unsafe {
312 // unsafe fn and method calls
321 unsafe_fn(); 313 unsafe_fn();
322 let b = u.b; 314 let b = u.b;
323 match u { 315 match u {
@@ -325,13 +317,22 @@ fn main() {
325 Union { a } => (), 317 Union { a } => (),
326 } 318 }
327 HasUnsafeFn.unsafe_method(); 319 HasUnsafeFn.unsafe_method();
328 let _y = *(x); 320
329 let z = -x; 321 // unsafe deref
322 let y = *x;
323
324 // unsafe access to a static mut
330 let a = global_mut.a; 325 let a = global_mut.a;
326
327 // unsafe ref of packed fields
331 let packed = Packed { a: 0 }; 328 let packed = Packed { a: 0 };
332 let _a = &packed.a; 329 let a = &packed.a;
333 let h = HasAligned{ a: NeedsAlign { a: 1 } }; 330 let ref a = packed.a;
334 h.a.calls_autoref(); 331 let Packed { ref a } = packed;
332 let Packed { a: ref _a } = packed;
333
334 // unsafe auto ref of packed field
335 packed.a.calls_autoref();
335 } 336 }
336} 337}
337"# 338"#