about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Daniel Faria <Nashenas88@users.noreply.github.com>2020-06-03 23:38:25 -0400
committerPaul Daniel Faria <Nashenas88@users.noreply.github.com>2020-08-10 08:44:54 -0400
commit263f9a7f231a474dd56d02adbcd7c57d079e88fd (patch)
tree125391657a69cb05ac14fadee37e6138dc0daae7
parentf3336509e52187a7a70a8043557a7317872e3a2f (diff)
downloadrust-263f9a7f231a474dd56d02adbcd7c57d079e88fd.tar.gz
rust-263f9a7f231a474dd56d02adbcd7c57d079e88fd.zip
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.rs18
-rw-r--r--crates/ra_hir_def/src/adt.rs56
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs24
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs11
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 44456e49e2b..6f9c56d2943 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<VariantData> {
         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 6cb56a1cd00..6d59c864280 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<VariantData>,
+    pub repr: Option<ReprKind>,
 }
 
 #[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<ReprKind> {
+    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<ReprKind> {
+    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<StructData> {
         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<StructData> {
         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 6b7874460a2..0cab684eb47 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 09062c38e75..f2c078d3479 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;
     }
 }
 "#