diff options
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 154 |
1 files changed, 83 insertions, 71 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 9697c7082..758d00409 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -282,83 +282,15 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
282 | } | 282 | } |
283 | 283 | ||
284 | pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { | 284 | pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { |
285 | method_call_expr | 285 | self.imp.is_unsafe_method_call(method_call_expr) |
286 | .expr() | ||
287 | .and_then(|expr| { | ||
288 | let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { | ||
289 | field_expr | ||
290 | } else { | ||
291 | return None; | ||
292 | }; | ||
293 | let ty = self.type_of_expr(&field_expr.expr()?)?; | ||
294 | if !ty.is_packed(self.db) { | ||
295 | return None; | ||
296 | } | ||
297 | |||
298 | let func = self.resolve_method_call(&method_call_expr)?; | ||
299 | let is_unsafe = func.has_self_param(self.db) | ||
300 | && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..))); | ||
301 | Some(is_unsafe) | ||
302 | }) | ||
303 | .unwrap_or(false) | ||
304 | } | 286 | } |
305 | 287 | ||
306 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { | 288 | pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { |
307 | ref_expr | 289 | self.imp.is_unsafe_ref_expr(ref_expr) |
308 | .expr() | ||
309 | .and_then(|expr| { | ||
310 | let field_expr = match expr { | ||
311 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
312 | _ => return None, | ||
313 | }; | ||
314 | let expr = field_expr.expr()?; | ||
315 | self.type_of_expr(&expr) | ||
316 | }) | ||
317 | // Binding a reference to a packed type is possibly unsafe. | ||
318 | .map(|ty| ty.is_packed(self.db)) | ||
319 | .unwrap_or(false) | ||
320 | |||
321 | // FIXME This needs layout computation to be correct. It will highlight | ||
322 | // more than it should with the current implementation. | ||
323 | } | 290 | } |
324 | 291 | ||
325 | pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool { | 292 | pub fn is_unsafe_bind_pat(&self, bind_pat: &ast::BindPat) -> bool { |
326 | bind_pat | 293 | self.imp.is_unsafe_bind_pat(bind_pat) |
327 | .syntax() | ||
328 | .parent() | ||
329 | .and_then(|parent| { | ||
330 | // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or | ||
331 | // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`, | ||
332 | // so this tries to lookup the `BindPat` anywhere along that structure to the | ||
333 | // `RecordPat` so we can get the containing type. | ||
334 | let record_pat = ast::RecordFieldPat::cast(parent.clone()) | ||
335 | .and_then(|record_pat| record_pat.syntax().parent()) | ||
336 | .or_else(|| Some(parent.clone())) | ||
337 | .and_then(|parent| { | ||
338 | ast::RecordFieldPatList::cast(parent)? | ||
339 | .syntax() | ||
340 | .parent() | ||
341 | .and_then(ast::RecordPat::cast) | ||
342 | }); | ||
343 | |||
344 | // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if | ||
345 | // this is initialized from a `FieldExpr`. | ||
346 | if let Some(record_pat) = record_pat { | ||
347 | self.type_of_pat(&ast::Pat::RecordPat(record_pat)) | ||
348 | } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { | ||
349 | let field_expr = match let_stmt.initializer()? { | ||
350 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
351 | _ => return None, | ||
352 | }; | ||
353 | |||
354 | self.type_of_expr(&field_expr.expr()?) | ||
355 | } else { | ||
356 | None | ||
357 | } | ||
358 | }) | ||
359 | // Binding a reference to a packed type is possibly unsafe. | ||
360 | .map(|ty| ty.is_packed(self.db)) | ||
361 | .unwrap_or(false) | ||
362 | } | 294 | } |
363 | } | 295 | } |
364 | 296 | ||
@@ -655,6 +587,86 @@ impl<'db> SemanticsImpl<'db> { | |||
655 | }); | 587 | }); |
656 | InFile::new(file_id, node) | 588 | InFile::new(file_id, node) |
657 | } | 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_bind_pat(&self, bind_pat: &ast::BindPat) -> bool { | ||
633 | bind_pat | ||
634 | .syntax() | ||
635 | .parent() | ||
636 | .and_then(|parent| { | ||
637 | // `BindPat` can live under `RecordPat` directly under `RecordFieldPat` or | ||
638 | // `RecordFieldPatList`. `RecordFieldPat` also lives under `RecordFieldPatList`, | ||
639 | // so this tries to lookup the `BindPat` anywhere along that structure to the | ||
640 | // `RecordPat` so we can get the containing type. | ||
641 | let record_pat = ast::RecordFieldPat::cast(parent.clone()) | ||
642 | .and_then(|record_pat| record_pat.syntax().parent()) | ||
643 | .or_else(|| Some(parent.clone())) | ||
644 | .and_then(|parent| { | ||
645 | ast::RecordFieldPatList::cast(parent)? | ||
646 | .syntax() | ||
647 | .parent() | ||
648 | .and_then(ast::RecordPat::cast) | ||
649 | }); | ||
650 | |||
651 | // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if | ||
652 | // this is initialized from a `FieldExpr`. | ||
653 | if let Some(record_pat) = record_pat { | ||
654 | self.type_of_pat(&ast::Pat::RecordPat(record_pat)) | ||
655 | } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { | ||
656 | let field_expr = match let_stmt.initializer()? { | ||
657 | ast::Expr::FieldExpr(field_expr) => field_expr, | ||
658 | _ => return None, | ||
659 | }; | ||
660 | |||
661 | self.type_of_expr(&field_expr.expr()?) | ||
662 | } else { | ||
663 | None | ||
664 | } | ||
665 | }) | ||
666 | // Binding a reference to a packed type is possibly unsafe. | ||
667 | .map(|ty| ty.is_packed(self.db)) | ||
668 | .unwrap_or(false) | ||
669 | } | ||
658 | } | 670 | } |
659 | 671 | ||
660 | pub trait ToDef: AstNode + Clone { | 672 | pub trait ToDef: AstNode + Clone { |