11use super :: MUT_RANGE_BOUND ;
2- use clippy_utils:: diagnostics:: span_lint ;
3- use clippy_utils:: { higher, path_to_local} ;
2+ use clippy_utils:: diagnostics:: span_lint_and_note ;
3+ use clippy_utils:: { get_enclosing_block , higher, path_to_local} ;
44use if_chain:: if_chain;
5- use rustc_hir:: { BindingAnnotation , Expr , HirId , Node , PatKind } ;
5+ use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
6+ use rustc_hir:: { BindingAnnotation , Expr , ExprKind , HirId , Node , PatKind } ;
67use rustc_infer:: infer:: TyCtxtInferExt ;
78use rustc_lint:: LateContext ;
9+ use rustc_middle:: hir:: map:: Map ;
810use rustc_middle:: { mir:: FakeReadCause , ty} ;
911use rustc_span:: source_map:: Span ;
1012use rustc_typeck:: expr_use_visitor:: { Delegate , ExprUseVisitor , PlaceBase , PlaceWithHirId } ;
1113
1214pub ( super ) fn check ( cx : & LateContext < ' _ > , arg : & Expr < ' _ > , body : & Expr < ' _ > ) {
13- if let Some ( higher:: Range {
14- start : Some ( start) ,
15- end : Some ( end) ,
16- ..
17- } ) = higher:: Range :: hir ( arg)
18- {
19- let mut_ids = vec ! [ check_for_mutability( cx, start) , check_for_mutability( cx, end) ] ;
20- if mut_ids[ 0 ] . is_some ( ) || mut_ids[ 1 ] . is_some ( ) {
21- let ( span_low, span_high) = check_for_mutation ( cx, body, & mut_ids) ;
15+ if_chain ! {
16+ if let Some ( higher:: Range {
17+ start: Some ( start) ,
18+ end: Some ( end) ,
19+ ..
20+ } ) = higher:: Range :: hir( arg) ;
21+ let ( mut_id_start, mut_id_end) = ( check_for_mutability( cx, start) , check_for_mutability( cx, end) ) ;
22+ if mut_id_start. is_some( ) || mut_id_end. is_some( ) ;
23+ then {
24+ let ( span_low, span_high) = check_for_mutation( cx, body, mut_id_start, mut_id_end) ;
2225 mut_warn_with_span( cx, span_low) ;
2326 mut_warn_with_span( cx, span_high) ;
2427 }
@@ -27,11 +30,13 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
2730
2831fn mut_warn_with_span ( cx : & LateContext < ' _ > , span : Option < Span > ) {
2932 if let Some ( sp) = span {
30- span_lint (
33+ span_lint_and_note (
3134 cx,
3235 MUT_RANGE_BOUND ,
3336 sp,
34- "attempt to mutate range bound within loop; note that the range of the loop is unchanged" ,
37+ "attempt to mutate range bound within loop" ,
38+ None ,
39+ "the range of the loop is unchanged" ,
3540 ) ;
3641 }
3742}
@@ -51,12 +56,13 @@ fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId>
5156fn check_for_mutation < ' tcx > (
5257 cx : & LateContext < ' tcx > ,
5358 body : & Expr < ' _ > ,
54- bound_ids : & [ Option < HirId > ] ,
59+ bound_id_start : Option < HirId > ,
60+ bound_id_end : Option < HirId > ,
5561) -> ( Option < Span > , Option < Span > ) {
5662 let mut delegate = MutatePairDelegate {
5763 cx,
58- hir_id_low : bound_ids [ 0 ] ,
59- hir_id_high : bound_ids [ 1 ] ,
64+ hir_id_low : bound_id_start ,
65+ hir_id_high : bound_id_end ,
6066 span_low : None ,
6167 span_high : None ,
6268 } ;
@@ -70,6 +76,7 @@ fn check_for_mutation<'tcx>(
7076 )
7177 . walk_expr ( body) ;
7278 } ) ;
79+
7380 delegate. mutation_span ( )
7481}
7582
@@ -87,10 +94,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
8794 fn borrow ( & mut self , cmt : & PlaceWithHirId < ' tcx > , diag_expr_id : HirId , bk : ty:: BorrowKind ) {
8895 if let ty:: BorrowKind :: MutBorrow = bk {
8996 if let PlaceBase :: Local ( id) = cmt. place . base {
90- if Some ( id) == self . hir_id_low {
97+ if Some ( id) == self . hir_id_low && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
9198 self . span_low = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
9299 }
93- if Some ( id) == self . hir_id_high {
100+ if Some ( id) == self . hir_id_high && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
94101 self . span_high = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
95102 }
96103 }
@@ -99,10 +106,10 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
99106
100107 fn mutate ( & mut self , cmt : & PlaceWithHirId < ' tcx > , diag_expr_id : HirId ) {
101108 if let PlaceBase :: Local ( id) = cmt. place . base {
102- if Some ( id) == self . hir_id_low {
109+ if Some ( id) == self . hir_id_low && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
103110 self . span_low = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
104111 }
105- if Some ( id) == self . hir_id_high {
112+ if Some ( id) == self . hir_id_high && ! BreakAfterExprVisitor :: is_found ( self . cx , diag_expr_id ) {
106113 self . span_high = Some ( self . cx . tcx . hir ( ) . span ( diag_expr_id) ) ;
107114 }
108115 }
@@ -116,3 +123,52 @@ impl MutatePairDelegate<'_, '_> {
116123 ( self . span_low , self . span_high )
117124 }
118125}
126+
127+ struct BreakAfterExprVisitor {
128+ hir_id : HirId ,
129+ past_expr : bool ,
130+ past_candidate : bool ,
131+ break_after_expr : bool ,
132+ }
133+
134+ impl BreakAfterExprVisitor {
135+ pub fn is_found ( cx : & LateContext < ' _ > , hir_id : HirId ) -> bool {
136+ let mut visitor = BreakAfterExprVisitor {
137+ hir_id,
138+ past_expr : false ,
139+ past_candidate : false ,
140+ break_after_expr : false ,
141+ } ;
142+
143+ get_enclosing_block ( cx, hir_id) . map_or ( false , |block| {
144+ visitor. visit_block ( block) ;
145+ visitor. break_after_expr
146+ } )
147+ }
148+ }
149+
150+ impl intravisit:: Visitor < ' tcx > for BreakAfterExprVisitor {
151+ type Map = Map < ' tcx > ;
152+
153+ fn nested_visit_map ( & mut self ) -> NestedVisitorMap < Self :: Map > {
154+ NestedVisitorMap :: None
155+ }
156+
157+ fn visit_expr ( & mut self , expr : & ' tcx Expr < ' tcx > ) {
158+ if self . past_candidate {
159+ return ;
160+ }
161+
162+ if expr. hir_id == self . hir_id {
163+ self . past_expr = true ;
164+ } else if self . past_expr {
165+ if matches ! ( & expr. kind, ExprKind :: Break ( ..) ) {
166+ self . break_after_expr = true ;
167+ }
168+
169+ self . past_candidate = true ;
170+ } else {
171+ intravisit:: walk_expr ( self , expr) ;
172+ }
173+ }
174+ }
0 commit comments