Skip to content

Commit e6a100b

Browse files
authored
Rollup merge of #99438 - WaffleLapkin:dont_wrap_in_non_zero, r=compiler-errors
Improve suggestions for `NonZeroT` <- `T` coercion error Currently, when encountering a type mismatch error with `NonZeroT` and `T` (for example `NonZeroU8` and `u8`) we errorneusly suggest wrapping expression in `NonZeroT`: ```text error[E0308]: mismatched types --> ./t.rs:7:35 | 7 | let _: std::num::NonZeroU64 = 1; | -------------------- ^ expected struct `NonZeroU64`, found integer | | | expected due to this | help: try wrapping the expression in `std::num::NonZeroU64` | 7 | let _: std::num::NonZeroU64 = std::num::NonZeroU64(1); | +++++++++++++++++++++ + ``` I've removed this suggestion and added suggestions to call `new` (for `Option<NonZeroT>` <- `T` case) or `new` and `unwrap` (for `NonZeroT` <- `T` case): ```text error[E0308]: mismatched types --> ./t.rs:7:35 | 7 | let _: std::num::NonZeroU64 = 1; | -------------------- ^ expected struct `NonZeroU64`, found integer | | | expected due to this | help: Consider calling `NonZeroU64::new` | 7 | let _: std::num::NonZeroU64 = NonZeroU64::new(1).unwrap(); | ++++++++++++++++ ++++++++++ error[E0308]: mismatched types --> ./t.rs:8:43 | 8 | 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` | 8 | let _: Option<std::num::NonZeroU64> = NonZeroU64::new(1); | ++++++++++++++++ + ``` r? `@compiler-errors`
2 parents 19932a5 + 5bd88df commit e6a100b

File tree

7 files changed

+207
-6
lines changed

7 files changed

+207
-6
lines changed

compiler/rustc_span/src/symbol.rs

+10
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,16 @@ symbols! {
223223
LintPass,
224224
Mutex,
225225
N,
226+
NonZeroI128,
227+
NonZeroI16,
228+
NonZeroI32,
229+
NonZeroI64,
230+
NonZeroI8,
231+
NonZeroU128,
232+
NonZeroU16,
233+
NonZeroU32,
234+
NonZeroU64,
235+
NonZeroU8,
226236
None,
227237
Ok,
228238
Option,

compiler/rustc_typeck/src/check/demand.rs

+73-6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3333
self.annotate_expected_due_to_let_ty(err, expr, error);
3434
self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr);
3535
self.suggest_compatible_variants(err, expr, expected, expr_ty);
36+
self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty);
3637
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
3738
return;
3839
}
@@ -347,14 +348,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
347348
}
348349
}
349350

350-
let compatible_variants: Vec<String> = expected_adt
351+
let compatible_variants: Vec<(String, Option<String>)> = expected_adt
351352
.variants()
352353
.iter()
353354
.filter(|variant| {
354355
variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
355356
})
356357
.filter_map(|variant| {
357358
let sole_field = &variant.fields[0];
359+
360+
let field_is_local = sole_field.did.is_local();
361+
let field_is_accessible =
362+
sole_field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx);
363+
364+
if !field_is_local && !field_is_accessible {
365+
return None;
366+
}
367+
368+
let note_about_variant_field_privacy = (field_is_local && !field_is_accessible)
369+
.then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)"));
370+
358371
let sole_field_ty = sole_field.ty(self.tcx, substs);
359372
if self.can_coerce(expr_ty, sole_field_ty) {
360373
let variant_path =
@@ -363,9 +376,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
363376
if let Some(path) = variant_path.strip_prefix("std::prelude::")
364377
&& let Some((_, path)) = path.split_once("::")
365378
{
366-
return Some(path.to_string());
379+
return Some((path.to_string(), note_about_variant_field_privacy));
367380
}
368-
Some(variant_path)
381+
Some((variant_path, note_about_variant_field_privacy))
369382
} else {
370383
None
371384
}
@@ -379,10 +392,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
379392

