@@ -58,6 +58,61 @@ impl TypeLimits {
5858 }
5959}
6060
61+ /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint.
62+ /// Returns `true` iff the lint was overridden.
63+ fn lint_overflowing_range_endpoint < ' a , ' tcx > (
64+ cx : & LateContext < ' a , ' tcx > ,
65+ lit : & ast:: Lit ,
66+ lit_val : u128 ,
67+ max : u128 ,
68+ expr : & ' tcx hir:: Expr ,
69+ parent_expr : & ' tcx hir:: Expr ,
70+ ty : impl std:: fmt:: Debug ,
71+ ) -> bool {
72+ // We only want to handle exclusive (`..`) ranges,
73+ // which are represented as `ExprKind::Struct`.
74+ if let ExprKind :: Struct ( _, eps, _) = & parent_expr. node {
75+ debug_assert_eq ! ( eps. len( ) , 2 ) ;
76+ // We can suggest using an inclusive range
77+ // (`..=`) instead only if it is the `end` that is
78+ // overflowing and only by 1.
79+ if eps[ 1 ] . expr . hir_id == expr. hir_id && lit_val - 1 == max {
80+ let mut err = cx. struct_span_lint (
81+ OVERFLOWING_LITERALS ,
82+ parent_expr. span ,
83+ & format ! ( "range endpoint is out of range for `{:?}`" , ty) ,
84+ ) ;
85+ if let Ok ( start) = cx. sess ( ) . source_map ( ) . span_to_snippet ( eps[ 0 ] . span ) {
86+ use ast:: { LitKind , LitIntType } ;
87+ // We need to preserve the literal's suffix,
88+ // as it may determine typing information.
89+ let suffix = match lit. node {
90+ LitKind :: Int ( _, LitIntType :: Signed ( s) ) => format ! ( "{}" , s) ,
91+ LitKind :: Int ( _, LitIntType :: Unsigned ( s) ) => format ! ( "{}" , s) ,
92+ LitKind :: Int ( _, LitIntType :: Unsuffixed ) => "" . to_owned ( ) ,
93+ _ => bug ! ( ) ,
94+ } ;
95+ let suggestion = format ! (
96+ "{}..={}{}" ,
97+ start,
98+ lit_val - 1 ,
99+ suffix,
100+ ) ;
101+ err. span_suggestion (
102+ parent_expr. span ,
103+ & "use an inclusive range instead" ,
104+ suggestion,
105+ Applicability :: MachineApplicable ,
106+ ) ;
107+ err. emit ( ) ;
108+ return true ;
109+ }
110+ }
111+ }
112+
113+ false
114+ }
115+
61116impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for TypeLimits {
62117 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , e : & ' tcx hir:: Expr ) {
63118 match e. node {
@@ -103,6 +158,27 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
103158 ) ;
104159 return ;
105160 }
161+
162+ let par_id = cx. tcx . hir ( ) . get_parent_node_by_hir_id ( e. hir_id ) ;
163+ if let Node :: Expr ( par_e) = cx. tcx . hir ( ) . get_by_hir_id ( par_id) {
164+ if let hir:: ExprKind :: Struct ( ..) = par_e. node {
165+ if is_range_literal ( cx. sess ( ) , par_e)
166+ && lint_overflowing_range_endpoint (
167+ cx,
168+ lit,
169+ v,
170+ max,
171+ e,
172+ par_e,
173+ t,
174+ )
175+ {
176+ // The overflowing literal lint was overridden.
177+ return ;
178+ }
179+ }
180+ }
181+
106182 cx. span_lint (
107183 OVERFLOWING_LITERALS ,
108184 e. span ,
@@ -150,61 +226,19 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for TypeLimits {
150226 }
151227 hir:: ExprKind :: Struct ( ..)
152228 if is_range_literal ( cx. sess ( ) , parent_expr) => {
153- // We only want to handle exclusive (`..`) ranges,
154- // which are represented as `ExprKind::Struct`.
155- if let ExprKind :: Struct ( _, eps, _) = & parent_expr. node {
156- debug_assert_eq ! ( eps. len( ) , 2 ) ;
157- // We can suggest using an inclusive range
158- // (`..=`) instead only if it is the `end` that is
159- // overflowing and only by 1.
160- if eps[ 1 ] . expr . hir_id == e. hir_id
161- && lit_val - 1 == max
162- {
163- let mut err = cx. struct_span_lint (
164- OVERFLOWING_LITERALS ,
165- parent_expr. span ,
166- & format ! (
167- "range endpoint is out of range \
168- for `{:?}`",
169- t,
170- ) ,
171- ) ;
172- if let Ok ( start) = cx. sess ( ) . source_map ( )
173- . span_to_snippet ( eps[ 0 ] . span )
174- {
175- use ast:: { LitKind :: * , LitIntType } ;
176- // We need to preserve the literal's suffix,
177- // as it may determine typing information.
178- let suffix = match lit. node {
179- Int ( _, LitIntType :: Signed ( s) ) => {
180- format ! ( "{}" , s)
181- }
182- Int ( _, LitIntType :: Unsigned ( s) ) => {
183- format ! ( "{}" , s)
184- }
185- Int ( _, LitIntType :: Unsuffixed ) => {
186- "" . to_owned ( )
187- }
188- _ => bug ! ( ) ,
189- } ;
190- let suggestion = format ! (
191- "{}..={}{}" ,
192- start,
193- lit_val - 1 ,
194- suffix,
195- ) ;
196- err. span_suggestion (
197- parent_expr. span ,
198- & "use an inclusive range instead" ,
199- suggestion,
200- Applicability :: MachineApplicable ,
201- ) ;
202- err. emit ( ) ;
203- return ;
204- }
205- }
229+ if lint_overflowing_range_endpoint (
230+ cx,
231+ lit,
232+ lit_val,
233+ max,
234+ e,
235+ parent_expr,
236+ t,
237+ ) {
238+ // The overflowing literal lint was overridden.
239+ return ;
206240 }
207- }
241+ }
208242 _ => { }
209243 }
210244 }
0 commit comments