Skip to content

Commit ec406c9

Browse files
sourcefrogclaude
andcommitted
Mutate NonZero<T> generic return types
Support the generic NonZero<T> form (stabilized in Rust 1.79) in addition to the existing NonZeroUsize, NonZeroI32, etc. type-specific matching. When T is a known unsigned type, generate 1; when signed, generate 1 and -1; when unknown, assume signed. Closes #595 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9c6b890 commit ec406c9

File tree

1 file changed

+79
-1
lines changed

1 file changed

+79
-1
lines changed

src/fnvalue.rs

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,33 @@ fn type_replacements(type_: &Type, error_exprs: &[Expr]) -> impl Iterator<Item =
5656
]
5757
} else if path_is_nonzero_unsigned(path) {
5858
vec![quote! { 1.try_into().unwrap() }]
59+
} else if let Some(inner_type) = match_first_type_arg(path, "NonZero") {
60+
// NonZero<T> generic form (stabilized in Rust 1.79)
61+
if let Type::Path(syn::TypePath {
62+
path: inner_path, ..
63+
}) = inner_type
64+
{
65+
if path_is_unsigned(inner_path) {
66+
vec![quote! { 1.try_into().unwrap() }]
67+
} else if path_is_signed(inner_path) {
68+
vec![
69+
quote! { 1.try_into().unwrap() },
70+
quote! { (-1).try_into().unwrap() },
71+
]
72+
} else {
73+
// Unknown T, assume it could be signed
74+
vec![
75+
quote! { 1.try_into().unwrap() },
76+
quote! { (-1).try_into().unwrap() },
77+
]
78+
}
79+
} else {
80+
// T is not a simple path, assume it could be signed
81+
vec![
82+
quote! { 1.try_into().unwrap() },
83+
quote! { (-1).try_into().unwrap() },
84+
]
85+
}
5986
} else if path_is_float(path) {
6087
vec![quote! { 0.0 }, quote! { 1.0 }, quote! { -1.0 }]
6188
} else if path_ends_with(path, "Result") {
@@ -393,7 +420,6 @@ fn path_is_nonzero_signed(path: &Path) -> bool {
393420
}
394421

395422
fn path_is_nonzero_unsigned(path: &Path) -> bool {
396-
// TODO: Also NonZero<usize> etc.
397423
if let Some(l) = path.segments.last().map(|p| p.ident.to_string()) {
398424
matches!(
399425
l.as_str(),
@@ -494,6 +520,58 @@ mod test {
494520
);
495521
}
496522

523+
#[test]
524+
fn nonzero_generic_unsigned_replacements() {
525+
check_replacements(
526+
&parse_quote! { -> NonZero<u32> },
527+
&[],
528+
&["1.try_into().unwrap()"],
529+
);
530+
531+
check_replacements(
532+
&parse_quote! { -> NonZero<usize> },
533+
&[],
534+
&["1.try_into().unwrap()"],
535+
);
536+
537+
check_replacements(
538+
&parse_quote! { -> std::num::NonZero<u8> },
539+
&[],
540+
&["1.try_into().unwrap()"],
541+
);
542+
}
543+
544+
#[test]
545+
fn nonzero_generic_signed_replacements() {
546+
check_replacements(
547+
&parse_quote! { -> NonZero<i32> },
548+
&[],
549+
&["1.try_into().unwrap()", "(-1).try_into().unwrap()"],
550+
);
551+
552+
check_replacements(
553+
&parse_quote! { -> NonZero<isize> },
554+
&[],
555+
&["1.try_into().unwrap()", "(-1).try_into().unwrap()"],
556+
);
557+
558+
check_replacements(
559+
&parse_quote! { -> std::num::NonZero<i64> },
560+
&[],
561+
&["1.try_into().unwrap()", "(-1).try_into().unwrap()"],
562+
);
563+
}
564+
565+
#[test]
566+
fn nonzero_generic_unknown_type_replacements() {
567+
// When T is not a recognized integer type, assume it could be signed.
568+
check_replacements(
569+
&parse_quote! { -> NonZero<T> },
570+
&[],
571+
&["1.try_into().unwrap()", "(-1).try_into().unwrap()"],
572+
);
573+
}
574+
497575
#[test]
498576
fn unit_replacement() {
499577
check_replacements(&parse_quote! { -> () }, &[], &["()"]);

0 commit comments

Comments
 (0)