Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/ra_hir/src/code_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -431,6 +432,10 @@ impl Struct {
Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id)
}

pub fn repr(self, db: &dyn HirDatabase) -> Option<ReprKind> {
db.struct_data(self.id).repr.clone()
}

fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
db.struct_data(self.id).variant_data.clone()
}
Expand Down Expand Up @@ -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) => matches!(s.repr(db), Some(ReprKind::Packed)),
_ => false,
}
}

pub fn is_raw_ptr(&self) -> bool {
matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. }))
}
Expand Down
2 changes: 1 addition & 1 deletion crates/ra_hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub use hir_def::{
docs::Documentation,
nameres::ModuleSource,
path::{ModPath, Path, PathKind},
type_ref::Mutability,
type_ref::{Mutability, TypeRef},
};
pub use hir_expand::{
hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc,
Expand Down
99 changes: 98 additions & 1 deletion crates/ra_hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use crate::{
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer},
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef,
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef,
Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef,
VariantDef,
};
use resolver::TypeNs;

Expand Down Expand Up @@ -279,6 +280,18 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
pub fn assert_contains_node(&self, node: &SyntaxNode) {
self.imp.assert_contains_node(node)
}

pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
self.imp.is_unsafe_method_call(method_call_expr)
}

pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
self.imp.is_unsafe_ref_expr(ref_expr)
}

pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
self.imp.is_unsafe_ident_pat(ident_pat)
}
}

impl<'db> SemanticsImpl<'db> {
Expand Down Expand Up @@ -574,6 +587,90 @@ impl<'db> SemanticsImpl<'db> {
});
InFile::new(file_id, node)
}

pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool {
method_call_expr
.expr()
.and_then(|expr| {
let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr {
field_expr
} else {
return None;
};
let ty = self.type_of_expr(&field_expr.expr()?)?;
if !ty.is_packed(self.db) {
return None;
}

let func = self.resolve_method_call(&method_call_expr).map(Function::from)?;
let is_unsafe = func.has_self_param(self.db)
&& matches!(func.params(self.db).first(), Some(TypeRef::Reference(..)));
Some(is_unsafe)
})
.unwrap_or(false)
}

pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool {
ref_expr
.expr()
.and_then(|expr| {
let field_expr = match expr {
ast::Expr::FieldExpr(field_expr) => field_expr,
_ => return None,
};
let expr = field_expr.expr()?;
self.type_of_expr(&expr)
})
// Binding a reference to a packed type is possibly unsafe.
.map(|ty| ty.is_packed(self.db))
.unwrap_or(false)

// FIXME This needs layout computation to be correct. It will highlight
// more than it should with the current implementation.
}

pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
if !ident_pat.ref_token().is_some() {
return false;
}

ident_pat
.syntax()
.parent()
.and_then(|parent| {
// `IdentPat` can live under `RecordPat` directly under `RecordPatField` or
// `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`,
// so this tries to lookup the `IdentPat` anywhere along that structure to the
// `RecordPat` so we can get the containing type.
let record_pat = ast::RecordPatField::cast(parent.clone())
.and_then(|record_pat| record_pat.syntax().parent())
.or_else(|| Some(parent.clone()))
.and_then(|parent| {
ast::RecordPatFieldList::cast(parent)?
.syntax()
.parent()
.and_then(ast::RecordPat::cast)
});

// If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if
// this is initialized from a `FieldExpr`.
if let Some(record_pat) = record_pat {
self.type_of_pat(&ast::Pat::RecordPat(record_pat))
} else if let Some(let_stmt) = ast::LetStmt::cast(parent) {
let field_expr = match let_stmt.initializer()? {
ast::Expr::FieldExpr(field_expr) => field_expr,
_ => return None,
};

self.type_of_expr(&field_expr.expr()?)
} else {
None
}
})
// Binding a reference to a packed type is possibly unsafe.
.map(|ty| ty.is_packed(self.db))
.unwrap_or(false)
}
}

pub trait ToDef: AstNode + Clone {
Expand Down
42 changes: 38 additions & 4 deletions crates/ra_hir_def/src/adt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ 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::{
body::{CfgExpander, LowerCtx},
db::DefDatabase,
item_tree::{Field, Fields, ItemTree},
item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem},
src::HasChildSource,
src::HasSource,
trace::Trace,
Expand All @@ -29,6 +30,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)]
Expand Down Expand Up @@ -58,26 +60,58 @@ 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).by_key("repr").tt_values().find_map(parse_repr_tt)
}

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()? {
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,
})
}
}

Expand Down
Loading