diff options
author | Paul Daniel Faria <[email protected]> | 2020-06-04 04:38:25 +0100 |
---|---|---|
committer | Paul Daniel Faria <[email protected]> | 2020-08-10 13:44:54 +0100 |
commit | 263f9a7f231a474dd56d02adbcd7c57d079e88fd (patch) | |
tree | 125391657a69cb05ac14fadee37e6138dc0daae7 | |
parent | f3336509e52187a7a70a8043557a7317872e3a2f (diff) |
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.
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 18 | ||||
-rw-r--r-- | crates/ra_hir_def/src/adt.rs | 56 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting.rs | 24 | ||||
-rw-r--r-- | crates/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 44456e49e..6f9c56d29 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}; | |||
4 | use arrayvec::ArrayVec; | 4 | use arrayvec::ArrayVec; |
5 | use either::Either; | 5 | use either::Either; |
6 | use hir_def::{ | 6 | use hir_def::{ |
7 | adt::ReprKind, | ||
7 | adt::StructKind, | 8 | adt::StructKind, |
8 | adt::VariantData, | 9 | adt::VariantData, |
9 | builtin_type::BuiltinType, | 10 | builtin_type::BuiltinType, |
@@ -431,6 +432,10 @@ impl Struct { | |||
431 | Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) | 432 | Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) |
432 | } | 433 | } |
433 | 434 | ||
435 | pub fn is_packed(self, db: &dyn HirDatabase) -> bool { | ||
436 | matches!(db.struct_data(self.id).repr, Some(ReprKind::Packed)) | ||
437 | } | ||
438 | |||
434 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { | 439 | fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> { |
435 | db.struct_data(self.id).variant_data.clone() | 440 | db.struct_data(self.id).variant_data.clone() |
436 | } | 441 | } |
@@ -1253,6 +1258,19 @@ impl Type { | |||
1253 | ) | 1258 | ) |
1254 | } | 1259 | } |
1255 | 1260 | ||
1261 | pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { | ||
1262 | let adt_id = match self.ty.value { | ||
1263 | Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id, | ||
1264 | _ => return false, | ||
1265 | }; | ||
1266 | |||
1267 | let adt = adt_id.into(); | ||
1268 | match adt { | ||
1269 | Adt::Struct(s) => s.is_packed(db), | ||
1270 | _ => false, | ||
1271 | } | ||
1272 | } | ||
1273 | |||
1256 | pub fn is_raw_ptr(&self) -> bool { | 1274 | pub fn is_raw_ptr(&self) -> bool { |
1257 | matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) | 1275 | matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) |
1258 | } | 1276 | } |
diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 6cb56a1cd..6d59c8642 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs | |||
@@ -9,11 +9,13 @@ use hir_expand::{ | |||
9 | }; | 9 | }; |
10 | use ra_arena::{map::ArenaMap, Arena}; | 10 | use ra_arena::{map::ArenaMap, Arena}; |
11 | use ra_syntax::ast::{self, NameOwner, VisibilityOwner}; | 11 | use ra_syntax::ast::{self, NameOwner, VisibilityOwner}; |
12 | use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; | ||
12 | 13 | ||
13 | use crate::{ | 14 | use crate::{ |
15 | attr::AttrInput, | ||
14 | body::{CfgExpander, LowerCtx}, | 16 | body::{CfgExpander, LowerCtx}, |
15 | db::DefDatabase, | 17 | db::DefDatabase, |
16 | item_tree::{Field, Fields, ItemTree}, | 18 | item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, |
17 | src::HasChildSource, | 19 | src::HasChildSource, |
18 | src::HasSource, | 20 | src::HasSource, |
19 | trace::Trace, | 21 | trace::Trace, |
@@ -29,6 +31,7 @@ use ra_cfg::CfgOptions; | |||
29 | pub struct StructData { | 31 | pub struct StructData { |
30 | pub name: Name, | 32 | pub name: Name, |
31 | pub variant_data: Arc<VariantData>, | 33 | pub variant_data: Arc<VariantData>, |
34 | pub repr: Option<ReprKind>, | ||
32 | } | 35 | } |
33 | 36 | ||
34 | #[derive(Debug, Clone, PartialEq, Eq)] | 37 | #[derive(Debug, Clone, PartialEq, Eq)] |
@@ -58,26 +61,71 @@ pub struct FieldData { | |||
58 | pub visibility: RawVisibility, | 61 | pub visibility: RawVisibility, |
59 | } | 62 | } |
60 | 63 | ||
64 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
65 | pub enum ReprKind { | ||
66 | Packed, | ||
67 | Other, | ||
68 | } | ||
69 | |||
70 | fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option<ReprKind> { | ||
71 | item_tree.attrs(of).iter().find_map(|a| { | ||
72 | if a.path.segments[0].to_string() == "repr" { | ||
73 | if let Some(AttrInput::TokenTree(subtree)) = &a.input { | ||
74 | parse_repr_tt(subtree) | ||
75 | } else { | ||
76 | None | ||
77 | } | ||
78 | } else { | ||
79 | None | ||
80 | } | ||
81 | }) | ||
82 | } | ||
83 | |||
84 | fn parse_repr_tt(tt: &Subtree) -> Option<ReprKind> { | ||
85 | match tt.delimiter { | ||
86 | Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {} | ||
87 | _ => return None, | ||
88 | } | ||
89 | |||
90 | let mut it = tt.token_trees.iter(); | ||
91 | match it.next() { | ||
92 | None => None, | ||
93 | Some(TokenTree::Leaf(Leaf::Ident(ident))) if ident.text == "packed" => { | ||
94 | Some(ReprKind::Packed) | ||
95 | } | ||
96 | _ => Some(ReprKind::Other), | ||
97 | } | ||
98 | } | ||
99 | |||
61 | impl StructData { | 100 | impl StructData { |
62 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { | 101 | pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc<StructData> { |
63 | let loc = id.lookup(db); | 102 | let loc = id.lookup(db); |
64 | let item_tree = db.item_tree(loc.id.file_id); | 103 | let item_tree = db.item_tree(loc.id.file_id); |
104 | let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); | ||
65 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | 105 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); |
66 | 106 | ||
67 | let strukt = &item_tree[loc.id.value]; | 107 | let strukt = &item_tree[loc.id.value]; |
68 | let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); | 108 | let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); |
69 | 109 | Arc::new(StructData { | |
70 | Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) }) | 110 | name: strukt.name.clone(), |
111 | variant_data: Arc::new(variant_data), | ||
112 | repr, | ||
113 | }) | ||
71 | } | 114 | } |
72 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { | 115 | pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc<StructData> { |
73 | let loc = id.lookup(db); | 116 | let loc = id.lookup(db); |
74 | let item_tree = db.item_tree(loc.id.file_id); | 117 | let item_tree = db.item_tree(loc.id.file_id); |
118 | let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); | ||
75 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); | 119 | let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); |
76 | 120 | ||
77 | let union = &item_tree[loc.id.value]; | 121 | let union = &item_tree[loc.id.value]; |
78 | let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); | 122 | let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); |
79 | 123 | ||
80 | Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) }) | 124 | Arc::new(StructData { |
125 | name: union.name.clone(), | ||
126 | variant_data: Arc::new(variant_data), | ||
127 | repr, | ||
128 | }) | ||
81 | } | 129 | } |
82 | } | 130 | } |
83 | 131 | ||
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index 6b7874460..0cab684eb 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -565,6 +565,30 @@ fn highlight_element( | |||
565 | _ => h, | 565 | _ => h, |
566 | } | 566 | } |
567 | } | 567 | } |
568 | REF_EXPR => { | ||
569 | let ref_expr = element.into_node().and_then(ast::RefExpr::cast)?; | ||
570 | let expr = ref_expr.expr()?; | ||
571 | let field_expr = match expr { | ||
572 | ast::Expr::FieldExpr(fe) => fe, | ||
573 | _ => return None, | ||
574 | }; | ||
575 | |||
576 | let expr = field_expr.expr()?; | ||
577 | let ty = match sema.type_of_expr(&expr) { | ||
578 | Some(ty) => ty, | ||
579 | None => { | ||
580 | println!("No type :("); | ||
581 | return None; | ||
582 | } | ||
583 | }; | ||
584 | if !ty.is_packed(db) { | ||
585 | return None; | ||
586 | } | ||
587 | |||
588 | // FIXME account for alignment... somehow | ||
589 | |||
590 | Highlight::new(HighlightTag::Operator) | HighlightModifier::Unsafe | ||
591 | } | ||
568 | p if p.is_punct() => match p { | 592 | p if p.is_punct() => match p { |
569 | T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => { | 593 | T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => { |
570 | HighlightTag::Operator.into() | 594 | HighlightTag::Operator.into() |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 09062c38e..f2c078d34 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 { | |||
292 | 292 | ||
293 | static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; | 293 | static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; |
294 | 294 | ||
295 | #[repr(packed)] | ||
296 | struct Packed { | ||
297 | a: u16, | ||
298 | b: u8, | ||
299 | c: u32, | ||
300 | } | ||
301 | |||
295 | fn main() { | 302 | fn main() { |
296 | let x = &5 as *const usize; | 303 | let x = &5 as *const usize; |
297 | let u = Union { b: 0 }; | 304 | let u = Union { b: 0 }; |
@@ -306,6 +313,10 @@ fn main() { | |||
306 | let y = *(x); | 313 | let y = *(x); |
307 | let z = -x; | 314 | let z = -x; |
308 | let a = global_mut.a; | 315 | let a = global_mut.a; |
316 | let packed = Packed { a: 0, b: 0, c: 0 }; | ||
317 | let a = &packed.a; | ||
318 | let b = &packed.b; | ||
319 | let c = &packed.c; | ||
309 | } | 320 | } |
310 | } | 321 | } |
311 | "# | 322 | "# |