1- use clippy_utils:: diagnostics:: span_lint_and_sugg ;
1+ use clippy_utils:: diagnostics:: span_lint_and_then ;
22use clippy_utils:: res:: { MaybeDef , MaybeQPath , MaybeResPath , MaybeTypeckRes } ;
33use clippy_utils:: source:: snippet_with_applicability;
44use clippy_utils:: { DefinedTy , ExprUseNode , expr_use_ctxt, peel_blocks, strip_pat_refs} ;
55use rustc_ast:: ast;
66use rustc_data_structures:: packed:: Pu128 ;
7- use rustc_errors:: Applicability ;
7+ use rustc_errors:: { Applicability , Diag } ;
88use rustc_hir as hir;
99use rustc_hir:: PatKind ;
1010use rustc_hir:: def:: { DefKind , Res } ;
@@ -59,6 +59,34 @@ struct Replacement {
5959 method_name : & ' static str ,
6060 has_args : bool ,
6161 has_generic_return : bool ,
62+ is_short_circuiting : bool ,
63+ }
64+
65+ impl Replacement {
66+ fn default_applicability ( & self ) -> Applicability {
67+ if self . is_short_circuiting {
68+ Applicability :: MaybeIncorrect
69+ } else {
70+ Applicability :: MachineApplicable
71+ }
72+ }
73+
74+ fn maybe_add_note ( & self , diag : & mut Diag < ' _ , ( ) > ) {
75+ if self . is_short_circuiting {
76+ diag. note ( format ! (
77+ "the `{}` method is short circuiting and may change the program semantics if the iterator has side effects" ,
78+ self . method_name
79+ ) ) ;
80+ }
81+ }
82+
83+ fn maybe_turbofish ( & self , ty : Ty < ' _ > ) -> String {
84+ if self . has_generic_return {
85+ format ! ( "::<{ty}>" )
86+ } else {
87+ String :: new ( )
88+ }
89+ }
6290}
6391
6492fn check_fold_with_op (
@@ -86,32 +114,29 @@ fn check_fold_with_op(
86114 && left_expr. res_local_id ( ) == Some ( first_arg_id)
87115 && ( replacement. has_args || right_expr. res_local_id ( ) == Some ( second_arg_id) )
88116 {
89- let mut applicability = Applicability :: MachineApplicable ;
90-
91- let turbofish = if replacement. has_generic_return {
92- format ! ( "::<{}>" , cx. typeck_results( ) . expr_ty_adjusted( right_expr) . peel_refs( ) )
93- } else {
94- String :: new ( )
95- } ;
96-
97- let sugg = if replacement. has_args {
98- format ! (
99- "{method}{turbofish}(|{second_arg_ident}| {r})" ,
100- method = replacement. method_name,
101- r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
102- )
103- } else {
104- format ! ( "{method}{turbofish}()" , method = replacement. method_name)
105- } ;
106-
107- span_lint_and_sugg (
117+ let span = fold_span. with_hi ( expr. span . hi ( ) ) ;
118+ span_lint_and_then (
108119 cx,
109120 UNNECESSARY_FOLD ,
110- fold_span . with_hi ( expr . span . hi ( ) ) ,
121+ span,
111122 "this `.fold` can be written more succinctly using another method" ,
112- "try" ,
113- sugg,
114- applicability,
123+ |diag| {
124+ let mut applicability = replacement. default_applicability ( ) ;
125+ let turbofish =
126+ replacement. maybe_turbofish ( cx. typeck_results ( ) . expr_ty_adjusted ( right_expr) . peel_refs ( ) ) ;
127+ let sugg = if replacement. has_args {
128+ format ! (
129+ "{method}{turbofish}(|{second_arg_ident}| {r})" ,
130+ method = replacement. method_name,
131+ r = snippet_with_applicability( cx, right_expr. span, "EXPR" , & mut applicability) ,
132+ )
133+ } else {
134+ format ! ( "{method}{turbofish}()" , method = replacement. method_name)
135+ } ;
136+
137+ diag. span_suggestion ( span, "try" , sugg, applicability) ;
138+ replacement. maybe_add_note ( diag) ;
139+ } ,
115140 ) ;
116141 return true ;
117142 }
@@ -131,22 +156,25 @@ fn check_fold_with_method(
131156 // Check if the function belongs to the operator
132157 && cx. tcx . is_diagnostic_item ( method, fn_did)
133158 {
134- let applicability = Applicability :: MachineApplicable ;
135-
136- let turbofish = if replacement. has_generic_return {
137- format ! ( "::<{}>" , cx. typeck_results( ) . expr_ty( expr) )
138- } else {
139- String :: new ( )
140- } ;
141-
142- span_lint_and_sugg (
159+ let span = fold_span. with_hi ( expr. span . hi ( ) ) ;
160+ span_lint_and_then (
143161 cx,
144162 UNNECESSARY_FOLD ,
145- fold_span . with_hi ( expr . span . hi ( ) ) ,
163+ span,
146164 "this `.fold` can be written more succinctly using another method" ,
147- "try" ,
148- format ! ( "{method}{turbofish}()" , method = replacement. method_name) ,
149- applicability,
165+ |diag| {
166+ diag. span_suggestion (
167+ span,
168+ "try" ,
169+ format ! (
170+ "{method}{turbofish}()" ,
171+ method = replacement. method_name,
172+ turbofish = replacement. maybe_turbofish( cx. typeck_results( ) . expr_ty( expr) )
173+ ) ,
174+ replacement. default_applicability ( ) ,
175+ ) ;
176+ replacement. maybe_add_note ( diag) ;
177+ } ,
150178 ) ;
151179 }
152180}
@@ -171,6 +199,7 @@ pub(super) fn check<'tcx>(
171199 method_name : "any" ,
172200 has_args : true ,
173201 has_generic_return : false ,
202+ is_short_circuiting : true ,
174203 } ;
175204 check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Or , replacement) ;
176205 } ,
@@ -179,6 +208,7 @@ pub(super) fn check<'tcx>(
179208 method_name : "all" ,
180209 has_args : true ,
181210 has_generic_return : false ,
211+ is_short_circuiting : true ,
182212 } ;
183213 check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: And , replacement) ;
184214 } ,
@@ -187,6 +217,7 @@ pub(super) fn check<'tcx>(
187217 method_name : "sum" ,
188218 has_args : false ,
189219 has_generic_return : needs_turbofish ( cx, expr) ,
220+ is_short_circuiting : false ,
190221 } ;
191222 if !check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Add , replacement) {
192223 check_fold_with_method ( cx, expr, acc, fold_span, sym:: add, replacement) ;
@@ -197,6 +228,7 @@ pub(super) fn check<'tcx>(
197228 method_name : "product" ,
198229 has_args : false ,
199230 has_generic_return : needs_turbofish ( cx, expr) ,
231+ is_short_circuiting : false ,
200232 } ;
201233 if !check_fold_with_op ( cx, expr, acc, fold_span, hir:: BinOpKind :: Mul , replacement) {
202234 check_fold_with_method ( cx, expr, acc, fold_span, sym:: mul, replacement) ;
0 commit comments