From 93bfeed513279837781482ce88707cb6a18ef8c1 Mon Sep 17 00:00:00 2001 From: Yukang Date: Tue, 22 Sep 2026 00:18:16 +0800 Subject: [PATCH 1/2] Add regression tests for Option predicate suggestion precedence --- tests/ui/suggestions/option-to-bool.rs | 29 ++++++++++++++- tests/ui/suggestions/option-to-bool.stderr | 43 +++++++++++++++++++++- 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/tests/ui/suggestions/option-to-bool.rs b/tests/ui/suggestions/option-to-bool.rs index bbc5d1d71cb72..b36c28c06ef9d 100644 --- a/tests/ui/suggestions/option-to-bool.rs +++ b/tests/ui/suggestions/option-to-bool.rs @@ -1,7 +1,34 @@ +// An `is_some()` suggestion must apply to the whole expression, preserving its precedence. + fn foo(x: Option) { if true && x {} //~^ ERROR mismatched types //~| HELP use `Option::is_some` to test if the `Option` has a value + + let reference = &x; + if *reference {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if &x {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if Value + Value {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value } -fn main() {} +struct Value; + +impl std::ops::Add for Value { + type Output = Option; + + fn add(self, _: Self) -> Self::Output { + Some(1) + } +} + +fn main() { + foo(Some(1)); +} diff --git a/tests/ui/suggestions/option-to-bool.stderr b/tests/ui/suggestions/option-to-bool.stderr index ab97eae6c5c55..2ee282670d794 100644 --- a/tests/ui/suggestions/option-to-bool.stderr +++ b/tests/ui/suggestions/option-to-bool.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:2:16 + --> $DIR/option-to-bool.rs:4:16 | LL | if true && x {} | ---- ^ expected `bool`, found `Option` @@ -13,6 +13,45 @@ help: use `Option::is_some` to test if the `Option` has a value LL | if true && x.is_some() {} | ++++++++++ -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/option-to-bool.rs:9:8 + | +LL | if *reference {} + | ^^^^^^^^^^ expected `bool`, found `Option` + | + = note: expected type `bool` + found enum `Option` +help: use `Option::is_some` to test if the `Option` has a value + | +LL | if *reference.is_some() {} + | ++++++++++ + +error[E0308]: mismatched types + --> $DIR/option-to-bool.rs:13:8 + | +LL | if &x {} + | ^^ expected `bool`, found `&Option` + | + = note: expected type `bool` + found reference `&Option` +help: use `Option::is_some` to test if the `Option` has a value + | +LL | if &x.is_some() {} + | ++++++++++ + +error[E0308]: mismatched types + --> $DIR/option-to-bool.rs:17:8 + | +LL | if Value + Value {} + | ^^^^^^^^^^^^^ expected `bool`, found `Option` + | + = note: expected type `bool` + found enum `Option` +help: use `Option::is_some` to test if the `Option` has a value + | +LL | if Value + Value.is_some() {} + | ++++++++++ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. From c9412ec1c0873c1ae551ebcf7b9d3ceb077aec9a Mon Sep 17 00:00:00 2001 From: Yukang Date: Wed, 23 Sep 2026 21:32:00 +0800 Subject: [PATCH 2/2] Fix suggestion for Option to bool with proper precedence handling --- .../src/fn_ctxt/suggestions.rs | 14 +++++--- tests/ui/suggestions/option-to-bool.fixed | 35 +++++++++++++++++++ tests/ui/suggestions/option-to-bool.rs | 1 + tests/ui/suggestions/option-to-bool.stderr | 20 +++++------ 4 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 tests/ui/suggestions/option-to-bool.fixed diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 7f8edae4f17c3..62d48af2f3f08 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1692,12 +1692,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } let suggestion = match self.tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!(": {ident}.is_some()"), - None => ".is_some()".to_string(), + Some(ident) => vec![(expr.span.shrink_to_hi(), format!(": {ident}.is_some()"))], + None if self.precedence(expr) < ExprPrecedence::Unambiguous => { + // Apply the method to the whole expression, e.g. `(*value).is_some()`. + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), ").is_some()".to_string()), + ] + } + None => vec![(expr.span.shrink_to_hi(), ".is_some()".to_string())], }; - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), + diag.multipart_suggestion( "use `Option::is_some` to test if the `Option` has a value", suggestion, Applicability::MachineApplicable, diff --git a/tests/ui/suggestions/option-to-bool.fixed b/tests/ui/suggestions/option-to-bool.fixed new file mode 100644 index 0000000000000..b6160a1bb8f15 --- /dev/null +++ b/tests/ui/suggestions/option-to-bool.fixed @@ -0,0 +1,35 @@ +// An `is_some()` suggestion must apply to the whole expression, preserving its precedence. +//@ run-rustfix + +fn foo(x: Option) { + if true && x.is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + let reference = &x; + if (*reference).is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if (&x).is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value + + if (Value + Value).is_some() {} + //~^ ERROR mismatched types + //~| HELP use `Option::is_some` to test if the `Option` has a value +} + +struct Value; + +impl std::ops::Add for Value { + type Output = Option; + + fn add(self, _: Self) -> Self::Output { + Some(1) + } +} + +fn main() { + foo(Some(1)); +} diff --git a/tests/ui/suggestions/option-to-bool.rs b/tests/ui/suggestions/option-to-bool.rs index b36c28c06ef9d..25272b96dfc28 100644 --- a/tests/ui/suggestions/option-to-bool.rs +++ b/tests/ui/suggestions/option-to-bool.rs @@ -1,4 +1,5 @@ // An `is_some()` suggestion must apply to the whole expression, preserving its precedence. +//@ run-rustfix fn foo(x: Option) { if true && x {} diff --git a/tests/ui/suggestions/option-to-bool.stderr b/tests/ui/suggestions/option-to-bool.stderr index 2ee282670d794..dd49b1b2b0ad1 100644 --- a/tests/ui/suggestions/option-to-bool.stderr +++ b/tests/ui/suggestions/option-to-bool.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:4:16 + --> $DIR/option-to-bool.rs:5:16 | LL | if true && x {} | ---- ^ expected `bool`, found `Option` @@ -14,7 +14,7 @@ LL | if true && x.is_some() {} | ++++++++++ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:9:8 + --> $DIR/option-to-bool.rs:10:8 | LL | if *reference {} | ^^^^^^^^^^ expected `bool`, found `Option` @@ -23,11 +23,11 @@ LL | if *reference {} found enum `Option` help: use `Option::is_some` to test if the `Option` has a value | -LL | if *reference.is_some() {} - | ++++++++++ +LL | if (*reference).is_some() {} + | + +++++++++++ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:13:8 + --> $DIR/option-to-bool.rs:14:8 | LL | if &x {} | ^^ expected `bool`, found `&Option` @@ -36,11 +36,11 @@ LL | if &x {} found reference `&Option` help: use `Option::is_some` to test if the `Option` has a value | -LL | if &x.is_some() {} - | ++++++++++ +LL | if (&x).is_some() {} + | + +++++++++++ error[E0308]: mismatched types - --> $DIR/option-to-bool.rs:17:8 + --> $DIR/option-to-bool.rs:18:8 | LL | if Value + Value {} | ^^^^^^^^^^^^^ expected `bool`, found `Option` @@ -49,8 +49,8 @@ LL | if Value + Value {} found enum `Option` help: use `Option::is_some` to test if the `Option` has a value | -LL | if Value + Value.is_some() {} - | ++++++++++ +LL | if (Value + Value).is_some() {} + | + +++++++++++ error: aborting due to 4 previous errors