|
1 | | -use std::cell::OnceCell; |
| 1 | +use std::{borrow::Cow, cell::OnceCell}; |
2 | 2 |
|
3 | 3 | use crate::{errors, FnCtxt, TypeckRootCtxt}; |
4 | 4 | use rustc_data_structures::{ |
5 | 5 | graph::{self, iterate::DepthFirstSearch, vec_graph::VecGraph}, |
6 | 6 | unord::{UnordBag, UnordMap, UnordSet}, |
7 | 7 | }; |
| 8 | +use rustc_errors::{DiagArgValue, IntoDiagArg}; |
8 | 9 | use rustc_hir as hir; |
9 | 10 | use rustc_hir::intravisit::Visitor; |
10 | 11 | use rustc_hir::HirId; |
@@ -374,12 +375,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> { |
374 | 375 | .filter_map(|x| unsafe_infer_vars.get(&x).copied()) |
375 | 376 | .collect::<Vec<_>>(); |
376 | 377 |
|
377 | | - for (hir_id, span) in affected_unsafe_infer_vars { |
| 378 | + for (hir_id, span, reason) in affected_unsafe_infer_vars { |
378 | 379 | self.tcx.emit_node_span_lint( |
379 | 380 | lint::builtin::NEVER_TYPE_FALLBACK_FLOWING_INTO_UNSAFE, |
380 | 381 | hir_id, |
381 | 382 | span, |
382 | | - errors::NeverTypeFallbackFlowingIntoUnsafe {}, |
| 383 | + errors::NeverTypeFallbackFlowingIntoUnsafe { reason }, |
383 | 384 | ); |
384 | 385 | } |
385 | 386 |
|
@@ -493,77 +494,169 @@ impl<'tcx> FnCtxt<'_, 'tcx> { |
493 | 494 | } |
494 | 495 | } |
495 | 496 |
|
496 | | -/// Finds all type variables which are passed to an `unsafe` function. |
| 497 | +#[derive(Debug, Copy, Clone)] |
| 498 | +pub(crate) enum UnsafeUseReason { |
| 499 | + Call, |
| 500 | + Method, |
| 501 | + Path, |
| 502 | + UnionField, |
| 503 | + Deref, |
| 504 | +} |
| 505 | + |
| 506 | +impl IntoDiagArg for UnsafeUseReason { |
| 507 | + fn into_diag_arg(self) -> DiagArgValue { |
| 508 | + let s = match self { |
| 509 | + UnsafeUseReason::Call => "call", |
| 510 | + UnsafeUseReason::Method => "method", |
| 511 | + UnsafeUseReason::Path => "path", |
| 512 | + UnsafeUseReason::UnionField => "union_field", |
| 513 | + UnsafeUseReason::Deref => "deref", |
| 514 | + }; |
| 515 | + DiagArgValue::Str(Cow::Borrowed(s)) |
| 516 | + } |
| 517 | +} |
| 518 | + |
| 519 | +/// Finds all type variables which are passed to an `unsafe` operation. |
497 | 520 | /// |
498 | 521 | /// For example, for this function `f`: |
499 | 522 | /// ```ignore (demonstrative) |
500 | 523 | /// fn f() { |
501 | 524 | /// unsafe { |
502 | 525 | /// let x /* ?X */ = core::mem::zeroed(); |
503 | | -/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span |
| 526 | +/// // ^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason |
504 | 527 | /// |
505 | 528 | /// let y = core::mem::zeroed::<Option<_ /* ?Y */>>(); |
506 | | -/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span |
| 529 | +/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- hir_id, span, reason |
507 | 530 | /// } |
508 | 531 | /// } |
509 | 532 | /// ``` |
510 | 533 | /// |
511 | | -/// Will return `{ id(?X) -> (hir_id, span) }` |
| 534 | +/// `compute_unsafe_infer_vars` will return `{ id(?X) -> (hir_id, span, Call) }` |
512 | 535 | fn compute_unsafe_infer_vars<'a, 'tcx>( |
513 | 536 | root_ctxt: &'a TypeckRootCtxt<'tcx>, |
514 | 537 | body_id: LocalDefId, |
515 | | -) -> UnordMap<ty::TyVid, (HirId, Span)> { |
516 | | - let tcx = root_ctxt.infcx.tcx; |
517 | | - let body_id = tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); |
518 | | - let body = tcx.hir().body(body_id); |
| 538 | +) -> UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)> { |
| 539 | + let body_id = |
| 540 | + root_ctxt.tcx.hir().maybe_body_owned_by(body_id).expect("body id must have an owner"); |
| 541 | + let body = root_ctxt.tcx.hir().body(body_id); |
519 | 542 | let mut res = UnordMap::default(); |
520 | 543 |
|
521 | 544 | struct UnsafeInferVarsVisitor<'a, 'tcx, 'r> { |
522 | 545 | root_ctxt: &'a TypeckRootCtxt<'tcx>, |
523 | | - res: &'r mut UnordMap<ty::TyVid, (HirId, Span)>, |
| 546 | + res: &'r mut UnordMap<ty::TyVid, (HirId, Span, UnsafeUseReason)>, |
524 | 547 | } |
525 | 548 |
|
526 | 549 | impl Visitor<'_> for UnsafeInferVarsVisitor<'_, '_, '_> { |
527 | 550 | fn visit_expr(&mut self, ex: &'_ hir::Expr<'_>) { |
528 | | - // FIXME: method calls |
529 | | - if let hir::ExprKind::Call(func, ..) = ex.kind { |
530 | | - let typeck_results = self.root_ctxt.typeck_results.borrow(); |
531 | | - |
532 | | - let func_ty = typeck_results.expr_ty(func); |
533 | | - |
534 | | - // `is_fn` is required to ignore closures (which can't be unsafe) |
535 | | - if func_ty.is_fn() |
536 | | - && let sig = func_ty.fn_sig(self.root_ctxt.infcx.tcx) |
537 | | - && let hir::Unsafety::Unsafe = sig.unsafety() |
538 | | - { |
539 | | - let mut collector = |
540 | | - InferVarCollector { hir_id: ex.hir_id, call_span: ex.span, res: self.res }; |
541 | | - |
542 | | - // Collect generic arguments of the function which are inference variables |
543 | | - typeck_results |
544 | | - .node_args(ex.hir_id) |
545 | | - .types() |
546 | | - .for_each(|t| t.visit_with(&mut collector)); |
547 | | - |
548 | | - // Also check the return type, for cases like `(unsafe_fn::<_> as unsafe fn() -> _)()` |
549 | | - sig.output().visit_with(&mut collector); |
| 551 | + let typeck_results = self.root_ctxt.typeck_results.borrow(); |
| 552 | + |
| 553 | + match ex.kind { |
| 554 | + hir::ExprKind::MethodCall(..) => { |
| 555 | + if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id) |
| 556 | + && let method_ty = self.root_ctxt.tcx.type_of(def_id).instantiate_identity() |
| 557 | + && let sig = method_ty.fn_sig(self.root_ctxt.tcx) |
| 558 | + && let hir::Unsafety::Unsafe = sig.unsafety() |
| 559 | + { |
| 560 | + let mut collector = InferVarCollector { |
| 561 | + value: (ex.hir_id, ex.span, UnsafeUseReason::Method), |
| 562 | + res: self.res, |
| 563 | + }; |
| 564 | + |
| 565 | + // Collect generic arguments (incl. `Self`) of the method |
| 566 | + typeck_results |
| 567 | + .node_args(ex.hir_id) |
| 568 | + .types() |
| 569 | + .for_each(|t| t.visit_with(&mut collector)); |
| 570 | + } |
550 | 571 | } |
551 | | - } |
| 572 | + |
| 573 | + hir::ExprKind::Call(func, ..) => { |
| 574 | + let func_ty = typeck_results.expr_ty(func); |
| 575 | + |
| 576 | + if func_ty.is_fn() |
| 577 | + && let sig = func_ty.fn_sig(self.root_ctxt.tcx) |
| 578 | + && let hir::Unsafety::Unsafe = sig.unsafety() |
| 579 | + { |
| 580 | + let mut collector = InferVarCollector { |
| 581 | + value: (ex.hir_id, ex.span, UnsafeUseReason::Call), |
| 582 | + res: self.res, |
| 583 | + }; |
| 584 | + |
| 585 | + // Try collecting generic arguments of the function. |
| 586 | + // Note that we do this below for any paths (that don't have to be called), |
| 587 | + // but there we do it with a different span/reason. |
| 588 | + // This takes priority. |
| 589 | + typeck_results |
| 590 | + .node_args(func.hir_id) |
| 591 | + .types() |
| 592 | + .for_each(|t| t.visit_with(&mut collector)); |
| 593 | + |
| 594 | + // Also check the return type, for cases like `returns_unsafe_fn_ptr()()` |
| 595 | + sig.output().visit_with(&mut collector); |
| 596 | + } |
| 597 | + } |
| 598 | + |
| 599 | + // Check paths which refer to functions. |
| 600 | + // We do this, instead of only checking `Call` to make sure the lint can't be |
| 601 | + // avoided by storing unsafe function in a variable. |
| 602 | + hir::ExprKind::Path(_) => { |
| 603 | + let ty = typeck_results.expr_ty(ex); |
| 604 | + |
| 605 | + // If this path refers to an unsafe function, collect inference variables which may affect it. |
| 606 | + // `is_fn` excludes closures, but those can't be unsafe. |
| 607 | + if ty.is_fn() |
| 608 | + && let sig = ty.fn_sig(self.root_ctxt.tcx) |
| 609 | + && let hir::Unsafety::Unsafe = sig.unsafety() |
| 610 | + { |
| 611 | + let mut collector = InferVarCollector { |
| 612 | + value: (ex.hir_id, ex.span, UnsafeUseReason::Path), |
| 613 | + res: self.res, |
| 614 | + }; |
| 615 | + |
| 616 | + // Collect generic arguments of the function |
| 617 | + typeck_results |
| 618 | + .node_args(ex.hir_id) |
| 619 | + .types() |
| 620 | + .for_each(|t| t.visit_with(&mut collector)); |
| 621 | + } |
| 622 | + } |
| 623 | + |
| 624 | + hir::ExprKind::Unary(hir::UnOp::Deref, pointer) => { |
| 625 | + if let ty::RawPtr(pointee, _) = typeck_results.expr_ty(pointer).kind() { |
| 626 | + pointee.visit_with(&mut InferVarCollector { |
| 627 | + value: (ex.hir_id, ex.span, UnsafeUseReason::Deref), |
| 628 | + res: self.res, |
| 629 | + }); |
| 630 | + } |
| 631 | + } |
| 632 | + |
| 633 | + hir::ExprKind::Field(base, _) => { |
| 634 | + let base_ty = typeck_results.expr_ty(base); |
| 635 | + |
| 636 | + if base_ty.is_union() { |
| 637 | + typeck_results.expr_ty(ex).visit_with(&mut InferVarCollector { |
| 638 | + value: (ex.hir_id, ex.span, UnsafeUseReason::UnionField), |
| 639 | + res: self.res, |
| 640 | + }); |
| 641 | + } |
| 642 | + } |
| 643 | + |
| 644 | + _ => (), |
| 645 | + }; |
552 | 646 |
|
553 | 647 | hir::intravisit::walk_expr(self, ex); |
554 | 648 | } |
555 | 649 | } |
556 | 650 |
|
557 | | - struct InferVarCollector<'r> { |
558 | | - hir_id: HirId, |
559 | | - call_span: Span, |
560 | | - res: &'r mut UnordMap<ty::TyVid, (HirId, Span)>, |
| 651 | + struct InferVarCollector<'r, V> { |
| 652 | + value: V, |
| 653 | + res: &'r mut UnordMap<ty::TyVid, V>, |
561 | 654 | } |
562 | 655 |
|
563 | | - impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_> { |
| 656 | + impl<'tcx, V: Copy> ty::TypeVisitor<TyCtxt<'tcx>> for InferVarCollector<'_, V> { |
564 | 657 | fn visit_ty(&mut self, t: Ty<'tcx>) { |
565 | 658 | if let Some(vid) = t.ty_vid() { |
566 | | - self.res.insert(vid, (self.hir_id, self.call_span)); |
| 659 | + _ = self.res.try_insert(vid, self.value); |
567 | 660 | } else { |
568 | 661 | t.super_visit_with(self) |
569 | 662 | } |
|
0 commit comments