1use hir::{ExprKind, Node, is_range_literal};
2use rustc_abi::{Integer, Size};
3use rustc_hir::{HirId, attrs};
4use rustc_middle::ty::Ty;
5use rustc_middle::ty::layout::IntegerExt;
6use rustc_middle::{bug, ty};
7use rustc_span::Span;
8use {rustc_ast as ast, rustc_hir as hir};
9
10use crate::LateContext;
11use crate::context::LintContext;
12use crate::lints::{
13 OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub,
14 OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
15 RangeEndpointOutOfRange, SurrogateCharCast, TooLargeCharCast, UseInclusiveRange,
16};
17use crate::types::{OVERFLOWING_LITERALS, TypeLimits};
18
19fn lint_overflowing_range_endpoint<'tcx>(
22 cx: &LateContext<'tcx>,
23 lit: &hir::Lit,
24 lit_val: u128,
25 max: u128,
26 hir_id: HirId,
27 lit_span: Span,
28 ty: &str,
29) -> bool {
30 let (hir_id, span) = if let Node::Expr(par_expr) = cx.tcx.parent_hir_node(hir_id)
32 && let ExprKind::Cast(_, _) = par_expr.kind
33 {
34 (par_expr.hir_id, par_expr.span)
35 } else {
36 (hir_id, lit_span)
37 };
38
39 let Node::ExprField(field) = cx.tcx.parent_hir_node(hir_id) else {
42 return false;
43 };
44 let Node::Expr(struct_expr) = cx.tcx.parent_hir_node(field.hir_id) else {
45 return false;
46 };
47 if !is_range_literal(struct_expr) {
48 return false;
49 };
50 let ExprKind::Struct(_, [start, end], _) = &struct_expr.kind else {
51 return false;
52 };
53
54 if !(end.expr.hir_id == hir_id && lit_val - 1 == max) {
58 return false;
59 };
60
61 use rustc_ast::{LitIntType, LitKind};
62 let suffix = match lit.node {
63 LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
64 LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
65 LitKind::Int(_, LitIntType::Unsuffixed) => "",
66 _ => bug!(),
67 };
68
69 let sub_sugg = if span.lo() == lit_span.lo() {
70 let Ok(start) = cx.sess().source_map().span_to_snippet(start.span) else {
71 return false;
72 };
73 UseInclusiveRange::WithoutParen {
74 sugg: struct_expr.span.shrink_to_lo().to(lit_span.shrink_to_hi()),
75 start,
76 literal: lit_val - 1,
77 suffix,
78 }
79 } else {
80 UseInclusiveRange::WithParen {
81 eq_sugg: span.shrink_to_lo(),
82 lit_sugg: lit_span,
83 literal: lit_val - 1,
84 suffix,
85 }
86 };
87
88 cx.emit_span_lint(
89 OVERFLOWING_LITERALS,
90 struct_expr.span,
91 RangeEndpointOutOfRange { ty, sub: sub_sugg },
92 );
93
94 true
97}
98
99pub(crate) fn int_ty_range(int_ty: ty::IntTy) -> (i128, i128) {
102 match int_ty {
103 ty::IntTy::Isize => (i64::MIN.into(), i64::MAX.into()),
104 ty::IntTy::I8 => (i8::MIN.into(), i8::MAX.into()),
105 ty::IntTy::I16 => (i16::MIN.into(), i16::MAX.into()),
106 ty::IntTy::I32 => (i32::MIN.into(), i32::MAX.into()),
107 ty::IntTy::I64 => (i64::MIN.into(), i64::MAX.into()),
108 ty::IntTy::I128 => (i128::MIN, i128::MAX),
109 }
110}
111
112pub(crate) fn uint_ty_range(uint_ty: ty::UintTy) -> (u128, u128) {
113 let max = match uint_ty {
114 ty::UintTy::Usize => u64::MAX.into(),
115 ty::UintTy::U8 => u8::MAX.into(),
116 ty::UintTy::U16 => u16::MAX.into(),
117 ty::UintTy::U32 => u32::MAX.into(),
118 ty::UintTy::U64 => u64::MAX.into(),
119 ty::UintTy::U128 => u128::MAX,
120 };
121 (0, max)
122}
123
124fn get_bin_hex_repr(cx: &LateContext<'_>, lit: &hir::Lit) -> Option<String> {
125 let src = cx.sess().source_map().span_to_snippet(lit.span).ok()?;
126 let firstch = src.chars().next()?;
127
128 if firstch == '0' {
129 match src.chars().nth(1) {
130 Some('x' | 'b') => return Some(src),
131 _ => return None,
132 }
133 }
134
135 None
136}
137
138fn report_bin_hex_error(
139 cx: &LateContext<'_>,
140 hir_id: HirId,
141 span: Span,
142 ty: attrs::IntType,
143 size: Size,
144 repr_str: String,
145 val: u128,
146 negative: bool,
147) {
148 let (t, actually) = match ty {
149 attrs::IntType::SignedInt(t) => {
150 let actually = if negative { -(size.sign_extend(val)) } else { size.sign_extend(val) };
151 (t.name_str(), actually.to_string())
152 }
153 attrs::IntType::UnsignedInt(t) => {
154 let actually = size.truncate(val);
155 (t.name_str(), actually.to_string())
156 }
157 };
158 let sign =
159 if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
160 let sub = get_type_suggestion(cx.typeck_results().node_type(hir_id), val, negative).map(
161 |suggestion_ty| {
162 if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
163 let (sans_suffix, _) = repr_str.split_at(pos);
164 OverflowingBinHexSub::Suggestion { span, suggestion_ty, sans_suffix }
165 } else {
166 OverflowingBinHexSub::Help { suggestion_ty }
167 }
168 },
169 );
170 let sign_bit_sub = (!negative)
171 .then(|| {
172 let ty::Int(int_ty) = cx.typeck_results().node_type(hir_id).kind() else {
173 return None;
174 };
175
176 let Some(bit_width) = int_ty.bit_width() else {
177 return None; };
179
180 if (val & (1 << (bit_width - 1))) == 0 {
182 return None;
183 }
184
185 let lit_no_suffix =
186 if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
187 repr_str.split_at(pos).0
188 } else {
189 &repr_str
190 };
191
192 Some(OverflowingBinHexSignBitSub {
193 span,
194 lit_no_suffix,
195 negative_val: actually.clone(),
196 int_ty: int_ty.name_str(),
197 uint_ty: Integer::fit_unsigned(val).uint_ty_str(),
198 })
199 })
200 .flatten();
201
202 cx.emit_span_lint(
203 OVERFLOWING_LITERALS,
204 span,
205 OverflowingBinHex {
206 ty: t,
207 lit: repr_str.clone(),
208 dec: val,
209 actually,
210 sign,
211 sub,
212 sign_bit_sub,
213 },
214 )
215}
216
217fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static str> {
221 match t.kind() {
222 ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None,
223 ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()),
224 ty::Int(_) => {
225 let signed = literal_to_i128(val, negative).map(Integer::fit_signed);
226 if negative {
227 signed.map(Integer::int_ty_str)
228 } else {
229 let unsigned = Integer::fit_unsigned(val);
230 Some(if let Some(signed) = signed {
231 if unsigned.size() < signed.size() {
232 unsigned.uint_ty_str()
233 } else {
234 signed.int_ty_str()
235 }
236 } else {
237 unsigned.uint_ty_str()
238 })
239 }
240 }
241 _ => None,
242 }
243}
244
245fn literal_to_i128(val: u128, negative: bool) -> Option<i128> {
246 if negative {
247 (val <= i128::MAX as u128 + 1).then(|| val.wrapping_neg() as i128)
248 } else {
249 val.try_into().ok()
250 }
251}
252
253fn lint_int_literal<'tcx>(
254 cx: &LateContext<'tcx>,
255 type_limits: &TypeLimits,
256 hir_id: HirId,
257 span: Span,
258 lit: &hir::Lit,
259 t: ty::IntTy,
260 v: u128,
261) {
262 let int_type = t.normalize(cx.sess().target.pointer_width);
263 let (min, max) = int_ty_range(int_type);
264 let max = max as u128;
265 let negative = type_limits.negated_expr_id == Some(hir_id);
266
267 if (negative && v > max + 1) || (!negative && v > max) {
270 if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
271 report_bin_hex_error(
272 cx,
273 hir_id,
274 span,
275 attrs::IntType::SignedInt(t),
276 Integer::from_int_ty(cx, t).size(),
277 repr_str,
278 v,
279 negative,
280 );
281 return;
282 }
283
284 if lint_overflowing_range_endpoint(cx, lit, v, max, hir_id, span, t.name_str()) {
285 return;
287 }
288
289 let span = if negative { type_limits.negated_expr_span.unwrap() } else { span };
290 let lit = cx
291 .sess()
292 .source_map()
293 .span_to_snippet(span)
294 .unwrap_or_else(|_| if negative { format!("-{v}") } else { v.to_string() });
295 let help = get_type_suggestion(cx.typeck_results().node_type(hir_id), v, negative)
296 .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
297
298 cx.emit_span_lint(
299 OVERFLOWING_LITERALS,
300 span,
301 OverflowingInt { ty: t.name_str(), lit, min, max, help },
302 );
303 }
304}
305
306fn lint_uint_literal<'tcx>(
307 cx: &LateContext<'tcx>,
308 hir_id: HirId,
309 span: Span,
310 lit: &hir::Lit,
311 t: ty::UintTy,
312) {
313 let uint_type = t.normalize(cx.sess().target.pointer_width);
314 let (min, max) = uint_ty_range(uint_type);
315 let lit_val: u128 = match lit.node {
316 ast::LitKind::Byte(_v) => return,
318 ast::LitKind::Int(v, _) => v.get(),
319 _ => bug!(),
320 };
321
322 if lit_val < min || lit_val > max {
323 if let Node::Expr(par_e) = cx.tcx.parent_hir_node(hir_id) {
324 match par_e.kind {
325 hir::ExprKind::Cast(..) => {
326 if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
327 if lit_val > 0x10FFFF {
328 cx.emit_span_lint(
329 OVERFLOWING_LITERALS,
330 par_e.span,
331 TooLargeCharCast { literal: lit_val },
332 );
333 } else if (0xD800..=0xDFFF).contains(&lit_val) {
334 cx.emit_span_lint(
335 OVERFLOWING_LITERALS,
336 par_e.span,
337 SurrogateCharCast { literal: lit_val },
338 );
339 } else {
340 cx.emit_span_lint(
341 OVERFLOWING_LITERALS,
342 par_e.span,
343 OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
344 );
345 }
346 return;
347 }
348 }
349 _ => {}
350 }
351 }
352 if lint_overflowing_range_endpoint(cx, lit, lit_val, max, hir_id, span, t.name_str()) {
353 return;
355 }
356 if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
357 report_bin_hex_error(
358 cx,
359 hir_id,
360 span,
361 attrs::IntType::UnsignedInt(t),
362 Integer::from_uint_ty(cx, t).size(),
363 repr_str,
364 lit_val,
365 false,
366 );
367 return;
368 }
369 cx.emit_span_lint(
370 OVERFLOWING_LITERALS,
371 span,
372 OverflowingUInt {
373 ty: t.name_str(),
374 lit: cx
375 .sess()
376 .source_map()
377 .span_to_snippet(lit.span)
378 .unwrap_or_else(|_| lit_val.to_string()),
379 min,
380 max,
381 },
382 );
383 }
384}
385
386pub(crate) fn lint_literal<'tcx>(
387 cx: &LateContext<'tcx>,
388 type_limits: &TypeLimits,
389 hir_id: HirId,
390 span: Span,
391 lit: &hir::Lit,
392 negated: bool,
393) {
394 match *cx.typeck_results().node_type(hir_id).kind() {
395 ty::Int(t) => {
396 match lit.node {
397 ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => {
398 lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get())
399 }
400 _ => bug!(),
401 };
402 }
403 ty::Uint(t) => {
404 assert!(!negated);
405 lint_uint_literal(cx, hir_id, span, lit, t)
406 }
407 ty::Float(t) => {
408 let (is_infinite, sym) = match lit.node {
409 ast::LitKind::Float(v, _) => match t {
410 ty::FloatTy::F16 => (Ok(false), v),
413 ty::FloatTy::F32 => (v.as_str().parse().map(f32::is_infinite), v),
414 ty::FloatTy::F64 => (v.as_str().parse().map(f64::is_infinite), v),
415 ty::FloatTy::F128 => (Ok(false), v),
416 },
417 _ => bug!(),
418 };
419 if is_infinite == Ok(true) {
420 cx.emit_span_lint(
421 OVERFLOWING_LITERALS,
422 span,
423 OverflowingLiteral {
424 ty: t.name_str(),
425 lit: cx
426 .sess()
427 .source_map()
428 .span_to_snippet(lit.span)
429 .unwrap_or_else(|_| sym.to_string()),
430 },
431 );
432 }
433 }
434 _ => {}
435 }
436}