@@ -3,8 +3,11 @@ use crate::{LateContext, LateLintPass, LintContext};
3
3
use hir:: { Expr , Pat } ;
4
4
use rustc_errors:: Applicability ;
5
5
use rustc_hir as hir;
6
- use rustc_middle:: ty;
7
- use rustc_span:: sym;
6
+ use rustc_infer:: traits:: TraitEngine ;
7
+ use rustc_infer:: { infer:: TyCtxtInferExt , traits:: ObligationCause } ;
8
+ use rustc_middle:: ty:: { self , List } ;
9
+ use rustc_span:: { sym, Span } ;
10
+ use rustc_trait_selection:: traits:: TraitEngineExt ;
8
11
9
12
declare_lint ! {
10
13
/// ### What it does
@@ -58,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
58
61
59
62
let ty = cx. typeck_results ( ) . expr_ty ( arg) ;
60
63
61
- let ty:: Adt ( adt, _ ) = ty. kind ( ) else { return } ;
64
+ let & ty:: Adt ( adt, substs ) = ty. kind ( ) else { return } ;
62
65
63
66
let ( article, ty, var) = match adt. did ( ) {
64
67
did if cx. tcx . is_diagnostic_item ( sym:: Option , did) => ( "an" , "Option" , "Some" ) ,
@@ -96,6 +99,15 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopOverFallibles {
96
99
) ;
97
100
}
98
101
102
+ if suggest_question_mark ( cx, adt, substs, expr. span ) {
103
+ warn. span_suggestion (
104
+ arg. span . shrink_to_hi ( ) ,
105
+ "consider unwrapping the `Result` with `?` to iterate over its contents" ,
106
+ "?" ,
107
+ Applicability :: MaybeIncorrect ,
108
+ ) ;
109
+ }
110
+
99
111
warn. multipart_suggestion_verbose (
100
112
"consider using `if let` to clear intent" ,
101
113
vec ! [
@@ -140,3 +152,53 @@ fn extract_iterator_next_call<'tcx>(
140
152
return None
141
153
}
142
154
}
155
+
156
+ fn suggest_question_mark < ' tcx > (
157
+ cx : & LateContext < ' tcx > ,
158
+ adt : ty:: AdtDef < ' tcx > ,
159
+ substs : & List < ty:: GenericArg < ' tcx > > ,
160
+ span : Span ,
161
+ ) -> bool {
162
+ let Some ( body_id) = cx. enclosing_body else { return false } ;
163
+ let Some ( into_iterator_did) = cx. tcx . get_diagnostic_item ( sym:: IntoIterator ) else { return false } ;
164
+
165
+ if !cx. tcx . is_diagnostic_item ( sym:: Result , adt. did ( ) ) {
166
+ return false ;
167
+ }
168
+
169
+ // Check that the function/closure/constant we are in has a `Result` type.
170
+ // Otherwise suggesting using `?` may not be a good idea.
171
+ {
172
+ let ty = cx. typeck_results ( ) . expr_ty ( & cx. tcx . hir ( ) . body ( body_id) . value ) ;
173
+ let ty:: Adt ( ret_adt, ..) = ty. kind ( ) else { return false } ;
174
+ if !cx. tcx . is_diagnostic_item ( sym:: Result , ret_adt. did ( ) ) {
175
+ return false ;
176
+ }
177
+ }
178
+
179
+ let ty = substs. type_at ( 0 ) ;
180
+ let is_iterator = cx. tcx . infer_ctxt ( ) . enter ( |infcx| {
181
+ let mut fulfill_cx = <dyn TraitEngine < ' _ > >:: new ( infcx. tcx ) ;
182
+
183
+ let cause = ObligationCause :: new (
184
+ span,
185
+ body_id. hir_id ,
186
+ rustc_infer:: traits:: ObligationCauseCode :: MiscObligation ,
187
+ ) ;
188
+ fulfill_cx. register_bound (
189
+ & infcx,
190
+ ty:: ParamEnv :: empty ( ) ,
191
+ // Erase any region vids from the type, which may not be resolved
192
+ infcx. tcx . erase_regions ( ty) ,
193
+ into_iterator_did,
194
+ cause,
195
+ ) ;
196
+
197
+ // Select all, including ambiguous predicates
198
+ let errors = fulfill_cx. select_all_or_error ( & infcx) ;
199
+
200
+ errors. is_empty ( )
201
+ } ) ;
202
+
203
+ is_iterator
204
+ }
0 commit comments