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