aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/semantics.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/semantics.rs')
-rw-r--r--crates/ra_hir/src/semantics.rs99
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};
30use resolver::TypeNs; 31use 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
284impl<'db> SemanticsImpl<'db> { 297impl<'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
579pub trait ToDef: AstNode + Clone { 676pub trait ToDef: AstNode + Clone {