diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-08-12 14:20:18 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-08-12 14:20:18 +0100 |
commit | 11de7ac2fb6514484076217acb8d93eb36468681 (patch) | |
tree | b727a8db741367634eebf4dc685e0f56fddb2a68 /crates/ra_hir/src | |
parent | 2d41cc0ea323e3c4d97300e4d66de11d6b7148ef (diff) | |
parent | 72baf1acdd544c645fd69c16967b91be9e75371b (diff) |
Merge #4743
4743: Add tracking of packed repr, use it to highlight unsafe refs r=matklad a=Nashenas88
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.
Fixes #4600
Co-authored-by: Paul Daniel Faria <[email protected]>
Co-authored-by: Paul Daniel Faria <[email protected]>
Co-authored-by: Paul Daniel Faria <[email protected]>
Diffstat (limited to 'crates/ra_hir/src')
-rw-r--r-- | crates/ra_hir/src/code_model.rs | 18 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 99 |
3 files changed, 117 insertions, 2 deletions
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 44456e49e..0007d7fa8 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 repr(self, db: &dyn HirDatabase) -> Option<ReprKind> { | ||
436 | db.struct_data(self.id).repr.clone() | ||
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) => matches!(s.repr(db), Some(ReprKind::Packed)), | ||
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/src/lib.rs b/crates/ra_hir/src/lib.rs index 31f3241c9..34b02c536 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -49,7 +49,7 @@ pub use hir_def::{ | |||
49 | docs::Documentation, | 49 | docs::Documentation, |
50 | nameres::ModuleSource, | 50 | nameres::ModuleSource, |
51 | path::{ModPath, Path, PathKind}, | 51 | path::{ModPath, Path, PathKind}, |
52 | type_ref::Mutability, | 52 | type_ref::{Mutability, TypeRef}, |
53 | }; | 53 | }; |
54 | pub use hir_expand::{ | 54 | pub use hir_expand::{ |
55 | hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, | 55 | hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, |
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index e392130ab..872f5fa4c 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -25,7 +25,8 @@ use crate::{ | |||
25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, | 25 | semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, |
26 | source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, | 26 | source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, |
27 | AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, | 27 | AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, |
28 | Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef, | 28 | Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef, |
29 | VariantDef, | ||
29 | }; | 30 | }; |
30 | use resolver::TypeNs; | 31 | use resolver::TypeNs; |
31 | 32 | ||
@@ -279,6 +280,18 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
279 | pub fn assert_contains_node(&self, node: &SyntaxNode) { | 280 | pub fn assert_contains_node(&self, node: &SyntaxNode) { |
280 | self.imp.assert_contains_node(node) | 281 | self.imp.assert_contains_node(node) |
281 | } | 282 | } |
283 | |||
284 | pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { | ||
285 | self.imp.is_unsafe_method_call(method_call_expr) | ||
286 | } | ||
287 | |||
288 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | ||
289 | self.imp.is_unsafe_ref_expr(ref_expr) | ||
290 | } | ||
291 | |||
292 | pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { | ||
293 | self.imp.is_unsafe_ident_pat(ident_pat) | ||
294 | } | ||
282 | } | 295 | } |
283 | 296 | ||
284 | impl<'db> SemanticsImpl<'db> { | 297 | impl<'db> SemanticsImpl<'db> { |
@@ -574,6 +587,90 @@ impl<'db> SemanticsImpl<'db> { | |||
574 | }); | 587 | }); |
575 | InFile::new(file_id, node) | 588 | InFile::new(file_id, node) |
576 | } | 589 | } |
590 | |||
591 | pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { | ||
592 | method_call_expr | ||
593 | .expr() | ||
594 | .and_then(|expr| { | ||
595 | let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { | ||
596 | field_expr | ||
597 | } else { | ||
598 | return None; | ||
599 | }; | ||
600 | let ty = self.type_of_expr(&field_expr.expr()?)?; | ||
601 | if !ty.is_packed(self.db) { | ||
602 | return None; | ||
603 | } | ||
604 | |||
605 | let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; | ||
606 | let is_unsafe = func.has_self_param(self.db) | ||
607 | && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..))); | ||
608 | Some(is_unsafe) | ||
609 | }) | ||
610 | .unwrap_or(false) | ||
611 | } | ||
612 | |||
613 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | ||
614 | ref_expr | ||
615 | .expr() | ||
616 | .and_then(|expr| { | ||
617 | let field_expr = match expr { | ||
618 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
619 | _ => return None, | ||
620 | }; | ||
621 | let expr = field_expr.expr()?; | ||
622 | self.type_of_expr(&expr) | ||
623 | }) | ||
624 | // Binding a reference to a packed type is possibly unsafe. | ||
625 | .map(|ty| ty.is_packed(self.db)) | ||
626 | .unwrap_or(false) | ||
627 | |||
628 | // FIXME This needs layout computation to be correct. It will highlight | ||
629 | // more than it should with the current implementation. | ||
630 | } | ||
631 | |||
632 | pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { | ||
633 | if !ident_pat.ref_token().is_some() { | ||
634 | return false; | ||
635 | } | ||
636 | |||
637 | ident_pat | ||
638 | .syntax() | ||
639 | .parent() | ||
640 | .and_then(|parent| { | ||
641 | // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or | ||
642 | // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`, | ||
643 | // so this tries to lookup the `IdentPat` anywhere along that structure to the | ||
644 | // `RecordPat` so we can get the containing type. | ||
645 | let record_pat = ast::RecordPatField::cast(parent.clone()) | ||
646 | .and_then(|record_pat| record_pat.syntax().parent()) | ||
647 | .or_else(|| Some(parent.clone())) | ||
648 | .and_then(|parent| { | ||
649 | ast::RecordPatFieldList::cast(parent)? | ||
650 | .syntax() | ||
651 | .parent() | ||
652 | .and_then(ast::RecordPat::cast) | ||
653 | }); | ||
654 | |||
655 | // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if | ||
656 | // this is initialized from a `FieldExpr`. | ||
657 | if let Some(record_pat) = record_pat { | ||
658 | self.type_of_pat(&ast::Pat::RecordPat(record_pat)) | ||
659 | } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { | ||
660 | let field_expr = match let_stmt.initializer()? { | ||
661 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
662 | _ => return None, | ||
663 | }; | ||
664 | |||
665 | self.type_of_expr(&field_expr.expr()?) | ||
666 | } else { | ||
667 | None | ||
668 | } | ||
669 | }) | ||
670 | // Binding a reference to a packed type is possibly unsafe. | ||
671 | .map(|ty| ty.is_packed(self.db)) | ||
672 | .unwrap_or(false) | ||
673 | } | ||
577 | } | 674 | } |
578 | 675 | ||
579 | pub trait ToDef: AstNode + Clone { | 676 | pub trait ToDef: AstNode + Clone { |