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/semantics.rs | |
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/semantics.rs')
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 99 |
1 files changed, 98 insertions, 1 deletions
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 { |