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:
parent
f3336509e5
commit
263f9a7f23
@ -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(..), .. }))
|
||||
}
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
"#
|
||||
|
Loading…
Reference in New Issue
Block a user