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.rs79
-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
-rw-r--r--src/test/ui/mismatched_types/wrap-suggestion-privacy.rs24
-rw-r--r--src/test/ui/mismatched_types/wrap-suggestion-privacy.stderr59
7 files changed, 207 insertions, 6 deletions
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 66aa48fd635..3ccd3fcbafa 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 740261cfe74..a2d8765289c 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -33,6 +33,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;
         }
@@ -347,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
 
-            let compatible_variants: Vec<String> = expected_adt
+            let compatible_variants: Vec<(String, Option<String>)> = expected_adt
                 .variants()
                 .iter()
                 .filter(|variant| {
@@ -355,6 +356,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 })
                 .filter_map(|variant| {
                     let sole_field = &variant.fields[0];
+
+                    let field_is_local = sole_field.did.is_local();
+                    let field_is_accessible =
+                        sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx);
+
+                    if !field_is_local && !field_is_accessible {
+                        return None;
+                    }
+
+                    let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
+                        .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)"));
+
                     let sole_field_ty = sole_field.ty(self.tcx, substs);
                     if self.can_coerce(expr_ty, sole_field_ty) {
                         let variant_path =
@@ -363,9 +376,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if let Some(path) = variant_path.strip_prefix("std::prelude::")
                             && let Some((_, path)) = path.split_once("::")
                         {
-                            return Some(path.to_string());
+                            return Some((path.to_string(), note_about_variant_field_privacy));
                         }
-                        Some(variant_path)
+                        Some((variant_path, note_about_variant_field_privacy))
                     } else {
                         None
                     }
@@ -379,10 +392,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             match &compatible_variants[..] {
                 [] => { /* No variants to format */ }
-                [variant] => {
+                [(variant, note)] => {
                     // Just a single matching variant.
                     err.multipart_suggestion_verbose(
-                        &format!("try wrapping the expression in `{variant}`"),
+                        &format!(
+                            "try wrapping the expression in `{variant}`{note}",
+                            note = note.as_deref().unwrap_or("")
+                        ),
                         vec![
                             (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
                             (expr.span.shrink_to_hi(), ")".to_string()),
@@ -397,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             "try wrapping the expression in a variant of `{}`",
                             self.tcx.def_path_str(expected_adt.did())
                         ),
-                        compatible_variants.into_iter().map(|variant| {
+                        compatible_variants.into_iter().map(|(variant, _)| {
                             vec![
                                 (expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
                                 (expr.span.shrink_to_hi(), ")".to_string()),
@@ -410,6 +426,57 @@ 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 ty::Adt(adt, _) = substs.type_at(0).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, t)| self.tcx.is_diagnostic_item(s, adt.did()) && 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`.
diff --git a/src/test/ui/mismatched_types/wrap-suggestion-privacy.rs b/src/test/ui/mismatched_types/wrap-suggestion-privacy.rs
new file mode 100644
index 00000000000..63cb1a12991
--- /dev/null
+++ b/src/test/ui/mismatched_types/wrap-suggestion-privacy.rs
@@ -0,0 +1,24 @@
+mod inner {
+    pub struct Wrapper<T>(T);
+}
+
+fn needs_wrapper(t: inner::Wrapper<i32>) {}
+fn needs_wrapping(t: std::num::Wrapping<i32>) {}
+fn needs_ready(t: std::future::Ready<i32>) {}
+
+fn main() {
+    // Suggest wrapping expression because type is local
+    // and its privacy can be easily changed
+    needs_wrapper(0);
+    //~^ ERROR mismatched types
+    //~| HELP  try wrapping the expression in `inner::Wrapper`
+
+    // Suggest wrapping expression because field is accessible
+    needs_wrapping(0);
+    //~^ ERROR mismatched types
+    //~| HELP  try wrapping the expression in `std::num::Wrapping`
+
+    // Do not suggest wrapping expression
+    needs_ready(Some(0));
+    //~^ ERROR mismatched types
+}
diff --git a/src/test/ui/mismatched_types/wrap-suggestion-privacy.stderr b/src/test/ui/mismatched_types/wrap-suggestion-privacy.stderr
new file mode 100644
index 00000000000..e8eb8d263ec
--- /dev/null
+++ b/src/test/ui/mismatched_types/wrap-suggestion-privacy.stderr
@@ -0,0 +1,59 @@
+error[E0308]: mismatched types
+  --> $DIR/wrap-suggestion-privacy.rs:12:19
+   |
+LL |     needs_wrapper(0);
+   |     ------------- ^ expected struct `Wrapper`, found integer
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected struct `Wrapper<i32>`
+                found type `{integer}`
+note: function defined here
+  --> $DIR/wrap-suggestion-privacy.rs:5:4
+   |
+LL | fn needs_wrapper(t: inner::Wrapper<i32>) {}
+   |    ^^^^^^^^^^^^^ ----------------------
+help: try wrapping the expression in `inner::Wrapper` (its field is private, but it's local to this crate and its privacy can be changed)
+   |
+LL |     needs_wrapper(inner::Wrapper(0));
+   |                   +++++++++++++++ +
+
+error[E0308]: mismatched types
+  --> $DIR/wrap-suggestion-privacy.rs:17:20
+   |
+LL |     needs_wrapping(0);
+   |     -------------- ^ expected struct `Wrapping`, found integer
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected struct `Wrapping<i32>`
+                found type `{integer}`
+note: function defined here
+  --> $DIR/wrap-suggestion-privacy.rs:6:4
+   |
+LL | fn needs_wrapping(t: std::num::Wrapping<i32>) {}
+   |    ^^^^^^^^^^^^^^ --------------------------
+help: try wrapping the expression in `std::num::Wrapping`
+   |
+LL |     needs_wrapping(std::num::Wrapping(0));
+   |                    +++++++++++++++++++ +
+
+error[E0308]: mismatched types
+  --> $DIR/wrap-suggestion-privacy.rs:22:17
+   |
+LL |     needs_ready(Some(0));
+   |     ----------- ^^^^^^^ expected struct `std::future::Ready`, found enum `Option`
+   |     |
+   |     arguments to this function are incorrect
+   |
+   = note: expected struct `std::future::Ready<i32>`
+                found enum `Option<{integer}>`
+note: function defined here
+  --> $DIR/wrap-suggestion-privacy.rs:7:4
+   |
+LL | fn needs_ready(t: std::future::Ready<i32>) {}
+   |    ^^^^^^^^^^^ --------------------------
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.