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