380393
match &compatible_variants[..] {
381394
[] => { /* No variants to format */ }
382-
[variant] => {
395+
[(variant, note)] => {
383396
// Just a single matching variant.
384397
err.multipart_suggestion_verbose(
385-
&format!("try wrapping the expression in `{variant}`"),
398+
&format!(
399+
"try wrapping the expression in `{variant}`{note}",
400+
note = note.as_deref().unwrap_or("")
401+
),
386402
vec![
387403
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
388404
(expr.span.shrink_to_hi(), ")".to_string()),
@@ -397,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
397413
"try wrapping the expression in a variant of `{}`",
398414
self.tcx.def_path_str(expected_adt.did())
399415
),
400-
compatible_variants.into_iter().map(|variant| {
416+
compatible_variants.into_iter().map(|(variant, _)| {
401417
vec![
402418
(expr.span.shrink_to_lo(), format!("{prefix}{variant}(")),
403419
(expr.span.shrink_to_hi(), ")".to_string()),
@@ -410,6 +426,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
410426
}
411427
}
412428

429+
fn suggest_non_zero_new_unwrap(
430+
&self,
431+
err: &mut Diagnostic,
432+
expr: &hir::Expr<'_>,
433+
expected: Ty<'tcx>,
434+
expr_ty: Ty<'tcx>,
435+
) {
436+
let tcx = self.tcx;
437+
let (adt, unwrap) = match expected.kind() {
438+
// In case Option<NonZero*> is wanted, but * is provided, suggest calling new
439+
ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => {
440+
// Unwrap option
441+
let ty::Adt(adt, _) = substs.type_at(0).kind() else { return };
442+
443+
(adt, "")
444+
}
445+
// In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types
446+
ty::Adt(adt, _) => (adt, ".unwrap()"),
447+
_ => return,
448+
};
449+
450+
let map = [
451+
(sym::NonZeroU8, tcx.types.u8),
452+
(sym::NonZeroU16, tcx.types.u16),
453+
(sym::NonZeroU32, tcx.types.u32),
454+
(sym::NonZeroU64, tcx.types.u64),
455+
(sym::NonZeroU128, tcx.types.u128),
456+
(sym::NonZeroI8, tcx.types.i8),
457+
(sym::NonZeroI16, tcx.types.i16),
458+
(sym::NonZeroI32, tcx.types.i32),
459+
(sym::NonZeroI64, tcx.types.i64),
460+
(sym::NonZeroI128, tcx.types.i128),
461+
];
462+
463+
let Some((s, _)) = map
464+
.iter()
465+
.find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t))
466+
else { return };
467+
468+
let path = self.tcx.def_path_str(adt.non_enum_variant().def_id);
469+
470+
err.multipart_suggestion(
471+
format!("consider calling `{s}::new`"),
472+
vec![
473+
(expr.span.shrink_to_lo(), format!("{path}::new(")),
474+
(expr.span.shrink_to_hi(), format!("){unwrap}")),
475+
],
476+
Applicability::MaybeIncorrect,
477+
);
478+
}
479+
413480
pub fn get_conversion_methods(
414481
&self,
415482
span: Span,

library/core/src/num/nonzero.rs

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ macro_rules! nonzero_integers {
3939
#[repr(transparent)]
4040
#[rustc_layout_scalar_valid_range_start(1)]
4141
#[rustc_nonnull_optimization_guaranteed]
42+
#[rustc_diagnostic_item = stringify!($Ty)]
4243
pub struct $Ty($Int);
4344

4445
impl $Ty {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
let _: std::num::NonZeroU64 = 1;
3+
//~^ ERROR mismatched types
4+
//~| HELP consider calling `NonZeroU64::new`
5+
6+
let _: Option<std::num::NonZeroU64> = 1;
7+
//~^ ERROR mismatched types
8+
//~| HELP consider calling `NonZeroU64::new`
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/non_zero_assigned_something.rs:2:35
3+
|
4+
LL | let _: std::num::NonZeroU64 = 1;
5+
| -------------------- ^ expected struct `NonZeroU64`, found integer
6+
| |
7+
| expected due to this
8+
|
9+
help: consider calling `NonZeroU64::new`
10+
|
11+
LL | let _: std::num::NonZeroU64 = NonZeroU64::new(1).unwrap();
12+
| ++++++++++++++++ ++++++++++
13+
14+
error[E0308]: mismatched types
15+
--> $DIR/non_zero_assigned_something.rs:6:43
16+
|
17+
LL | let _: Option<std::num::NonZeroU64> = 1;
18+
| ---------------------------- ^ expected enum `Option`, found integer
19+
| |
20+
| expected due to this
21+
|
22+
= note: expected enum `Option<NonZeroU64>`
23+
found type `{integer}`
24+
help: consider calling `NonZeroU64::new`
25+
|
26+
LL | let _: Option<std::num::NonZeroU64> = NonZeroU64::new(1);
27+
| ++++++++++++++++ +
28+
29+
error: aborting due to 2 previous errors
30+
31+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
mod inner {
2+
pub struct Wrapper<T>(T);
3+
}
4+
5+
fn needs_wrapper(t: inner::Wrapper<i32>) {}
6+
fn needs_wrapping(t: std::num::Wrapping<i32>) {}
7+
fn needs_ready(t: std::future::Ready<i32>) {}
8+
9+
fn main() {
10+
// Suggest wrapping expression because type is local
11+
// and its privacy can be easily changed
12+
needs_wrapper(0);
13+
//~^ ERROR mismatched types
14+
//~| HELP try wrapping the expression in `inner::Wrapper`
15+
16+
// Suggest wrapping expression because field is accessible
17+
needs_wrapping(0);
18+
//~^ ERROR mismatched types
19+
//~| HELP try wrapping the expression in `std::num::Wrapping`
20+
21+
// Do not suggest wrapping expression
22+
needs_ready(Some(0));
23+
//~^ ERROR mismatched types
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/wrap-suggestion-privacy.rs:12:19
3+
|
4+
LL | needs_wrapper(0);
5+
| ------------- ^ expected struct `Wrapper`, found integer
6+
| |
7+
| arguments to this function are incorrect
8+
|
9+
= note: expected struct `Wrapper<i32>`
10+
found type `{integer}`
11+
note: function defined here
12+
--> $DIR/wrap-suggestion-privacy.rs:5:4
13+
|
14+
LL | fn needs_wrapper(t: inner::Wrapper<i32>) {}
15+
| ^^^^^^^^^^^^^ ----------------------
16+
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)
17+
|
18+
LL | needs_wrapper(inner::Wrapper(0));
19+
| +++++++++++++++ +
20+
21+
error[E0308]: mismatched types
22+
--> $DIR/wrap-suggestion-privacy.rs:17:20
23+
|
24+
LL | needs_wrapping(0);
25+
| -------------- ^ expected struct `Wrapping`, found integer
26+
| |
27+
| arguments to this function are incorrect
28+
|
29+
= note: expected struct `Wrapping<i32>`
30+
found type `{integer}`
31+
note: function defined here
32+
--> $DIR/wrap-suggestion-privacy.rs:6:4
33+
|
34+
LL | fn needs_wrapping(t: std::num::Wrapping<i32>) {}
35+
| ^^^^^^^^^^^^^^ --------------------------
36+
help: try wrapping the expression in `std::num::Wrapping`
37+
|
38+
LL | needs_wrapping(std::num::Wrapping(0));
39+
| +++++++++++++++++++ +
40+
41+
error[E0308]: mismatched types
42+
--> $DIR/wrap-suggestion-privacy.rs:22:17
43+
|
44+
LL | needs_ready(Some(0));
45+
| ----------- ^^^^^^^ expected struct `std::future::Ready`, found enum `Option`
46+
| |
47+
| arguments to this function are incorrect
48+
|
49+
= note: expected struct `std::future::Ready<i32>`
50+
found enum `Option<{integer}>`
51+
note: function defined here
52+
--> $DIR/wrap-suggestion-privacy.rs:7:4
53+
|
54+
LL | fn needs_ready(t: std::future::Ready<i32>) {}
55+
| ^^^^^^^^^^^ --------------------------
56+
57+
error: aborting due to 3 previous errors
58+
59+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)