1
1
use rustc:: lint:: * ;
2
2
use rustc:: hir:: * ;
3
- use utils:: { is_integer_literal, paths, snippet, span_lint} ;
3
+ use syntax:: ast:: RangeLimits ;
4
+ use syntax:: codemap:: Spanned ;
5
+ use utils:: { is_integer_literal, paths, snippet, span_lint, span_lint_and_then} ;
4
6
use utils:: { get_trait_def_id, higher, implements_trait} ;
7
+ use utils:: sugg:: Sugg ;
5
8
6
9
/// **What it does:** Checks for calling `.step_by(0)` on iterators,
7
10
/// which never terminates.
@@ -38,16 +41,57 @@ declare_lint! {
38
41
"zipping iterator with a range when `enumerate()` would do"
39
42
}
40
43
44
+ /// **What it does:** Checks for exclusive ranges where 1 is added to the
45
+ /// upper bound, e.g. `x..(y+1)`.
46
+ ///
47
+ /// **Why is this bad?** The code is more readable with an inclusive range
48
+ /// like `x..=y`.
49
+ ///
50
+ /// **Known problems:** None.
51
+ ///
52
+ /// **Example:**
53
+ /// ```rust
54
+ /// for x..(y+1) { .. }
55
+ /// ```
56
+ declare_lint ! {
57
+ pub RANGE_PLUS_ONE ,
58
+ Allow ,
59
+ "`x..(y+1)` reads better as `x..=y`"
60
+ }
61
+
62
+ /// **What it does:** Checks for inclusive ranges where 1 is subtracted from
63
+ /// the upper bound, e.g. `x..=(y-1)`.
64
+ ///
65
+ /// **Why is this bad?** The code is more readable with an exclusive range
66
+ /// like `x..y`.
67
+ ///
68
+ /// **Known problems:** None.
69
+ ///
70
+ /// **Example:**
71
+ /// ```rust
72
+ /// for x..=(y-1) { .. }
73
+ /// ```
74
+ declare_lint ! {
75
+ pub RANGE_MINUS_ONE ,
76
+ Warn ,
77
+ "`x..=(y-1)` reads better as `x..y`"
78
+ }
79
+
41
80
#[ derive( Copy , Clone ) ]
42
- pub struct StepByZero ;
81
+ pub struct Pass ;
43
82
44
- impl LintPass for StepByZero {
83
+ impl LintPass for Pass {
45
84
fn get_lints ( & self ) -> LintArray {
46
- lint_array ! ( ITERATOR_STEP_BY_ZERO , RANGE_ZIP_WITH_LEN )
85
+ lint_array ! (
86
+ ITERATOR_STEP_BY_ZERO ,
87
+ RANGE_ZIP_WITH_LEN ,
88
+ RANGE_PLUS_ONE ,
89
+ RANGE_MINUS_ONE
90
+ )
47
91
}
48
92
}
49
93
50
- impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for StepByZero {
94
+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Pass {
51
95
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
52
96
if let ExprMethodCall ( ref path, _, ref args) = expr. node {
53
97
let name = path. name . as_str ( ) ;
@@ -92,6 +136,46 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for StepByZero {
92
136
} }
93
137
}
94
138
}
139
+
140
+ // exclusive range plus one: x..(y+1)
141
+ if_let_chain ! { [
142
+ let Some ( higher:: Range { start, end: Some ( end) , limits: RangeLimits :: HalfOpen } ) = higher:: range( expr) ,
143
+ let Some ( y) = y_plus_one( end) ,
144
+ ] , {
145
+ span_lint_and_then(
146
+ cx,
147
+ RANGE_PLUS_ONE ,
148
+ expr. span,
149
+ "an inclusive range would be more readable" ,
150
+ |db| {
151
+ let start = start. map_or( "" . to_owned( ) , |x| Sugg :: hir( cx, x, "x" ) . to_string( ) ) ;
152
+ let end = Sugg :: hir( cx, y, "y" ) ;
153
+ db. span_suggestion( expr. span,
154
+ "use" ,
155
+ format!( "{}..={}" , start, end) ) ;
156
+ } ,
157
+ ) ;
158
+ } }
159
+
160
+ // inclusive range minus one: x..=(y-1)
161
+ if_let_chain ! { [
162
+ let Some ( higher:: Range { start, end: Some ( end) , limits: RangeLimits :: Closed } ) = higher:: range( expr) ,
163
+ let Some ( y) = y_minus_one( end) ,
164
+ ] , {
165
+ span_lint_and_then(
166
+ cx,
167
+ RANGE_MINUS_ONE ,
168
+ expr. span,
169
+ "an exclusive range would be more readable" ,
170
+ |db| {
171
+ let start = start. map_or( "" . to_owned( ) , |x| Sugg :: hir( cx, x, "x" ) . to_string( ) ) ;
172
+ let end = Sugg :: hir( cx, y, "y" ) ;
173
+ db. span_suggestion( expr. span,
174
+ "use" ,
175
+ format!( "{}..{}" , start, end) ) ;
176
+ } ,
177
+ ) ;
178
+ } }
95
179
}
96
180
}
97
181
@@ -102,3 +186,25 @@ fn has_step_by(cx: &LateContext, expr: &Expr) -> bool {
102
186
103
187
get_trait_def_id ( cx, & paths:: ITERATOR ) . map_or ( false , |iterator_trait| implements_trait ( cx, ty, iterator_trait, & [ ] ) )
104
188
}
189
+
190
+ fn y_plus_one ( expr : & Expr ) -> Option < & Expr > {
191
+ match expr. node {
192
+ ExprBinary ( Spanned { node : BiAdd , .. } , ref lhs, ref rhs) => {
193
+ if is_integer_literal ( lhs, 1 ) {
194
+ Some ( rhs)
195
+ } else if is_integer_literal ( rhs, 1 ) {
196
+ Some ( lhs)
197
+ } else {
198
+ None
199
+ }
200
+ } ,
201
+ _ => None ,
202
+ }
203
+ }
204
+
205
+ fn y_minus_one ( expr : & Expr ) -> Option < & Expr > {
206
+ match expr. node {
207
+ ExprBinary ( Spanned { node : BiSub , .. } , ref lhs, ref rhs) if is_integer_literal ( rhs, 1 ) => Some ( lhs) ,
208
+ _ => None ,
209
+ }
210
+ }
0 commit comments