|
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