about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_span/src/symbol.rs10
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs54
-rw-r--r--library/core/src/num/nonzero.rs1
-rw-r--r--src/test/ui/mismatched_types/non_zero_assigned_something.rs9
-rw-r--r--src/test/ui/mismatched_types/non_zero_assigned_something.stderr31
5 files changed, 105 insertions, 0 deletions
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index d5e65c0b442..6ddcfbb05dc 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -223,6 +223,16 @@ symbols! {
         LintPass,
         Mutex,
         N,
+        NonZeroI128,
+        NonZeroI16,
+        NonZeroI32,
+        NonZeroI64,
+        NonZeroI8,
+        NonZeroU128,
+        NonZeroU16,
+        NonZeroU32,
+        NonZeroU64,
+        NonZeroU8,
         None,
         Ok,
         Option,
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index 867570b42da..31da5cfe7fa 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -34,6 +34,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.annotate_expected_due_to_let_ty(err, expr, error);
         self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
         self.suggest_compatible_variants(err, expr, expected, expr_ty);
+        self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
         if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
             return;
         }
@@ -418,6 +419,59 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggest_non_zero_new_unwrap(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        expected: Ty<'tcx>,
+        expr_ty: Ty<'tcx>,
+    ) {
+        let tcx = self.tcx;
+        let (adt, unwrap) = match expected.kind() {
+            // In case Option<NonZero*> is wanted, but * is provided, suggest calling new
+            ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
+                // Unwrap option
+                let Some(fst) = substs.first() else { return };
+                let ty::Adt(adt, _) = fst.expect_ty().kind() else { return };
+
+                (adt, "")
+            }
+            // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
+            ty::Adt(adt, _) => (adt, ".unwrap()"),
+            _ => return,
+        };
+
+        let map = [
+            (sym::NonZeroU8, tcx.types.u8),
+            (sym::NonZeroU16, tcx.types.u16),
+            (sym::NonZeroU32, tcx.types.u32),
+            (sym::NonZeroU64, tcx.types.u64),
+            (sym::NonZeroU128, tcx.types.u128),
+            (sym::NonZeroI8, tcx.types.i8),
+            (sym::NonZeroI16, tcx.types.i16),
+            (sym::NonZeroI32, tcx.types.i32),
+            (sym::NonZeroI64, tcx.types.i64),
+            (sym::NonZeroI128, tcx.types.i128),
+        ];
+
+        let Some((s, _)) = map
+            .iter()
+            .find(|&&(s, _)| self.tcx.is_diagnostic_item(s, adt.did()))
+            .filter(|&&(_, t)| { self.can_coerce(expr_ty, t) })
+            else { return };
+
+        let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
+
+        err.multipart_suggestion(
+            format!("consider calling `{s}::new`"),
+            vec![
+                (expr.span.shrink_to_lo(), format!("{path}::new(")),
+                (expr.span.shrink_to_hi(), format!("){unwrap}")),
+            ],
+            Applicability::MaybeIncorrect,
+        );
+    }
+
     pub fn get_conversion_methods(
         &self,
         span: Span,
diff --git a/library/core/src/num/nonzero.rs b/library/core/src/num/nonzero.rs
index 92d03b724b4..4de0a0cf564 100644
--- a/library/core/src/num/nonzero.rs
+++ b/library/core/src/num/nonzero.rs
@@ -39,6 +39,7 @@ macro_rules! nonzero_integers {
             #[repr(transparent)]
             #[rustc_layout_scalar_valid_range_start(1)]
             #[rustc_nonnull_optimization_guaranteed]
+            #[rustc_diagnostic_item = stringify!($Ty)]
             pub struct $Ty($Int);
 
             impl $Ty {
diff --git a/src/test/ui/mismatched_types/non_zero_assigned_something.rs b/src/test/ui/mismatched_types/non_zero_assigned_something.rs
new file mode 100644
index 00000000000..d2adbe01c18
--- /dev/null
+++ b/src/test/ui/mismatched_types/non_zero_assigned_something.rs
@@ -0,0 +1,9 @@
+fn main() {
+    let _: std::num::NonZeroU64 = 1;
+    //~^ ERROR mismatched types
+    //~| HELP  consider calling `NonZeroU64::new`
+
+    let _: Option<std::num::NonZeroU64> = 1;
+    //~^ ERROR mismatched types
+    //~| HELP  consider calling `NonZeroU64::new`
+}
diff --git a/src/test/ui/mismatched_types/non_zero_assigned_something.stderr b/src/test/ui/mismatched_types/non_zero_assigned_something.stderr
new file mode 100644
index 00000000000..d4b2c902f9b
--- /dev/null
+++ b/src/test/ui/mismatched_types/non_zero_assigned_something.stderr
@@ -0,0 +1,31 @@
+error[E0308]: mismatched types
+  --> $DIR/non_zero_assigned_something.rs:2:35
+   |
+LL |     let _: std::num::NonZeroU64 = 1;
+   |            --------------------   ^ expected struct `NonZeroU64`, found integer
+   |            |
+   |            expected due to this
+   |
+help: consider calling `NonZeroU64::new`
+   |
+LL |     let _: std::num::NonZeroU64 = NonZeroU64::new(1).unwrap();
+   |                                   ++++++++++++++++ ++++++++++
+
+error[E0308]: mismatched types
+  --> $DIR/non_zero_assigned_something.rs:6:43
+   |
+LL |     let _: Option<std::num::NonZeroU64> = 1;
+   |            ----------------------------   ^ expected enum `Option`, found integer
+   |            |
+   |            expected due to this
+   |
+   = note: expected enum `Option<NonZeroU64>`
+              found type `{integer}`
+help: consider calling `NonZeroU64::new`
+   |
+LL |     let _: Option<std::num::NonZeroU64> = NonZeroU64::new(1);
+   |                                           ++++++++++++++++ +
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.