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.
This commit is contained in:
Paul Daniel Faria 2020-06-03 23:38:25 -04:00
parent f3336509e5
commit 263f9a7f23
4 changed files with 105 additions and 4 deletions

View File

@ -4,6 +4,7 @@
use arrayvec::ArrayVec;
use either::Either;
use hir_def::{
adt::ReprKind,
adt::StructKind,
adt::VariantData,
builtin_type::BuiltinType,
@ -431,6 +432,10 @@ pub fn ty(self, db: &dyn HirDatabase) -> Type {
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 @@ pub fn is_fn(&self) -> bool {
)
}
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(..), .. }))
}

View File

@ -9,11 +9,13 @@
};
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 @@
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,
})
}
}

View File

@ -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()

View File

@ -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;
}
}
"#