@@ -3365,4 +3365,113 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33653365 err. span_label ( block. span , "this block is missing a tail expression" ) ;
33663366 }
33673367 }
3368+
3369+ pub ( crate ) fn annotate_incorrect_or_expr (
3370+ & self ,
3371+ diag : & mut Diag < ' _ > ,
3372+ expr : & ' tcx hir:: Expr < ' tcx > ,
3373+ expr_ty : Ty < ' tcx > ,
3374+ expected_ty : Ty < ' tcx > ,
3375+ ) {
3376+ if expected_ty != self . tcx . types . bool {
3377+ return ;
3378+ }
3379+ let hir:: Node :: Expr ( & hir:: Expr {
3380+ kind :
3381+ hir:: ExprKind :: Binary (
3382+ hir:: BinOp { node : hir:: BinOpKind :: Or , span : binop_span } ,
3383+ lhs,
3384+ rhs,
3385+ ) ,
3386+ hir_id : parent_hir_id,
3387+ span : full_span,
3388+ ..
3389+ } ) = self . tcx . parent_hir_node ( expr. hir_id )
3390+ else {
3391+ return ;
3392+ } ;
3393+ if rhs. hir_id != expr. hir_id {
3394+ return ;
3395+ }
3396+ let hir:: Expr {
3397+ kind :
3398+ hir:: ExprKind :: Binary ( hir:: BinOp { node : hir:: BinOpKind :: Eq , span : eq_span } , lhs, _) ,
3399+ ..
3400+ } = * lhs
3401+ else {
3402+ return ;
3403+ } ;
3404+ let Some ( lhs_ty) = self . typeck_results . borrow ( ) . expr_ty_opt ( lhs) else {
3405+ return ;
3406+ } ;
3407+ // Coercion here is not totally right, but w/e.
3408+ if !self . can_coerce ( expr_ty, lhs_ty) {
3409+ return ;
3410+ }
3411+
3412+ // Track whether all of the exprs to the right, i.e. `|| a || b || c` are all pattern-like.
3413+ let mut is_literal = rhs. is_approximately_pattern ( ) ;
3414+ // Track the span of the outermost `||` expr.
3415+ let mut full_span = full_span;
3416+
3417+ // Walk up the expr tree gathering up the binop spans of any subsequent `|| a || b || c`.
3418+ let mut expr_hir_id = parent_hir_id;
3419+ let mut binop_spans = vec ! [ binop_span] ;
3420+ while let hir:: Node :: Expr ( & hir:: Expr {
3421+ kind :
3422+ hir:: ExprKind :: Binary (
3423+ hir:: BinOp { node : hir:: BinOpKind :: Or , span : binop_span } ,
3424+ lhs,
3425+ rhs,
3426+ ) ,
3427+ hir_id : parent_hir_id,
3428+ span,
3429+ ..
3430+ } ) = self . tcx . parent_hir_node ( expr_hir_id)
3431+ && lhs. hir_id == expr_hir_id
3432+ {
3433+ binop_spans. push ( binop_span) ;
3434+ expr_hir_id = parent_hir_id;
3435+ full_span = span;
3436+ is_literal |= rhs. is_approximately_pattern ( ) ;
3437+ }
3438+
3439+ // If the type is structural peq, then suggest `matches!(x, a | b | c)`.
3440+ // Otherwise, suggest adding `x == ` to every `||`.
3441+ if is_literal
3442+ // I know this logic may look a bit sketchy, but int vars don't implement
3443+ // `StructuralPeq` b/c they're unconstrained, so just check for those manually.
3444+ && ( self . tcx . lang_items ( ) . structural_peq_trait ( ) . is_some_and ( |structural_peq_def_id| {
3445+ self . type_implements_trait ( structural_peq_def_id, [ lhs_ty] , self . param_env )
3446+ . must_apply_modulo_regions ( )
3447+ } ) || lhs_ty. is_integral ( ) )
3448+ {
3449+ let eq_span = lhs. span . shrink_to_hi ( ) . to ( eq_span) ;
3450+ diag. multipart_suggestion_verbose (
3451+ "use `match!()` to match against multiple values" ,
3452+ [
3453+ ( full_span. shrink_to_lo ( ) , "matches!(" . to_string ( ) ) ,
3454+ ( full_span. shrink_to_hi ( ) , ")" . to_string ( ) ) ,
3455+ ( eq_span, "," . to_string ( ) ) ,
3456+ ]
3457+ . into_iter ( )
3458+ . chain ( binop_spans. into_iter ( ) . map ( |span| ( span, "|" . to_string ( ) ) ) )
3459+ . collect ( ) ,
3460+ Applicability :: MachineApplicable ,
3461+ ) ;
3462+ } else if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = lhs. kind
3463+ && let Res :: Local ( local) = path. res
3464+ {
3465+ let local = self . tcx . hir ( ) . name ( local) ;
3466+ let local_name = format ! ( " {local} ==" ) ;
3467+ diag. multipart_suggestion_verbose (
3468+ "use `match!()` to match against multiple values" ,
3469+ binop_spans
3470+ . into_iter ( )
3471+ . map ( |span| ( span. shrink_to_hi ( ) , local_name. clone ( ) ) )
3472+ . collect ( ) ,
3473+ Applicability :: MachineApplicable ,
3474+ ) ;
3475+ }
3476+ }
33683477}
0 commit comments